@saurbit/oauth2-jwt
v0.1.9
Published
JWT utilities for @saurbit/oauth2 (jose-based)
Readme
@saurbit/oauth2-jwt
JWT utilities and JWKS authority for @saurbit/oauth2. Wraps
jose to provide ready-made implementations of the JWT-related
interfaces required by @saurbit/oauth2.
Installation
Node.js / Bun
npm install @saurbit/oauth2-jwt
# or
yarn add @saurbit/oauth2-jwt
# or
pnpm add @saurbit/oauth2-jwt
# or
bun add @saurbit/oauth2-jwtDeno / JSR
deno add jsr:@saurbit/oauth2-jwtJWT Utilities
@saurbit/oauth2-jwt provides ready-made functions that satisfy the JwtVerify, JwtDecode,
ClientAssertionJwtVerify, and JwkVerify interfaces expected by @saurbit/oauth2.
verifyClientAssertionJwt and decodeJwt
Used by the ClientSecretJwt and PrivateKeyJwt client authentication methods.
verifyClientAssertionJwt automatically enforces issuer and subject equal to the client ID as
required by RFC 7523.
ClientSecretJwt
import { ClientSecretJwt } from "@saurbit/oauth2";
import { decodeJwt, verifyClientAssertionJwt } from "@saurbit/oauth2-jwt";
const clientSecretJwt = new ClientSecretJwt(decodeJwt, verifyClientAssertionJwt);PrivateKeyJwt
import { PrivateKeyJwt } from "@saurbit/oauth2";
import { decodeJwt, verifyClientAssertionJwt } from "@saurbit/oauth2-jwt";
const privateKeyJwt = new PrivateKeyJwt(decodeJwt, verifyClientAssertionJwt);createClientAssertionJwtVerify
Creates a ClientAssertionJwtVerify function with pre-configured or dynamically resolved claim
verification options. Useful when you need to enforce additional claims (e.g. audience) or when
verification options depend on the request context.
import { ClientSecretJwt } from "@saurbit/oauth2";
import { createClientAssertionJwtVerify, decodeJwt } from "@saurbit/oauth2-jwt";
// Static options
const verifyClientAssertion = createClientAssertionJwtVerify({
audience: "https://auth.example.com/token",
});
const clientSecretJwt = new ClientSecretJwt(decodeJwt, verifyClientAssertion);import { PrivateKeyJwt } from "@saurbit/oauth2";
import { createClientAssertionJwtVerify, decodeJwt } from "@saurbit/oauth2-jwt";
// Dynamic options from context
const verifyClientAssertion = createClientAssertionJwtVerify(
async (context, request) => {
const url = new URL(request.url);
return {
audience: url.origin + url.pathname,
};
},
);
const privateKeyJwt = new PrivateKeyJwt(decodeJwt, verifyClientAssertion);verifyJwk
Used by DPoPTokenType for Demonstration of Proof-of-Possession token validation.
import { createInMemoryReplayStore, DPoPTokenType } from "@saurbit/oauth2";
import { calculateJwkThumbprint, verifyJwk } from "@saurbit/oauth2-jwt";
const dpop = new DPoPTokenType(verifyJwk, calculateJwkThumbprint, createInMemoryReplayStore());verifyJwk only accepts ES256, ES384, ES512, PS256, PS384, and PS512 algorithm tokens.
You can also customize the allowed algorithms by passing an array of algorithm names to
createDPoPJwkVerify.
createDPoPJwkVerify
Used to create a JwkVerify function with a custom set of allowed algorithms.
import { createInMemoryReplayStore, DPoPTokenType } from "@saurbit/oauth2";
import { calculateJwkThumbprint, createDPoPJwkVerify, verifyJwk } from "@saurbit/oauth2-jwt";
const dpop = new DPoPTokenType(
createDPoPJwkVerify(["ES256", "PS256"]), // only allow ES256 and PS256 algorithms
calculateJwkThumbprint,
createInMemoryReplayStore(),
);calculateJwkThumbprint
JWKS Authority
JoseJwksAuthority manages RS256 signing key pairs, signs and verifies JWTs, and returns the JWKS
endpoint payload. Keys are stored in a JwksKeyStore and generated automatically on first use.
Setup
import { createInMemoryKeyStore, JoseJwksAuthority } from "@saurbit/oauth2-jwt";
const store = createInMemoryKeyStore();
const authority = new JoseJwksAuthority(store, 8.64e+6); // public keys valid for 100 daysSign a JWT
const { token } = await authority.sign({
sub: "user-123",
iss: "https://auth.example.com",
aud: "my-client",
iat: Math.floor(Date.now() / 1000),
exp: Math.floor(Date.now() / 1000) + 3600,
jti: crypto.randomUUID(),
});Verify a JWT
const payload = await authority.verify(token);
console.log(payload.sub); // "user-123"JWKS endpoint
getJwksEndpointResponse() returns the set of current public keys in JWKS format, ready to be
served at a well-known endpoint (e.g. /.well-known/jwks.json).
// Example with Hono
app.get("/jwks", async (c) => {
return c.json(await authority.getJwksEndpointResponse());
});The response has the shape { keys: RawKey[] } and is safe to return directly as JSON.
Key Rotation
JwksRotator manages scheduled key rotation. It compares the current time against the last rotation
timestamp and generates a new key pair only when the configured interval has elapsed.
Call checkAndRotateKeys() at service startup and/or on a recurring schedule (e.g. every hour). If
a rotation is due a new key pair is generated; otherwise the call is a no-op. During rotation the
previous public key remains available in the JWKS until its TTL expires, so in-flight tokens
continue to verify correctly.
Setup
import { createInMemoryKeyStore, JoseJwksAuthority, JwksRotator } from "@saurbit/oauth2-jwt";
const store = createInMemoryKeyStore();
const authority = new JoseJwksAuthority(store, 8.64e+6); // 100 days key TTL
const rotator = new JwksRotator({
keyGenerator: authority,
rotationTimestampStore: store,
rotationIntervalMs: 7.884e9, // 91 days
});Usage
// At startup
await rotator.checkAndRotateKeys();
// Or on a recurring schedule
setInterval(async () => {
await rotator.checkAndRotateKeys();
}, 60 * 60 * 1000); // every hour