Skip to content

fix(tv-native): sign in again when the server rejects the session (401) - #71

Open
beckness wants to merge 1 commit into
Quixomatic:mainfrom
beckness:fix/tvos-401-recovery
Open

beckness wants to merge 1 commit into
Quixomatic:mainfrom
beckness:fix/tvos-401-recovery

Conversation

@beckness

Copy link
Copy Markdown
Contributor

What & why

This is the 401 auto-logout trimmed from #63 for scope ("happy to look at any of them as a separate PR"), brought back as its own focused change. It was reworked against current main, not cherry-picked.

The defect: once the stored bearer stops being honoured (Better Auth sessions expire after 30 days unused, or get revoked), tv-native never recognises the 401. The guide falls through to the empty state, "No channels yet — Once channels are set up on your server, they'll appear here", which reads as an empty server rather than a dead session. The only way out is to go to Account → Sign out by hand. tv-web and tv-tauri already sign out on a guide 401 (guide-screen.tsx); tv-native is the odd one out. We hit the same failure on Roku (#70), where the sidebar bug made it a hard trap. Apple TV's sidebar doesn't have that bug.

This change:

  • api.ts: a 401 clears the token, keeps the server URL and replaces to /login. Network failures, 5xx and 403 never touch the credentials. A 401 is ignored if the token or the server changed after the request went out, so a late response from before a fresh login, or from the previous server, can't sign the user out.
  • auth.ts: token writes go through one queue, and invalidateToken re-checks the rejected token inside it. Without the queue there's a window between the outer check and the async delete where a concurrent login can be wiped. fix(tvos): persist server configuration and sessions #63 listed this serialization as a separate trimmed item, but the stale-401 guard depends on it. The mutation check below shows that.
  • Tests: 11 bun regressions, run with pnpm --filter tv-native test. Each phase runs in a fresh process; only storage, native modules and network are substituted, and auth / cred-store / session-storage / api are the production modules. They're adapted from fix(tvos): persist server configuration and sessions #63's suite: that suite mocked the dropped Swift module and the removed startup.ts / session-route.ts, and now mocks RN Settings. It's scoped to the 401 lifecycle.

Not included (the other pieces trimmed from #63): the startup retry screen and the storage-error UI on sign-out / Change Server. The #63 CI workflow isn't included either.

Area

  • Server / admin (backend, channels, scheduling, AI lineup, imports)
  • A TV / player client (Apple TV, Fire TV, Android TV, Roku, webOS, tv-web)
  • Desktop server app (Windows / macOS / Linux)
  • Docs / website (getairwave.tv)
  • CI / tooling

Testing

  • pnpm --filter tv-native test: 11 pass, 0 fail. Covered:
    • a valid response keeps the session;
    • a 401 clears the token and cookie, keeps the server and routes to /login, and that survives a restart;
    • signing in again after a 401 restores the guide;
    • network failure, 503 and 403 each keep the session;
    • a late 401 after a newer login, and a late 401 from the previous server, are both ignored;
    • invalidation can't erase a concurrent login, in either ordering;
    • iOS/Android (SecureStore) behave the same.
  • Against untouched main: 4 fail (the 401 and concurrency cases) and 7 pass (the guards). The suite catches the defect.
  • Mutation check: the outer stale-token check alone, without the write queue, fails both concurrency tests.
  • pnpm --filter tv-native check-types: clean.
  • Not verified on a device. I don't have an Apple TV build or TestFlight. The router.replace("/login") from inside the request path, and the real 401 → Login transition, still need a check on hardware.

Checklist

  • Typechecks / builds locally (pnpm check-types, relevant build)
  • I did not bump versions or tag (maintainer handles releases)
  • No secrets, certs, or .env values committed

Credit to @SHRIVES93: the design and the original suite are from #63.

🤖 Generated with Claude Code

When the stored bearer stops being honoured (sessions expire after 30 days
unused, or get revoked), tv-native has no 401 handling: the guide shows
"No channels yet — once channels are set up on your server…", which reads
as an empty server, and the only way out is Account → Sign out by hand.
tv-web and tv-tauri already sign out on a guide 401.

This is the 401 piece trimmed from Quixomatic#63 for scope, brought back on its own:

- api.ts: a 401 clears the token (server URL kept) and routes to /login.
  Network / 5xx / 403 failures never touch it, and a 401 is ignored if the
  token or server has changed since the request went out.
- auth.ts: token writes go through one queue; invalidateToken re-checks the
  rejected token inside it, so a late 401 can't delete a login that landed
  between the check and the delete.
- 11 bun regressions (fresh process per phase, only storage/native/network
  substituted), adapted from Quixomatic#63's suite to the current RN Settings store.

Co-authored-by: SHRIVES93 <75397028+SHRIVES93@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Oct 17, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated
airwave-site Skipped Skipped Oct 17, 2026 9:55pm UTC

@beckness

Copy link
Copy Markdown
Contributor Author

Cross-ref: #70 fixes the same failure, a TV client not recovering once the server rejects its session (401), on tv-roku, where it also left Settings/Account unusable. The two PRs are independently scoped and can be reviewed and merged separately; neither depends on the other.

This branch was previously deployed

1 inactive deployment
Preview — f0b729d5 Deployed Oct 17, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant