Getting Started
Install
Use the package manager that fits your project:
bash
bun add keylabbash
npm install keylabbash
yarn add keylabGenerate a key pair
ts
import { getKeyPair, JwtAlgorithmsEnum as Algs } from "keylab"
const keyPair = await getKeyPair({
keyFormat: "jwk",
algorithmIdentifier: Algs.ES256,
keySize: 2048,
})Sign a token
ts
import { signJwtWithPrivateKey, JwtAlgorithmsEnum as Algs } from "keylab"
const token = await signJwtWithPrivateKey(
{
sub: "user-123",
iss: "https://issuer.example",
aud: ["web-app"],
scp: "profile:read",
},
Algs.ES256,
keyPair.privateKey,
{ kid: keyPair.kid },
)Verify with an in-memory public key
ts
import { checkTokenValidness } from "keylab"
const verified = await checkTokenValidness(token, {
adhoc: [keyPair.publicKey],
})Verify with a JWKS endpoint
ts
import { checkTokenValidness } from "keylab"
const verified = await checkTokenValidness(token, {
jwksUri: "https://issuer.example/.well-known/jwks.json",
requiredIssuer: "https://issuer.example",
requiredAudiences: ["web-app"],
})When to use JWK vs PEM
- Prefer
jwkwhen you want runtime portability. - Use
pemwhen you are integrating with existing Node.js or OpenSSL-based systems. - For
ES256K,Ed448, andX448, prefer JWK in Workers and browsers.
Token expiration utilities
Check if a token has expired or get time remaining:
ts
import { isTokenExpired, getTimeToExpiry } from "keylab"
if (isTokenExpired(token)) {
console.log("Token has expired")
}
const secondsLeft = getTimeToExpiry(token)
if (secondsLeft < 60) {
// proactively refresh
}JWE (Encryption)
Encrypt and decrypt data using JWE compact serialization:
ts
import { encryptJwe, decryptJwe, getKeyPair, JwtAlgorithmsEnum as Algs } from "keylab"
// Generate RSA-OAEP keys
const keys = await getKeyPair({
algorithmIdentifier: Algs.RSA_OAEP,
keyFormat: "jwk",
keySize: 2048,
})
// Encrypt
const jwe = await encryptJwe("sensitive payload", {
alg: "RSA-OAEP",
enc: "A256GCM",
key: keys.publicKey,
})
// Decrypt
const { plaintext } = await decryptJwe(jwe, {
key: keys.privateKey,
expectedAlg: "RSA-OAEP",
})JWKS Cache
Use the built-in cache for efficient JWKS endpoint access:
ts
import { JwksCache } from "keylab"
const cache = new JwksCache({
ttlMs: 600_000, // cache for 10 minutes
maxRetries: 3, // retry failed requests
timeoutMs: 5_000, // 5s request timeout
onKeyRotation: (oldKids, newKids) => {
console.log("Key rotation detected")
},
})
const keys = await cache.getKeys("https://issuer.example/.well-known/jwks.json")Express middleware
Protect routes with JWT verification:
ts
import express from "express"
import { createJwtMiddleware } from "keylab"
const app = express()
app.use(createJwtMiddleware({
jwksUri: "https://issuer.example/.well-known/jwks.json",
}))
app.get("/api/me", (req, res) => {
res.json(req.auth)
})Workers / Bun / Deno handler
Use createJwtHandler for non-Express runtimes:
ts
import { createJwtHandler } from "keylab"
const verify = createJwtHandler({
jwksUri: "https://issuer.example/.well-known/jwks.json",
})
export default {
async fetch(request: Request) {
const result = await verify(request)
if (!result.success) {
return new Response("Unauthorized", { status: 401 })
}
return new Response(JSON.stringify(result.auth))
},
}Error handling
Use specific error classes for programmatic error handling:
ts
import { checkTokenValidness, TokenExpiredError, InvalidSignatureError } from "keylab"
try {
const result = await checkTokenValidness(token, { secret: "my-secret" })
} catch (err) {
if (err instanceof TokenExpiredError) {
console.log("Token expired at:", err.expiredAt)
} else if (err instanceof InvalidSignatureError) {
console.log("Invalid signature")
}
}