nextauthjs / next-auth

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

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

Open gislerro opened 2 months ago

gislerro commented 2 months ago

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

Expected behavior

gislerro commented 2 months ago

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