Skip to content

fix(auth): no-issue: fall back to cleartext token storage when Web Crypto unavailable (backport 2.59)#10153

Open
passcod wants to merge 1 commit into
release/2.59from
fix/iti-web-crypto-fallback-2.59
Open

fix(auth): no-issue: fall back to cleartext token storage when Web Crypto unavailable (backport 2.59)#10153
passcod wants to merge 1 commit into
release/2.59from
fix/iti-web-crypto-fallback-2.59

Conversation

@passcod

@passcod passcod commented Jun 26, 2026

Copy link
Copy Markdown
Member

Changes

🤖 Backport of #9942 to release/2.59.

On plain HTTP connections (e.g. local-network access to an ITI), window.crypto.subtle is unavailable because browsers only expose Web Crypto in secure contexts. writePersistedAuthToken was throwing, which caused setFacilityId to silently swallow the error and leave facilityId unset — making the facility selector appear even with a single facility, then showing the crypto error on submit.

Fix: fall back to storing the token as cleartext when crypto.subtle is absent. The session is already transmitted unencrypted over HTTP, so cleartext localStorage is no worse; a console.warn makes the condition visible. Encrypted storage continues to work as before on HTTPS.

This is needed for deployments where secure origins are not possible and security is ensured through other means.

Auto-Deploy

  • Deploy
Options
  • Artillery load test
  • Seed from closest snapshot
  • Generate fake data
  • More data (20Gi)
  • No facility servers (central-only)
  • No sync (facility tasks scaled to zero)
  • AMD64 architecture (default is arm64)
  • Skip mobile build
  • Always build mobile
  • Stay up for 8 hours
  • Stay up for 24 hours
  • Stay up (no TTL)
  • Build images only (don't deploy)
  • Pause this deploy

Tests

  • Run E2E tests
  • Run DAST scan

Review Hero

  • Run Review Hero
  • Auto-fix review suggestions Wait for Review Hero to finish, resolve any comments you disagree with or want to fix manually, then check this to auto-fix the rest.
  • Auto-fix CI failures Check this to auto-fix lint errors, test failures, and other CI issues.
  • Auto-merge upstream Check this to merge the base branch into this PR, with AI conflict resolution if needed.
  • Save suppressions Check this to capture 👎 reactions on Review Hero comments as suppression rules in .github/review-hero/suppressions.yml. Also runs automatically at the end of any auto-fix run.

Remember to...

  • ...write or update tests
  • ...add UI screenshots and testing notes to the Linear issue
  • ...add any manual upgrade steps to the Linear issue
  • ...update the config reference, settings reference, or any relevant runbook(s)
  • ...call out additions or changes to config files for the deployment team to take note of

…ypto unavailable

Web Crypto (crypto.subtle) is only available in secure contexts (HTTPS or
localhost). On plain HTTP local-network connections to ITIs, writePersistedAuthToken
was throwing, which caused setFacilityId to silently swallow the error and leave
facilityId unset — surfacing the facility selector even with a single facility, then
showing the crypto error on submit.

Fall back to cleartext localStorage when crypto.subtle is absent. The session is
already transmitted unencrypted over HTTP so cleartext storage is no worse; a
console.warn is emitted to make the condition visible.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@review-hero

review-hero Bot commented Jun 26, 2026

Copy link
Copy Markdown

🦸 Review Hero (could not post inline comments — showing here instead)

packages/api-client/src/browserTokenStorage.ts:197

[Security] suggestion

The cleartext fallback stores the raw bearer token indefinitely in localStorage with no expiry marker. On HTTP, a stolen device or shared browser profile exposes a valid token for its entire server-side lifetime. The comment argues this is 'no worse' than HTTP network transmission, but localStorage persists across sessions and is accessible to any same-origin script (e.g. a browser extension or injected third-party script), whereas network interception requires active monitoring during the session. Consider adding a sessionStorage fallback instead of localStorage when Web Crypto is unavailable — tokens would still survive page reloads but not be left behind after the browser closes, reducing the persistence window.


packages/api-client/src/browserTokenStorage.ts:230

[Security] suggestion

The legacy-to-encrypted migration in readPersistedAuthToken (lines 230-236) fires on every read of any non-encrypted token. With the new cleartext fallback, when Web Crypto is unavailable writePersistedAuthToken no longer throws — it silently succeeds by writing cleartext again. Because a cleartext token has no tamanuenc prefix, isEncryptedStoredValue(raw) is always false for it, so every single call to readPersistedAuthToken will call writePersistedAuthToken and emit another console.warn. The fix is to guard the migration attempt with the same !globalThis.crypto?.subtle check used in writePersistedAuthToken, or skip the upgrade attempt entirely when crypto is unavailable, so the warn fires once on write rather than on every read.

@review-hero

review-hero Bot commented Jun 26, 2026

Copy link
Copy Markdown

🦸 Review Hero Summary
6 agents reviewed this PR | 0 critical | 2 suggestions | 0 nitpicks | Filtering: consensus 3 voters

Local fix prompt (copy to your coding agent)

Fix these issues identified on the pull request. One commit per issue fixed.


packages/api-client/src/browserTokenStorage.ts:197: The cleartext fallback stores the raw bearer token indefinitely in localStorage with no expiry marker. On HTTP, a stolen device or shared browser profile exposes a valid token for its entire server-side lifetime. The comment argues this is 'no worse' than HTTP network transmission, but localStorage persists across sessions and is accessible to any same-origin script (e.g. a browser extension or injected third-party script), whereas network interception requires active monitoring during the session. Consider adding a sessionStorage fallback instead of localStorage when Web Crypto is unavailable — tokens would still survive page reloads but not be left behind after the browser closes, reducing the persistence window.


packages/api-client/src/browserTokenStorage.ts:230: The legacy-to-encrypted migration in readPersistedAuthToken (lines 230-236) fires on every read of any non-encrypted token. With the new cleartext fallback, when Web Crypto is unavailable writePersistedAuthToken no longer throws — it silently succeeds by writing cleartext again. Because a cleartext token has no tamanuenc prefix, isEncryptedStoredValue(raw) is always false for it, so every single call to readPersistedAuthToken will call writePersistedAuthToken and emit another console.warn. The fix is to guard the migration attempt with the same !globalThis.crypto?.subtle check used in writePersistedAuthToken, or skip the upgrade attempt entirely when crypto is unavailable, so the warn fires once on write rather than on every read.

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