Skip to content

Commit f20d679

Browse files
feat(core): detect NEXTAUTH_SECRET (#3783)
* feat(core): detect `NEXTAUTH_SECRET` env variable * chore(dev): use detected `NEXTAUTH_SECRET` in dev app
1 parent 53baf6d commit f20d679

File tree

6 files changed

+26
-18
lines changed

6 files changed

+26
-18
lines changed

app/.env.local.example

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ NEXTAUTH_URL=http://localhost:3000
77
# https://generate-secret.vercel.app/32 to generate a secret.
88
# Note: Changing a secret may invalidate existing sessions
99
# and/or verification tokens.
10-
SECRET=secret
10+
NEXTAUTH_SECRET=secret
1111

1212
AUTH0_ID=
1313
AUTH0_SECRET=

app/pages/api/auth/[...nextauth].ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,9 +189,8 @@ export const authOptions: NextAuthOptions = {
189189
PatreonProvider({
190190
clientId: process.env.PATREON_ID,
191191
clientSecret: process.env.PATREON_SECRET,
192-
})
192+
}),
193193
],
194-
secret: process.env.SECRET,
195194
debug: true,
196195
theme: {
197196
colorScheme: "auto",

src/core/types.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,10 @@ export interface NextAuthOptions {
2727
providers: Provider[]
2828
/**
2929
* A random string used to hash tokens, sign cookies and generate cryptographic keys.
30-
* If not specified is uses a hash of all configuration options, including Client ID / Secrets for entropy.
31-
* The default behavior is volatile, and **it is strongly recommended** you explicitly specify a value
32-
* to avoid invalidating end user sessions when configuration changes are deployed.
30+
* If not specified, it falls back to `jwt.secret` or `NEXTAUTH_SECRET` from environment vairables.
31+
* Otherwise it will use a hash of all configuration options, including Client ID / Secrets for entropy.
32+
*
33+
* NOTE: The last behavior is extrmely volatile, and will throw an error in production.
3334
* * **Default value**: `string` (SHA hash of the "options" object)
3435
* * **Required**: No - **but strongly recommended**!
3536
*

src/jwt/index.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,8 @@ const DEFAULT_MAX_AGE = 30 * 24 * 60 * 60 // 30 days
1313
const now = () => (Date.now() / 1000) | 0
1414

1515
/** Issues a JWT. By default, the JWT is encrypted using "A256GCM". */
16-
export async function encode({
17-
token = {},
18-
secret,
19-
maxAge = DEFAULT_MAX_AGE,
20-
}: JWTEncodeParams) {
16+
export async function encode(params: JWTEncodeParams) {
17+
const { token = {}, secret, maxAge = DEFAULT_MAX_AGE } = params
2118
const encryptionSecret = await getDerivedEncryptionKey(secret)
2219
return await new EncryptJWT(token)
2320
.setProtectedHeader({ alg: "dir", enc: "A256GCM" })
@@ -28,10 +25,8 @@ export async function encode({
2825
}
2926

3027
/** Decodes a NextAuth.js issued JWT. */
31-
export async function decode({
32-
token,
33-
secret,
34-
}: JWTDecodeParams): Promise<JWT | null> {
28+
export async function decode(params: JWTDecodeParams): Promise<JWT | null> {
29+
const { token, secret } = params
3530
if (!token) return null
3631
const encryptionSecret = await getDerivedEncryptionKey(secret)
3732
const { payload } = await jwtDecrypt(token, encryptionSecret, {
@@ -55,7 +50,11 @@ export interface GetTokenParams<R extends boolean = false> {
5550
* @default false
5651
*/
5752
raw?: R
58-
secret: string
53+
/**
54+
* The same `secret` used in the `NextAuth` configuration.
55+
* Defaults to the `NEXTAUTH_SECRET` environment variable.
56+
*/
57+
secret?: string
5958
decode?: JWTOptions["decode"]
6059
logger?: LoggerInstance | Console
6160
}
@@ -78,6 +77,7 @@ export async function getToken<R extends boolean = false>(
7877
raw,
7978
decode: _decode = decode,
8079
logger = console,
80+
secret = process.env.NEXTAUTH_SECRET,
8181
} = params ?? {}
8282

8383
if (!req) throw new Error("Must pass `req` to JWT getToken()")
@@ -103,7 +103,7 @@ export async function getToken<R extends boolean = false>(
103103

104104
try {
105105
// @ts-expect-error
106-
return await _decode({ token, ...params })
106+
return await _decode({ token, secret })
107107
} catch {
108108
// @ts-expect-error
109109
return null

src/jwt/types.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,11 @@ export interface JWTDecodeParams {
3434
}
3535

3636
export interface JWTOptions {
37-
/** The secret used to encode/decode the NextAuth.js issued JWT. */
37+
/**
38+
* The secret used to encode/decode the NextAuth.js issued JWT.
39+
* @deprecated Set the `NEXTAUTH_SECRET` environment vairable or
40+
* use the top-level `secret` option instead
41+
*/
3842
secret: string
3943
/**
4044
* The maximum age of the NextAuth.js issued JWT in seconds.

src/next/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ async function NextAuthNextHandler(
1919
options: NextAuthOptions
2020
) {
2121
const { nextauth, ...query } = req.query
22+
23+
options.secret =
24+
options.secret ?? options.jwt?.secret ?? process.env.NEXTAUTH_SECRET
25+
2226
const handler = await NextAuthHandler({
2327
req: {
2428
host: detectHost(req.headers["x-forwarded-host"]),

0 commit comments

Comments
 (0)