Nextjs
Next.js JWT Utilities
Verify and consume Keyloom JWTs on the server and in middleware using @keyloom/nextjs/jwt-server.
JWT Utilities (server)
Utilities for verifying JWT access/refresh tokens issued by Keyloom.
import {
extractAccessToken,
extractRefreshToken,
verifyJwtToken,
getJwtSession,
requireJwtAuth,
createJwtConfig,
clearServerJwksCache,
} from "@keyloom/nextjs/jwt-server";Cookies
__keyloom_access: short-lived access token__keyloom_refresh: long-lived refresh token
Create JwtConfig from env
const jwt = createJwtConfig({
KEYLOOM_JWT_JWKS_URL: process.env.KEYLOOM_JWT_JWKS_URL,
KEYLOOM_JWT_ISSUER: process.env.KEYLOOM_JWT_ISSUER,
KEYLOOM_JWT_AUDIENCE: process.env.KEYLOOM_JWT_AUDIENCE,
KEYLOOM_JWT_CLOCK_SKEW_SEC: process.env.KEYLOOM_JWT_CLOCK_SKEW_SEC,
});Verify a token
const { valid, claims, error } = await verifyJwtToken(token, {
jwksUrl: process.env.KEYLOOM_JWT_JWKS_URL!,
expectedIssuer: process.env.KEYLOOM_JWT_ISSUER,
expectedAudience: process.env.KEYLOOM_JWT_AUDIENCE,
clockSkewSec: 60,
});Get current session (server)
const { user, session, claims } = await getJwtSession({
jwksUrl: process.env.KEYLOOM_JWT_JWKS_URL!,
expectedIssuer: process.env.KEYLOOM_JWT_ISSUER,
});user:{ id, email? } | nullsession:{ id, userId, expiresAt } | null
Require authentication
const { user, session } = await requireJwtAuth({
jwksUrl: process.env.KEYLOOM_JWT_JWKS_URL!,
});Throws Error("Authentication required") when not authenticated.
Middleware access
import type { NextRequest } from "next/server";
import { getJwtSession } from "@keyloom/nextjs/jwt-server";
export function middleware(req: NextRequest) {
// Beware: network fetch to JWKS may be slower in middleware; rely on server guards if needed
}JWKS Cache
clearServerJwksCache() clears the in-memory JWKS cache, useful for tests.
Prerequisites
- JWT strategy enabled or JWKS exposed by your server
KEYLOOM_JWT_JWKS_URLset to your/api/auth/jwksendpoint- If verifying
aud/iss, setKEYLOOM_JWT_AUDIENCEandKEYLOOM_JWT_ISSUER
API reference
| Function | Signature | Returns |
|---|---|---|
createJwtConfig(env) | ({ KEYLOOM_JWT_JWKS_URL, KEYLOOM_JWT_ISSUER?, KEYLOOM_JWT_AUDIENCE?, KEYLOOM_JWT_CLOCK_SKEW_SEC? }) | JwtVerifyOptions |
verifyJwtToken(token, opts) | (string, { jwksUrl, expectedIssuer?, expectedAudience?, clockSkewSec? }) | { valid: boolean; claims?; error? } |
getJwtSession(opts?) | ({ jwksUrl, expectedIssuer?, expectedAudience? }) | { user, session, claims } |
requireJwtAuth(opts?) | same as above | throws on unauthenticated |
extractAccessToken() | () => string | null | from cookie or Authorization |
extractRefreshToken() | () => string | null | from cookie |
clearServerJwksCache() | () => void | clears cache |
Runnable examples
import { requireJwtAuth } from "@keyloom/nextjs/jwt-server";
export async function GET() {
try {
const { user, session } = await requireJwtAuth({
jwksUrl: process.env.KEYLOOM_JWT_JWKS_URL!,
expectedIssuer: process.env.KEYLOOM_JWT_ISSUER,
});
return Response.json({ user, session });
} catch (e) {
return Response.json({ error: "unauthorized" }, { status: 401 });
}
}import { getJwtSession } from "@keyloom/nextjs/jwt-server";
export default async function Page() {
const { user } = await getJwtSession({
jwksUrl: process.env.KEYLOOM_JWKS_URL!,
});
if (!user) return <a href="/sign-in">Sign in</a>;
return <p>Hi {user.id}</p>;
}Error handling
- Map verification errors to 401; avoid leaking details to clients
- For expired tokens, prompt a refresh flow (server issues new access using refresh)
const { valid, error } = await verifyJwtToken(token, { jwksUrl });
if (!valid) return Response.json({ error: "invalid_token" }, { status: 401 });Edge notes
- Avoid verifying refresh tokens in middleware; use server routes
- Cache JWKS in memory for a few minutes to minimize fetches
Performance
- Verify once at request boundary and pass claims through
- Batch external calls after verification; do not mix verification and data loads
Troubleshooting
fetchto JWKS blocked in middleware: verify your deployment allows outgoing fetches at edgekidnot found: ensure your JWKS contains active and overlapping keys during rotationissmismatch: setKEYLOOM_JWT_ISSUERto your public app URL
See also
- Core JWT: /docs/core/jwt
- Handler endpoints: /docs/nextjs/handler
- Middleware: /docs/nextjs/middleware
Next steps
- Add refresh flow endpoints and integrate session UX
How is this guide?