or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

authentication-providers.mdcore-authentication.mddatabase-integration.mdindex.mdjwt-management.mdmiddleware-protection.mdreact-integration.mdserver-side-sessions.md

jwt-management.mddocs/

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

```