Skip to content

Commit 490d59d

Browse files
authoredOct 9, 2022
fix(middleware): improve handling of custom Next.js basePath (#5109)
* fix(middleware): improve handling of custom nextjs basePath * fix(middleware): improve extraction of nextjs base path from req.nextUrl * adapt to req.nextUrl.basePath * Fix indent * Add middleware test for custom-base and simplified code a little bit * Fix indent * Add another test * Rename basePath and nextJsBasePath * Fix lint error
1 parent 26a8c5f commit 490d59d

File tree

2 files changed

+61
-6
lines changed

2 files changed

+61
-6
lines changed
 

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

+6-6
Original file line numberDiff line numberDiff line change
@@ -101,17 +101,17 @@ async function handleMiddleware(
101101
options: NextAuthMiddlewareOptions | undefined,
102102
onSuccess?: (token: JWT | null) => Promise<NextMiddlewareResult>
103103
) {
104-
const { pathname, search, origin } = req.nextUrl
104+
const { pathname, search, origin, basePath } = req.nextUrl
105105

106106
const signInPage = options?.pages?.signIn ?? "/api/auth/signin"
107107
const errorPage = options?.pages?.error ?? "/api/auth/error"
108-
const basePath = parseUrl(process.env.NEXTAUTH_URL).path
108+
const authPath = parseUrl(process.env.NEXTAUTH_URL).path
109109
const publicPaths = ["/_next", "/favicon.ico"]
110110

111111
// Avoid infinite redirects/invalid response
112112
// on paths that never require authentication
113113
if (
114-
pathname.startsWith(basePath) ||
114+
`${basePath}${pathname}`.startsWith(authPath) ||
115115
[signInPage, errorPage].includes(pathname) ||
116116
publicPaths.some((p) => pathname.startsWith(p))
117117
) {
@@ -125,7 +125,7 @@ async function handleMiddleware(
125125
`\nhttps://next-auth.js.org/errors#no_secret`
126126
)
127127

128-
const errorUrl = new URL(errorPage, origin)
128+
const errorUrl = new URL(`${basePath}${errorPage}`, origin)
129129
errorUrl.searchParams.append("error", "Configuration")
130130

131131
return NextResponse.redirect(errorUrl)
@@ -145,8 +145,8 @@ async function handleMiddleware(
145145
if (isAuthorized) return await onSuccess?.(token)
146146

147147
// the user is not logged in, redirect to the sign-in page
148-
const signInUrl = new URL(signInPage, origin)
149-
signInUrl.searchParams.append("callbackUrl", `${pathname}${search}`)
148+
const signInUrl = new URL(`${basePath}${signInPage}`, origin)
149+
signInUrl.searchParams.append("callbackUrl", `${basePath}${pathname}${search}`)
150150
return NextResponse.redirect(signInUrl)
151151
}
152152

‎packages/next-auth/tests/middleware.test.ts

+55
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,58 @@ it("should not redirect on public paths", async () => {
3838
const res = await handleMiddleware(req, null as any)
3939
expect(res).toBeUndefined()
4040
})
41+
42+
it("should redirect according to nextUrl basePath", async () => {
43+
const options: NextAuthMiddlewareOptions = {
44+
secret: "secret"
45+
}
46+
const nextUrl: any = {
47+
pathname: "/protected/pathA",
48+
search: "",
49+
origin: "http://127.0.0.1",
50+
basePath: "/custom-base-path",
51+
}
52+
const req: any = { nextUrl, headers: { authorization: "" } }
53+
54+
const handleMiddleware = withAuth(options) as NextMiddleware
55+
const res = await handleMiddleware(req, null as any)
56+
expect(res).toBeDefined()
57+
expect(res.status).toEqual(307)
58+
expect(res.headers.get('location')).toContain("http://127.0.0.1/custom-base-path/api/auth/signin?callbackUrl=%2Fcustom-base-path%2Fprotected%2FpathA")
59+
})
60+
61+
it("should redirect according to nextUrl basePath", async () => {
62+
// given
63+
const options: NextAuthMiddlewareOptions = {
64+
secret: "secret"
65+
}
66+
const handleMiddleware = withAuth(options) as NextMiddleware
67+
68+
// when
69+
const res = await handleMiddleware({
70+
nextUrl: {
71+
pathname: "/protected/pathA",
72+
search: "",
73+
origin: "http://127.0.0.1",
74+
basePath: "/custom-base-path"
75+
}, headers: { authorization: "" }
76+
} as any, null as any)
77+
78+
// then
79+
expect(res).toBeDefined()
80+
expect(res.status).toEqual(307)
81+
expect(res.headers.get("location")).toContain("http://127.0.0.1/custom-base-path/api/auth/signin?callbackUrl=%2Fcustom-base-path%2Fprotected%2FpathA")
82+
83+
// and when follow redirect
84+
const resFromRedirectedUrl = await handleMiddleware({
85+
nextUrl: {
86+
pathname: "/api/auth/signin",
87+
search: "callbackUrl=%2Fcustom-base-path%2Fprotected%2FpathA",
88+
origin: "http://127.0.0.1",
89+
basePath: "/custom-base-path"
90+
}, headers: { authorization: "" }
91+
} as any, null as any)
92+
93+
// then return sign in page
94+
expect(resFromRedirectedUrl).toBeUndefined()
95+
})

0 commit comments

Comments
 (0)
Please sign in to comment.