Skip to content

feat(oauth): surface actionable callback failure codes#103

Merged
Bccorb merged 1 commit into
mainfrom
feat/oauth-profile-error-codes
Jul 25, 2026
Merged

feat(oauth): surface actionable callback failure codes#103
Bccorb merged 1 commit into
mainfrom
feat/oauth-profile-error-codes

Conversation

@Bccorb

@Bccorb Bccorb commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Closes #91.

What

The OAuth callback computed specific, user-actionable failure reasons and then discarded them, returning a generic 400 { error: 'OAuth login failed' } for all of them. It now returns a stable machine-readable code alongside the existing error string for the curated, user-actionable cases.

  • oauthService.ts: new OAuthProfileError { code } thrown for the three curated conditions instead of bare Error.
  • oauth.ts (controller): the catch maps an OAuthProfileError to { error, code } and records the specific reason in the audit event; every other error keeps the generic { error: 'OAuth login failed' } with reason: 'callback_failed'.
  • oauth.responses.ts / oauth.routes.ts: dedicated OAuthLoginErrorResponseSchema with an optional code enum wired to the callback 400 (the response validator strips unknown keys, so a dedicated schema is required for code to survive).

Surfaced codes: oauth_missing_email, oauth_email_not_verified, oauth_missing_subject.

Deliberate scope limit

Only the curated set is exposed. Upstream detail (OAuth profile fetch failed with status <n>, token-exchange failures, raw provider bodies) stays behind the generic message.

Contract change / ripple

This adds a code field to the OAuth callback (POST /oauth/:providerId/callback) 400 response. The client side is already being prepared in fells-code/seamless-auth-react#83 (throws a SeamlessAuthError carrying HTTP status and parsed body), which can then map body.code. I will survey @seamless-auth/react and @seamless-auth/server for concrete consume sites and propose coordinated edits separately; no sibling repo changes are included here.

Acceptance criteria

  • Missing-email and unverified-email cases return a distinct, stable code
  • Existing error string field preserved for backward compatibility
  • Unexpected failures still return the generic message with no internal detail
  • Audit events record the specific reason rather than callback_failed for the known cases
  • Tests cover each surfaced code and confirm unknown errors stay generic

Checks

typecheck, lint, OAuth integration + unit suites, OpenAPI/route-schema tests all pass. Security review passed (no findings). Changeset included (minor).

@Bccorb
Bccorb merged commit 47282ee into main Jul 25, 2026
2 of 3 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.

OAuth callback discards actionable failure reasons, including missing profile email

1 participant