SSO skips 2FA verification when using callbackURL #1818

Closed
opened 2026-03-13 09:05:27 -05:00 by GiteaMirror · 1 comment
Owner

Originally created by @fyfirman on GitHub (Sep 1, 2025).

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

When using SSO login with Microsoft (or other providers) and setting callbackURL: "/dashboard", the 2FA verification step is skipped. Instead of respecting the onSuccess handler logic for twoFactorRedirect, the SSO flow immediately redirects to /dashboard.

Code Example:

await authClient.signIn.social(
  {
    provider: "microsoft",
    callbackURL: "/dashboard",
  },
  {
    onSuccess: async (ctx) => {
      if (ctx.data.twoFactorRedirect) {
        // User has 2FA enabled, redirect to verification page
        router.push("/2fa");
      } else {
        // Normal login flow
        router.push("/dashboard");
      }
    },
    onError: (ctx) => {
      console.error("Microsoft SSO error:", ctx.error);
      toast.error(ctx.error.message ?? "Failed to sign in with Microsoft");
    },
  }
);

Environment:

  • Framework: NextJS
  • Provider: Microsoft SSO

Notes / Possible Fix:
It seems that the callbackURL parameter takes precedence and short-circuits the intended 2FA redirect. A possible solution might be to handle 2FA verification before applying callbackURL.

Current vs. Expected behavior

Expected behavior:

  • If the user has 2FA enabled, they should be redirected to /2fa before accessing /dashboard, even when callbackURL is set.
  • The onSuccess callback should consistently handle the twoFactorRedirect flag.

Actual behavior:

  • With callbackURL: "/dashboard", the app bypasses the onSuccess logic and goes straight to /dashboard, skipping 2FA verification.

What version of Better Auth are you using?

1.0.7

System info

System:
    OS: macOS 15.6
    CPU: (8) arm64 Apple M1
    Memory: 122.30 MB / 16.00 GB
    Shell: 5.9 - /bin/zsh
  Browsers:
    Chrome: 139.0.7258.155
    Safari: 18.6

Which area(s) are affected? (Select all that apply)

Client, Backend

Auth config (if applicable)

import { betterAuth } from "better-auth"
export const auth = betterAuth({
  emailAndPassword: {  
    enabled: true
  },
});

Additional context

No response

Originally created by @fyfirman on GitHub (Sep 1, 2025). ### Is this suited for github? - [ ] Yes, this is suited for github ### To Reproduce When using SSO login with Microsoft (or other providers) and setting `callbackURL: "/dashboard"`, the 2FA verification step is skipped. Instead of respecting the `onSuccess` handler logic for `twoFactorRedirect`, the SSO flow immediately redirects to `/dashboard`. **Code Example:** ```ts await authClient.signIn.social( { provider: "microsoft", callbackURL: "/dashboard", }, { onSuccess: async (ctx) => { if (ctx.data.twoFactorRedirect) { // User has 2FA enabled, redirect to verification page router.push("/2fa"); } else { // Normal login flow router.push("/dashboard"); } }, onError: (ctx) => { console.error("Microsoft SSO error:", ctx.error); toast.error(ctx.error.message ?? "Failed to sign in with Microsoft"); }, } ); ``` **Environment:** * Framework: NextJS * Provider: Microsoft SSO **Notes / Possible Fix:** It seems that the `callbackURL` parameter takes precedence and short-circuits the intended 2FA redirect. A possible solution might be to handle 2FA verification before applying `callbackURL`. ### Current vs. Expected behavior **Expected behavior:** * If the user has 2FA enabled, they should be redirected to `/2fa` before accessing `/dashboard`, even when `callbackURL` is set. * The `onSuccess` callback should consistently handle the `twoFactorRedirect` flag. **Actual behavior:** * With `callbackURL: "/dashboard"`, the app bypasses the `onSuccess` logic and goes straight to `/dashboard`, skipping 2FA verification. ### What version of Better Auth are you using? 1.0.7 ### System info ```bash System: OS: macOS 15.6 CPU: (8) arm64 Apple M1 Memory: 122.30 MB / 16.00 GB Shell: 5.9 - /bin/zsh Browsers: Chrome: 139.0.7258.155 Safari: 18.6 ``` ### Which area(s) are affected? (Select all that apply) Client, Backend ### Auth config (if applicable) ```typescript import { betterAuth } from "better-auth" export const auth = betterAuth({ emailAndPassword: { enabled: true }, }); ``` ### Additional context _No response_
GiteaMirror added the nextjs label 2026-03-13 09:05:27 -05:00
Author
Owner

@dosubot[bot] commented on GitHub (Sep 1, 2025):

Hi @fyfirman! I'm Dosu and I’m helping the better-auth team.

This is suited for a GitHub issue. Better Auth v1.0.7 does not enforce 2FA in SSO flows when using callbackURL—after successful SSO authentication, the backend immediately redirects to the callbackURL and creates a session, skipping any 2FA checks. The two-factor enforcement middleware only applies to credential-based logins (email, username, phone), not SSO endpoints, so the client-side onSuccess handler for twoFactorRedirect cannot be reliably used in SSO flows. Fixing this would require significant changes to backend logic to check 2FA status before honoring callbackURL, which is a structural limitation in the current version. No recent pull requests or documentation address this issue, so it is not a known or intended feature and should be tracked for resolution.
SSO plugin code
2FA plugin code

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other  Chat with Dosu Join Discord Share on X

@dosubot[bot] commented on GitHub (Sep 1, 2025): <!-- Greeting --> Hi @fyfirman! I'm [Dosu](https://go.dosu.dev/dosubot) and I’m helping the better-auth team. <!-- Answer --> This is suited for a GitHub issue. Better Auth v1.0.7 does not enforce 2FA in SSO flows when using callbackURL—after successful SSO authentication, the backend immediately redirects to the callbackURL and creates a session, skipping any 2FA checks. The two-factor enforcement middleware only applies to credential-based logins (email, username, phone), not SSO endpoints, so the client-side onSuccess handler for twoFactorRedirect cannot be reliably used in SSO flows. Fixing this would require significant changes to backend logic to check 2FA status before honoring callbackURL, which is a structural limitation in the current version. No recent pull requests or documentation address this issue, so it is not a known or intended feature and should be tracked for resolution. [SSO plugin code](https://github.com/better-auth/better-auth/blob/cf7f2f118147b6a79a99fcf48898408f038390f5/packages/better-auth/src/plugins/sso/index.ts) [2FA plugin code](https://github.com/better-auth/better-auth/blob/cf7f2f118147b6a79a99fcf48898408f038390f5/packages/better-auth/src/plugins/two-factor/index.ts) <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback/5198e781-d6e2-443b-9d29-4ec85633984a?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/5198e781-d6e2-443b-9d29-4ec85633984a?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/5198e781-d6e2-443b-9d29-4ec85633984a?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/5198e781-d6e2-443b-9d29-4ec85633984a?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/5198e781-d6e2-443b-9d29-4ec85633984a?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/5198e781-d6e2-443b-9d29-4ec85633984a?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/5198e781-d6e2-443b-9d29-4ec85633984a?feedback_type=other)</sup>&nbsp;&nbsp;[![Chat with Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/cdda13d9-dd27-4d31-b09a-5d8bec92de21/ask?utm_source=github)&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot)&nbsp;[![Share on X](https://img.shields.io/badge/X-share-black)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/better-auth/better-auth/issues/4340)
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#1818