Authentication library for Next.js applications with support for OAuth, email, and credentials authentication
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
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.
Creates encrypted JWT tokens using NextAuth.js encryption standards with "A256GCM" encryption by default.
/**
* Issues a JWT token encrypted using "A256GCM" by default
* @param params - Token encoding parameters
* @returns Promise resolving to encrypted JWT string
*/
function encode(params: JWTEncodeParams): Promise<string>;
interface JWTEncodeParams {
/** JWT payload to encode (default: empty object) */
token?: JWT;
/** Secret used for encryption */
secret: string;
/** Maximum age in seconds (default: 2592000 - 30 days) */
maxAge?: number;
/** Salt for key derivation (empty string means session token) */
salt?: string;
}Usage Examples:
import { encode } from "next-auth/jwt";
// Basic JWT encoding
const jwt = await encode({
token: { userId: "123", role: "user" },
secret: process.env.NEXTAUTH_SECRET!,
});
// Custom expiration
const shortLivedJwt = await encode({
token: { action: "email-verification" },
secret: process.env.NEXTAUTH_SECRET!,
maxAge: 3600, // 1 hour
});
// Custom salt for different token types
const apiToken = await encode({
token: { userId: "123", scopes: ["read", "write"] },
secret: process.env.NEXTAUTH_SECRET!,
salt: "api-token",
});Decodes and verifies NextAuth.js issued JWT tokens with automatic expiration checking.
/**
* Decodes a NextAuth.js issued JWT token
* @param params - Token decoding parameters
* @returns Promise resolving to JWT payload or null if invalid/expired
*/
function decode(params: JWTDecodeParams): Promise<JWT | null>;
interface JWTDecodeParams {
/** JWT token string to decode */
token?: string;
/** Secret used for decryption */
secret: string;
/** Salt for key derivation (must match encoding salt) */
salt?: string;
}Usage Examples:
import { decode } from "next-auth/jwt";
// Basic JWT decoding
const payload = await decode({
token: "eyJhbGciOiJkaXIi...",
secret: process.env.NEXTAUTH_SECRET!,
});
if (payload) {
console.log("User ID:", payload.sub);
console.log("Token expires:", new Date(payload.exp! * 1000));
}
// Decode API token with custom salt
const apiPayload = await decode({
token: apiTokenString,
secret: process.env.NEXTAUTH_SECRET!,
salt: "api-token",
});Extracts JWT tokens from NextAuth.js requests, checking both cookies and Authorization headers.
/**
* Extracts JWT from NextAuth.js request (cookies or Authorization header)
* @param params - Token extraction parameters
* @returns Promise resolving to JWT payload, raw token string, or null
*/
function getToken<R extends boolean = false>(
params: GetTokenParams<R>
): Promise<R extends true ? string : JWT | null>;
interface GetTokenParams<R extends boolean = false> {
/** Request containing the JWT */
req: GetServerSidePropsContext["req"] | NextRequest | NextApiRequest;
/** Use secure prefix for cookie name (auto-detected from NEXTAUTH_URL) */
secureCookie?: boolean;
/** Custom cookie name to look for JWT */
cookieName?: string;
/** Return raw JWT string instead of decoded payload */
raw?: R;
/** Secret for decryption (defaults to NEXTAUTH_SECRET) */
secret?: string;
/** Custom decode function */
decode?: JWTOptions["decode"];
/** Custom logger instance */
logger?: LoggerInstance | Console;
}Usage Examples:
import { getToken } from "next-auth/jwt";
// API Route - get decoded token
export default async function handler(req, res) {
const token = await getToken({ req });
if (!token) {
return res.status(401).json({ error: "No valid token" });
}
console.log("User ID:", token.sub);
console.log("User email:", token.email);
res.json({ message: `Hello ${token.name}` });
}
// Get raw JWT string
export default async function handler(req, res) {
const rawToken = await getToken({ req, raw: true });
if (rawToken) {
// Forward token to external API
const response = await fetch("https://api.example.com/data", {
headers: {
Authorization: `Bearer ${rawToken}`,
},
});
}
}
// Middleware - check token
import { NextRequest } from "next/server";
export async function middleware(request: NextRequest) {
const token = await getToken({
req: request,
secret: process.env.NEXTAUTH_SECRET,
});
if (!token) {
return Response.redirect(new URL("/login", request.url));
}
// Check user role
if (request.nextUrl.pathname.startsWith("/admin") && token.role !== "admin") {
return Response.redirect(new URL("/unauthorized", request.url));
}
}
// Server Component - extract token
import { headers } from "next/headers";
export default async function ServerComponent() {
const token = await getToken({
req: {
headers: Object.fromEntries(headers() as Headers),
cookies: {}, // Will be populated by getToken
} as any,
secret: process.env.NEXTAUTH_SECRET,
});
return <div>User: {token?.name || "Anonymous"}</div>;
}Configuration options for customizing JWT behavior in NextAuth.js.
/**
* JWT configuration options for NextAuth.js
*/
interface JWTOptions {
/** Secret used for JWT signing and encryption */
secret: string;
/** Maximum JWT age in seconds */
maxAge: number;
/** Custom JWT encoding function */
encode?: (params: JWTEncodeParams) => Awaitable<string>;
/** Custom JWT decoding function */
decode?: (params: JWTDecodeParams) => Awaitable<JWT | null>;
}Usage Example:
// Custom JWT configuration in NextAuth
export default NextAuth({
providers: [...],
jwt: {
secret: process.env.JWT_SECRET,
maxAge: 24 * 60 * 60, // 24 hours
encode: async ({ secret, token, maxAge }) => {
// Custom encoding logic
return customEncode(token, secret, maxAge);
},
decode: async ({ secret, token }) => {
// Custom decoding logic
return customDecode(token, secret);
},
},
});Standard JWT payload structure used by NextAuth.js with extensible properties.
/**
* JWT token payload structure
*/
interface JWT extends Record<string, unknown>, DefaultJWT {
/** User identifier (subject) */
sub?: string;
/** User name */
name?: string | null;
/** User email address */
email?: string | null;
/** User profile picture URL */
picture?: string | null;
/** Token issued at timestamp (seconds since epoch) */
iat?: number;
/** Token expiration timestamp (seconds since epoch) */
exp?: number;
/** JWT ID (unique identifier) */
jti?: string;
/** Additional custom properties can be added via callbacks */
[key: string]: unknown;
}
interface DefaultJWT extends Record<string, unknown> {
name?: string | null;
email?: string | null;
picture?: string | null;
sub?: string;
}JWT callback for customizing token contents and adding custom claims.
/**
* JWT callback for customizing token contents
*/
interface JWTCallback {
jwt: (params: {
/** Current JWT token */
token: JWT;
/** User object (available on sign-in) */
user: User | AdapterUser;
/** Account object (available on sign-in) */
account: Account | null;
/** OAuth profile (available on sign-in) */
profile?: Profile;
/** Trigger that caused JWT callback */
trigger?: "signIn" | "signUp" | "update";
/** Whether this is a new user */
isNewUser?: boolean;
/** Session data when trigger is "update" */
session?: any;
}) => Awaitable<JWT>;
}Usage Examples:
// Add custom claims to JWT
export default NextAuth({
providers: [...],
callbacks: {
async jwt({ token, user, account, profile, trigger }) {
// Add user role on sign-in
if (account && user) {
token.role = user.role;
token.provider = account.provider;
}
// Add custom data on token update
if (trigger === "update" && user) {
token.lastUpdated = Date.now();
}
return token;
},
},
});
// Use JWT data in API routes
export default async function handler(req, res) {
const token = await getToken({ req });
if (!token) {
return res.status(401).json({ error: "Unauthorized" });
}
// Access custom claims
if (token.role !== "admin") {
return res.status(403).json({ error: "Insufficient permissions" });
}
res.json({
message: "Admin access granted",
provider: token.provider,
lastUpdated: token.lastUpdated,
});
}Advanced patterns for creating custom tokens for specific use cases.
/**
* Custom token generation utilities
*/
interface CustomTokenGeneration {
/** Generate API access token */
generateApiToken: (userId: string, scopes: string[]) => Promise<string>;
/** Generate email verification token */
generateVerificationToken: (email: string) => Promise<string>;
/** Generate password reset token */
generateResetToken: (userId: string) => Promise<string>;
}Usage Examples:
// Generate custom API token
export async function generateApiToken(userId: string, scopes: string[]) {
return await encode({
token: {
sub: userId,
type: "api-token",
scopes,
iat: Math.floor(Date.now() / 1000),
},
secret: process.env.NEXTAUTH_SECRET!,
salt: "api-token",
maxAge: 24 * 60 * 60, // 24 hours
});
}
// Generate email verification token
export async function generateEmailVerificationToken(email: string) {
return await encode({
token: {
email,
type: "email-verification",
iat: Math.floor(Date.now() / 1000),
},
secret: process.env.NEXTAUTH_SECRET!,
salt: "email-verification",
maxAge: 15 * 60, // 15 minutes
});
}
// Verify custom token
export async function verifyCustomToken(token: string, salt: string) {
const payload = await decode({
token,
secret: process.env.NEXTAUTH_SECRET!,
salt,
});
if (!payload) {
throw new Error("Invalid or expired token");
}
return payload;
}/**
* Token security utilities and patterns
*/
interface TokenSecurity {
/** Validate token audience and issuer */
validateTokenClaims: (token: JWT, requiredClaims: Partial<JWT>) => boolean;
/** Check token expiration with tolerance */
isTokenExpired: (token: JWT, toleranceSeconds?: number) => boolean;
/** Rotate token if near expiration */
rotateTokenIfNeeded: (token: JWT, thresholdSeconds: number) => Promise<string | null>;
}Usage Examples:
// Token validation utility
function validateTokenClaims(token: JWT, requiredClaims: Partial<JWT>): boolean {
for (const [key, value] of Object.entries(requiredClaims)) {
if (token[key] !== value) {
return false;
}
}
return true;
}
// Check token expiration
function isTokenExpired(token: JWT, toleranceSeconds = 0): boolean {
if (!token.exp) return true;
const now = Math.floor(Date.now() / 1000);
return now > (token.exp + toleranceSeconds);
}
// API route with token validation
export default async function secureHandler(req, res) {
const token = await getToken({ req });
if (!token) {
return res.status(401).json({ error: "No token provided" });
}
// Validate custom claims
if (!validateTokenClaims(token, { type: "api-token" })) {
return res.status(403).json({ error: "Invalid token type" });
}
// Check if token is close to expiration
if (isTokenExpired(token, -300)) { // 5 minutes tolerance
return res.status(401).json({ error: "Token expired or expiring soon" });
}
res.json({ data: "Secure data" });
}interface GetServerSidePropsContext {
req: IncomingMessage & {
cookies: Partial<{ [key: string]: string }>;
};
}
interface NextApiRequest extends IncomingMessage {
query: Partial<{ [key: string]: string | string[] }>;
cookies: Partial<{ [key: string]: string }>;
body: any;
}
interface NextRequest extends Request {
cookies: {
get(name: string): { name: string; value: string } | undefined;
getAll(): Array<{ name: string; value: string }>;
};
nextUrl: {
pathname: string;
search: string;
origin: string;
};
}type Awaitable<T> = T | PromiseLike<T>;
interface LoggerInstance {
error: (code: string, metadata?: any) => void;
warn: (code: string) => void;
debug: (code: string, metadata?: any) => void;
}