nextauthjs / next-auth

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

Overriding the Error Page #7880

Closed harleyharl closed 1 year ago

harleyharl commented 1 year ago

Question 💬

When I get errors during the OAuth login process, I'm sent back to this page:
http://localhost:3000/api/auth/signin?error=OAuthCallback

However, I want to redirect users to an error like this: http://localhost:3000/auth/error?error=OAuthCallback

And I have a component defined at pages/auth/error to display the error and I've overridden the default error page like this in my configuration file (as shown in the docs):

pages: {
    error: '/auth/error'
}

I've found that if I overwrite the signIn page with "/auth/error" I can render the error page, but then that page is rendered on successful sign in too:

pages: {
    error: '/auth/error'
    signIn: '/auth/error'
  }

How to reproduce ☕️

pages/api/auth/[...nextAuth].ts

import NextAuth, { NextAuthOptions } from "next-auth"
import Auth0Provider from "next-auth/providers/auth0"

export const authOptions: NextAuthOptions = {
  providers: [
    Auth0Provider({
      // invalid inputs to force an error when sign in is clicked
      clientId: "",
      clientSecret: "", 
      issuer: "", 
    }),
  ],
  pages: {
    // I want users to be directed here with error passed in query string as ?error=
    error: "/auth/error", 
  },
}

export default NextAuth(authOptions)

pages/auth/error

import { useRouter } from "next/router"

const errors = {
  Signin: "Try signing with a different account.",
  OAuthSignin: "Try signing with a different account.",
  OAuthCallback: "Try signing with a different account.",
  OAuthCreateAccount: "Try signing with a different account.",
  EmailCreateAccount: "Try signing with a different account.",
  Callback: "Try signing with a different account.",
  OAuthAccountNotLinked:
    "To confirm your identity, sign in with the same account you used originally.",
  EmailSignin: "Check your email address.",
  CredentialsSignin:
    "Sign in failed. Check the details you provided are correct.",
  default: "Unable to sign in.",
}

const Error = () => {

  const router = useRouter()

  const { error } = router.query

  const errorMessage = error && (errors[error as keyof typeof errors] ?? errors.default)

  return (
    <>
      <h1>Oops! Something went wrong</h1>
      <h2>{errorMessage}</h2>
    </>
  )
}

export default Error

And the component where sign in is called (which is wrapped in a session provider, you get the idea):

import { signIn, useSession } from "next-auth/react"

export default function Header() {
  const { data: session } = useSession()
  return (
    <header>
      <div>
        <p>
          {!session && (
            <>
              <span>
                You are not signed in
              </span>
              <a
                href={`/api/auth/signin`}
                onClick={(e) => {
                  e.preventDefault()
                  signIn()
                }}
              >
                Sign in
              </a>
            </>
          )}

        </p>
      </div>
    </header>
  )
}

Contributing 🙌🏽

Yes, I am willing to help answer this question in a PR

balazsorban44 commented 1 year ago

Hi, this is intended, as some errors are shown inline on the sign-in page to give the user the option to try to sign in again. See the possible error codes and which page they belong to at: https://next-auth.js.org/configuration/pages#error-codes

BrandonCorn commented 8 months ago

Similar Question

Hi, I'm having a similar issue to what's described and I have a custom signin page. The issue I'm experiencing is that when a user already has a linked account they redirect to /api/auth/signin?error=OAuthAccountNotLinked. My expectation is to be redirected to /auth/signin?error=OAuthAccountNotLinked. I may be missing something in the documentation though I read "The following errors are passed as error query parameters to the default or overridden sign-in page:" at the page you had linked above.

Dependencies

    "next": "^13.4.14-canary.0", // Using App Router
    "next-auth": "^4.23.0",

SignIn Page

http://localhost:3000/auth/signin

Steps to Reproduce:

Expected Behavior:

NextAuth Options

export const authOptions: NextAuthOptions = {
  adapter: PrismaAdapter(prisma),
  //configure one or more auth providers
  providers: [
    GitHubProvider({
      clientId: process.env.GITHUB_CLIENT_ID!,
      clientSecret: process.env.GITHUB_CLIENT_SECRET!,
    }),
    DiscordProvider({
      clientId: process.env.DISCORD_CLIENT_ID!,
      clientSecret: process.env.DISCORD_CLIENT_SECRET!,
    }),
    CredentialsProvider({
      name: "Credentials",
      credentials: {
        email: { label: "email", type: "text", placeholder: "email" },
        password: {
          label: "password",
          type: "password",
          placeholder: "password",
        },
      },
      async authorize(credentials, req) {
        const res = await fetch(`${process.env.NEXTAUTH_URL}/api/auth/signin`, {
          method: "POST",
          body: JSON.stringify(credentials),
          headers: { "Content-Type": "application/json" },
        });

        const user = await res.json();
        //no error and user exists
        if (res.ok && user) {
          return user;
        }
        //no valid user
        return null;
      },
    }),
  ],
  pages: {
    signIn: '/auth/signin',
    error: '/auth/signin',
  },
  secret: process.env.NEXTAUTH_SECRET,
  callbacks: {
    async session({ session }){

      return session;
    },
  }, 
  session: {
    strategy: 'jwt'
  }
}

Custom signin handler uses

import { signIn } from 'next-auth/react

  const handleProviderSignIn = (
    providerName: string
  ) => {
    signIn(providerName, {
      callbackUrl: process.env.VERCEL_URL || "http://localhost:3000",
    });
  };
rajeshdavidbabu commented 6 months ago

Similar Question

Hi, I'm having a similar issue to what's described and I have a custom signin page. The issue I'm experiencing is that when a user already has a linked account they redirect to /api/auth/signin?error=OAuthAccountNotLinked. My expectation is to be redirected to /auth/signin?error=OAuthAccountNotLinked. I may be missing something in the documentation though I read "The following errors are passed as error query parameters to the default or overridden sign-in page:" at the page you had linked above.

Dependencies

    "next": "^13.4.14-canary.0", // Using App Router
    "next-auth": "^4.23.0",

SignIn Page

http://localhost:3000/auth/signin

Steps to Reproduce:

  • User with no account signs in with Github provider successfully
  • User signs out
  • User with account from Github provider signs in with Discord provider
  • Discord requests authorization
  • User is redirected to /api/auth/signin?error=OAuthAccountNotLinked

Expected Behavior:

NextAuth Options

export const authOptions: NextAuthOptions = {
  adapter: PrismaAdapter(prisma),
  //configure one or more auth providers
  providers: [
    GitHubProvider({
      clientId: process.env.GITHUB_CLIENT_ID!,
      clientSecret: process.env.GITHUB_CLIENT_SECRET!,
    }),
    DiscordProvider({
      clientId: process.env.DISCORD_CLIENT_ID!,
      clientSecret: process.env.DISCORD_CLIENT_SECRET!,
    }),
    CredentialsProvider({
      name: "Credentials",
      credentials: {
        email: { label: "email", type: "text", placeholder: "email" },
        password: {
          label: "password",
          type: "password",
          placeholder: "password",
        },
      },
      async authorize(credentials, req) {
        const res = await fetch(`${process.env.NEXTAUTH_URL}/api/auth/signin`, {
          method: "POST",
          body: JSON.stringify(credentials),
          headers: { "Content-Type": "application/json" },
        });

        const user = await res.json();
        //no error and user exists
        if (res.ok && user) {
          return user;
        }
        //no valid user
        return null;
      },
    }),
  ],
  pages: {
    signIn: '/auth/signin',
    error: '/auth/signin',
  },
  secret: process.env.NEXTAUTH_SECRET,
  callbacks: {
    async session({ session }){

      return session;
    },
  }, 
  session: {
    strategy: 'jwt'
  }
}

Custom signin handler uses

import { signIn } from 'next-auth/react

  const handleProviderSignIn = (
    providerName: string
  ) => {
    signIn(providerName, {
      callbackUrl: process.env.VERCEL_URL || "http://localhost:3000",
    });
  };

Can you check your basePath in the config and also the pages field ?

is-it-ayush commented 6 months ago

Hi! I'm stuck on the same page as OP. In which scenarios would next-auth redirect to /auth/error (if it was the overridden error page) instead of /auth/signin (if it was the overridden signin page)?