Skip to content

fix(auth): answer 503 on account store read outage instead of refusing session (#1942) - #1951

Closed
Maaz2212 wants to merge 1 commit into
libredb:mainfrom
Maaz2212:fix/account-store-outage
Closed

Maaz2212 wants to merge 1 commit into
libredb:mainfrom
Maaz2212:fix/account-store-outage

Conversation

@Maaz2212

Copy link
Copy Markdown
Contributor

Summary

Resolves #1942.

With password sign-in and STORAGE_PROVIDER=sqlite or postgres, getSession in src/lib/auth.ts previously returned null when getStorageProvider, reconcileOnce, or getAccount threw. While following getSession's no-throw contract, it collapsed transient storage store read outages into a refused account:

  • guardRoute answered 401 AUTH_REQUIRED and recorded a permission_denied audit event.
  • GET /api/auth/me answered 401 { authenticated: false }.
  • mcpTokenOwnerAllowed treated valid bearer tokens as revoked and answered 401 invalid_token.
  • The browser was told to sign in again while its auth-token cookie still verified, creating redirect oscillations between /login and the editor.

This change separates a store read failure from a missing or refused session across all three authentication surfaces.

Changes

1. readSession() and getSession() (src/lib/auth.ts)

  • Added readSession() which returns a discriminated union SessionReading:
    • { kind: "authenticated", session: UserPayload }
    • { kind: "anonymous", reason: "no_cookie" | "invalid_jwt" | "account_refused" } (calling logout() to clear the cookie when refused)
    • { kind: "store_unavailable", error: unknown } (preserving the client cookie)
  • getSession() now wraps readSession(), returning session on "authenticated" and null otherwise to preserve its existing non-throwing contract.

2. Route Guards & Endpoints

  • guardRoute (src/lib/api/require-session.ts):
    • Answers 503 { "error": "The account store is unavailable. Try again.", "code": "ACCOUNT_STORE_UNAVAILABLE" } on store_unavailable without logging an erroneous permission_denied audit entry.
    • Answers 401 AUTH_REQUIRED on anonymous.
  • GET /api/auth/me (src/app/api/auth/me/route.ts):
    • Answers 503 ACCOUNT_STORE_UNAVAILABLE on store_unavailable.
    • Answers 401 { "authenticated": false } on anonymous.
  • MCP (src/lib/mcp/account-check.ts, src/app/api/mcp/route.ts):
    • mcpTokenOwnerAllowed returns "store_unavailable" when the account store throws.
    • /api/mcp answers 503 ACCOUNT_STORE_UNAVAILABLE without revoking tokens or recording denial audits.

3. Client & Browser Handling

  • appFetch in src/lib/config/base-path.ts triggers sign-in redirection only on 401 with code: AUTH_REQUIRED. An ACCOUNT_STORE_UNAVAILABLE 503 avoids triggering /login redirects.
  • In WorkspaceOwnerGate (src/components/auth/WorkspaceOwnerGate.tsx), an unreadable account via 503 triggers the "failed" alert state, holding the session cookie in the browser so that reloading when the store recovers restores the workspace without re-authenticating.

4. Cleanup & Documentation

  • Documented ACCOUNT_STORE_UNAVAILABLE in docs/API_DOCS.md and added store outage troubleshooting guidelines in docs/STORAGE.md.
  • Removed dead/unused test fixtures from tests/unit/db/search/introspect.test.ts.
  • Resolved react-hooks/set-state-in-effect lint warning in src/components/DataProfiler.tsx.

Testing

  • bun run typecheck (tsc --noEmit) passes with 0 errors.
  • bun x eslint and bun x biome check pass with 0 errors / 0 warnings.
  • bun test tests/api/auth/me.test.ts (5/5 pass)
  • bun test tests/security/route-auth.test.ts (48/48 pass)
  • bun test tests/security/mcp-auth.test.ts (52/52 pass)
  • bun test tests/unit/lib/auth.test.ts (44/44 pass)
  • bun test tests/components/auth/WorkspaceOwnerGate.test.tsx (23/23 pass)
  • bun test tests/components/DataProfiler.test.tsx (52/52 pass)
  • bun test tests/unit/db/search/introspect.test.ts (10/10 pass)

…g session (libredb#1942)

- Introduce readSession() returning discriminated SessionReading union, with getSession() wrapping it to preserve the no-throw contract.
- Return 503 ACCOUNT_STORE_UNAVAILABLE from guardRoute, GET /api/auth/me, and /api/mcp when the account store is unreadable.
- Preserve session cookie and MCP bearer tokens on store outage, avoiding /login redirect loops.
- Clean up unused introspect test helpers and address effect set-state lint warning in DataProfiler.
- Update API and storage documentation with store outage error specifications.
@Maaz2212 Maaz2212 closed this Oct 24, 2026
@Maaz2212
Maaz2212 deleted the fix/account-store-outage branch October 24, 2026 15:47
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.

[BUG] An unreadable account store signs the browser out instead of answering an outage

1 participant