Skip to content

Commit aedabc8

Browse files
fix: avoid redirect on always public paths (#5000)
* type safe babel config * avoid auth redirect for `_next` * force render default error page on user miconfig * add slash to _next path * use `.some` * add docs * change from localhost * add favicon to public path
1 parent 9f2cdad commit aedabc8

File tree

5 files changed

+55
-15
lines changed

5 files changed

+55
-15
lines changed

docs/docs/configuration/nextjs.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,10 @@ Callbacks are asynchronous functions you can use to control what happens when an
137137

138138
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.
139139

140+
:::note
141+
This should match the `pages` configuration that's found in `[...nextauth].ts`.
142+
:::
143+
140144
#### Example (default value)
141145

142146
```js

docs/docs/errors.md

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ This error occurs when there was an issue deleting the session from the database
152152

153153
---
154154

155-
### Other
155+
### Configuration
156156

157157
#### MISSING_NEXTAUTH_API_ROUTE_ERROR
158158

@@ -164,6 +164,23 @@ Make sure the file is there and the filename is written correctly.
164164

165165
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)
166166

167+
168+
#### AUTH_ON_ERROR_PAGE_ERROR
169+
170+
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.
171+
172+
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).
173+
174+
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.
175+
176+
Useful links:
177+
178+
- https://next-auth.js.org/configuration/nextjs#pages
179+
- https://next-auth.js.org/configuration/pages
180+
- https://nextjs.org/docs/advanced-features/middleware#matcher
181+
182+
### Other
183+
167184
#### oauth_callback_error expected 200 OK with body but no body was returned
168185

169186
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)

packages/next-auth/config/babel.config.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1+
// @ts-check
12
// We aim to have the same support as Next.js
23
// https://nextjs.org/docs/getting-started#system-requirements
34
// https://nextjs.org/docs/basic-features/supported-browsers-features
45

6+
/** @type {import("@babel/core").ConfigFunction} */
57
module.exports = (api) => {
68
const isTest = api.env("test")
79
if (isTest) {

packages/next-auth/src/core/index.ts

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -96,13 +96,28 @@ export async function NextAuthHandler<
9696
// Bail out early if there's an error in the user config
9797
const { pages, theme } = userOptions
9898
logger.error(assertionResult.code, assertionResult)
99-
if (pages?.error) {
100-
return {
101-
redirect: `${pages.error}?error=Configuration`,
99+
100+
const authOnErrorPage =
101+
pages?.error &&
102+
req.action === "signin" &&
103+
req.query?.callbackUrl.startsWith(pages.error)
104+
105+
if (!pages?.error || authOnErrorPage) {
106+
if (authOnErrorPage) {
107+
logger.error(
108+
"AUTH_ON_ERROR_PAGE_ERROR",
109+
new Error(
110+
`The error page ${pages?.error} should not require authentication`
111+
)
112+
)
102113
}
114+
const render = renderPage({ theme })
115+
return render.error({ error: "configuration" })
116+
}
117+
118+
return {
119+
redirect: `${pages.error}?error=Configuration`,
103120
}
104-
const render = renderPage({ theme })
105-
return render.error({ error: "configuration" })
106121
}
107122

108123
const { action, providerId, error, method = "GET" } = req

packages/next-auth/src/next/middleware.ts

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -101,13 +101,18 @@ async function handleMiddleware(
101101
options: NextAuthMiddlewareOptions | undefined,
102102
onSuccess?: (token: JWT | null) => Promise<NextMiddlewareResult>
103103
) {
104+
const { pathname, search, origin } = req.nextUrl
105+
104106
const signInPage = options?.pages?.signIn ?? "/api/auth/signin"
105107
const errorPage = options?.pages?.error ?? "/api/auth/error"
106108
const basePath = parseUrl(process.env.NEXTAUTH_URL).path
107-
// Avoid infinite redirect loop
109+
const publicPaths = [signInPage, errorPage, "/_next", "/favicon.ico"]
110+
111+
// Avoid infinite redirects/invalid response
112+
// on paths that never require authentication
108113
if (
109-
req.nextUrl.pathname.startsWith(basePath) ||
110-
[signInPage, errorPage].includes(req.nextUrl.pathname)
114+
pathname.startsWith(basePath) ||
115+
publicPaths.some((p) => pathname.startsWith(p))
111116
) {
112117
return
113118
}
@@ -119,7 +124,7 @@ async function handleMiddleware(
119124
`\nhttps://next-auth.js.org/errors#no_secret`
120125
)
121126

122-
const errorUrl = new URL(errorPage, req.nextUrl.origin)
127+
const errorUrl = new URL(errorPage, origin)
123128
errorUrl.searchParams.append("error", "Configuration")
124129

125130
return NextResponse.redirect(errorUrl)
@@ -139,11 +144,8 @@ async function handleMiddleware(
139144
if (isAuthorized) return await onSuccess?.(token)
140145

141146
// the user is not logged in, redirect to the sign-in page
142-
const signInUrl = new URL(signInPage, req.nextUrl.origin)
143-
signInUrl.searchParams.append(
144-
"callbackUrl",
145-
`${req.nextUrl.pathname}${req.nextUrl.search}`
146-
)
147+
const signInUrl = new URL(signInPage, origin)
148+
signInUrl.searchParams.append("callbackUrl", `${pathname}${search}`)
147149
return NextResponse.redirect(signInUrl)
148150
}
149151

0 commit comments

Comments
 (0)