nextauthjs / next-auth

Authentication for the Web.
https://authjs.dev
ISC License
25.12k stars 3.56k forks source link

Opening login page in 2 separate tabs results in RPError: state mismatch #7894

Open benitazhang opened 1 year ago

benitazhang commented 1 year ago

Environment

System: OS: macOS 11.7.2 CPU: (8) x64 Intel(R) Core(TM) i7-8569U CPU @ 2.80GHz Memory: 292.34 MB / 16.00 GB Shell: 5.8 - /bin/zsh Binaries: Node: 18.15.0 - ~/.nvm/versions/node/v18.15.0/bin/node Yarn: 1.22.19 - /usr/local/bin/yarn npm: 9.5.0 - ~/.nvm/versions/node/v18.15.0/bin/npm Browsers: Chrome: 114.0.5735.198 Safari: 16.2

Reproduction URL

https://github.com/benitazhang/next-auth-example

Describe the issue

Here's a simplified version of the code I'm using:

export default function App({
  Component,
  pageProps: { session, ...pageProps },
}: AppProps) {
  return (
    <SessionProvider session={session}>
      <Auth>
        <Component {...pageProps} />
      </Auth>
    </SessionProvider>
  )
}

function Auth({ children }: { children: JSX.Element }) {
  const { data, status } = useSession();
  const isUser = !!data?.user;

  useEffect(() => {
    if (status !== "loading" && !isUser) {
      signIn("auth0");
    }
  }, [isUser, status]);

  if (isUser) {
    const user = data.user;
    return children;
  }

  return <div></div>;
}

It's similar to the suggestions from this conversation

I'm calling signIn in the useEffect so that I can bypass the NextAuth login page and automatically redirect to the Auth0 login widget. This works perfectly when just using a single tab. However, If I open 2 separate tabs that both redirect to the login widget. Attempting to login on the 1st tab results in an error that looks like

https://next-auth.js.org/errors#oauth_callback_error state mismatch, expected N8V3aJhJiGJmNg39DaTUPpHEIw4-K4QN8zmHu53Q, got: xTEKk8YUiz1IzGiCFMav2fswzsV {
  error: RPError: state mismatch, expected N8V3aJhJiGJmNg39DaTUPpHEIw4-K4QN8zmHu53Q, got: xTEKk8YUiz1IzGiCFMav2fswzsV

How to reproduce

the example uses auth0, so you'll need to configure the appropriate env var

1) start up the app with npm run dev 2) open 2 separate tabs and navigate to localhost:3000. you should be redirected to the auth0 login widget 3) attempt to signin on the first tab you opened, it should fail with a state mismatch error 4) attempt to signin on the second tab, it should succeed

Expected behavior

I should be able to login to both tabs

paul-vd commented 1 year ago

It might be better to do this with a middleware? for example

export { default } from 'next-auth/middleware';

export const config = { matcher: ['/dashboard'] }; // all the routes that require auth

I'm not sure but I think the issue you are having is that each tab is generating their own instance for the login token which communicates with auth0 so then 1 of them will no longer be valid (probably the first one which triggered)

But i'm just assuming here, I'm not certain.

mrviniciux commented 1 year ago

This is currently hapenning to me as well using:

node v18.17.1 next: "13.3.0", next-auth: "4.20.1" (also tried on versions: 4.24.4, 4.20.1, 4.3.1, 4.18.4) Provider: Keycloak

@paul-vd I tried to use middleware but the problem still persists

[next-auth][error][OAUTH_CALLBACK_ERROR] 
https://next-auth.js.org/errors#oauth_callback_error state mismatch, expected eJPM-WYI5Tue4tSEjOUIjKZgrcdSvCf3zykTKf8GLvU, got: mtW4R7QYK_5om2RR7bX4GeJM8ESnXPDsVf2GgLpNaYM {
  error: RPError: state mismatch, expected eJPM-WYI5Tue4tSEjOUIjKZgrcdSvCf3zykTKf8GLvU, got: mtW4R7QYK_5om2RR7bX4GeJM8ESnXPDsVf2GgLpNaYM
      at Client.callback (/Users/***/Software/***/node_modules/openid-client/lib/client.js:416:13)
      at oAuthCallback (/Users/***/Software/***/node_modules/next-auth/core/lib/oauth/callback.js:109:29)
      at async Object.callback (/Users/***/Software/***/node_modules/next-auth/core/routes/callback.js:52:11)
      at async AuthHandler (/Users/***/Software/***/node_modules/next-auth/core/index.js:201:28)
      at async NextAuthHandler (/Users/***/Software/***/node_modules/next-auth/next/index.js:24:19)
      at async auth (webpack-internal:///(api)/./src/pages/api/auth/[...nextauth].ts:34:20) {
    name: 'OAuthCallbackError',
    code: undefined
  },
  providerId: 'keycloak',
  message: 'state mismatch, expected eJPM-WYI5Tue4tSEjOUIjKZgrcdSvCf3zykTKf8GLvU, got: mtW4R7QYK_5om2RR7bX4GeJM8ESnXPDsVf2GgLpNaYM'
}
mrviniciux commented 1 year ago

Here's a detailed log of issue

Opened login window 1

[next-auth][debug][CREATE_STATE] { value: 'xSWq_6Aus3I6S-v60W65HuDvqLEbMlx-4nb2F2pSYdA', maxAge: 900 }
[next-auth][debug][CREATE_PKCECODEVERIFIER] { value: 'n_Z4rdneU9v2G0V7tEWHCUlkSFZ96MVMg2DKYsDzpp0', maxAge: 900 }
[next-auth][debug][GET_AUTHORIZATION_URL] {
  url: 'https://mykeycloakdomain.com/auth/realms/my-app/protocol/openid-connect/auth?client_id=my-app-front-local&scope=openid&response_type=code&redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Fapi%2Fauth%2Fcallback%2Fkeycloak&state=xSWq_6Aus3I6S-v60W65HuDvqLEbMlx-4nb2F2pSYdA&code_challenge=3ifudhCUd5GrZl09nQZd4VBsZw0KI7iU8Gxr1h-4wKY&code_challenge_method=S256',
  cookies: [
    {
      name: 'next-auth.state',
      value: 'eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIn0..MnD2KLorZF_-pgRc.jhhR5_z4SqFAUJirGkt8KrMLZ10iGi_59s01BYOSaSudJ730wAJwqzOAgythnJtchhGN2RjcgpgKvO8n-Sp2taWLG9WLA48ETABRiRY61WuOTjtPEVwnWtrkTl1MhEoD8FWkjmK6lFZJ4RVF4hnhcNJkJ_0aEYE8wVhbSGZ10qWFHNHvLX0.tIPIimvkuHpA6tOHWi-krQ',
      options: [Object]
    },
    {
      name: 'next-auth.pkce.code_verifier',
      value: 'eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIn0..BrAha9DPEUX4VXt0.dl_jXCoT7hlc2L4kHp445KrN0oN8DLU7cDyCnGohs6BeffRJcTGj6viBNsW5AJLjvDAUuGnQJoJyjDigjXKhCNb45oUBJ7OT-tHvX9dMDwE8sl3y_Pk3FBMKJbjpt9uBrGONE4cslzpSPmJqskLLqd9RtN0JleB8_IJM9bYmDRgjYs244uc.8NU8br68xlaG_GJz6xzYsA',
      options: [Object]
    }
  ],
  provider: {
    id: 'keycloak',
    name: 'Keycloak',
    wellKnown: 'https://mykeycloakdomain.com/auth/realms/my-app/.well-known/openid-configuration',
    type: 'oauth',
    authorization: { params: [Object] },
    checks: [ 'pkce', 'state' ],
    idToken: true,
    profile: [Function: profile],
    style: {
      logo: '/keycloak.svg',
      logoDark: '/keycloak.svg',
      bg: '#fff',
      text: '#000',
      bgDark: '#fff',
      textDark: '#000'
    },
    clientId: 'my-app-front-local',
    clientSecret: 'sY40FqoWSZc1fQKOKXJ9/T2P+VyXMXqSkfPffS/yGS8s=',
    issuer: 'https://mykeycloakdomain.com/auth/realms/my-app',
    signinUrl: 'http://localhost:3000/api/auth/signin/keycloak',
    callbackUrl: 'http://localhost:3000/api/auth/callback/keycloak'
  }
}

Then, I opened another window (also happens in a new tab). Login window 2:


[next-auth][debug][CREATE_STATE] { value: 'yOxao7slDobEiuE6RB6DmnsIFIgF8wKmyBlbaJH2bmA', maxAge: 900 }
[next-auth][debug][CREATE_PKCECODEVERIFIER] { value: '_wuF74vwU2zzdqvCM4a8qf5CyjmB51bBsFr0Q3wkxHk', maxAge: 900 }
[next-auth][debug][GET_AUTHORIZATION_URL] {
  url: 'https://mykeycloakdomain.com/auth/realms/my-app/protocol/openid-connect/auth?client_id=my-app-front-local&scope=openid&response_type=code&redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Fapi%2Fauth%2Fcallback%2Fkeycloak&state=yOxao7slDobEiuE6RB6DmnsIFIgF8wKmyBlbaJH2bmA&code_challenge=f9xWVhs_a2P2URlDVREj2jLLGlj-nuk63RaLnGcSWr4&code_challenge_method=S256',
  cookies: [
    {
      name: 'next-auth.state',
      value: 'eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIn0..elwCd5tBzJnPeO-5.qsvfa4udYlLxRbh1eMABQi41o6zSqOX6ybCWk24vOC8TsJQ4yi4uj7a9D1LNYdA4__n0ymvklJng5zCGiMIqCMNuC0MGwG3cB6UH21kfGfU1YKNsIIElgq3Dw8dE22NsIbJC8HYxDdEw8jHaS7-gcSZM1VHqxPwdloTgYwbXPioorRvTsv0.ZoIJva1qcURAXm-CS0Dhjw',
      options: [Object]
    },
    {
      name: 'next-auth.pkce.code_verifier',
      value: 'eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIn0..wK54vyG80yMRqj5P.MX7ycF55dZbCIe8W6mtnvJGG85ZXZ0AUQG6NQvtQklpnOsPW6RvFJAL2wuUfsBVBn2TnAGYFzNMjBxlG6PpwDihdB13erBCaAUERQHvw3zLpQLL_TRsETvkufgCRtlOCs81tOKaA_LRfhUazUeBFaCzQU3svyEfXRf4ZnnV5CXMllzJeDEs.hTO119nDH3RoLMWcD78x9w',
      options: [Object]
    }
  ],
  provider: {
    id: 'keycloak',
    name: 'Keycloak',
    wellKnown: 'https://mykeycloakdomain.com/auth/realms/my-app/.well-known/openid-configuration',
    type: 'oauth',
    authorization: { params: [Object] },
    checks: [ 'pkce', 'state' ],
    idToken: true,
    profile: [Function: profile],
    style: {
      logo: '/keycloak.svg',
      logoDark: '/keycloak.svg',
      bg: '#fff',
      text: '#000',
      bgDark: '#fff',
      textDark: '#000'
    },
    clientId: 'my-app-front-local',
    clientSecret: 'sY40FqoWSZc1fQKOKXJ9/T2P+VyXMXqSkfPffS/yGS8s=',
    issuer: 'https://mykeycloakdomain.com/auth/realms/my-app',
    signinUrl: 'http://localhost:3000/api/auth/signin/keycloak',
    callbackUrl: 'http://localhost:3000/api/auth/callback/keycloak'
  }
}

Now I went to window number 1 and tried to login:

[next-auth][error][OAUTH_CALLBACK_ERROR] 
https://next-auth.js.org/errors#oauth_callback_error state mismatch, expected yOxao7slDobEiuE6RB6DmnsIFIgF8wKmyBlbaJH2bmA, got: xSWq_6Aus3I6S-v60W65HuDvqLEbMlx-4nb2F2pSYdA {
  error: RPError: state mismatch, expected yOxao7slDobEiuE6RB6DmnsIFIgF8wKmyBlbaJH2bmA, got: xSWq_6Aus3I6S-v60W65HuDvqLEbMlx-4nb2F2pSYdA
      at Client.callback (/Users/99765/Software/my-app-front/node_modules/openid-client/lib/client.js:399:13)
      at oAuthCallback (/Users/99765/Software/my-app-front/node_modules/next-auth/core/lib/oauth/callback.js:109:29)
      at async Object.callback (/Users/99765/Software/my-app-front/node_modules/next-auth/core/routes/callback.js:58:11)
      at async AuthHandler (/Users/99765/Software/my-app-front/node_modules/next-auth/core/index.js:201:28)
      at async NextAuthHandler (/Users/99765/Software/my-app-front/node_modules/next-auth/next/index.js:24:19)
      at async auth (webpack-internal:///(api)/./src/pages/api/auth/[...nextauth].ts:29:20) {
    name: 'OAuthCallbackError',
    code: undefined
  },
  providerId: 'keycloak',
  message: 'state mismatch, expected yOxao7slDobEiuE6RB6DmnsIFIgF8wKmyBlbaJH2bmA, got: xSWq_6Aus3I6S-v60W65HuDvqLEbMlx-4nb2F2pSYdA'
}

Client side error:

Captura de Tela 2023-11-09 às 18 16 47

The exception is clearly throwing because ~next-auth~ openid-client state was expecting the state id from window 2 instead of 1

dmunch commented 5 months ago

I assume this discussion went stale since there's no real solution for this?