[PR #2641] [CLOSED] Fix OIDC Post-Login Flow #12666

Closed
opened 2026-04-13 08:31:35 -05:00 by GiteaMirror · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/better-auth/better-auth/pull/2641
Author: @IhsenBouallegue
Created: 5/13/2025
Status: Closed

Base: v1.3Head: fix/oidc-provider-cors-error


📝 Commits (4)

  • ffc4d93 fix(oidc-provider): fix oidc provider request handling
  • 26c53f5 docs(oidc-provider): update OIDC provider documentation for clarity and accuracy, including improved examples and descriptions
  • 7e5f509 Merge branch 'v1.3' into fix/oidc-provider-cors-error
  • 96f6f5b fix type

📊 Changes

3 files changed (+407 additions, -163 deletions)

View changed files

📝 docs/content/docs/plugins/oidc-provider.mdx (+237 -137)
📝 packages/better-auth/src/plugins/oidc-provider/authorize.ts (+84 -4)
📝 packages/better-auth/src/plugins/oidc-provider/index.ts (+86 -22)

📄 Description

Fix OIDC Post-Login Flow and Consent Handling

Closes #1160
Closes #984

Addresses:

  • CORS errors when an unauthenticated user logs in via the OIDC provider, and the provider attempts to redirect back to the client application.

Summary of Issues:

  1. CORS Error on Post-Login Redirect:
    When an unauthenticated user was redirected to the OIDC provider's internal login page, upon successful login (e.g., via signIn.email), the OIDC plugin's after hook would attempt to resume the OIDC flow. It did this by re-running the authorization logic and directly issuing an HTTP 302 redirect from the POST /api/auth/sign-in/email response. This 302 redirected to the client application's callback URL. Browsers have strict CORS policies for redirects originating from fetch POST requests, especially if they appear cross-origin or involve complex redirect chains, leading to the observed CORS errors and the redirect failing.

  2. Consent Screen Skipped After Internal Login:
    The OIDC authorize endpoint's decision to show a consent screen was primarily based on an explicit prompt=consent parameter in the URL. When the OIDC flow was resumed by the after hook post-login, the original prompt parameter (which might have been undefined or different) was used. This often led to the consent check being bypassed, even if OIDC best practices suggest prompting for consent at least the first time a user/client combination requests specific scopes.

Solution Implemented:

  1. Modified OIDC Provider after Hook (src/plugins/oidc-provider/index.ts):

    • Instead of the after hook directly causing a 302 redirect, it now returns a 200 OK JSON response to the signIn.email (or equivalent login) call.
    • This JSON response includes:
      • Standard user and session token information.
      • A flag oidcContinuation: true.
      • An oidcAuthorizeURL: This is the URL to the OIDC provider's own /oauth2/authorize endpoint, constructed with the original OIDC request parameters (from the oidc_login_prompt cookie).
    • Crucially, when constructing this oidcAuthorizeURL, the hook now intelligently sets prompt=consent if the original OIDC request did not specify prompt=none. This ensures that the subsequent call to /oauth2/authorize will properly evaluate the need for consent.
    • This change ensures that the Set-Cookie headers for the new session and for clearing the oidc_login_prompt are correctly handled by the framework via ctx.json().
  2. Client-Side Change on OIDC Provider's Login Page:

    • The client-side JavaScript that calls authClient.signIn.email(...) on the OIDC provider's login page must be updated in its onSuccess handler.
    • It now checks for context.data.oidcContinuation === true.
    • If true, it performs a window.location.href = context.data.oidcAuthorizeURL;. This action by the client initiates a new, full-page GET navigation to the OIDC provider's /oauth2/authorize endpoint.
  3. OIDC authorize Endpoint (src/plugins/oidc-provider/authorize.ts):

    • When this endpoint is hit via the GET request (from the client-side navigation):
      • It finds the active session (established by the preceding signIn.email).
      • It now correctly receives prompt=consent (if set by the after hook).
      • The existing logic for if (query.prompt === 'consent') then correctly proceeds to check hasAlreadyConsented. If consent is needed (user hasn't consented to these scopes for this client before), the consent page is shown.
      • If consent has already been granted, it issues a 302 redirect to the client application's callback URL. This redirect is now in response to a GET request and is handled correctly by browsers without the previous CORS issues.

Impact:

  • The CORS error during the post-login redirect in the OIDC flow is resolved because the redirect to the client application now originates from a GET request to the /oauth2/authorize endpoint.
  • The consent screen is more reliably presented after a user logs in via the OIDC provider's login page if they haven't previously consented to the requested scopes for the client, or if prompt=consent was part of the original request. The system now also respects an explicit prompt=none if present in the original request to allow for silent authentication attempts.
  • The solution aligns with standard web practices by having the client initiate navigations that change the top-level browser context, rather than trying to achieve this via redirects from background fetch POST requests.
  • Logging has been reviewed and refined in both modified files to be more concise for production while retaining essential information for debugging.

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/better-auth/better-auth/pull/2641 **Author:** [@IhsenBouallegue](https://github.com/IhsenBouallegue) **Created:** 5/13/2025 **Status:** ❌ Closed **Base:** `v1.3` ← **Head:** `fix/oidc-provider-cors-error` --- ### 📝 Commits (4) - [`ffc4d93`](https://github.com/better-auth/better-auth/commit/ffc4d93009e3e4bb96b6230140cf52dde1c18d7d) fix(oidc-provider): fix oidc provider request handling - [`26c53f5`](https://github.com/better-auth/better-auth/commit/26c53f53f3dce53840880305207f2155aff24ace) docs(oidc-provider): update OIDC provider documentation for clarity and accuracy, including improved examples and descriptions - [`7e5f509`](https://github.com/better-auth/better-auth/commit/7e5f5091be56e0a40b41b9d15418b093bd787976) Merge branch 'v1.3' into fix/oidc-provider-cors-error - [`96f6f5b`](https://github.com/better-auth/better-auth/commit/96f6f5b2944699c9c4606565ed2a4acc3f7fcd0a) fix type ### 📊 Changes **3 files changed** (+407 additions, -163 deletions) <details> <summary>View changed files</summary> 📝 `docs/content/docs/plugins/oidc-provider.mdx` (+237 -137) 📝 `packages/better-auth/src/plugins/oidc-provider/authorize.ts` (+84 -4) 📝 `packages/better-auth/src/plugins/oidc-provider/index.ts` (+86 -22) </details> ### 📄 Description ## Fix OIDC Post-Login Flow and Consent Handling Closes #1160 Closes #984 **Addresses:** - CORS errors when an unauthenticated user logs in via the OIDC provider, and the provider attempts to redirect back to the client application. **Summary of Issues:** 1. **CORS Error on Post-Login Redirect:** When an unauthenticated user was redirected to the OIDC provider's internal login page, upon successful login (e.g., via `signIn.email`), the OIDC plugin's `after` hook would attempt to resume the OIDC flow. It did this by re-running the authorization logic and directly issuing an HTTP 302 redirect from the `POST /api/auth/sign-in/email` response. This 302 redirected to the client application's callback URL. Browsers have strict CORS policies for redirects originating from `fetch` POST requests, especially if they appear cross-origin or involve complex redirect chains, leading to the observed CORS errors and the redirect failing. 2. **Consent Screen Skipped After Internal Login:** The OIDC `authorize` endpoint's decision to show a consent screen was primarily based on an explicit `prompt=consent` parameter in the URL. When the OIDC flow was resumed by the `after` hook post-login, the original `prompt` parameter (which might have been undefined or different) was used. This often led to the consent check being bypassed, even if OIDC best practices suggest prompting for consent at least the first time a user/client combination requests specific scopes. **Solution Implemented:** 1. **Modified OIDC Provider `after` Hook (`src/plugins/oidc-provider/index.ts`):** - Instead of the `after` hook directly causing a 302 redirect, it now returns a 200 OK JSON response to the `signIn.email` (or equivalent login) call. - This JSON response includes: - Standard user and session token information. - A flag `oidcContinuation: true`. - An `oidcAuthorizeURL`: This is the URL to the OIDC provider's own `/oauth2/authorize` endpoint, constructed with the original OIDC request parameters (from the `oidc_login_prompt` cookie). - Crucially, when constructing this `oidcAuthorizeURL`, the hook now intelligently sets `prompt=consent` if the original OIDC request did not specify `prompt=none`. This ensures that the subsequent call to `/oauth2/authorize` will properly evaluate the need for consent. - This change ensures that the `Set-Cookie` headers for the new session and for clearing the `oidc_login_prompt` are correctly handled by the framework via `ctx.json()`. 2. **Client-Side Change on OIDC Provider's Login Page:** - The client-side JavaScript that calls `authClient.signIn.email(...)` on the OIDC provider's login page must be updated in its `onSuccess` handler. - It now checks for `context.data.oidcContinuation === true`. - If true, it performs a `window.location.href = context.data.oidcAuthorizeURL;`. This action by the client initiates a new, full-page GET navigation to the OIDC provider's `/oauth2/authorize` endpoint. 3. **OIDC `authorize` Endpoint (`src/plugins/oidc-provider/authorize.ts`):** - When this endpoint is hit via the GET request (from the client-side navigation): - It finds the active session (established by the preceding `signIn.email`). - It now correctly receives `prompt=consent` (if set by the `after` hook). - The existing logic for `if (query.prompt === 'consent')` then correctly proceeds to check `hasAlreadyConsented`. If consent is needed (user hasn't consented to these scopes for this client before), the consent page is shown. - If consent has already been granted, it issues a 302 redirect to the client application's callback URL. This redirect is now in response to a GET request and is handled correctly by browsers without the previous CORS issues. **Impact:** - The CORS error during the post-login redirect in the OIDC flow is resolved because the redirect to the client application now originates from a GET request to the `/oauth2/authorize` endpoint. - The consent screen is more reliably presented after a user logs in via the OIDC provider's login page if they haven't previously consented to the requested scopes for the client, or if `prompt=consent` was part of the original request. The system now also respects an explicit `prompt=none` if present in the original request to allow for silent authentication attempts. - The solution aligns with standard web practices by having the client initiate navigations that change the top-level browser context, rather than trying to achieve this via redirects from background `fetch` POST requests. - Logging has been reviewed and refined in both modified files to be more concise for production while retaining essential information for debugging. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
GiteaMirror added the pull-request label 2026-04-13 08:31:36 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#12666