Skip to content

Commit ba39efb

Browse files
feat: rename session strategy (#3144)
BREAKING CHANGE: The `session.jwt: boolean` option has been renamed to `session.strategy: "jwt" | "database"`. The goal is to make the user's options more intuitive: 1. No adapter, `strategy: "jwt"`: This is the default. The session is saved in a cookie and never persisted anywhere. 2. With Adapter, `strategy: "database"`: If an Adapter is defined, this will be the implicit setting. No user config is needed. 3. With Adapter, `strategy: "jwt"`: The user can explicitly instruct `next-auth` to use JWT even if a database is available. This can result in faster lookups in compromise of lowered security. Read more about: https://next-auth.js.org/faq#json-web-tokens Example: ```diff session: { - jwt: true, + strategy: "jwt", } ```
1 parent 6502b63 commit ba39efb

File tree

8 files changed

+56
-30
lines changed

8 files changed

+56
-30
lines changed

src/core/init.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,8 @@ export async function init({
8585
providers,
8686
// Session options
8787
session: {
88-
jwt: !userOptions.adapter, // If no adapter specified, force use of JSON Web Tokens (stateless)
88+
// If no adapter specified, force use of JSON Web Tokens (stateless)
89+
strategy: userOptions.adapter ? "database" : "jwt",
8990
maxAge,
9091
updateAge: 24 * 60 * 60,
9192
...userOptions.session,

src/core/lib/callback-handler.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export default async function callbackHandler(params: {
3636
adapter,
3737
jwt,
3838
events,
39-
session: { jwt: useJwtSession },
39+
session: { strategy: sessionStrategy },
4040
} = options
4141

4242
// If no adapter is configured then we don't have a database and cannot
@@ -61,6 +61,8 @@ export default async function callbackHandler(params: {
6161
let user: AdapterUser | null = null
6262
let isNewUser = false
6363

64+
const useJwtSession = sessionStrategy === "jwt"
65+
6466
if (sessionToken) {
6567
if (useJwtSession) {
6668
try {

src/core/lib/cookie.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
// REVIEW: Is there any way to defer two types of strings?
2-
import { NextAuthResponse } from "../../lib/types"
3-
import { CookiesOptions } from "../.."
4-
import { CookieOption } from "../types"
5-
import { ServerResponse } from "http"
2+
import type { NextAuthResponse } from "../../lib/types"
3+
import type { CookiesOptions } from "../.."
4+
import type { CookieOption, SessionStrategy } from "../types"
5+
import type { ServerResponse } from "http"
66

77
/** Stringified form of `JWT`. Extract the content with `jwt.decode` */
88
export type JWTString = string
@@ -12,8 +12,11 @@ export type SetCookieOptions = Partial<CookieOption["options"]> & {
1212
encode?: (val: unknown) => string
1313
}
1414

15-
/** If `options.session.jwt` is set to `true`, this is a stringified `JWT`. In case of a database persisted session, this is the `sessionToken` of the session in the database.. */
16-
export type SessionToken<T extends "jwt" | "db" = "jwt"> = T extends "jwt"
15+
/**
16+
* If `options.session.strategy` is set to `jwt`, this is a stringified `JWT`.
17+
* In case of `strategy: "database"`, this is the `sessionToken` of the session in the database.
18+
*/
19+
export type SessionToken<T extends SessionStrategy = "jwt"> = T extends "jwt"
1720
? JWTString
1821
: string
1922

src/core/routes/callback.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,14 @@ export default async function callback(params: {
2727
jwt,
2828
events,
2929
callbacks,
30-
session: { jwt: useJwtSession, maxAge: sessionMaxAge },
30+
session: { strategy: sessionStrategy, maxAge: sessionMaxAge },
3131
logger,
3232
} = options
3333

3434
const cookies: cookie.Cookie[] = []
3535

36+
const useJwtSession = sessionStrategy === "jwt"
37+
3638
if (provider.type === "oauth") {
3739
try {
3840
const {

src/core/routes/session.ts

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,14 @@ export default async function session(
1818
params: SessionParams
1919
): Promise<OutgoingResponse<Session | {}>> {
2020
const { options, sessionToken } = params
21-
const { adapter, jwt, events, callbacks, logger } = options
22-
const useJwtSession = options.session.jwt
23-
const sessionMaxAge = options.session.maxAge
21+
const {
22+
adapter,
23+
jwt,
24+
events,
25+
callbacks,
26+
logger,
27+
session: { strategy: sessionStrategy, maxAge: sessionMaxAge },
28+
} = options
2429

2530
const response: OutgoingResponse<Session | {}> = {
2631
body: {},
@@ -30,7 +35,7 @@ export default async function session(
3035

3136
if (!sessionToken) return response
3237

33-
if (useJwtSession) {
38+
if (sessionStrategy === "jwt") {
3439
try {
3540
// Decrypt and verify token
3641
const decodedToken = await jwt.decode({ ...jwt, token: sessionToken })
@@ -77,7 +82,7 @@ export default async function session(
7782
await events.session?.({ session, token })
7883
} catch (error) {
7984
// If JWT not verifiable, make sure the cookie for it is removed and return empty object
80-
logger.error("JWT_SESSION_ERROR", (error as Error))
85+
logger.error("JWT_SESSION_ERROR", error as Error)
8186

8287
response.cookies?.push({
8388
name: options.cookies.sessionToken.name,
@@ -160,7 +165,7 @@ export default async function session(
160165
})
161166
}
162167
} catch (error) {
163-
logger.error("SESSION_ERROR", (error as Error))
168+
logger.error("SESSION_ERROR", error as Error)
164169
}
165170
}
166171

src/core/routes/signout.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,14 @@ export default async function signout(params: {
99
sessionToken?: string
1010
}): Promise<OutgoingResponse> {
1111
const { options, sessionToken } = params
12-
const { adapter, cookies, events, jwt, callbackUrl, logger } = options
12+
const { adapter, cookies, events, jwt, callbackUrl, logger, session } =
13+
options
1314

1415
if (!sessionToken) {
1516
return { redirect: callbackUrl }
1617
}
1718

18-
const useJwtSession = options.session.jwt
19-
20-
if (useJwtSession) {
19+
if (session.strategy === "jwt") {
2120
// Dispatch signout event
2221
try {
2322
const decodedJwt = await jwt.decode({ ...jwt, token: sessionToken })

src/core/types.ts

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -429,9 +429,23 @@ export interface DefaultSession extends Record<string, unknown> {
429429
*/
430430
export interface Session extends Record<string, unknown>, DefaultSession {}
431431

432+
export type SessionStrategy = "jwt" | "database"
433+
432434
/** [Documentation](https://next-auth.js.org/configuration/options#session) */
433435
export interface SessionOptions {
434-
jwt: boolean
436+
/**
437+
* Choose how you want to save the user session.
438+
* The default is `"jwt"`, an encrypted JWT (JWE) in the session cookie.
439+
*
440+
* If you use an `adapter` however, we default it to `"database"` instead.
441+
* You can still force a JWT session by explicitly defining `"jwt"`.
442+
*
443+
* When using `"database"`, the session cookie will only contain a `sessionToken` value,
444+
* which is used to look up the session in the database.
445+
*
446+
* [Documentation](https://next-auth.js.org/configuration/options#session) | [Adapter](https://next-auth.js.org/configuration/options#adapter) | [About JSON Web Tokens](https://next-auth.js.org/faq#json-web-tokens)
447+
*/
448+
strategy: SessionStrategy
435449
/**
436450
* Relative time from now in seconds when to expire the session
437451
* @default 2592000 // 30 days
@@ -463,13 +477,3 @@ export interface DefaultUser {
463477
* [`profile` OAuth provider callback](https://next-auth.js.org/configuration/providers#using-a-custom-provider)
464478
*/
465479
export interface User extends Record<string, unknown>, DefaultUser {}
466-
467-
declare global {
468-
// eslint-disable-next-line @typescript-eslint/no-namespace
469-
namespace NodeJS {
470-
interface ProcessEnv {
471-
NEXTAUTH_URL?: string
472-
VERCEL_URL?: string
473-
}
474-
}
475-
}

src/next/index.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,3 +121,13 @@ export async function getServerSession(
121121
if (body && Object.keys(body).length) return body as Session
122122
return null
123123
}
124+
125+
declare global {
126+
// eslint-disable-next-line @typescript-eslint/no-namespace
127+
namespace NodeJS {
128+
interface ProcessEnv {
129+
NEXTAUTH_URL?: string
130+
VERCEL_URL?: string
131+
}
132+
}
133+
}

0 commit comments

Comments
 (0)