Skip to content

Commit a595ca7

Browse files
fix: next-auth basePath should be set to /api/auth by default (#9687)
* tweak dev app * respect `basePath` in `useSession` * fix type * default `basePath` to `/api/auth` * add `createActionURL` method
1 parent 963086b commit a595ca7

File tree

7 files changed

+59
-40
lines changed

7 files changed

+59
-40
lines changed

apps/dev/nextjs/auth.config.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
import type { NextAuthConfig } from "next-auth"
22
import Credentials from "next-auth/providers/credentials"
3+
import GitHub from "next-auth/providers/github"
4+
import Google from "next-auth/providers/google"
5+
import Facebook from "next-auth/providers/facebook"
6+
import Auth0 from "next-auth/providers/auth0"
7+
import Twitter from "next-auth/providers/twitter"
38

49
declare module "next-auth" {
510
/**
@@ -32,16 +37,17 @@ export default {
3237
}
3338
},
3439
}),
35-
process.env.AUTH_GITHUB_ID && (await import("next-auth/providers/github")).default,
36-
process.env.AUTH_GOOGLE_ID && (await import("next-auth/providers/google")).default,
37-
process.env.AUTH_FACEBOOK_ID && (await import("next-auth/providers/facebook")).default,
38-
process.env.AUTH_AUTH0_ID && (await import("next-auth/providers/auth0")).default,
39-
process.env.AUTH_TWITTER_ID && (await import("next-auth/providers/twitter")).default
40+
GitHub,
41+
Google,
42+
Facebook,
43+
Auth0,
44+
Twitter,
4045
].filter(Boolean) as NextAuthConfig["providers"],
4146
callbacks: {
4247
jwt({ token, trigger, session }) {
4348
if (trigger === "update") token.name = session.user.name
4449
return token
4550
},
4651
},
52+
basePath: "/auth",
4753
} satisfies NextAuthConfig

apps/dev/nextjs/middleware.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import NextAuth from "next-auth"
22
import authConfig from "auth.config"
33

4-
// export const middleware = NextAuth(authConfig).auth
4+
export const middleware = NextAuth(authConfig).auth
55

6-
export const middleware = NextAuth((req) => {
7-
console.log("middleware", req)
8-
return authConfig
9-
}).auth
6+
// export const middleware = NextAuth((req) => {
7+
// console.log("middleware", req)
8+
// return authConfig
9+
// }).auth
1010

1111
export const config = {
1212
matcher: ["/((?!api|_next/static|_next/image|favicon.ico).*)"],

packages/next-auth/src/index.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,9 @@ export interface NextAuthResult {
358358
* ```
359359
*/
360360
export default function NextAuth(
361-
config: NextAuthConfig | ((request: Request | undefined) => NextAuthConfig)
361+
config:
362+
| NextAuthConfig
363+
| ((request: NextRequest | undefined) => NextAuthConfig)
362364
): NextAuthResult {
363365
if (typeof config === "function") {
364366
const httpHandler = (req: NextRequest) => {

packages/next-auth/src/lib/actions.ts

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ import { Auth, raw, skipCSRFCheck } from "@auth/core"
22
import { headers as nextHeaders, cookies } from "next/headers"
33
import { redirect } from "next/navigation"
44

5-
import { detectOrigin } from "./env.js"
6-
5+
import type { AuthAction } from "@auth/core/types.js"
76
import type { NextAuthConfig } from "./index.js"
87
import type { NextAuthResult, Session } from "../index.js"
8+
import type { headers } from "next/headers"
99

1010
type SignInParams = Parameters<NextAuthResult["signIn"]>
1111
export async function signIn(
@@ -22,7 +22,7 @@ export async function signIn(
2222
} = options instanceof FormData ? Object.fromEntries(options) : options
2323

2424
const callbackUrl = redirectTo?.toString() ?? headers.get("Referer") ?? "/"
25-
const base = authUrl(detectOrigin(headers), "signin")
25+
const base = createActionURL("signin", headers, config.basePath)
2626

2727
if (!provider) {
2828
const url = `${base}?${new URLSearchParams({ callbackUrl })}`
@@ -70,7 +70,7 @@ export async function signOut(
7070
const headers = new Headers(nextHeaders())
7171
headers.set("Content-Type", "application/x-www-form-urlencoded")
7272

73-
const url = authUrl(detectOrigin(headers), "signout")
73+
const url = createActionURL("signout", headers, config.basePath)
7474
const callbackUrl = options?.redirectTo ?? headers.get("Referer") ?? "/"
7575
const body = new URLSearchParams({ callbackUrl })
7676
const req = new Request(url, { method: "POST", headers, body })
@@ -92,7 +92,7 @@ export async function update(
9292
const headers = new Headers(nextHeaders())
9393
headers.set("Content-Type", "application/json")
9494

95-
const url = authUrl(detectOrigin(headers), "session")
95+
const url = createActionURL("session", headers, config.basePath)
9696
const body = JSON.stringify({ data })
9797
const req = new Request(url, { method: "POST", headers, body })
9898

@@ -103,10 +103,25 @@ export async function update(
103103
return res.body
104104
}
105105

106-
/** Determine an action's URL */
107-
function authUrl(base: URL, action: string) {
108-
let pathname
109-
if (base.pathname === "/") pathname ??= `/api/auth/${action}`
110-
else pathname ??= `${base.pathname}/${action}`
111-
return new URL(pathname, base.origin)
106+
/**
107+
* Extract the origin and base path from either `AUTH_URL` or `NEXTAUTH_URL` environment variables,
108+
* or the request's headers and the {@link NextAuthConfig.basePath} option.
109+
*/
110+
export function createActionURL(
111+
action: AuthAction,
112+
h: Headers | ReturnType<typeof headers>,
113+
basePath?: string
114+
) {
115+
const envUrl = process.env.AUTH_URL ?? process.env.NEXTAUTH_URL
116+
if (envUrl) {
117+
const { origin, pathname } = new URL(envUrl)
118+
const separator = pathname.endsWith("/") ? "" : "/"
119+
return `${origin}${pathname}${separator}${action}`
120+
}
121+
const host = h.get("x-forwarded-host") ?? h.get("host")
122+
const protocol = h.get("x-forwarded-proto") === "http" ? "http" : "https"
123+
// @ts-expect-error `basePath` value is default'ed to "/api/auth" in `setEnvDefaults`
124+
const { origin, pathname } = new URL(basePath, `${protocol}://${host}`)
125+
const separator = pathname.endsWith("/") ? "" : "/"
126+
return `${origin}${pathname}${separator}${action}`
112127
}

packages/next-auth/src/lib/env.ts

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
11
import { NextRequest } from "next/server"
2-
import type { headers } from "next/headers"
32

43
import type { NextAuthConfig } from "./index.js"
54

65
export function setEnvDefaults(config: NextAuthConfig) {
76
config.secret ??= process.env.AUTH_SECRET ?? process.env.NEXTAUTH_SECRET
7+
try {
8+
const url = process.env.AUTH_URL ?? process.env.NEXTAUTH_URL
9+
if (url) config.basePath = new URL(url).pathname
10+
} catch {
11+
} finally {
12+
config.basePath ??= "/api/auth"
13+
}
14+
815
config.trustHost ??= !!(
916
process.env.AUTH_URL ??
1017
process.env.NEXTAUTH_URL ??
@@ -27,18 +34,6 @@ export function setEnvDefaults(config: NextAuthConfig) {
2734
})
2835
}
2936

30-
/**
31-
* Extract the origin from `NEXTAUTH_URL` or `AUTH_URL`
32-
* environment variables, or the request's headers.
33-
*/
34-
export function detectOrigin(h: Headers | ReturnType<typeof headers>) {
35-
const url = process.env.AUTH_URL ?? process.env.NEXTAUTH_URL
36-
if (url) return new URL(url)
37-
const host = h.get("x-forwarded-host") ?? h.get("host")
38-
const protocol = h.get("x-forwarded-proto") === "http" ? "http" : "https"
39-
return new URL(`${protocol}://${host}`)
40-
}
41-
4237
/** If `NEXTAUTH_URL` or `AUTH_URL` is defined, override the request's URL. */
4338
export function reqWithEnvUrl(req: NextRequest): NextRequest {
4439
const url = process.env.AUTH_URL ?? process.env.NEXTAUTH_URL

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

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { Auth, type AuthConfig } from "@auth/core"
22
import { headers } from "next/headers"
33
import { NextResponse } from "next/server"
4-
import { detectOrigin, reqWithEnvUrl } from "./env.js"
4+
import { reqWithEnvUrl } from "./env.js"
5+
import { createActionURL } from "./actions.js"
56

67
import type { AuthAction, Awaitable, Session } from "@auth/core/types"
78
import type {
@@ -59,8 +60,8 @@ export interface NextAuthConfig extends Omit<AuthConfig, "raw"> {
5960
}
6061

6162
async function getSession(headers: Headers, config: NextAuthConfig) {
62-
const origin = detectOrigin(headers)
63-
const request = new Request(`${origin}/session`, {
63+
const url = createActionURL("session", headers, config.basePath)
64+
const request = new Request(url, {
6465
headers: { cookie: headers.get("cookie") ?? "" },
6566
})
6667

@@ -249,7 +250,7 @@ async function handleAuth(
249250
(await userMiddlewareOrRoute(augmentedReq, args[1])) ??
250251
NextResponse.next()
251252
} else if (!authorized) {
252-
const signInPage = config.pages?.signIn ?? "/api/auth/signin"
253+
const signInPage = config.pages?.signIn ?? `${config.basePath}/signin`
253254
if (request.nextUrl.pathname !== signInPage) {
254255
// Redirect to signin page by default if not authorized
255256
const signInUrl = request.nextUrl.clone()

packages/next-auth/src/react.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ export function useSession<R extends boolean>(
137137

138138
React.useEffect(() => {
139139
if (requiredAndNotLoading) {
140-
const url = `/api/auth/signin?${new URLSearchParams({
140+
const url = `${__NEXTAUTH.basePath}/signin?${new URLSearchParams({
141141
error: "SessionRequired",
142142
callbackUrl: window.location.href,
143143
})}`

0 commit comments

Comments
 (0)