Skip to content

OAuth provider failures were not handled properly in the frontend, causing unclear errors and broken login behavior.#415

Open
Suvam-paul145 wants to merge 6 commits intoGetBindu:mainfrom
Suvam-paul145:295
Open

OAuth provider failures were not handled properly in the frontend, causing unclear errors and broken login behavior.#415
Suvam-paul145 wants to merge 6 commits intoGetBindu:mainfrom
Suvam-paul145:295

Conversation

@Suvam-paul145
Copy link
Copy Markdown
Contributor

  • Why it matters: Users experienced failed sign-ins without feedback, and the app risked crashes or hanging when providers were down.
  • What changed: Added try/catch handling, structured 502 error responses, UI toast messaging, fixed sign-in component, and added unit + e2e tests.
  • What did NOT change (scope boundary): No changes to core auth logic or successful login flow.

Change Type (select all that apply)

  • Bug fix
  • Feature
  • Refactor
  • Documentation
  • Security hardening
  • Tests
  • Chore/infra

Scope (select all touched areas)

  • Server / API endpoints
  • Extensions (DID, x402, etc.)
  • Storage backends
  • Scheduler backends
  • Observability / monitoring
  • Authentication / authorization
  • CLI / utilities
  • Tests
  • Documentation
  • CI/CD / infra

Linked Issue/PR

User-Visible / Behavior Changes

  • Users now see a clear toast message when OAuth login fails.
  • Login failures redirect with ?error=<CODE> for UI handling.
  • Consistent error messaging for provider downtime or failures.

Security Impact (required)

  • New permissions/capabilities? (No)
  • Secrets/credentials handling changed? (No)
  • New/changed network calls? (No)
  • Database schema/migration changes? (No)
  • Authentication/authorization changes? (No)
  • If any Yes, explain risk + mitigation: None

Verification

Environment

  • OS: Not specified
  • Python version: N/A
  • Storage backend: Not affected
  • Scheduler backend: Not affected

Steps to Test

  1. Simulate OAuth provider failure.
  2. Attempt login via frontend.
  3. Verify redirect and toast message.
  4. Run unit and e2e tests.

Expected Behavior

  • API returns structured 502 error with code + message.
  • UI displays a user-friendly toast message.

Actual Behavior

  • Matches expected behavior; tests passing (20/20).

Evidence (attach at least one)

  • Failing test before + passing after
  • Test output / logs
  • Screenshot / recording
  • Performance metrics (if relevant)

Human Verification (required)

What you personally verified (not just CI):

  • Verified scenarios: OAuth provider failure handling, UI error display, structured API response.
  • Edge cases checked: Multiple provider error codes, HTML vs API request handling.
  • What you did NOT verify: Real external provider outage in production.

Compatibility / Migration

  • Backward compatible? (Yes)
  • Config/env changes? (No)
  • Database migration needed? (No)
  • If yes, exact upgrade steps: N/A

Failure Recovery (if this breaks)

  • How to disable/revert this change quickly: Revert PR Add Error Handling for OAuth Provider Failures in Frontend #298.
  • Files/config to restore: auth.ts, hooks.server.ts, +server.ts, +layout.svelte, SignInButton
  • Known bad symptoms reviewers should watch for: Missing error toast, broken login UI, incorrect error responses.

Risks and Mitigations

  • Risk: Incorrect error handling could mislead users.
    • Mitigation: Structured error codes + unit and e2e test coverage.

Checklist

  • Tests pass (uv run pytest)
  • Pre-commit hooks pass (uv run pre-commit run --all-files)
  • Documentation updated (if needed)
  • Security impact assessed
  • Human verification completed
  • Backward compatibility considered

Copilot AI and others added 6 commits March 7, 2026 13:30
…auth-failures

[WIP] Add error handling for OAuth provider failures in frontend
…OAuth provider failures.

- Enhance getOIDCUserData and triggerOauthFlow functions to handle network and provider errors gracefully.
- Update login callback to log and respond to OAuth provider errors with appropriate messages.
- Introduce SignInButton component for user sign-in with error handling via query parameters.
- Create integration tests for OAuth provider failures to ensure correct error propagation and responses.
- Add mongodb-memory-server dependency for testing purposes.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings March 27, 2026 18:15
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR improves resilience and UX around OAuth provider outages by introducing structured provider-failure errors (502 + {code,message}), mapping those errors to user-facing toasts, and adding automated test coverage for failure paths.

Changes:

  • Add typed OAuthProviderError / OAuthErrorCode and wrap OIDC discovery/callback flows to emit structured 502 JSON errors on provider failures.
  • Redirect HTML navigations that hit provider failures to /?error=<CODE> so the UI can show a toast.
  • Add Vitest coverage for provider-down scenarios in both the auth helper and the login callback route.

Reviewed changes

Copilot reviewed 9 out of 10 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
frontend/src/lib/server/auth.ts Adds OAuthProviderError + wraps provider calls to classify/log failures and return structured errors.
frontend/src/hooks.server.ts Intercepts 502 from triggerOauthFlow and redirects HTML requests to /?error=<CODE>.
frontend/src/routes/login/callback/+server.ts Catches OAuthProviderError during callback token exchange and returns a structured 502 JSON response.
frontend/src/routes/+layout.svelte Reads ?error= and maps OAuth error codes to toast messages.
frontend/src/lib/components/SignInButton.svelte Adds a sign-in button component that also reads ?error= and sets the error store.
frontend/src/lib/server/auth.spec.ts Adds unit tests for OAuthProviderError, triggerOauthFlow, and getOIDCUserData failure handling.
frontend/src/routes/login/callback/auth-provider-failure.spec.ts Adds integration-style tests for provider downtime behavior.
frontend/src/routes/login/callback/provider-error-response.spec.ts Adds tests asserting the callback route returns structured 502 JSON payloads.
frontend/package.json Adds mongodb-memory-server to support server Vitest environment setup.
frontend/package-lock.json Updates lockfile for the added dev dependency and its transitive deps.
Files not reviewed (1)
  • frontend/package-lock.json: Language not supported

Comment on lines +215 to +218
if (!oauthErrorCode || oauthErrorCode === lastOAuthErrorCode) {
return;
}

Copy link

Copilot AI Mar 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lastOAuthErrorCode prevents showing the toast again for the same ?error= code. If a user retries login and hits the same provider failure, they may not get any feedback on subsequent redirects/navigations. Consider clearing the error query param after consuming it (e.g., via goto with replaceState) or resetting lastOAuthErrorCode when the URL no longer has error, so repeated failures still surface a toast.

Suggested change
if (!oauthErrorCode || oauthErrorCode === lastOAuthErrorCode) {
return;
}
if (!oauthErrorCode) {
lastOAuthErrorCode = null;
return;
}
if (oauthErrorCode === lastOAuthErrorCode) {
return;
}

Copilot uses AI. Check for mistakes.
Comment on lines +14 to +27
let { classNames = "", label = "Sign in" }: Props = $props();

const OAUTH_ERROR_MESSAGES: Record<string, string> = {
OAUTH_PROVIDER_UNAVAILABLE:
"Sign-in service is temporarily unavailable. Please try again later.",
OAUTH_PROVIDER_ERROR: "An error occurred during sign-in. Please try again.",
};

onMount(() => {
const errorCode = page.url.searchParams.get("error");
if (!errorCode) return;

errorStore.set(OAUTH_ERROR_MESSAGES[errorCode] ?? ERROR_MESSAGES.default);
});
Copy link

Copilot AI Mar 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This new SignInButton component isn't referenced anywhere under frontend/src (no imports/usages found), so it may be dead code and won’t affect the login UX. Either wire it into the relevant page(s) or remove it; if it is intended to handle ?error= toasts, that logic is now duplicated with the root +layout.svelte handler and should be centralized in one place to avoid double-setting the error store.

Copilot uses AI. Check for mistakes.
Comment on lines +180 to +202
const oauthResponse = await triggerOauthFlow(event);
if (oauthResponse.status === 502) {
const accept = event.request.headers.get("accept") || "";
const wantsHtml = accept.includes("text/html");

if (wantsHtml) {
try {
const body = await oauthResponse.clone().json();
if (typeof body?.code === "string") {
return new Response(null, {
status: 302,
headers: {
Location: `${base}/?error=${encodeURIComponent(body.code)}`,
"Cache-Control": "no-store",
},
});
}
} catch {
// fall through and return original structured 502 response
}
}
}
return oauthResponse;
Copy link

Copilot AI Mar 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The 502-to-302 HTML redirect handling is duplicated in two branches in this file, which makes it easy for the behavior to drift over time (e.g., headers/message parsing). Consider extracting this into a small helper function (e.g., redirectOauthErrorIfHtml(event, oauthResponse)) and reusing it in both call sites.

Copilot uses AI. Check for mistakes.
Comment on lines +12 to +15
const { mockIssuerDiscover } = vi.hoisted(() => ({
mockIssuerDiscover: vi.fn(),
}));

Copy link

Copilot AI Mar 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These newly added tests use spaces for indentation, but the repo's Prettier config sets useTabs: true (frontend/.prettierrc). This will likely fail npm run lint (prettier --check .) unless reformatted. Please run Prettier (or adjust indentation to tabs) on this file.

Copilot uses AI. Check for mistakes.
Comment on lines +12 to +15
const { mockIssuerDiscover } = vi.hoisted(() => ({
mockIssuerDiscover: vi.fn(),
}));

Copy link

Copilot AI Mar 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These newly added tests use spaces for indentation, but the repo's Prettier config sets useTabs: true (frontend/.prettierrc). This will likely fail npm run lint (prettier --check .) unless reformatted. Please run Prettier (or adjust indentation to tabs) on this file.

Copilot uses AI. Check for mistakes.
Comment on lines +349 to +361
// Re-wrap as a typed error so callers (e.g. callback +server.ts) can distinguish
// provider unavailability from other failures without logging sensitive token data.
const isNetworkError =
err instanceof TypeError ||
(err instanceof Error && (err.message.includes("ECONNREFUSED") || err.message.includes("ENOTFOUND") || err.message.includes("fetch failed")));
const code: OAuthErrorCode = isNetworkError
? "OAUTH_PROVIDER_UNAVAILABLE"
: "OAUTH_PROVIDER_ERROR";
logger.error({ code, msg: err instanceof Error ? err.message : String(err) }, "OAuth provider error in getOIDCUserData");
throw new OAuthProviderError(
isNetworkError ? "OAuth provider is unreachable" : "OAuth provider returned an error",
code
);
Copy link

Copilot AI Mar 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both getOIDCUserData and triggerOauthFlow implement similar network-error classification logic via string matching on err.message. Consider extracting this into a shared helper (e.g., classifyOAuthProviderError(err): OAuthErrorCode) so the mapping stays consistent and is easier to update when new error shapes/messages appear.

Copilot uses AI. Check for mistakes.
Comment on lines +71 to +89
} catch (err) {
if (err instanceof OAuthProviderError) {
const message =
err.code === "OAUTH_PROVIDER_UNAVAILABLE"
? "The sign-in service is temporarily unavailable. Please try again later."
: "An error occurred during sign-in. Please try again.";

logger.error(
{ code: err.code, msg: err.message },
"OAuth provider error during callback token exchange"
);

return new Response(
JSON.stringify({ code: err.code, message }),
{
status: 502,
headers: { "Content-Type": "application/json" },
}
);
Copy link

Copilot AI Mar 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The callback route returns a JSON 502 response for OAuthProviderError regardless of request type. In the browser OAuth callback flow this is typically an HTML navigation, so users may land on a raw JSON error page instead of being redirected to /?error=<CODE> for the toast handling described in the PR. Consider branching on the request's Accept header (similar to hooks.server.ts): redirect for text/html and keep the structured JSON 502 for API callers (also add Cache-Control: no-store on the redirect/error responses).

Copilot uses AI. Check for mistakes.
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]: Add Error Handling for OAuth Provider Failures in Frontend

3 participants