Skip to content

Handle sign-in failures with error page instead of retry loop - #263

Merged
jalexw merged 2 commits into
mainfrom
claude/eloquent-feynman-7oyq6q
Oct 15, 2026
Merged

jalexw merged 2 commits into
mainfrom
claude/eloquent-feynman-7oyq6q

Conversation

@jalexw

@jalexw jalexw commented Oct 15, 2026

Copy link
Copy Markdown
Contributor

Summary

When a client app's authorization code exchange fails at the token endpoint (e.g., the app is not connected to its default audience's API server), redirect the user to the error page instead of back into the login flow. This prevents infinite bouncing between login and authorize pages when the auth server session is still valid.

Key Changes

  • New error classification: Added TokenEndpointRequestFailedError exception class that carries RFC 6749 §5.2 error codes (oauth_error), descriptions, HTTP status, and session state from token endpoint responses
  • Error page resolution: Implemented resolveLoginErrorPage() function that maps sign-in failures to appropriate error page entries:
    • invalid_target (app not connected to API server) → app_not_connected_to_api_server (403)
    • Other token endpoint failures → login_failure with original HTTP status or 500
    • Traverses error cause chains to find the underlying token endpoint failure
  • React provider integration: Updated both useTradeAuthorizationCodeForTokensEffect() and useStartLoginOauthPKCEFlow() to redirect to error pages on sign-in failures
  • Authorization code deduplication: Added caching of in-flight code exchanges to prevent duplicate redemptions when React re-runs effects (StrictMode, dependency changes)
  • Error message catalog: Added app_not_connected_to_api_server and login_failure error IDs to the common error message catalog
  • E2E test coverage: Added comprehensive Cypress test (DefaultAudienceNotConnected.cy.ts) verifying the error page flow when an app is disconnected from its default audience

Implementation Details

  • TokenEndpointRequestFailedError preserves the original error message format for backward compatibility while exposing structured OAuth error details
  • Error cause chains are inspected up to 5 levels deep to find token endpoint failures wrapped in higher-level errors
  • Authorization code exchanges are memoized by code value to handle React's effect re-runs safely
  • Invalid callback parameters (missing code/challenge) are distinguished from token endpoint failures with InvalidAuthorizeCallbackError
  • Documentation updated in README and SKILL.md with examples and test environment setup instructions

https://claude.ai/code/session_01VkeBhNN2Cv7oosBKvfXJXb

…ovider:0.18.3, auth-ui:0.21.6, openapi-operations:0.4.6, auth-resource-server-codegen-templates:0.0.166, auth-server-sdk:0.33.5, trpc-backend-init:0.9.87, auth-server:0.56.1, e2e-auth-tests:0.12.13 - send failed sign-ins to /auth/error instead of looping through the login flow

When the token endpoint refused the authorization code exchange (e.g.
`invalid_target`: the client app is not connected to its default
audience's API server), the generated /auth/authorize page redirected to
`successful_logout_redirect_uri`. On apps whose `/` leads back to
/auth/login (the registry), the auth server's live session minted a new
code at once and the browser bounced between /auth/login and
/auth/authorize forever.

- auth-client-sdk: token endpoint refusals that keep the session throw
  `TokenEndpointRequestFailedError` (oauth_error, error_description,
  status); `resolveLoginErrorPage()` maps a failed sign-in to an error
  page entry (walks the cause chain).
- auth-common: `app_not_connected_to_api_server` and `login_failure`
  error catalog entries.
- auth-react-provider: the authorize hook sends every failed exchange to
  the app's error page, redeems each code once per page load (effect
  re-runs await the first exchange) and returns early when the app
  already has a session; the login hook shows the error page when a
  signed-in user's default access tokens cannot be acquired.
- e2e: DefaultAudienceNotConnected.cy.ts pins the error page.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VkeBhNN2Cv7oosBKvfXJXb
@jalexw jalexw self-assigned this Oct 15, 2026
… in DefaultAudienceNotConnected

create_and_login_as_superuser() asserts that nobody is signed in, but the
spec ends signed in to the auth server as its own user and test isolation
does not clear cookies before `after` hooks, so the reconnect hook failed
and left the example app disconnected for the rest of the shard.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VkeBhNN2Cv7oosBKvfXJXb
@jalexw
jalexw merged commit 3e00716 into main Oct 15, 2026
35 checks passed
@jalexw
jalexw deleted the claude/eloquent-feynman-7oyq6q branch October 15, 2026 19:17
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