Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom signin page leads to CSRF mismatch when navigating to it with signIn() #11713

Open
gislerro opened this issue Aug 29, 2024 · 1 comment
Labels
bug Something isn't working triage Unseen or unconfirmed by a maintainer yet. Provide extra information in the meantime.

Comments

@gislerro
Copy link

gislerro commented Aug 29, 2024

Environment

  System:
    OS: Linux 5.15 Debian GNU/Linux 12 (bookworm) 12 (bookworm)
    CPU: (24) x64 13th Gen Intel(R) Core(TM) i7-13700K
    Memory: 4.78 GB / 15.51 GB
    Container: Yes
    Shell: 5.2.15 - /bin/bash
  Binaries:
    Node: 22.6.0 - /usr/local/bin/node
    Yarn: 1.22.22 - /usr/local/bin/yarn
    npm: 10.8.2 - /usr/local/bin/npm
    pnpm: 9.7.1 - /usr/local/share/npm-global/bin/pnpm

Reproduction URL

https://github.com/gislerro/authjs-customsignin-issue

Describe the issue

I followed the getting started and the custom pages documentation to setup a basic site with a custom signin page and a signin/signout button on the homepage.

However the social signin with GitHub doesn't work on my custom signin page when being directed to it with the signIn() method.

The redirect to my custom error page doesn't occur on AuthErrors either.

Note that signing in works when navigating directly to the custom signin page /signin

How to reproduce

  • clone the repo
  • add AUTH_GITHUB_ID, AUTH_GITHUB_SECRET. AUTH_SECRET to .env.local for github provider
  • go to the homepage
  • click on Signin, I'm being redirected to api/auth/signin?callbackUrl=... and not to my custom signin page /signin?callbackUrl=...
  • click on Signin with GitHub, which leads to the following CSRF mismatch:
 POST /api/auth/signin?callbackUrl=http%3A%2F%2Flocalhost%3A3000%2F 302 in 8ms
 GET /signin?error=MissingCSRF 200 in 13ms
[auth][error] MissingCSRF: CSRF token was missing during an action signin. Read more at https://errors.authjs.dev#missingcsrf
    at validateCSRF (webpack-internal:///(rsc)/./node_modules/@auth/core/lib/actions/callback/oauth/csrf-token.js:45:11)
    at AuthInternal (webpack-internal:///(rsc)/./node_modules/@auth/core/lib/index.js:71:100)
    at async Auth (webpack-internal:///(rsc)/./node_modules/@auth/core/index.js:126:34)
    at async /workspaces/typescript-node-2/my-app/node_modules/next/dist/compiled/next-server/app-route.runtime.dev.js:6:55038
    at async ek.execute (/workspaces/typescript-node-2/my-app/node_modules/next/dist/compiled/next-server/app-route.runtime.dev.js:6:45808)
    at async ek.handle (/workspaces/typescript-node-2/my-app/node_modules/next/dist/compiled/next-server/app-route.runtime.dev.js:6:56292)
    at async doRender (/workspaces/typescript-node-2/my-app/node_modules/next/dist/server/base-server.js:1357:42)
    at async cacheEntry.responseCache.get.routeKind (/workspaces/typescript-node-2/my-app/node_modules/next/dist/server/base-server.js:1567:40)
    at async DevServer.renderToResponseWithComponentsImpl (/workspaces/typescript-node-2/my-app/node_modules/next/dist/server/base-server.js:1487:28)
    at async DevServer.renderPageComponent (/workspaces/typescript-node-2/my-app/node_modules/next/dist/server/base-server.js:1911:24)
    at async DevServer.renderToResponseImpl (/workspaces/typescript-node-2/my-app/node_modules/next/dist/server/base-server.js:1949:32)
    at async DevServer.pipeImpl (/workspaces/typescript-node-2/my-app/node_modules/next/dist/server/base-server.js:916:25)
    at async NextNodeServer.handleCatchallRenderRequest (/workspaces/typescript-node-2/my-app/node_modules/next/dist/server/next-server.js:272:17)
    at async DevServer.handleRequestImpl (/workspaces/typescript-node-2/my-app/node_modules/next/dist/server/base-server.js:812:17)
    at async /workspaces/typescript-node-2/my-app/node_modules/next/dist/server/dev/next-dev-server.js:339:20
    at async Span.traceAsyncFn (/workspaces/typescript-node-2/my-app/node_modules/next/dist/trace/trace.js:154:20)
    at async DevServer.handleRequest (/workspaces/typescript-node-2/my-app/node_modules/next/dist/server/dev/next-dev-server.js:336:24)
    at async invokeRender (/workspaces/typescript-node-2/my-app/node_modules/next/dist/server/lib/router-server.js:173:21)
    at async handleRequest (/workspaces/typescript-node-2/my-app/node_modules/next/dist/server/lib/router-server.js:350:24)
    at async requestHandlerImpl (/workspaces/typescript-node-2/my-app/node_modules/next/dist/server/lib/router-server.js:374:13)
    at async Server.requestListener (/workspaces/typescript-node-2/my-app/node_modules/next/dist/server/lib/start-server.js:141:13)
 POST /api/auth/signin?callbackUrl=http%3A%2F%2Flocalhost%3A3000%2F 302 in 7ms
 GET /signin?error=MissingCSRF 200 in 14ms
  • I'm not even redirected to the error page defined in src/auth.ts

Expected behavior

  • I click on the sign in button on the homepage, go to the sign in page, sign in with GitHub and get redirected back to the homepage
  • I should be redirected to my error page on AuthErrors
@gislerro gislerro added bug Something isn't working triage Unseen or unconfirmed by a maintainer yet. Provide extra information in the meantime. labels Aug 29, 2024
@gislerro
Copy link
Author

I think the issue is in next-auth/src/lib/actions.ts and the resulting call to core/src/lib/utils/env.ts:createActionURL. The action URL doesn't consider the custom pages defined in the AuthConfig

type SignInParams = Parameters<NextAuthResult["signIn"]>
export async function signIn(
  provider: SignInParams[0],
  options: SignInParams[1] = {},
  authorizationParams: SignInParams[2],
  config: NextAuthConfig
) {
  const headers = new Headers(nextHeaders())
  const {
    redirect: shouldRedirect = true,
    redirectTo,
    ...rest
  } = options instanceof FormData ? Object.fromEntries(options) : options


  const callbackUrl = redirectTo?.toString() ?? headers.get("Referer") ?? "/"
  const signInURL = createActionURL(
    "signin",
    // @ts-expect-error `x-forwarded-proto` is not nullable, next.js sets it by default
    headers.get("x-forwarded-proto"),
    headers,
    process.env,
    config
  )

 // signInUrl = config.basePath + /signin
//  Expected: signInUrl = config.pages.signIn = '/signin'

  if (!provider) {
    signInURL.searchParams.append("callbackUrl", callbackUrl)
    if (shouldRedirect) redirect(signInURL.toString())
    return signInURL.toString()
  }

...
export function createActionURL(
  action: AuthAction,
  protocol: string,
  headers: Headers,
  envObject: any,
  config: Pick<AuthConfig, "basePath" | "logger">  // <----- should consider pages config
): URL {
   ...
}

Note that in my repro when I replace the await signIn() with await redirect('/signin') on the sign in button, I get the expected behavior

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working triage Unseen or unconfirmed by a maintainer yet. Provide extra information in the meantime.
Projects
None yet
Development

No branches or pull requests

1 participant