diff --git a/docs/docs/configuration/nextjs.md b/docs/docs/configuration/nextjs.md index a04d04c324..d73d73f972 100644 --- a/docs/docs/configuration/nextjs.md +++ b/docs/docs/configuration/nextjs.md @@ -137,6 +137,10 @@ Callbacks are asynchronous functions you can use to control what happens when an Specify URLs to be used if you want to create custom sign in, and error pages. Pages specified will override the corresponding built-in page. +:::note +This should match the `pages` configuration that's found in `[...nextauth].ts`. +::: + #### Example (default value) ```js diff --git a/docs/docs/errors.md b/docs/docs/errors.md index 3fd1e39f20..ba503618a8 100644 --- a/docs/docs/errors.md +++ b/docs/docs/errors.md @@ -152,7 +152,7 @@ This error occurs when there was an issue deleting the session from the database --- -### Other +### Configuration #### MISSING_NEXTAUTH_API_ROUTE_ERROR @@ -164,6 +164,23 @@ Make sure the file is there and the filename is written correctly. In production, we expect you to define a `secret` property in your configuration. In development, this is shown as a warning for convenience. [Read more](/configuration/options#secret) + +#### AUTH_ON_ERROR_PAGE_ERROR + +You have a custom error page defined that was rendered due to an error, but the page also required authentication. To avoid an infinite redirect loop, NextAuth.js bailed out and rendered its default error page instead. + +If you are using a Middleware, make sure you include the same `pages` configuration in your `middleware.ts` and `[...nextauth].ts` files. Or use the `matcher` option to only require authentication for certain sites (and exclude your custom error page). + +If you do not use a Middleware, make sure you don't try redirecting the user to the sign-in page when hitting your custom error page. + +Useful links: + +- https://next-auth.js.org/configuration/nextjs#pages +- https://next-auth.js.org/configuration/pages +- https://nextjs.org/docs/advanced-features/middleware#matcher + +### Other + #### oauth_callback_error expected 200 OK with body but no body was returned This error might happen with some of the providers. It happens due to `openid-client`(which is peer dependency) node version mismatch. For instance, `openid-client` requires `>=14.2.0` for `lts/fermium` and has similar limits for the other versions. For the full list of the compatible node versions please see [package.json](https://github.com/panva/node-openid-client/blob/2a84e46992e1ebeaf685c3f87b65663d126e81aa/package.json#L78) diff --git a/packages/next-auth/config/babel.config.js b/packages/next-auth/config/babel.config.js index db46e1d353..965aef77bf 100644 --- a/packages/next-auth/config/babel.config.js +++ b/packages/next-auth/config/babel.config.js @@ -1,7 +1,9 @@ +// @ts-check // We aim to have the same support as Next.js // https://nextjs.org/docs/getting-started#system-requirements // https://nextjs.org/docs/basic-features/supported-browsers-features +/** @type {import("@babel/core").ConfigFunction} */ module.exports = (api) => { const isTest = api.env("test") if (isTest) { diff --git a/packages/next-auth/src/core/index.ts b/packages/next-auth/src/core/index.ts index 7acd7454b9..a49418ba6b 100644 --- a/packages/next-auth/src/core/index.ts +++ b/packages/next-auth/src/core/index.ts @@ -96,13 +96,28 @@ export async function NextAuthHandler< // Bail out early if there's an error in the user config const { pages, theme } = userOptions logger.error(assertionResult.code, assertionResult) - if (pages?.error) { - return { - redirect: `${pages.error}?error=Configuration`, + + const authOnErrorPage = + pages?.error && + req.action === "signin" && + req.query?.callbackUrl.startsWith(pages.error) + + if (!pages?.error || authOnErrorPage) { + if (authOnErrorPage) { + logger.error( + "AUTH_ON_ERROR_PAGE_ERROR", + new Error( + `The error page ${pages?.error} should not require authentication` + ) + ) } + const render = renderPage({ theme }) + return render.error({ error: "configuration" }) + } + + return { + redirect: `${pages.error}?error=Configuration`, } - const render = renderPage({ theme }) - return render.error({ error: "configuration" }) } const { action, providerId, error, method = "GET" } = req diff --git a/packages/next-auth/src/next/middleware.ts b/packages/next-auth/src/next/middleware.ts index c230d8240e..9702d155e2 100644 --- a/packages/next-auth/src/next/middleware.ts +++ b/packages/next-auth/src/next/middleware.ts @@ -101,13 +101,18 @@ async function handleMiddleware( options: NextAuthMiddlewareOptions | undefined, onSuccess?: (token: JWT | null) => Promise ) { + const { pathname, search, origin } = req.nextUrl + const signInPage = options?.pages?.signIn ?? "/api/auth/signin" const errorPage = options?.pages?.error ?? "/api/auth/error" const basePath = parseUrl(process.env.NEXTAUTH_URL).path - // Avoid infinite redirect loop + const publicPaths = [signInPage, errorPage, "/_next", "/favicon.ico"] + + // Avoid infinite redirects/invalid response + // on paths that never require authentication if ( - req.nextUrl.pathname.startsWith(basePath) || - [signInPage, errorPage].includes(req.nextUrl.pathname) + pathname.startsWith(basePath) || + publicPaths.some((p) => pathname.startsWith(p)) ) { return } @@ -119,7 +124,7 @@ async function handleMiddleware( `\nhttps://next-auth.js.org/errors#no_secret` ) - const errorUrl = new URL(errorPage, req.nextUrl.origin) + const errorUrl = new URL(errorPage, origin) errorUrl.searchParams.append("error", "Configuration") return NextResponse.redirect(errorUrl) @@ -139,11 +144,8 @@ async function handleMiddleware( if (isAuthorized) return await onSuccess?.(token) // the user is not logged in, redirect to the sign-in page - const signInUrl = new URL(signInPage, req.nextUrl.origin) - signInUrl.searchParams.append( - "callbackUrl", - `${req.nextUrl.pathname}${req.nextUrl.search}` - ) + const signInUrl = new URL(signInPage, origin) + signInUrl.searchParams.append("callbackUrl", `${pathname}${search}`) return NextResponse.redirect(signInUrl) }