0
# JWT Token Management
1
2
JWT utilities for encoding, decoding, and extracting tokens from requests. Essential for API authentication, custom session handling, and implementing stateless authentication patterns in Next.js applications.
3
4
## Capabilities
5
6
### JWT Encoding
7
8
Creates encrypted JWT tokens using NextAuth.js encryption standards with "A256GCM" encryption by default.
9
10
```typescript { .api }
11
/**
12
* Issues a JWT token encrypted using "A256GCM" by default
13
* @param params - Token encoding parameters
14
* @returns Promise resolving to encrypted JWT string
15
*/
16
function encode(params: JWTEncodeParams): Promise<string>;
17
18
interface JWTEncodeParams {
19
/** JWT payload to encode (default: empty object) */
20
token?: JWT;
21
/** Secret used for encryption */
22
secret: string;
23
/** Maximum age in seconds (default: 2592000 - 30 days) */
24
maxAge?: number;
25
/** Salt for key derivation (empty string means session token) */
26
salt?: string;
27
}
28
```
29
30
**Usage Examples:**
31
32
```typescript
33
import { encode } from "next-auth/jwt";
34
35
// Basic JWT encoding
36
const jwt = await encode({
37
token: { userId: "123", role: "user" },
38
secret: process.env.NEXTAUTH_SECRET!,
39
});
40
41
// Custom expiration
42
const shortLivedJwt = await encode({
43
token: { action: "email-verification" },
44
secret: process.env.NEXTAUTH_SECRET!,
45
maxAge: 3600, // 1 hour
46
});
47
48
// Custom salt for different token types
49
const apiToken = await encode({
50
token: { userId: "123", scopes: ["read", "write"] },
51
secret: process.env.NEXTAUTH_SECRET!,
52
salt: "api-token",
53
});
54
```
55
56
### JWT Decoding
57
58
Decodes and verifies NextAuth.js issued JWT tokens with automatic expiration checking.
59
60
```typescript { .api }
61
/**
62
* Decodes a NextAuth.js issued JWT token
63
* @param params - Token decoding parameters
64
* @returns Promise resolving to JWT payload or null if invalid/expired
65
*/
66
function decode(params: JWTDecodeParams): Promise<JWT | null>;
67
68
interface JWTDecodeParams {
69
/** JWT token string to decode */
70
token?: string;
71
/** Secret used for decryption */
72
secret: string;
73
/** Salt for key derivation (must match encoding salt) */
74
salt?: string;
75
}
76
```
77
78
**Usage Examples:**
79
80
```typescript
81
import { decode } from "next-auth/jwt";
82
83
// Basic JWT decoding
84
const payload = await decode({
85
token: "eyJhbGciOiJkaXIi...",
86
secret: process.env.NEXTAUTH_SECRET!,
87
});
88
89
if (payload) {
90
console.log("User ID:", payload.sub);
91
console.log("Token expires:", new Date(payload.exp! * 1000));
92
}
93
94
// Decode API token with custom salt
95
const apiPayload = await decode({
96
token: apiTokenString,
97
secret: process.env.NEXTAUTH_SECRET!,
98
salt: "api-token",
99
});
100
```
101
102
### Token Extraction
103
104
Extracts JWT tokens from NextAuth.js requests, checking both cookies and Authorization headers.
105
106
```typescript { .api }
107
/**
108
* Extracts JWT from NextAuth.js request (cookies or Authorization header)
109
* @param params - Token extraction parameters
110
* @returns Promise resolving to JWT payload, raw token string, or null
111
*/
112
function getToken<R extends boolean = false>(
113
params: GetTokenParams<R>
114
): Promise<R extends true ? string : JWT | null>;
115
116
interface GetTokenParams<R extends boolean = false> {
117
/** Request containing the JWT */
118
req: GetServerSidePropsContext["req"] | NextRequest | NextApiRequest;
119
/** Use secure prefix for cookie name (auto-detected from NEXTAUTH_URL) */
120
secureCookie?: boolean;
121
/** Custom cookie name to look for JWT */
122
cookieName?: string;
123
/** Return raw JWT string instead of decoded payload */
124
raw?: R;
125
/** Secret for decryption (defaults to NEXTAUTH_SECRET) */
126
secret?: string;
127
/** Custom decode function */
128
decode?: JWTOptions["decode"];
129
/** Custom logger instance */
130
logger?: LoggerInstance | Console;
131
}
132
```
133
134
**Usage Examples:**
135
136
```typescript
137
import { getToken } from "next-auth/jwt";
138
139
// API Route - get decoded token
140
export default async function handler(req, res) {
141
const token = await getToken({ req });
142
143
if (!token) {
144
return res.status(401).json({ error: "No valid token" });
145
}
146
147
console.log("User ID:", token.sub);
148
console.log("User email:", token.email);
149
150
res.json({ message: `Hello ${token.name}` });
151
}
152
153
// Get raw JWT string
154
export default async function handler(req, res) {
155
const rawToken = await getToken({ req, raw: true });
156
157
if (rawToken) {
158
// Forward token to external API
159
const response = await fetch("https://api.example.com/data", {
160
headers: {
161
Authorization: `Bearer ${rawToken}`,
162
},
163
});
164
}
165
}
166
167
// Middleware - check token
168
import { NextRequest } from "next/server";
169
170
export async function middleware(request: NextRequest) {
171
const token = await getToken({
172
req: request,
173
secret: process.env.NEXTAUTH_SECRET,
174
});
175
176
if (!token) {
177
return Response.redirect(new URL("/login", request.url));
178
}
179
180
// Check user role
181
if (request.nextUrl.pathname.startsWith("/admin") && token.role !== "admin") {
182
return Response.redirect(new URL("/unauthorized", request.url));
183
}
184
}
185
186
// Server Component - extract token
187
import { headers } from "next/headers";
188
189
export default async function ServerComponent() {
190
const token = await getToken({
191
req: {
192
headers: Object.fromEntries(headers() as Headers),
193
cookies: {}, // Will be populated by getToken
194
} as any,
195
secret: process.env.NEXTAUTH_SECRET,
196
});
197
198
return <div>User: {token?.name || "Anonymous"}</div>;
199
}
200
```
201
202
### JWT Options Configuration
203
204
Configuration options for customizing JWT behavior in NextAuth.js.
205
206
```typescript { .api }
207
/**
208
* JWT configuration options for NextAuth.js
209
*/
210
interface JWTOptions {
211
/** Secret used for JWT signing and encryption */
212
secret: string;
213
/** Maximum JWT age in seconds */
214
maxAge: number;
215
/** Custom JWT encoding function */
216
encode?: (params: JWTEncodeParams) => Awaitable<string>;
217
/** Custom JWT decoding function */
218
decode?: (params: JWTDecodeParams) => Awaitable<JWT | null>;
219
}
220
```
221
222
**Usage Example:**
223
224
```typescript
225
// Custom JWT configuration in NextAuth
226
export default NextAuth({
227
providers: [...],
228
jwt: {
229
secret: process.env.JWT_SECRET,
230
maxAge: 24 * 60 * 60, // 24 hours
231
encode: async ({ secret, token, maxAge }) => {
232
// Custom encoding logic
233
return customEncode(token, secret, maxAge);
234
},
235
decode: async ({ secret, token }) => {
236
// Custom decoding logic
237
return customDecode(token, secret);
238
},
239
},
240
});
241
```
242
243
### JWT Token Structure
244
245
Standard JWT payload structure used by NextAuth.js with extensible properties.
246
247
```typescript { .api }
248
/**
249
* JWT token payload structure
250
*/
251
interface JWT extends Record<string, unknown>, DefaultJWT {
252
/** User identifier (subject) */
253
sub?: string;
254
/** User name */
255
name?: string | null;
256
/** User email address */
257
email?: string | null;
258
/** User profile picture URL */
259
picture?: string | null;
260
/** Token issued at timestamp (seconds since epoch) */
261
iat?: number;
262
/** Token expiration timestamp (seconds since epoch) */
263
exp?: number;
264
/** JWT ID (unique identifier) */
265
jti?: string;
266
/** Additional custom properties can be added via callbacks */
267
[key: string]: unknown;
268
}
269
270
interface DefaultJWT extends Record<string, unknown> {
271
name?: string | null;
272
email?: string | null;
273
picture?: string | null;
274
sub?: string;
275
}
276
```
277
278
### JWT Callback Integration
279
280
JWT callback for customizing token contents and adding custom claims.
281
282
```typescript { .api }
283
/**
284
* JWT callback for customizing token contents
285
*/
286
interface JWTCallback {
287
jwt: (params: {
288
/** Current JWT token */
289
token: JWT;
290
/** User object (available on sign-in) */
291
user: User | AdapterUser;
292
/** Account object (available on sign-in) */
293
account: Account | null;
294
/** OAuth profile (available on sign-in) */
295
profile?: Profile;
296
/** Trigger that caused JWT callback */
297
trigger?: "signIn" | "signUp" | "update";
298
/** Whether this is a new user */
299
isNewUser?: boolean;
300
/** Session data when trigger is "update" */
301
session?: any;
302
}) => Awaitable<JWT>;
303
}
304
```
305
306
**Usage Examples:**
307
308
```typescript
309
// Add custom claims to JWT
310
export default NextAuth({
311
providers: [...],
312
callbacks: {
313
async jwt({ token, user, account, profile, trigger }) {
314
// Add user role on sign-in
315
if (account && user) {
316
token.role = user.role;
317
token.provider = account.provider;
318
}
319
320
// Add custom data on token update
321
if (trigger === "update" && user) {
322
token.lastUpdated = Date.now();
323
}
324
325
return token;
326
},
327
},
328
});
329
330
// Use JWT data in API routes
331
export default async function handler(req, res) {
332
const token = await getToken({ req });
333
334
if (!token) {
335
return res.status(401).json({ error: "Unauthorized" });
336
}
337
338
// Access custom claims
339
if (token.role !== "admin") {
340
return res.status(403).json({ error: "Insufficient permissions" });
341
}
342
343
res.json({
344
message: "Admin access granted",
345
provider: token.provider,
346
lastUpdated: token.lastUpdated,
347
});
348
}
349
```
350
351
### Custom Token Generation
352
353
Advanced patterns for creating custom tokens for specific use cases.
354
355
```typescript { .api }
356
/**
357
* Custom token generation utilities
358
*/
359
interface CustomTokenGeneration {
360
/** Generate API access token */
361
generateApiToken: (userId: string, scopes: string[]) => Promise<string>;
362
/** Generate email verification token */
363
generateVerificationToken: (email: string) => Promise<string>;
364
/** Generate password reset token */
365
generateResetToken: (userId: string) => Promise<string>;
366
}
367
```
368
369
**Usage Examples:**
370
371
```typescript
372
// Generate custom API token
373
export async function generateApiToken(userId: string, scopes: string[]) {
374
return await encode({
375
token: {
376
sub: userId,
377
type: "api-token",
378
scopes,
379
iat: Math.floor(Date.now() / 1000),
380
},
381
secret: process.env.NEXTAUTH_SECRET!,
382
salt: "api-token",
383
maxAge: 24 * 60 * 60, // 24 hours
384
});
385
}
386
387
// Generate email verification token
388
export async function generateEmailVerificationToken(email: string) {
389
return await encode({
390
token: {
391
email,
392
type: "email-verification",
393
iat: Math.floor(Date.now() / 1000),
394
},
395
secret: process.env.NEXTAUTH_SECRET!,
396
salt: "email-verification",
397
maxAge: 15 * 60, // 15 minutes
398
});
399
}
400
401
// Verify custom token
402
export async function verifyCustomToken(token: string, salt: string) {
403
const payload = await decode({
404
token,
405
secret: process.env.NEXTAUTH_SECRET!,
406
salt,
407
});
408
409
if (!payload) {
410
throw new Error("Invalid or expired token");
411
}
412
413
return payload;
414
}
415
```
416
417
### Token Security Best Practices
418
419
```typescript { .api }
420
/**
421
* Token security utilities and patterns
422
*/
423
interface TokenSecurity {
424
/** Validate token audience and issuer */
425
validateTokenClaims: (token: JWT, requiredClaims: Partial<JWT>) => boolean;
426
/** Check token expiration with tolerance */
427
isTokenExpired: (token: JWT, toleranceSeconds?: number) => boolean;
428
/** Rotate token if near expiration */
429
rotateTokenIfNeeded: (token: JWT, thresholdSeconds: number) => Promise<string | null>;
430
}
431
```
432
433
**Usage Examples:**
434
435
```typescript
436
// Token validation utility
437
function validateTokenClaims(token: JWT, requiredClaims: Partial<JWT>): boolean {
438
for (const [key, value] of Object.entries(requiredClaims)) {
439
if (token[key] !== value) {
440
return false;
441
}
442
}
443
return true;
444
}
445
446
// Check token expiration
447
function isTokenExpired(token: JWT, toleranceSeconds = 0): boolean {
448
if (!token.exp) return true;
449
const now = Math.floor(Date.now() / 1000);
450
return now > (token.exp + toleranceSeconds);
451
}
452
453
// API route with token validation
454
export default async function secureHandler(req, res) {
455
const token = await getToken({ req });
456
457
if (!token) {
458
return res.status(401).json({ error: "No token provided" });
459
}
460
461
// Validate custom claims
462
if (!validateTokenClaims(token, { type: "api-token" })) {
463
return res.status(403).json({ error: "Invalid token type" });
464
}
465
466
// Check if token is close to expiration
467
if (isTokenExpired(token, -300)) { // 5 minutes tolerance
468
return res.status(401).json({ error: "Token expired or expiring soon" });
469
}
470
471
res.json({ data: "Secure data" });
472
}
473
```
474
475
## Types
476
477
### Request Types
478
479
```typescript { .api }
480
interface GetServerSidePropsContext {
481
req: IncomingMessage & {
482
cookies: Partial<{ [key: string]: string }>;
483
};
484
}
485
486
interface NextApiRequest extends IncomingMessage {
487
query: Partial<{ [key: string]: string | string[] }>;
488
cookies: Partial<{ [key: string]: string }>;
489
body: any;
490
}
491
492
interface NextRequest extends Request {
493
cookies: {
494
get(name: string): { name: string; value: string } | undefined;
495
getAll(): Array<{ name: string; value: string }>;
496
};
497
nextUrl: {
498
pathname: string;
499
search: string;
500
origin: string;
501
};
502
}
503
```
504
505
### Utility Types
506
507
```typescript { .api }
508
type Awaitable<T> = T | PromiseLike<T>;
509
510
interface LoggerInstance {
511
error: (code: string, metadata?: any) => void;
512
warn: (code: string) => void;
513
debug: (code: string, metadata?: any) => void;
514
}
515
```