Skip to content

Commit c78f6e0

Browse files
feat: support advanced initialization in v5 (#9638)
Co-authored-by: Balázs Orbán <[email protected]>
1 parent 9e802b0 commit c78f6e0

File tree

4 files changed

+161
-16
lines changed

4 files changed

+161
-16
lines changed

apps/dev/nextjs/auth.ts

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import NextAuth from "next-auth"
2-
import Email from "next-auth/providers/email"
2+
// import Email from "next-auth/providers/email"
33
import authConfig from "auth.config"
4-
import { PrismaClient } from "@prisma/client"
5-
import { PrismaAdapter } from "@auth/prisma-adapter"
4+
// import { PrismaClient } from "@prisma/client"
5+
// import { PrismaAdapter } from "@auth/prisma-adapter"
66

77
// globalThis.prisma ??= new PrismaClient()
88

@@ -12,8 +12,26 @@ import { PrismaAdapter } from "@auth/prisma-adapter"
1212
// Email({ server: "smtp://127.0.0.1:1025?tls.rejectUnauthorized=false" })
1313
// )
1414

15-
export const { handlers, auth, signIn, signOut, unstable_update } = NextAuth({
16-
// adapter: PrismaAdapter(globalThis.prisma),
17-
session: { strategy: "jwt" },
18-
...authConfig,
19-
})
15+
export const { handlers, auth, signIn, signOut, unstable_update } = NextAuth(
16+
(request) => {
17+
if (request?.nextUrl.searchParams.get("test")) {
18+
return {
19+
// adapter: PrismaAdapter(globalThis.prisma),
20+
session: { strategy: "jwt" },
21+
...authConfig,
22+
providers: [],
23+
}
24+
}
25+
return {
26+
// adapter: PrismaAdapter(globalThis.prisma),
27+
session: { strategy: "jwt" },
28+
...authConfig,
29+
}
30+
}
31+
)
32+
33+
// export const { handlers, auth, signIn, signOut, unstable_update } = NextAuth({
34+
// // adapter: PrismaAdapter(globalThis.prisma),
35+
// session: { strategy: "jwt" },
36+
// ...authConfig,
37+
// })

apps/dev/nextjs/middleware.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +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
5+
6+
export const middleware = NextAuth((req) => {
7+
console.log("middleware", req)
8+
return authConfig
9+
}).auth
510

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

packages/next-auth/src/index.tsx

Lines changed: 67 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@
77
* npm install next-auth@beta
88
* ```
99
*
10-
* ## Environment variable inferrence
10+
* ## Environment variable inference
1111
*
1212
* `NEXTAUTH_URL` and `NEXTAUTH_SECRET` have been inferred since v4.
1313
*
14-
* Since NextAuth.js v5 can also automatically infer environment variables that are prefiexed with `AUTH_`.
14+
* Since NextAuth.js v5 can also automatically infer environment variables that are prefixed with `AUTH_`.
1515
*
1616
* For example `AUTH_GITHUB_ID` and `AUTH_GITHUB_SECRET` will be used as the `clientId` and `clientSecret` options for the GitHub provider.
1717
*
@@ -45,6 +45,25 @@
4545
*
4646
* If you need to override the default values for a provider, you can still call it as a function `GitHub({...})` as before.
4747
*
48+
* ## Lazy initialization
49+
* You can also initialize NextAuth.js lazily (previously known as advanced intialization), which allows you to access the request context in the configuration in some cases, like Route Handlers, Middleware, API Routes or `getServerSideProps`.
50+
* The above example becomes:
51+
*
52+
* ```ts title="auth.ts"
53+
* import NextAuth from "next-auth"
54+
* import GitHub from "next-auth/providers/github"
55+
* export const { handlers, auth } = NextAuth(req => {
56+
* if (req) {
57+
* console.log(req) // do something with the request
58+
* }
59+
* return { providers: [ GitHub ] }
60+
* })
61+
* ```
62+
*
63+
* :::tip
64+
* This is useful if you want to customize the configuration based on the request, for example, to add a different provider in staging/dev environments.
65+
* :::
66+
*
4867
* @module next-auth
4968
*/
5069

@@ -323,8 +342,53 @@ export interface NextAuthResult {
323342
*
324343
* export const { handlers, auth } = NextAuth({ providers: [GitHub] })
325344
* ```
345+
*
346+
* Lazy initialization:
347+
* @example
348+
* ```ts title="auth.ts"
349+
* import NextAuth from "next-auth"
350+
* import GitHub from "@auth/core/providers/github"
351+
*
352+
* export const { handlers, auth } = NextAuth((req) => {
353+
* console.log(req) // do something with the request
354+
* return {
355+
* providers: [GitHub],
356+
* },
357+
* })
358+
* ```
326359
*/
327-
export default function NextAuth(config: NextAuthConfig): NextAuthResult {
360+
export default function NextAuth(
361+
config: NextAuthConfig | ((request: Request | undefined) => NextAuthConfig)
362+
): NextAuthResult {
363+
if (typeof config === "function") {
364+
const httpHandler = (req: NextRequest) => {
365+
const _config = config(req)
366+
setEnvDefaults(_config)
367+
return Auth(reqWithEnvUrl(req), _config)
368+
}
369+
370+
return {
371+
handlers: { GET: httpHandler, POST: httpHandler } as const,
372+
// @ts-expect-error
373+
auth: initAuth(config, (c) => setEnvDefaults(c)),
374+
375+
signIn: (provider, options, authorizationParams) => {
376+
const _config = config(undefined)
377+
setEnvDefaults(_config)
378+
return signIn(provider, options, authorizationParams, _config)
379+
},
380+
signOut: (options) => {
381+
const _config = config(undefined)
382+
setEnvDefaults(_config)
383+
return signOut(options, _config)
384+
},
385+
unstable_update: (data) => {
386+
const _config = config(undefined)
387+
setEnvDefaults(_config)
388+
return update(data, _config)
389+
},
390+
}
391+
}
328392
setEnvDefaults(config)
329393
const httpHandler = (req: NextRequest) => Auth(reqWithEnvUrl(req), config)
330394
return {

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

Lines changed: 62 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@ export interface NextAuthConfig extends Omit<AuthConfig, "raw"> {
5858
}
5959
}
6060

61-
/** Server-side method to read the session. */
6261
async function getSession(headers: Headers, config: NextAuthConfig) {
6362
const origin = detectOrigin(headers)
6463
const request = new Request(`${origin}/session`, {
@@ -106,22 +105,81 @@ function isReqWrapper(arg: any): arg is NextAuthMiddleware | AppRouteHandlerFn {
106105
return typeof arg === "function"
107106
}
108107

109-
export function initAuth(config: NextAuthConfig) {
108+
export function initAuth(
109+
config:
110+
| NextAuthConfig
111+
| ((request: NextRequest | undefined) => NextAuthConfig),
112+
onLazyLoad?: (config: NextAuthConfig) => void // To set the default env vars
113+
) {
114+
if (typeof config === "function") {
115+
return (...args: WithAuthArgs) => {
116+
if (!args.length) {
117+
// React Server Components
118+
const _headers = headers()
119+
const _config = config(undefined) // Review: Should we pass headers() here instead?
120+
onLazyLoad?.(_config)
121+
122+
return getSession(_headers, _config).then((r) => r.json())
123+
}
124+
125+
if (args[0] instanceof Request) {
126+
// middleware.ts inline
127+
// export { auth as default } from "auth"
128+
const req = args[0]
129+
const ev = args[1]
130+
const _config = config(req)
131+
onLazyLoad?.(_config)
132+
133+
// args[0] is supposed to be NextRequest but the instanceof check is failing.
134+
return handleAuth([req, ev], _config)
135+
}
136+
137+
if (isReqWrapper(args[0])) {
138+
// middleware.ts wrapper/route.ts
139+
// import { auth } from "auth"
140+
// export default auth((req) => { console.log(req.auth) }})
141+
const userMiddlewareOrRoute = args[0]
142+
return async (
143+
...args: Parameters<NextAuthMiddleware | AppRouteHandlerFn>
144+
) => {
145+
return handleAuth(args, config(args[0]), userMiddlewareOrRoute)
146+
}
147+
}
148+
// API Routes, getServerSideProps
149+
const request = "req" in args[0] ? args[0].req : args[0]
150+
const response: any = "res" in args[0] ? args[0].res : args[1]
151+
// @ts-expect-error -- request is NextRequest
152+
const _config = config(request)
153+
onLazyLoad?.(_config)
154+
155+
// @ts-expect-error -- request is NextRequest
156+
return getSession(new Headers(request.headers), _config).then(
157+
async (authResponse) => {
158+
const auth = await authResponse.json()
159+
160+
for (const cookie of authResponse.headers.getSetCookie())
161+
response.headers.append("set-cookie", cookie)
162+
163+
return auth satisfies Session | null
164+
}
165+
)
166+
}
167+
}
110168
return (...args: WithAuthArgs) => {
111169
if (!args.length) {
112170
// React Server Components
113171
return getSession(headers(), config).then((r) => r.json())
114172
}
115173
if (args[0] instanceof Request) {
116-
// middleware.ts
174+
// middleware.ts inline
117175
// export { auth as default } from "auth"
118176
const req = args[0]
119177
const ev = args[1]
120178
return handleAuth([req, ev], config)
121179
}
122180

123181
if (isReqWrapper(args[0])) {
124-
// middleware.ts/router.ts
182+
// middleware.ts wrapper/route.ts
125183
// import { auth } from "auth"
126184
// export default auth((req) => { console.log(req.auth) }})
127185
const userMiddlewareOrRoute = args[0]

0 commit comments

Comments
 (0)