Skip to content

Handle JWT key manager misconfiguration errors in route guards - #264

Merged
jalexw merged 1 commit into
mainfrom
claude/jwks-misconfiguration-error-page
Oct 16, 2026
Merged

jalexw merged 1 commit into
mainfrom
claude/jwks-misconfiguration-error-page

Conversation

@jalexw

@jalexw jalexw commented Oct 15, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR adds proper handling for JWT key manager misconfiguration errors in route guards and token decoding. When the auth server refuses a JWKS download (4xx response) or the JWKS access private key is unusable, the system now reports a server misconfiguration (500) instead of treating it as an unauthenticated request (401). This prevents infinite redirect loops where signing in again would only mint another token the server cannot verify.

Key Changes

  • New JwtKeyManagerMisconfigurationError class: Distinguishes between two types of configuration failures:

    • jwks_download_refused: Auth server refused the JWKS download (4xx status, typically 401/403/404)
    • jwks_access_private_key_unusable: JWKS access private key is missing, unparseable, or cannot sign assertions
  • Enhanced loadRemoteJwks():

    • Added isJwksDownloadRefusal() helper to identify 4xx responses (excluding transient 408/429)
    • Throws JwtKeyManagerMisconfigurationError for refused downloads with status code and auth server response details
    • Throws JwtKeyManagerMisconfigurationError for unusable private keys with clear error messaging
  • Route guard error handling:

    • withAuthenticatedServerComponentRouteGuard: Catches misconfiguration errors and redirects to /auth/error?error=500&error_id=server_misconfiguration instead of login
    • withAuthenticatedApiRouteGuard: Returns 500 JSON response instead of 401 for misconfiguration errors
    • Both log the error with the reason for debugging
  • Token decoding improvements:

    • decodeJWTsWithKeyManager() now throws JwtKeyManagerMisconfigurationError when no token verifies due to misconfiguration
    • Distinguishes between misconfiguration (throw) and transient failures (return null user)
    • Updated JSDoc to document the new throwing behavior
  • OpenAPI operations resolver: Updated to return 500 auth_not_configured for misconfiguration errors instead of 401

  • Comprehensive test coverage: Added tests for both server component and API route guards, verifying correct error responses and that transient failures still result in 401

Implementation Details

  • Misconfiguration errors are caught at the route guard level before attempting to render protected content
  • Transient failures (5xx, 408, 429, network errors) continue to be treated as unauthenticated requests
  • Error messages include environment variable names and auth server URLs to aid debugging
  • The distinction prevents the browser from looping through login flows when the server cannot verify any tokens

https://claude.ai/code/session_019cgR5TkZN85MnxS8MevFx4

…- show server_misconfiguration when the JWKS cannot be loaded instead of looping through login

A resource server whose JWKS download the auth server refuses (its JWKS
access key is not the API server's active one, or the API server id is
wrong) or whose SCHEMAVAULTS_AUTH_JWKS_ACCESS_PRIVATE_KEY is unusable
cannot verify any token. The failure was swallowed as "no user", so
withAuthenticatedServerComponentRouteGuard sent the browser to
/auth/login; with a live auth server session the login flow minted a
fresh token the server could not verify either, and the browser looped.

- RemoteJwtKeyManager / loadRemoteJwks throw JwtKeyManagerMisconfigurationError
  (reason jwks_download_refused for a 4xx other than 408/429, with the
  status and the auth server's answer; jwks_access_private_key_unusable
  when the key cannot be loaded or cannot sign the access assertion).
  Outages (5xx, 408, 429, network) stay unauthenticated.
- loadJwtDecodingKeys rethrows it; decodeJWTsWithKeyManager (and so
  RouteGuardFactory.createGuardFromTokenSources) throws it when no token
  verified.
- withAuthenticatedServerComponentRouteGuard redirects to the codegen'd
  /auth/error?error=500&error_id=server_misconfiguration;
  withAuthenticatedApiRouteGuard answers 500; createSchemaVaultsAuthResolvers
  answers 500 auth_not_configured naming the cause (instead of 401s that
  send clients to sign in again).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019cgR5TkZN85MnxS8MevFx4
@jalexw jalexw self-assigned this Oct 15, 2026
@jalexw
jalexw merged commit 0cba7f5 into main Oct 16, 2026
35 checks passed
@jalexw
jalexw deleted the claude/jwks-misconfiguration-error-page branch October 16, 2026 00:28
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.

2 participants