fix(auth): redirect OAuth login failures to login page instead of raw JSON (#1273) - #2955
fix(auth): redirect OAuth login failures to login page instead of raw JSON (#1273)#2955dokterbob wants to merge 7 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
This PR improves OAuth failure UX by redirecting OAuth callback errors to the login page (instead of returning raw JSON/500s), and updates the login error i18n lookup to correctly resolve camelCase error keys like oauthSignin. It also adds backend unit tests and a Cypress spec to cover key redirect/error-display paths.
Changes:
- Backend: Redirect more OAuth callback failure paths to
/login?error=oauthSignininstead of raisingHTTPException(JSON) or returning 500s. - Frontend: i18n lookup now tries the exact error key before attempting a lowercased fallback.
- Tests: Adds backend unit tests and a Cypress E2E spec for OAuth error redirects and friendly messaging.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
backend/chainlit/server.py |
Redirects additional OAuth failure paths to the login page instead of JSON/500 responses. |
backend/tests/test_server.py |
Adds regression tests for OAuth callback redirect behavior (including Azure hybrid flow). |
frontend/src/components/LoginForm.tsx |
Improves translation-key resolution for OAuth error query params (camelCase keys). |
cypress/e2e/oauth_auth/spec.cy.ts |
Adds E2E coverage for redirect-on-error and user-friendly login error message rendering. |
cypress/e2e/oauth_auth/main.py |
Provides a Chainlit app entrypoint configuring OAuth env vars for the new Cypress spec. |
|
The Windows e2e shards are red ("Cypress binary is missing" on windows-latest-5, fail-fast cancellation on the other four), but this is pre-existing, repo-wide infrastructure — not introduced by this PR. The identical failure signature appears on the unrelated Root cause and proposed fixes are tracked in #2957. This PR can be reviewed and merged independently of that infra issue. |
|
Nice, this is a solid fix for #1273. OAuth failures redirect back to One thing though: I don't think it fully closes #2956. Happy to put up a small follow-up for that on top of this if you'd want it. |
|
This PR is stale because it has been open for 14 days with no activity. |
|
This PR is stale because it has been open for 14 days with no activity. |
There was a problem hiding this comment.
All reported issues were addressed
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
@postoso You're right, and yes please — take it. Also: closing #2970 as a duplicate of this PR was the wrong call on our side. It wasn't a dup. #2970 added the translation key across all 23 locales but didn't touch Four things worth knowing before you start, so this lands complete: 1. Your #2970 strings are good — carry them over, don't rewrite. The en-US one in particular:
2. Rename the key to 3. Base it on 4. File checklist — the first item is the piece #2970 was missing:
No frontend change needed: Ping us when it's up and @dokterbob will review. |
@postoso One correction to my checklist, because it affects how you verify your work. I wrote that key parity across
Practical consequence for you: if you add Until there's a real check, this is the way to verify locally: import json, os
from chainlit.translations import compare_json_structures
d = "backend/chainlit/translations"
truth = json.load(open(os.path.join(d, "en-US.json"), encoding="utf-8"))
for f in sorted(os.listdir(d)):
if f.endswith(".json"):
for e in compare_json_structures(truth, json.load(open(os.path.join(d, f), encoding="utf-8"))):
print(f, e)Silent output means you got all 23. Heads up that this currently prints six pre-existing gaps unrelated to your change ( |
… JSON (#1273) All user-facing failure paths in oauth_callback and oauth_azure_hf_callback now redirect to /login?error=oauthSignin instead of returning raw JSON or a bare 500. Developer-facing config errors (missing oauth_callback, unknown provider) remain HTTPException. The frontend i18n lookup now tries the raw error key before lowercasing so camelCase keys (oauthSignin) resolve to their specific messages. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…1273) - Rework `_get_oauth_redirect_error` to accept a `status_code` param (default 302); POST azure-hybrid handler now uses 303 (See Other) so the browser issues a GET to /login instead of re-POSTing. - Map all provider-returned OAuth errors (including access_denied) to the friendly `oauthSignin` key; raw provider code is logged for debugging. - Add `asyncio.CancelledError: raise` guard before the broad `except Exception` in both OAuth callback handlers. - Reword `oauthSignin` across all 23 locale files: "Sign in failed. Please try again, or use a different sign-in method." - Add `role="alert"` to `Alert.tsx` (ARIA + stable Cypress selector). - Fix Cypress `oauth_auth/main.py`: remove unused `# noqa: E402` that caused RUF100 lint failure in CI. - Update `spec.cy.ts`: assert new message text, fix ineffective body assertion (use content-type check), assert provider-error redirects to `error=oauthSignin`. - Tighten `test_server.py` redirect-status assertions to exact codes (302 for GET handler, 303 for POST handler) and check `oauthSignin` key for provider-error case. - Follow-up issue for access_denied-specific UX: #2956. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…n log to warning - Alert: apply role="alert" only for error variant; info uses role="status" (assertive announcement is appropriate only for errors, not notices) - oauth_callback: demote logger.exception → logger.warning for invalid state cookie (expected user-facing failure, not an application error) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…apture logger.warning with exc_info=True keeps the traceback (needed by Sentry) while logging at WARNING rather than ERROR level, since an invalid OAuth state cookie is an expected user-facing event, not an application error. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…Signin key All redirect location assertions now check for `error=oauthSignin` rather than the generic `/login?error=`, and Cypress status assertions use exact 302 instead of oneOf([3xx]). This prevents regressions in error-key mapping that a looser check would miss. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
AzureADHybridOAuthProvider sets response_mode=form_post, so the provider POSTs error responses as form fields. `error` was declared as a bare Optional[str], which FastAPI binds as a query parameter, so it never populated and the provider-error warning added in this PR never fired for the hybrid flow. Accept both sources rather than swapping query for form, so any existing query-param caller keeps working. The redirect was already correct (303 -> /login?error=oauthSignin); this restores the diagnostic log line, so the new test asserts on the warning rather than the redirect. Also adds the missing -no postposition to the Gujarati oauthSignin string, matching the existing house style at chat.favorites.use. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
8cf5a15 to
1ba0eb4
Compare
Summary
oauth_callbackandoauth_azure_hf_callback) now redirect to/login?error=oauthSignininstead of returning raw JSON or a bare 500 — fixes the screenshot in UX bug: raw JSON on OAuth errors / Exception handling in auth #1273HTTPException(400/401), now redirect; developer-config errors (missingoauth_callback, unknown provider) remainHTTPExceptionoauthSigninnow resolve to their specific message ("Try signing in with a different account") instead of always falling back to the generic defaultTest plan
backend/tests/test_server.py) — all failure paths for both callback handlerscypress/e2e/oauth_auth/) — provider error redirect, invalid-state redirect, friendly message render at/login?error=oauthSigninuv run pytest tests/test_server.py— 61 passed)Closes #1273
🤖 Generated with Claude Code
Summary by cubic
Redirects all OAuth login failures to the login page with a localized
oauthSigninmessage and correct redirect codes (302 for GET, 303 for POST). Fixes #1273.oauth_callback(GET) andoauth_azure_hf_callback(POST) now redirect to/login?error=oauthSignin; dev-config errors remainHTTPException. Maps provider errors (incl.access_denied) to the friendly key, reads Azureerrorfrom the form body (with query fallback), re-raisesasyncio.CancelledError, and logs invalid state as a warning withexc_info=True.LoginFormtries the raw error key before lowercasing sooauthSigninresolves correctly;Alertusesrole="alert"for errors androle="status"otherwise. UpdatedoauthSignincopy across all locales to: "Sign in failed. Please try again, or use a different sign-in method."error=oauthSigninkey, verify exact status codes (302 for GET, 303 for POST), and check Azure form-post error logging.Written for commit 16f4802. Summary will update on new commits.