Skip to content

feat(auth): unify token lifecycle with coordinated silent refresh across REST, WebSocket, and offline queues - #1150

Merged
RUKAYAT-CODER merged 1 commit into
rinafcode:mainfrom
oladev2026-tech:feat/1142-unified-token-lifecycle
Aug 23, 2026
Merged

feat(auth): unify token lifecycle with coordinated silent refresh across REST, WebSocket, and offline queues#1150
RUKAYAT-CODER merged 1 commit into
rinafcode:mainfrom
oladev2026-tech:feat/1142-unified-token-lifecycle

Conversation

@oladev2026-tech

Copy link
Copy Markdown
Contributor

Closes #1142

Summary

Introduces a single TokenManager that owns the authentication token lifecycle, so the REST client, WebSocket connections, the notification socket and the offline replay queue all obtain their token from one place, share one refresh path, and react to rotation/revocation consistently.

What changed

  • TokenManager (src/lib/auth/tokenManager.ts, new) — the single source of truth:
    • Single-flight refresh: concurrent callers share one in-flight /auth/refresh request, so a burst of 401s collapses into a single network round-trip (no thundering herd).
    • Proactive silent refresh: a background timer refreshes a configurable skew before exp, so long-lived sockets and queued offline operations never carry an about-to-lapse token.
    • Lifecycle events: token:rotated / token:revoked / auth:logout let consumers re-authenticate or tear down instead of each discovering the change on its own failed request.
    • Metrics: auth.refresh_success, auth.refresh_failure, auth.token_rotated, auth.forced_logout.
  • REST (api.ts, apiInterceptors.ts) — the client sources its token from the manager, proactively refreshes before sending, and performs one coordinated refresh + replay on a 401 (guarded against loops). The request interceptor attaches a freshly-refreshed token; the error interceptor hard-logs-out only when the refresh itself fails.
  • Sockets (websocketManager.ts, notifications/socket.ts) — re-authenticate live connections in place on token:rotated and disconnect on token:revoked, so a revoked session cannot keep receiving realtime data.
  • Offline (offlineApi.ts, offlineSync.ts) — replay is gated on a valid token: it refreshes silently, and refuses (dead-letters via OfflineAuthRequiredError / skips the drain) when logged out, rather than burning retries against a dead credential.
  • Supportingjwt.ts gains a client-safe decodeTokenPayload and a verifyTokenDetailed that surfaces the specific exp/nbf/signature failure reason; environment.ts/app.constants.ts add the refresh endpoint and skew; monitoring/metrics.ts adds a typed recordAuthMetric.

Acceptance criteria

  • A single access-token refresh occurs under concurrent 401s (single-flight) — verified by test
  • Access tokens refresh silently before expiry using the configured skew; in-flight REST, socket and offline operations resume without a user-visible failure
  • Token rotation triggers a socket re-auth; revocation disconnects sockets and halts offline replay with dead-lettering
  • Auth lifecycle metrics are emitted with the documented tags; forced logout clears tokens from every consumer
  • Unit tests cover dedupe, skew scheduling, rotation/revocation, persistence and forced logout

Verification

All repo CI checks pass locally: pnpm run type-check, pnpm run lint (--max-warnings=0), pnpm run validate:ui, pnpm run validate:web3, pnpm run build, and the new Vitest suite (13/13 passing) for the token manager.

Introduces a single TokenManager that owns the access/refresh tokens so the
REST client, WebSocket connections, the notification socket and the offline
replay queue all share one credential and one refresh path.

- TokenManager (src/lib/auth/tokenManager.ts): single-flight refresh so
  concurrent 401s collapse into one /auth/refresh call; proactive silent
  refresh a configurable skew before exp; token:rotated / token:revoked /
  auth:logout events; auth lifecycle metrics.
- REST: api.ts sources its token from the manager and performs one
  coordinated refresh + replay on 401; apiInterceptors attaches a freshly
  refreshed token and routes 401 through the manager, hard-logging-out only
  when the refresh itself fails.
- Sockets: websocketManager re-authenticates live connections on rotation and
  drops them on revocation; the notification socket does the same in place.
- Offline: offlineApi and offlineSync gate replay on a valid token, refreshing
  silently and refusing (dead-letter) when logged out so the queue is not
  burned against a dead credential.
- jwt.ts adds a client-safe decode and a detailed verifier that surfaces the
  exp/nbf/signature failure reason; config/constants add the refresh endpoint
  and skew.

Adds unit tests covering single-flight dedupe, skew scheduling, rotation and
revocation, persistence and forced logout.
@oladev2026-tech
oladev2026-tech force-pushed the feat/1142-unified-token-lifecycle branch from bffc950 to 367328b Compare August 23, 2026 20:57
@RUKAYAT-CODER

Copy link
Copy Markdown
Contributor

Thank you for contributing to the project.

@RUKAYAT-CODER
RUKAYAT-CODER merged commit 72454cd into rinafcode:main Aug 23, 2026
6 checks passed
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.

[Security] Unify authentication token lifecycle with coordinated silent refresh across REST, WebSocket, and offline queues

2 participants