Skip to content

Redesign auth: Auth0-managed sessions replace self-minted JWTs#257

Draft
baely wants to merge 6 commits into
mainfrom
auth0-token-redesign
Draft

Redesign auth: Auth0-managed sessions replace self-minted JWTs#257
baely wants to merge 6 commits into
mainfrom
auth0-token-redesign

Conversation

@baely

@baely baely commented Jul 9, 2026

Copy link
Copy Markdown
Owner

What

Auth0 was only consulted at the moment of login — the callback verified the ID token, then minted a home-grown 30-day HS256 JWT signed with a static SIGNING_KEY. Nothing could refresh or revoke a login, and logout just dropped the cookie.

Auth0 now manages the login token lifecycle:

  • Login requests the offline_access scope, so Auth0 issues a rotating refresh token alongside the ID token.
  • The cookie holds only an opaque session ID; the Auth0 token set lives server-side in Redis (internal/auth/session.go) with a 30-day absolute lifetime, matching Auth0's default refresh-token expiry.
  • When the token expires, the session refreshes against Auth0 — revoking a user's grant at Auth0 ends their session at the next refresh. Concurrent refreshes single-flight through a Redis lock so the rotated token isn't burned; refreshed ID tokens are verified and subject-checked, and calls to Auth0 carry a 10s timeout.
  • Logout revokes the refresh token at Auth0's revocation endpoint and deletes the session, on both /logout and /auth/logout.
  • The HS256 JWT machinery, SIGNING_KEY config and golang-jwt dependency are removed. Session cookies are now Secure on https and SameSite=Lax. API secrets and the mobile native exchange are unchanged.

Deployment notes

  • The prod Auth0 application needs the Refresh Token grant enabled (rotation recommended). Without it the app still works but sessions end when the access token expires (a warning is logged). The local OIDC mock now sets AllowOfflineAccess: true.
  • Everyone re-logs in once — old JWT cookies no longer resolve and are cleared cleanly.
  • SIGNING_KEY is no longer read. The deploy workflows still inject it (harmless); left for a later clean-up since fix: stats-collector secret names + render last-updated in Melbourne time #246 has pending workflow changes.

Verification

  • go build, go vet, full go test ./... green; new unit tests cover session round-trip, refresh + rotation, refused refresh, concurrent-refresh coordination and logout revocation against a fake Auth0 tenant.
  • Verified live end-to-end against an isolated OIDC mock with a deliberately short 10-second token lifetime: browser login → Redis session with refresh token → real refresh-token grants with rotation persisted → logout produced "Token revocation complete" on the provider and deleted the session. Stale/garbage cookies are cleared and bounce to the hero page.

🤖 Generated with Claude Code

baely and others added 2 commits July 9, 2026 22:27
Previously Auth0 was only consulted at the moment of login; the app then
minted its own 30-day HS256 JWT with a static SIGNING_KEY. Nothing could
revoke or refresh a login once issued.

Auth0 now manages the login token lifecycle:

- Login requests the offline_access scope, so Auth0 issues a rotating
  refresh token alongside the ID token.
- The cookie holds only an opaque session ID; the Auth0 token set lives
  server-side in Redis (30-day absolute lifetime, matching Auth0's
  default refresh-token expiry).
- When the token expires, the session is refreshed against Auth0 -- so
  revoking a user's grant at Auth0 ends their session at the next
  refresh. Concurrent refreshes single-flight through a Redis lock so
  rotation isn't burned.
- Logout revokes the refresh token at Auth0's revocation endpoint and
  deletes the session, rather than just dropping the cookie.
- The HS256 JWT machinery, SIGNING_KEY config and golang-jwt dependency
  are removed. Session cookies are now Secure on https and SameSite=Lax.

The local OIDC mock now allows offline access so the dev login flow can
exercise refresh and revocation. Existing logins are invalidated on
deploy (old JWT cookies no longer resolve); users log in once to get a
session. The Auth0 application must have the Refresh Token grant
enabled, with rotation recommended.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
baely and others added 2 commits July 16, 2026 00:04
Re-expresses the cookie rename (#263) in session terms: sessions are
issued under the env-specific 'user' name, legacy __session cookies are
still accepted for lookup, and MigrateLegacyCookie re-issues the same
session ID under the current name instead of minting a JWT. Logout
resolves the session from either cookie name before revoking at Auth0.
The JWT machinery main still carried stays deleted, along with the
golang-jwt dependency.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The GitHub update-branch merge resolved the same conflicts by keeping
the __session-only cookie name and the golang-jwt dependency, which
would regress the #263 rename on merge to main. This keeps the
resolution that preserves #263: env-specific 'user' cookie, legacy
__session accepted and migrated, JWT machinery and dependency removed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* origin/auth0-token-redesign:
baely and others added 2 commits July 16, 2026 00:43
Refresh failures are now classified: a definitive rejection (4xx, e.g.
invalid_grant for a revoked grant) still ends the session immediately,
but timeouts, connection failures and 5xx responses serve the stale
session within a 24-hour grace window, retrying the refresh at most once
a minute so an outage doesn't add the Auth0 timeout to every request.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Beta runs at debug level so per-request auth timings are visible:
auth resolution in the middleware (total added latency per request),
session store get/set, and the Auth0 refresh call.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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