@lacspace/jwt 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -113,6 +113,17 @@ const next = await rotateRefreshToken(refreshToken, secret, { isUsed: (jti) => s
113
113
  store.add(next.usedJti); // mark the old one spent
114
114
  ```
115
115
 
116
+ `issueTokenPair` stamps the access token with `typ: "access"` and the refresh token with
117
+ `typ: "refresh"`. Since both are signed with the same secret, **always verify a refresh token
118
+ with `{ requireTyp: "refresh" }`** so an access token can never be replayed as a refresh token
119
+ (and gate protected routes with `{ requireTyp: "access" }`):
120
+
121
+ ```ts
122
+ const claims = await verify(refreshToken, secret, { requireTyp: "refresh" }); // rejects non-refresh typ
123
+ ```
124
+
125
+ `requireTyp` is off by default, so existing `verify(...)` calls are unchanged.
126
+
116
127
  Plus `audience: string[]` matching and `importJwk()`.
117
128
 
118
129
  ## Licensing
@@ -121,4 +132,14 @@ This package is **free** under the **[Lacspace Free Licence](https://lacspace.co
121
132
 
122
133
  Not every Lacspace package is free. We also offer **Commercial** (paid), **Client-specific**, and **Private** (proprietary) packages under separate terms. See the full **[Lacspace Licence Centre](https://lacspace.com/licenses)**.
123
134
 
135
+ ---
136
+
137
+ <div align="center">
138
+
139
+ **Part of the Lacspace ecosystem — 35 zero-dependency, isomorphic TypeScript packages.**
140
+
141
+ [All packages ↗](https://lacspace.com/packages) · [npm org ↗](https://www.npmjs.com/org/lacspace) · [Licence Centre ↗](https://lacspace.com/licenses) · [GitHub ↗](https://github.com/lacspace/npm-packages)
142
+
143
+ </div>
144
+
124
145
  <div align="center"><sub>Built with care by <a href="https://lacspace.com">Lacspace</a> · Lacspace Free Licence · <a href="https://github.com/lacspace/npm-packages">source</a></sub></div>
package/dist/index.cjs CHANGED
@@ -138,6 +138,8 @@ async function verify(token, key, opts = {}) {
138
138
  const have = Array.isArray(payload.aud) ? payload.aud : payload.aud ? [payload.aud] : [];
139
139
  if (!want.some((a) => have.includes(a))) throw new JwtError("audience mismatch", "audience");
140
140
  }
141
+ if (opts.requireTyp !== void 0 && payload.typ !== opts.requireTyp)
142
+ throw new JwtError(`token type mismatch (expected ${opts.requireTyp})`, "malformed");
141
143
  return payload;
142
144
  }
143
145
  function pemToDer(pem) {
@@ -183,7 +185,8 @@ function createRemoteJWKS(url, opts = {}) {
183
185
  async function issueTokenPair(payload, secret, opts = {}) {
184
186
  const subject = opts.subject ?? payload.sub;
185
187
  const common = { algorithm: opts.algorithm, issuer: opts.issuer, audience: opts.audience, subject, keyId: opts.keyId };
186
- const accessToken = await sign(payload, secret, { ...common, expiresIn: opts.accessTtl ?? 900 });
188
+ const accessPayload = payload.typ !== void 0 ? payload : { typ: "access", ...payload };
189
+ const accessToken = await sign(accessPayload, secret, { ...common, expiresIn: opts.accessTtl ?? 900 });
187
190
  const refreshJti = randomToken(16);
188
191
  const refreshToken = await sign({ typ: "refresh", jti: refreshJti }, secret, {
189
192
  ...common,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"names":["hmac","toBase64url","fromBase64url","constantTimeEqual","randomBytes"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * @lacspace/jwt\n * JSON Web Tokens (HMAC) + secure random tokens — done safely.\n *\n * Sign and verify HS256/HS384/HS512 JWTs over Web Crypto (isomorphic), with\n * strict expiry / not-before / issuer / audience checks and constant-time\n * signature comparison. Plus opaque secure tokens and CSRF tokens.\n *\n * Zero dependencies (bar @lacspace/crypto) · isomorphic · fully typed.\n */\n\nimport { hmac, toBase64url, fromBase64url, constantTimeEqual, randomBytes, type HashAlgorithm } from \"@lacspace/crypto\";\n\nexport type Algorithm =\n | \"HS256\" | \"HS384\" | \"HS512\"\n | \"RS256\" | \"RS384\" | \"RS512\"\n | \"ES256\" | \"ES384\" | \"ES512\";\n\nconst ALG_HASH: Record<Algorithm, HashAlgorithm> = {\n HS256: \"SHA-256\", HS384: \"SHA-384\", HS512: \"SHA-512\",\n RS256: \"SHA-256\", RS384: \"SHA-384\", RS512: \"SHA-512\",\n ES256: \"SHA-256\", ES384: \"SHA-384\", ES512: \"SHA-512\",\n};\n\n/** A signing/verification key: an HMAC secret, or a Web Crypto key for RS and ES. */\nexport type SigningKey = string | Uint8Array | CryptoKey;\n/** Resolve a verification key from the token header (e.g. by `kid`). */\nexport type KeyResolver = (header: { alg: Algorithm; kid?: string }) => SigningKey | Promise<SigningKey>;\n\nexport class JwtError extends Error {\n constructor(\n message: string,\n public code:\n | \"malformed\"\n | \"signature\"\n | \"expired\"\n | \"not_active\"\n | \"issuer\"\n | \"audience\"\n | \"algorithm\"\n | \"reuse\",\n ) {\n super(message);\n this.name = \"JwtError\";\n }\n}\n\nfunction getSubtle(): SubtleCrypto {\n const c = (globalThis as { crypto?: Crypto }).crypto;\n if (!c || !c.subtle) throw new JwtError(\"Web Crypto API unavailable for asymmetric JWT\", \"algorithm\");\n return c.subtle;\n}\n\nfunction b64ToBytes(b64: string): Uint8Array {\n if (typeof atob !== \"undefined\") {\n const bin = atob(b64);\n const out = new Uint8Array(bin.length);\n for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);\n return out;\n }\n return new Uint8Array(Buffer.from(b64, \"base64\"));\n}\n\nfunction subtleSignAlgo(alg: Algorithm): AlgorithmIdentifier | EcdsaParams {\n if (alg.startsWith(\"RS\")) return { name: \"RSASSA-PKCS1-v1_5\" };\n if (alg.startsWith(\"ES\")) return { name: \"ECDSA\", hash: ALG_HASH[alg] };\n throw new JwtError(`not an asymmetric algorithm: ${alg}`, \"algorithm\");\n}\n\nfunction subtleImportAlgo(alg: Algorithm): RsaHashedImportParams | EcKeyImportParams {\n if (alg.startsWith(\"RS\")) return { name: \"RSASSA-PKCS1-v1_5\", hash: ALG_HASH[alg] };\n const curve = alg === \"ES256\" ? \"P-256\" : alg === \"ES384\" ? \"P-384\" : \"P-521\";\n return { name: \"ECDSA\", namedCurve: curve };\n}\n\nexport interface JwtPayload {\n iss?: string;\n sub?: string;\n aud?: string | string[];\n exp?: number;\n nbf?: number;\n iat?: number;\n jti?: string;\n [key: string]: unknown;\n}\n\nexport interface SignOptions {\n algorithm?: Algorithm;\n /** Seconds from now until expiry, e.g. 3600. */\n expiresIn?: number;\n issuer?: string;\n audience?: string | string[];\n subject?: string;\n /** `kid` header — which key signed this (for rotation / JWKS). */\n keyId?: string;\n}\n\nasync function signBytes(alg: Algorithm, key: SigningKey, input: string): Promise<Uint8Array> {\n if (alg.startsWith(\"HS\")) {\n if (typeof key !== \"string\" && !(key instanceof Uint8Array))\n throw new JwtError(\"HMAC algorithms need a string/bytes secret\", \"algorithm\");\n return hmac(key, input, ALG_HASH[alg]);\n }\n if (!(typeof CryptoKey !== \"undefined\" && key instanceof CryptoKey))\n throw new JwtError(`${alg} needs a CryptoKey — use importPkcs8 / importJwk`, \"algorithm\");\n const sig = await getSubtle().sign(subtleSignAlgo(alg), key, enc.encode(input) as unknown as BufferSource);\n return new Uint8Array(sig);\n}\n\nconst enc = new TextEncoder();\nconst dec = new TextDecoder();\n\nfunction b64urlJson(obj: unknown): string {\n return toBase64url(enc.encode(JSON.stringify(obj)));\n}\n\nfunction nowSec(): number {\n return Math.floor(Date.now() / 1000);\n}\n\n/** Sign a JWT. Pass an HMAC secret (HS*) or a Web Crypto private key (RS and ES). */\nexport async function sign(\n payload: JwtPayload,\n secret: SigningKey,\n opts: SignOptions = {},\n): Promise<string> {\n const alg = opts.algorithm ?? \"HS256\";\n const iat = nowSec();\n const body: JwtPayload = { iat, ...payload };\n if (opts.expiresIn !== undefined) body.exp = iat + opts.expiresIn;\n if (opts.issuer) body.iss = opts.issuer;\n if (opts.audience) body.aud = opts.audience;\n if (opts.subject) body.sub = opts.subject;\n\n const header: Record<string, unknown> = { alg, typ: \"JWT\" };\n if (opts.keyId) header.kid = opts.keyId;\n const signingInput = `${b64urlJson(header)}.${b64urlJson(body)}`;\n const sig = await signBytes(alg, secret, signingInput);\n return `${signingInput}.${toBase64url(sig)}`;\n}\n\nexport interface VerifyOptions {\n algorithms?: Algorithm[];\n issuer?: string;\n audience?: string | string[];\n /** Allowed clock skew in seconds. Default 0. */\n clockTolerance?: number;\n}\n\n/** Decode a JWT without verifying (never trust the result for auth). */\nexport function decode(token: string): { header: Record<string, unknown>; payload: JwtPayload } {\n const parts = token.split(\".\");\n if (parts.length !== 3) throw new JwtError(\"malformed token\", \"malformed\");\n return {\n header: JSON.parse(dec.decode(fromBase64url(parts[0]!))),\n payload: JSON.parse(dec.decode(fromBase64url(parts[1]!))),\n };\n}\n\n/**\n * Verify a JWT's signature and claims. Throws {@link JwtError} on any failure.\n * `key` is an HMAC secret, a Web Crypto public key (RS and ES), or a resolver\n * (e.g. from {@link createRemoteJWKS}) that returns the key for the token header.\n */\nexport async function verify<T extends JwtPayload = JwtPayload>(\n token: string,\n key: SigningKey | KeyResolver,\n opts: VerifyOptions = {},\n): Promise<T> {\n const parts = token.split(\".\");\n if (parts.length !== 3) throw new JwtError(\"malformed token\", \"malformed\");\n const [h, p, s] = parts as [string, string, string];\n\n let header: { alg?: Algorithm; kid?: string };\n try {\n header = JSON.parse(dec.decode(fromBase64url(h)));\n } catch {\n throw new JwtError(\"malformed header\", \"malformed\");\n }\n const alg = header.alg;\n if (!alg || !(alg in ALG_HASH)) throw new JwtError(`unsupported algorithm ${alg}`, \"algorithm\");\n if (opts.algorithms && !opts.algorithms.includes(alg))\n throw new JwtError(`algorithm ${alg} not allowed`, \"algorithm\");\n\n const resolved = typeof key === \"function\" ? await key({ alg, kid: header.kid }) : key;\n\n const signingInput = `${h}.${p}`;\n const sig = fromBase64url(s);\n let valid: boolean;\n if (alg.startsWith(\"HS\")) {\n if (typeof resolved !== \"string\" && !(resolved instanceof Uint8Array))\n throw new JwtError(\"HMAC algorithms need a string/bytes secret\", \"algorithm\");\n valid = constantTimeEqual(await hmac(resolved, signingInput, ALG_HASH[alg]), sig);\n } else {\n if (!(typeof CryptoKey !== \"undefined\" && resolved instanceof CryptoKey))\n throw new JwtError(`${alg} needs a CryptoKey — use importSpki / importJwk / createRemoteJWKS`, \"algorithm\");\n valid = await getSubtle().verify(\n subtleSignAlgo(alg),\n resolved,\n sig as unknown as BufferSource,\n enc.encode(signingInput) as unknown as BufferSource,\n );\n }\n if (!valid) throw new JwtError(\"signature verification failed\", \"signature\");\n\n let payload: T;\n try {\n payload = JSON.parse(dec.decode(fromBase64url(p)));\n } catch {\n throw new JwtError(\"malformed payload\", \"malformed\");\n }\n\n const now = nowSec();\n const skew = opts.clockTolerance ?? 0;\n if (payload.exp !== undefined && now > payload.exp + skew)\n throw new JwtError(\"token expired\", \"expired\");\n if (payload.nbf !== undefined && now + skew < payload.nbf)\n throw new JwtError(\"token not yet active\", \"not_active\");\n if (opts.issuer && payload.iss !== opts.issuer)\n throw new JwtError(\"issuer mismatch\", \"issuer\");\n if (opts.audience) {\n const want = Array.isArray(opts.audience) ? opts.audience : [opts.audience];\n const have = Array.isArray(payload.aud) ? payload.aud : payload.aud ? [payload.aud] : [];\n if (!want.some((a) => have.includes(a))) throw new JwtError(\"audience mismatch\", \"audience\");\n }\n return payload;\n}\n\n/* ------------------------------ asymmetric keys & JWKS ------------------------------ */\n\nfunction pemToDer(pem: string): Uint8Array {\n const b64 = pem.replace(/-----BEGIN [^-]+-----/, \"\").replace(/-----END [^-]+-----/, \"\").replace(/\\s+/g, \"\");\n return b64ToBytes(b64);\n}\n\n/** Import a PKCS#8 PEM private key for signing (RS and ES). */\nexport function importPkcs8(pem: string, alg: Algorithm): Promise<CryptoKey> {\n return getSubtle().importKey(\"pkcs8\", pemToDer(pem) as unknown as BufferSource, subtleImportAlgo(alg), false, [\"sign\"]);\n}\n\n/** Import an SPKI PEM public key for verification (RS and ES). */\nexport function importSpki(pem: string, alg: Algorithm): Promise<CryptoKey> {\n return getSubtle().importKey(\"spki\", pemToDer(pem) as unknown as BufferSource, subtleImportAlgo(alg), false, [\"verify\"]);\n}\n\n/** Import a JWK (public or private) for verify/sign. */\nexport function importJwk(jwk: JsonWebKey, alg: Algorithm): Promise<CryptoKey> {\n const usage: KeyUsage[] = jwk.d ? [\"sign\"] : [\"verify\"];\n return getSubtle().importKey(\"jwk\", jwk, subtleImportAlgo(alg), false, usage);\n}\n\nexport interface JwksOptions {\n /** Cache the key set this long (ms). Default 600000 (10 min). */\n cacheMaxAgeMs?: number;\n /** Custom fetch (tests / edge). */\n fetch?: typeof fetch;\n}\n\n/**\n * A cached remote JWKS resolver for {@link verify}. Fetches the JSON Web Key\n * Set once, caches it, and returns the right public key by the token's `kid`.\n * @example const jwks = createRemoteJWKS(\"https://issuer/.well-known/jwks.json\");\n * const payload = await verify(token, jwks, { algorithms: [\"RS256\"] });\n */\nexport function createRemoteJWKS(url: string, opts: JwksOptions = {}): KeyResolver {\n const ttl = opts.cacheMaxAgeMs ?? 600000;\n const f = opts.fetch ?? (typeof fetch !== \"undefined\" ? fetch.bind(globalThis) : undefined);\n if (!f) throw new JwtError(\"no global fetch — pass opts.fetch\", \"signature\");\n let cache: { keys: JsonWebKey[]; at: number } | null = null;\n const imported = new Map<string, CryptoKey>();\n\n return async ({ alg, kid }) => {\n if (!cache || Date.now() - cache.at > ttl) {\n const res = await f(url);\n if (!res.ok) throw new JwtError(`JWKS fetch failed: ${res.status}`, \"signature\");\n const data = (await res.json()) as { keys?: JsonWebKey[] };\n cache = { keys: data.keys ?? [], at: Date.now() };\n imported.clear();\n }\n const jwk = kid ? cache.keys.find((k) => (k as { kid?: string }).kid === kid) : cache.keys[0];\n if (!jwk) throw new JwtError(`no JWK found for kid \"${kid}\"`, \"signature\");\n const keyAlg = ((jwk as { alg?: string }).alg as Algorithm) ?? alg;\n const cacheKey = `${keyAlg}:${kid ?? \"\"}`;\n let ck = imported.get(cacheKey);\n if (!ck) {\n ck = await importJwk(jwk, keyAlg);\n imported.set(cacheKey, ck);\n }\n return ck;\n };\n}\n\n/* ------------------------------ refresh-token flow ------------------------------ */\n\nexport interface TokenPair {\n accessToken: string;\n refreshToken: string;\n /** The refresh token's `jti` — persist it to detect reuse on rotation. */\n refreshJti: string;\n}\n\nexport interface IssuePairOptions {\n algorithm?: Algorithm;\n /** Access-token lifetime in seconds. Default 900 (15 min). */\n accessTtl?: number;\n /** Refresh-token lifetime in seconds. Default 2592000 (30 days). */\n refreshTtl?: number;\n issuer?: string;\n audience?: string | string[];\n subject?: string;\n keyId?: string;\n}\n\n/** Issue a short-lived access token + a long-lived refresh token (with a `jti`). */\nexport async function issueTokenPair(\n payload: JwtPayload,\n secret: SigningKey,\n opts: IssuePairOptions = {},\n): Promise<TokenPair> {\n const subject = opts.subject ?? (payload.sub as string | undefined);\n const common = { algorithm: opts.algorithm, issuer: opts.issuer, audience: opts.audience, subject, keyId: opts.keyId };\n const accessToken = await sign(payload, secret, { ...common, expiresIn: opts.accessTtl ?? 900 });\n const refreshJti = randomToken(16);\n const refreshToken = await sign({ typ: \"refresh\", jti: refreshJti }, secret, {\n ...common,\n expiresIn: opts.refreshTtl ?? 2592000,\n });\n return { accessToken, refreshToken, refreshJti };\n}\n\n/** Verify a refresh token (must carry `typ: \"refresh\"`). */\nexport async function verifyRefreshToken<T extends JwtPayload = JwtPayload>(\n token: string,\n key: SigningKey | KeyResolver,\n opts: VerifyOptions = {},\n): Promise<T> {\n const payload = await verify<T>(token, key, opts);\n if (payload.typ !== \"refresh\") throw new JwtError(\"not a refresh token\", \"malformed\");\n return payload;\n}\n\nexport interface RotateOptions extends IssuePairOptions, VerifyOptions {\n /** New access-token payload. Defaults to `{ sub }` from the refresh token. */\n payload?: JwtPayload;\n /** Reuse detection: return true if this refresh `jti` was already used/revoked. */\n isUsed?: (jti: string) => boolean | Promise<boolean>;\n}\n\n/**\n * Rotate a refresh token: verify it, optionally detect reuse, and issue a fresh\n * pair. Returns the new pair plus `usedJti` (persist it as spent).\n */\nexport async function rotateRefreshToken(\n refreshToken: string,\n secret: SigningKey,\n opts: RotateOptions = {},\n): Promise<TokenPair & { usedJti?: string }> {\n const claims = await verifyRefreshToken(refreshToken, secret, opts);\n const jti = claims.jti;\n if (jti && opts.isUsed && (await opts.isUsed(jti)))\n throw new JwtError(\"refresh token reuse detected\", \"reuse\");\n const pair = await issueTokenPair(opts.payload ?? { sub: claims.sub }, secret, {\n ...opts,\n subject: opts.subject ?? (claims.sub as string | undefined),\n });\n return { ...pair, usedJti: jti };\n}\n\n/* ------------------------------ opaque tokens ------------------------------ */\n\n/** A cryptographically-random URL-safe token (default 32 bytes → 43 chars). */\nexport function randomToken(bytes = 32): string {\n return toBase64url(randomBytes(bytes));\n}\n\n/** A CSRF token (alias of a 32-byte random token). */\nexport function csrfToken(): string {\n return randomToken(32);\n}\n\n/* ------------------------------ adapters ------------------------------ */\n\n/** Anything we can read a header off: a Fetch Request/Headers, or Node `req.headers`. */\nexport type HeadersLike =\n | Request\n | Headers\n | { headers: Headers | Record<string, string | string[] | undefined> }\n | Record<string, string | string[] | undefined>;\n\nfunction readHeader(src: HeadersLike, name: string): string | undefined {\n const lower = name.toLowerCase();\n const h: unknown = (src as { headers?: unknown }).headers ?? src;\n if (h && typeof (h as Headers).get === \"function\") return (h as Headers).get(name) ?? undefined;\n const rec = h as Record<string, string | string[] | undefined>;\n const v = rec[name] ?? rec[lower];\n return Array.isArray(v) ? v[0] : v ?? undefined;\n}\n\n/** Extract a bearer token from an `Authorization` header (or a named cookie). */\nexport function extractBearer(src: HeadersLike, opts: { cookieName?: string } = {}): string | undefined {\n const auth = readHeader(src, \"authorization\");\n if (auth) {\n const m = /^Bearer\\s+(.+)$/i.exec(auth.trim());\n if (m) return m[1];\n }\n if (opts.cookieName) {\n const cookie = readHeader(src, \"cookie\");\n if (cookie) {\n const found = cookie.split(/;\\s*/).find((c) => c.startsWith(`${opts.cookieName}=`));\n if (found) return decodeURIComponent(found.slice(opts.cookieName.length + 1));\n }\n }\n return undefined;\n}\n\n/** Read the bearer token from a request and verify it. Throws {@link JwtError}. */\nexport async function authenticate<T extends JwtPayload = JwtPayload>(\n src: HeadersLike,\n secret: SigningKey | KeyResolver,\n opts: VerifyOptions & { cookieName?: string } = {},\n): Promise<T> {\n const token = extractBearer(src, { cookieName: opts.cookieName });\n if (!token) throw new JwtError(\"no bearer token\", \"malformed\");\n return verify<T>(token, secret, opts);\n}\n\nexport interface AuthCookieOptions {\n name?: string;\n maxAge?: number; // seconds\n path?: string;\n domain?: string;\n secure?: boolean;\n httpOnly?: boolean;\n sameSite?: \"Strict\" | \"Lax\" | \"None\";\n}\n\n/** Build a hardened `Set-Cookie` value carrying an auth token. */\nexport function toAuthCookie(token: string, opts: AuthCookieOptions = {}): string {\n const name = opts.name ?? \"token\";\n const parts = [`${name}=${encodeURIComponent(token)}`, `Path=${opts.path ?? \"/\"}`, `SameSite=${opts.sameSite ?? \"Lax\"}`];\n if (opts.maxAge !== undefined) parts.push(`Max-Age=${opts.maxAge}`);\n if (opts.domain) parts.push(`Domain=${opts.domain}`);\n if (opts.httpOnly !== false) parts.push(\"HttpOnly\");\n if (opts.secure !== false) parts.push(\"Secure\");\n return parts.join(\"; \");\n}\n\n/** Build a `Set-Cookie` value that clears the auth cookie. */\nexport function clearAuthCookie(opts: Pick<AuthCookieOptions, \"name\" | \"path\" | \"domain\"> = {}): string {\n const name = opts.name ?? \"token\";\n const parts = [`${name}=`, `Path=${opts.path ?? \"/\"}`, \"Max-Age=0\"];\n if (opts.domain) parts.push(`Domain=${opts.domain}`);\n return parts.join(\"; \");\n}\n\n/** Minimal Express-style middleware: verifies the bearer token → `req.user`, else 401. */\nexport function expressJwt<T extends JwtPayload = JwtPayload>(\n secret: SigningKey | KeyResolver,\n opts: VerifyOptions & { cookieName?: string; userKey?: string } = {},\n) {\n const userKey = opts.userKey ?? \"user\";\n return async (\n req: { headers: Record<string, string | string[] | undefined>; [k: string]: unknown },\n res: { status: (n: number) => { json: (b: unknown) => unknown } },\n next: (err?: unknown) => void,\n ): Promise<void> => {\n try {\n req[userKey] = await authenticate<T>(req, secret, opts);\n next();\n } catch (err) {\n const code = err instanceof JwtError ? err.code : \"malformed\";\n res.status(401).json({ error: \"unauthorized\", code });\n }\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/index.ts"],"names":["hmac","toBase64url","fromBase64url","constantTimeEqual","randomBytes"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * @lacspace/jwt\n * JSON Web Tokens (HMAC) + secure random tokens — done safely.\n *\n * Sign and verify HS256/HS384/HS512 JWTs over Web Crypto (isomorphic), with\n * strict expiry / not-before / issuer / audience checks and constant-time\n * signature comparison. Plus opaque secure tokens and CSRF tokens.\n *\n * Zero dependencies (bar @lacspace/crypto) · isomorphic · fully typed.\n */\n\nimport { hmac, toBase64url, fromBase64url, constantTimeEqual, randomBytes, type HashAlgorithm } from \"@lacspace/crypto\";\n\nexport type Algorithm =\n | \"HS256\" | \"HS384\" | \"HS512\"\n | \"RS256\" | \"RS384\" | \"RS512\"\n | \"ES256\" | \"ES384\" | \"ES512\";\n\nconst ALG_HASH: Record<Algorithm, HashAlgorithm> = {\n HS256: \"SHA-256\", HS384: \"SHA-384\", HS512: \"SHA-512\",\n RS256: \"SHA-256\", RS384: \"SHA-384\", RS512: \"SHA-512\",\n ES256: \"SHA-256\", ES384: \"SHA-384\", ES512: \"SHA-512\",\n};\n\n/** A signing/verification key: an HMAC secret, or a Web Crypto key for RS and ES. */\nexport type SigningKey = string | Uint8Array | CryptoKey;\n/** Resolve a verification key from the token header (e.g. by `kid`). */\nexport type KeyResolver = (header: { alg: Algorithm; kid?: string }) => SigningKey | Promise<SigningKey>;\n\nexport class JwtError extends Error {\n constructor(\n message: string,\n public code:\n | \"malformed\"\n | \"signature\"\n | \"expired\"\n | \"not_active\"\n | \"issuer\"\n | \"audience\"\n | \"algorithm\"\n | \"reuse\",\n ) {\n super(message);\n this.name = \"JwtError\";\n }\n}\n\nfunction getSubtle(): SubtleCrypto {\n const c = (globalThis as { crypto?: Crypto }).crypto;\n if (!c || !c.subtle) throw new JwtError(\"Web Crypto API unavailable for asymmetric JWT\", \"algorithm\");\n return c.subtle;\n}\n\nfunction b64ToBytes(b64: string): Uint8Array {\n if (typeof atob !== \"undefined\") {\n const bin = atob(b64);\n const out = new Uint8Array(bin.length);\n for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);\n return out;\n }\n return new Uint8Array(Buffer.from(b64, \"base64\"));\n}\n\nfunction subtleSignAlgo(alg: Algorithm): AlgorithmIdentifier | EcdsaParams {\n if (alg.startsWith(\"RS\")) return { name: \"RSASSA-PKCS1-v1_5\" };\n if (alg.startsWith(\"ES\")) return { name: \"ECDSA\", hash: ALG_HASH[alg] };\n throw new JwtError(`not an asymmetric algorithm: ${alg}`, \"algorithm\");\n}\n\nfunction subtleImportAlgo(alg: Algorithm): RsaHashedImportParams | EcKeyImportParams {\n if (alg.startsWith(\"RS\")) return { name: \"RSASSA-PKCS1-v1_5\", hash: ALG_HASH[alg] };\n const curve = alg === \"ES256\" ? \"P-256\" : alg === \"ES384\" ? \"P-384\" : \"P-521\";\n return { name: \"ECDSA\", namedCurve: curve };\n}\n\nexport interface JwtPayload {\n iss?: string;\n sub?: string;\n aud?: string | string[];\n exp?: number;\n nbf?: number;\n iat?: number;\n jti?: string;\n [key: string]: unknown;\n}\n\nexport interface SignOptions {\n algorithm?: Algorithm;\n /** Seconds from now until expiry, e.g. 3600. */\n expiresIn?: number;\n issuer?: string;\n audience?: string | string[];\n subject?: string;\n /** `kid` header — which key signed this (for rotation / JWKS). */\n keyId?: string;\n}\n\nasync function signBytes(alg: Algorithm, key: SigningKey, input: string): Promise<Uint8Array> {\n if (alg.startsWith(\"HS\")) {\n if (typeof key !== \"string\" && !(key instanceof Uint8Array))\n throw new JwtError(\"HMAC algorithms need a string/bytes secret\", \"algorithm\");\n return hmac(key, input, ALG_HASH[alg]);\n }\n if (!(typeof CryptoKey !== \"undefined\" && key instanceof CryptoKey))\n throw new JwtError(`${alg} needs a CryptoKey — use importPkcs8 / importJwk`, \"algorithm\");\n const sig = await getSubtle().sign(subtleSignAlgo(alg), key, enc.encode(input) as unknown as BufferSource);\n return new Uint8Array(sig);\n}\n\nconst enc = new TextEncoder();\nconst dec = new TextDecoder();\n\nfunction b64urlJson(obj: unknown): string {\n return toBase64url(enc.encode(JSON.stringify(obj)));\n}\n\nfunction nowSec(): number {\n return Math.floor(Date.now() / 1000);\n}\n\n/** Sign a JWT. Pass an HMAC secret (HS*) or a Web Crypto private key (RS and ES). */\nexport async function sign(\n payload: JwtPayload,\n secret: SigningKey,\n opts: SignOptions = {},\n): Promise<string> {\n const alg = opts.algorithm ?? \"HS256\";\n const iat = nowSec();\n const body: JwtPayload = { iat, ...payload };\n if (opts.expiresIn !== undefined) body.exp = iat + opts.expiresIn;\n if (opts.issuer) body.iss = opts.issuer;\n if (opts.audience) body.aud = opts.audience;\n if (opts.subject) body.sub = opts.subject;\n\n const header: Record<string, unknown> = { alg, typ: \"JWT\" };\n if (opts.keyId) header.kid = opts.keyId;\n const signingInput = `${b64urlJson(header)}.${b64urlJson(body)}`;\n const sig = await signBytes(alg, secret, signingInput);\n return `${signingInput}.${toBase64url(sig)}`;\n}\n\nexport interface VerifyOptions {\n algorithms?: Algorithm[];\n issuer?: string;\n audience?: string | string[];\n /** Allowed clock skew in seconds. Default 0. */\n clockTolerance?: number;\n /** If set, reject unless the token's `typ` claim equals this (e.g. \"refresh\"). */\n requireTyp?: string;\n}\n\n/** Decode a JWT without verifying (never trust the result for auth). */\nexport function decode(token: string): { header: Record<string, unknown>; payload: JwtPayload } {\n const parts = token.split(\".\");\n if (parts.length !== 3) throw new JwtError(\"malformed token\", \"malformed\");\n return {\n header: JSON.parse(dec.decode(fromBase64url(parts[0]!))),\n payload: JSON.parse(dec.decode(fromBase64url(parts[1]!))),\n };\n}\n\n/**\n * Verify a JWT's signature and claims. Throws {@link JwtError} on any failure.\n * `key` is an HMAC secret, a Web Crypto public key (RS and ES), or a resolver\n * (e.g. from {@link createRemoteJWKS}) that returns the key for the token header.\n */\nexport async function verify<T extends JwtPayload = JwtPayload>(\n token: string,\n key: SigningKey | KeyResolver,\n opts: VerifyOptions = {},\n): Promise<T> {\n const parts = token.split(\".\");\n if (parts.length !== 3) throw new JwtError(\"malformed token\", \"malformed\");\n const [h, p, s] = parts as [string, string, string];\n\n let header: { alg?: Algorithm; kid?: string };\n try {\n header = JSON.parse(dec.decode(fromBase64url(h)));\n } catch {\n throw new JwtError(\"malformed header\", \"malformed\");\n }\n const alg = header.alg;\n if (!alg || !(alg in ALG_HASH)) throw new JwtError(`unsupported algorithm ${alg}`, \"algorithm\");\n if (opts.algorithms && !opts.algorithms.includes(alg))\n throw new JwtError(`algorithm ${alg} not allowed`, \"algorithm\");\n\n const resolved = typeof key === \"function\" ? await key({ alg, kid: header.kid }) : key;\n\n const signingInput = `${h}.${p}`;\n const sig = fromBase64url(s);\n let valid: boolean;\n if (alg.startsWith(\"HS\")) {\n if (typeof resolved !== \"string\" && !(resolved instanceof Uint8Array))\n throw new JwtError(\"HMAC algorithms need a string/bytes secret\", \"algorithm\");\n valid = constantTimeEqual(await hmac(resolved, signingInput, ALG_HASH[alg]), sig);\n } else {\n if (!(typeof CryptoKey !== \"undefined\" && resolved instanceof CryptoKey))\n throw new JwtError(`${alg} needs a CryptoKey — use importSpki / importJwk / createRemoteJWKS`, \"algorithm\");\n valid = await getSubtle().verify(\n subtleSignAlgo(alg),\n resolved,\n sig as unknown as BufferSource,\n enc.encode(signingInput) as unknown as BufferSource,\n );\n }\n if (!valid) throw new JwtError(\"signature verification failed\", \"signature\");\n\n let payload: T;\n try {\n payload = JSON.parse(dec.decode(fromBase64url(p)));\n } catch {\n throw new JwtError(\"malformed payload\", \"malformed\");\n }\n\n const now = nowSec();\n const skew = opts.clockTolerance ?? 0;\n if (payload.exp !== undefined && now > payload.exp + skew)\n throw new JwtError(\"token expired\", \"expired\");\n if (payload.nbf !== undefined && now + skew < payload.nbf)\n throw new JwtError(\"token not yet active\", \"not_active\");\n if (opts.issuer && payload.iss !== opts.issuer)\n throw new JwtError(\"issuer mismatch\", \"issuer\");\n if (opts.audience) {\n const want = Array.isArray(opts.audience) ? opts.audience : [opts.audience];\n const have = Array.isArray(payload.aud) ? payload.aud : payload.aud ? [payload.aud] : [];\n if (!want.some((a) => have.includes(a))) throw new JwtError(\"audience mismatch\", \"audience\");\n }\n if (opts.requireTyp !== undefined && payload.typ !== opts.requireTyp)\n throw new JwtError(`token type mismatch (expected ${opts.requireTyp})`, \"malformed\");\n return payload;\n}\n\n/* ------------------------------ asymmetric keys & JWKS ------------------------------ */\n\nfunction pemToDer(pem: string): Uint8Array {\n const b64 = pem.replace(/-----BEGIN [^-]+-----/, \"\").replace(/-----END [^-]+-----/, \"\").replace(/\\s+/g, \"\");\n return b64ToBytes(b64);\n}\n\n/** Import a PKCS#8 PEM private key for signing (RS and ES). */\nexport function importPkcs8(pem: string, alg: Algorithm): Promise<CryptoKey> {\n return getSubtle().importKey(\"pkcs8\", pemToDer(pem) as unknown as BufferSource, subtleImportAlgo(alg), false, [\"sign\"]);\n}\n\n/** Import an SPKI PEM public key for verification (RS and ES). */\nexport function importSpki(pem: string, alg: Algorithm): Promise<CryptoKey> {\n return getSubtle().importKey(\"spki\", pemToDer(pem) as unknown as BufferSource, subtleImportAlgo(alg), false, [\"verify\"]);\n}\n\n/** Import a JWK (public or private) for verify/sign. */\nexport function importJwk(jwk: JsonWebKey, alg: Algorithm): Promise<CryptoKey> {\n const usage: KeyUsage[] = jwk.d ? [\"sign\"] : [\"verify\"];\n return getSubtle().importKey(\"jwk\", jwk, subtleImportAlgo(alg), false, usage);\n}\n\nexport interface JwksOptions {\n /** Cache the key set this long (ms). Default 600000 (10 min). */\n cacheMaxAgeMs?: number;\n /** Custom fetch (tests / edge). */\n fetch?: typeof fetch;\n}\n\n/**\n * A cached remote JWKS resolver for {@link verify}. Fetches the JSON Web Key\n * Set once, caches it, and returns the right public key by the token's `kid`.\n * @example const jwks = createRemoteJWKS(\"https://issuer/.well-known/jwks.json\");\n * const payload = await verify(token, jwks, { algorithms: [\"RS256\"] });\n */\nexport function createRemoteJWKS(url: string, opts: JwksOptions = {}): KeyResolver {\n const ttl = opts.cacheMaxAgeMs ?? 600000;\n const f = opts.fetch ?? (typeof fetch !== \"undefined\" ? fetch.bind(globalThis) : undefined);\n if (!f) throw new JwtError(\"no global fetch — pass opts.fetch\", \"signature\");\n let cache: { keys: JsonWebKey[]; at: number } | null = null;\n const imported = new Map<string, CryptoKey>();\n\n return async ({ alg, kid }) => {\n if (!cache || Date.now() - cache.at > ttl) {\n const res = await f(url);\n if (!res.ok) throw new JwtError(`JWKS fetch failed: ${res.status}`, \"signature\");\n const data = (await res.json()) as { keys?: JsonWebKey[] };\n cache = { keys: data.keys ?? [], at: Date.now() };\n imported.clear();\n }\n const jwk = kid ? cache.keys.find((k) => (k as { kid?: string }).kid === kid) : cache.keys[0];\n if (!jwk) throw new JwtError(`no JWK found for kid \"${kid}\"`, \"signature\");\n const keyAlg = ((jwk as { alg?: string }).alg as Algorithm) ?? alg;\n const cacheKey = `${keyAlg}:${kid ?? \"\"}`;\n let ck = imported.get(cacheKey);\n if (!ck) {\n ck = await importJwk(jwk, keyAlg);\n imported.set(cacheKey, ck);\n }\n return ck;\n };\n}\n\n/* ------------------------------ refresh-token flow ------------------------------ */\n\nexport interface TokenPair {\n accessToken: string;\n refreshToken: string;\n /** The refresh token's `jti` — persist it to detect reuse on rotation. */\n refreshJti: string;\n}\n\nexport interface IssuePairOptions {\n algorithm?: Algorithm;\n /** Access-token lifetime in seconds. Default 900 (15 min). */\n accessTtl?: number;\n /** Refresh-token lifetime in seconds. Default 2592000 (30 days). */\n refreshTtl?: number;\n issuer?: string;\n audience?: string | string[];\n subject?: string;\n keyId?: string;\n}\n\n/** Issue a short-lived access token + a long-lived refresh token (with a `jti`). */\nexport async function issueTokenPair(\n payload: JwtPayload,\n secret: SigningKey,\n opts: IssuePairOptions = {},\n): Promise<TokenPair> {\n const subject = opts.subject ?? (payload.sub as string | undefined);\n const common = { algorithm: opts.algorithm, issuer: opts.issuer, audience: opts.audience, subject, keyId: opts.keyId };\n const accessPayload = payload.typ !== undefined ? payload : { typ: \"access\", ...payload };\n const accessToken = await sign(accessPayload, secret, { ...common, expiresIn: opts.accessTtl ?? 900 });\n const refreshJti = randomToken(16);\n const refreshToken = await sign({ typ: \"refresh\", jti: refreshJti }, secret, {\n ...common,\n expiresIn: opts.refreshTtl ?? 2592000,\n });\n return { accessToken, refreshToken, refreshJti };\n}\n\n/** Verify a refresh token (must carry `typ: \"refresh\"`). */\nexport async function verifyRefreshToken<T extends JwtPayload = JwtPayload>(\n token: string,\n key: SigningKey | KeyResolver,\n opts: VerifyOptions = {},\n): Promise<T> {\n const payload = await verify<T>(token, key, opts);\n if (payload.typ !== \"refresh\") throw new JwtError(\"not a refresh token\", \"malformed\");\n return payload;\n}\n\nexport interface RotateOptions extends IssuePairOptions, VerifyOptions {\n /** New access-token payload. Defaults to `{ sub }` from the refresh token. */\n payload?: JwtPayload;\n /** Reuse detection: return true if this refresh `jti` was already used/revoked. */\n isUsed?: (jti: string) => boolean | Promise<boolean>;\n}\n\n/**\n * Rotate a refresh token: verify it, optionally detect reuse, and issue a fresh\n * pair. Returns the new pair plus `usedJti` (persist it as spent).\n */\nexport async function rotateRefreshToken(\n refreshToken: string,\n secret: SigningKey,\n opts: RotateOptions = {},\n): Promise<TokenPair & { usedJti?: string }> {\n const claims = await verifyRefreshToken(refreshToken, secret, opts);\n const jti = claims.jti;\n if (jti && opts.isUsed && (await opts.isUsed(jti)))\n throw new JwtError(\"refresh token reuse detected\", \"reuse\");\n const pair = await issueTokenPair(opts.payload ?? { sub: claims.sub }, secret, {\n ...opts,\n subject: opts.subject ?? (claims.sub as string | undefined),\n });\n return { ...pair, usedJti: jti };\n}\n\n/* ------------------------------ opaque tokens ------------------------------ */\n\n/** A cryptographically-random URL-safe token (default 32 bytes → 43 chars). */\nexport function randomToken(bytes = 32): string {\n return toBase64url(randomBytes(bytes));\n}\n\n/** A CSRF token (alias of a 32-byte random token). */\nexport function csrfToken(): string {\n return randomToken(32);\n}\n\n/* ------------------------------ adapters ------------------------------ */\n\n/** Anything we can read a header off: a Fetch Request/Headers, or Node `req.headers`. */\nexport type HeadersLike =\n | Request\n | Headers\n | { headers: Headers | Record<string, string | string[] | undefined> }\n | Record<string, string | string[] | undefined>;\n\nfunction readHeader(src: HeadersLike, name: string): string | undefined {\n const lower = name.toLowerCase();\n const h: unknown = (src as { headers?: unknown }).headers ?? src;\n if (h && typeof (h as Headers).get === \"function\") return (h as Headers).get(name) ?? undefined;\n const rec = h as Record<string, string | string[] | undefined>;\n const v = rec[name] ?? rec[lower];\n return Array.isArray(v) ? v[0] : v ?? undefined;\n}\n\n/** Extract a bearer token from an `Authorization` header (or a named cookie). */\nexport function extractBearer(src: HeadersLike, opts: { cookieName?: string } = {}): string | undefined {\n const auth = readHeader(src, \"authorization\");\n if (auth) {\n const m = /^Bearer\\s+(.+)$/i.exec(auth.trim());\n if (m) return m[1];\n }\n if (opts.cookieName) {\n const cookie = readHeader(src, \"cookie\");\n if (cookie) {\n const found = cookie.split(/;\\s*/).find((c) => c.startsWith(`${opts.cookieName}=`));\n if (found) return decodeURIComponent(found.slice(opts.cookieName.length + 1));\n }\n }\n return undefined;\n}\n\n/** Read the bearer token from a request and verify it. Throws {@link JwtError}. */\nexport async function authenticate<T extends JwtPayload = JwtPayload>(\n src: HeadersLike,\n secret: SigningKey | KeyResolver,\n opts: VerifyOptions & { cookieName?: string } = {},\n): Promise<T> {\n const token = extractBearer(src, { cookieName: opts.cookieName });\n if (!token) throw new JwtError(\"no bearer token\", \"malformed\");\n return verify<T>(token, secret, opts);\n}\n\nexport interface AuthCookieOptions {\n name?: string;\n maxAge?: number; // seconds\n path?: string;\n domain?: string;\n secure?: boolean;\n httpOnly?: boolean;\n sameSite?: \"Strict\" | \"Lax\" | \"None\";\n}\n\n/** Build a hardened `Set-Cookie` value carrying an auth token. */\nexport function toAuthCookie(token: string, opts: AuthCookieOptions = {}): string {\n const name = opts.name ?? \"token\";\n const parts = [`${name}=${encodeURIComponent(token)}`, `Path=${opts.path ?? \"/\"}`, `SameSite=${opts.sameSite ?? \"Lax\"}`];\n if (opts.maxAge !== undefined) parts.push(`Max-Age=${opts.maxAge}`);\n if (opts.domain) parts.push(`Domain=${opts.domain}`);\n if (opts.httpOnly !== false) parts.push(\"HttpOnly\");\n if (opts.secure !== false) parts.push(\"Secure\");\n return parts.join(\"; \");\n}\n\n/** Build a `Set-Cookie` value that clears the auth cookie. */\nexport function clearAuthCookie(opts: Pick<AuthCookieOptions, \"name\" | \"path\" | \"domain\"> = {}): string {\n const name = opts.name ?? \"token\";\n const parts = [`${name}=`, `Path=${opts.path ?? \"/\"}`, \"Max-Age=0\"];\n if (opts.domain) parts.push(`Domain=${opts.domain}`);\n return parts.join(\"; \");\n}\n\n/** Minimal Express-style middleware: verifies the bearer token → `req.user`, else 401. */\nexport function expressJwt<T extends JwtPayload = JwtPayload>(\n secret: SigningKey | KeyResolver,\n opts: VerifyOptions & { cookieName?: string; userKey?: string } = {},\n) {\n const userKey = opts.userKey ?? \"user\";\n return async (\n req: { headers: Record<string, string | string[] | undefined>; [k: string]: unknown },\n res: { status: (n: number) => { json: (b: unknown) => unknown } },\n next: (err?: unknown) => void,\n ): Promise<void> => {\n try {\n req[userKey] = await authenticate<T>(req, secret, opts);\n next();\n } catch (err) {\n const code = err instanceof JwtError ? err.code : \"malformed\";\n res.status(401).json({ error: \"unauthorized\", code });\n }\n };\n}\n"]}
package/dist/index.d.cts CHANGED
@@ -48,6 +48,8 @@ interface VerifyOptions {
48
48
  audience?: string | string[];
49
49
  /** Allowed clock skew in seconds. Default 0. */
50
50
  clockTolerance?: number;
51
+ /** If set, reject unless the token's `typ` claim equals this (e.g. "refresh"). */
52
+ requireTyp?: string;
51
53
  }
52
54
  /** Decode a JWT without verifying (never trust the result for auth). */
53
55
  declare function decode(token: string): {
package/dist/index.d.ts CHANGED
@@ -48,6 +48,8 @@ interface VerifyOptions {
48
48
  audience?: string | string[];
49
49
  /** Allowed clock skew in seconds. Default 0. */
50
50
  clockTolerance?: number;
51
+ /** If set, reject unless the token's `typ` claim equals this (e.g. "refresh"). */
52
+ requireTyp?: string;
51
53
  }
52
54
  /** Decode a JWT without verifying (never trust the result for auth). */
53
55
  declare function decode(token: string): {
package/dist/index.js CHANGED
@@ -136,6 +136,8 @@ async function verify(token, key, opts = {}) {
136
136
  const have = Array.isArray(payload.aud) ? payload.aud : payload.aud ? [payload.aud] : [];
137
137
  if (!want.some((a) => have.includes(a))) throw new JwtError("audience mismatch", "audience");
138
138
  }
139
+ if (opts.requireTyp !== void 0 && payload.typ !== opts.requireTyp)
140
+ throw new JwtError(`token type mismatch (expected ${opts.requireTyp})`, "malformed");
139
141
  return payload;
140
142
  }
141
143
  function pemToDer(pem) {
@@ -181,7 +183,8 @@ function createRemoteJWKS(url, opts = {}) {
181
183
  async function issueTokenPair(payload, secret, opts = {}) {
182
184
  const subject = opts.subject ?? payload.sub;
183
185
  const common = { algorithm: opts.algorithm, issuer: opts.issuer, audience: opts.audience, subject, keyId: opts.keyId };
184
- const accessToken = await sign(payload, secret, { ...common, expiresIn: opts.accessTtl ?? 900 });
186
+ const accessPayload = payload.typ !== void 0 ? payload : { typ: "access", ...payload };
187
+ const accessToken = await sign(accessPayload, secret, { ...common, expiresIn: opts.accessTtl ?? 900 });
185
188
  const refreshJti = randomToken(16);
186
189
  const refreshToken = await sign({ typ: "refresh", jti: refreshJti }, secret, {
187
190
  ...common,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @lacspace/jwt\n * JSON Web Tokens (HMAC) + secure random tokens — done safely.\n *\n * Sign and verify HS256/HS384/HS512 JWTs over Web Crypto (isomorphic), with\n * strict expiry / not-before / issuer / audience checks and constant-time\n * signature comparison. Plus opaque secure tokens and CSRF tokens.\n *\n * Zero dependencies (bar @lacspace/crypto) · isomorphic · fully typed.\n */\n\nimport { hmac, toBase64url, fromBase64url, constantTimeEqual, randomBytes, type HashAlgorithm } from \"@lacspace/crypto\";\n\nexport type Algorithm =\n | \"HS256\" | \"HS384\" | \"HS512\"\n | \"RS256\" | \"RS384\" | \"RS512\"\n | \"ES256\" | \"ES384\" | \"ES512\";\n\nconst ALG_HASH: Record<Algorithm, HashAlgorithm> = {\n HS256: \"SHA-256\", HS384: \"SHA-384\", HS512: \"SHA-512\",\n RS256: \"SHA-256\", RS384: \"SHA-384\", RS512: \"SHA-512\",\n ES256: \"SHA-256\", ES384: \"SHA-384\", ES512: \"SHA-512\",\n};\n\n/** A signing/verification key: an HMAC secret, or a Web Crypto key for RS and ES. */\nexport type SigningKey = string | Uint8Array | CryptoKey;\n/** Resolve a verification key from the token header (e.g. by `kid`). */\nexport type KeyResolver = (header: { alg: Algorithm; kid?: string }) => SigningKey | Promise<SigningKey>;\n\nexport class JwtError extends Error {\n constructor(\n message: string,\n public code:\n | \"malformed\"\n | \"signature\"\n | \"expired\"\n | \"not_active\"\n | \"issuer\"\n | \"audience\"\n | \"algorithm\"\n | \"reuse\",\n ) {\n super(message);\n this.name = \"JwtError\";\n }\n}\n\nfunction getSubtle(): SubtleCrypto {\n const c = (globalThis as { crypto?: Crypto }).crypto;\n if (!c || !c.subtle) throw new JwtError(\"Web Crypto API unavailable for asymmetric JWT\", \"algorithm\");\n return c.subtle;\n}\n\nfunction b64ToBytes(b64: string): Uint8Array {\n if (typeof atob !== \"undefined\") {\n const bin = atob(b64);\n const out = new Uint8Array(bin.length);\n for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);\n return out;\n }\n return new Uint8Array(Buffer.from(b64, \"base64\"));\n}\n\nfunction subtleSignAlgo(alg: Algorithm): AlgorithmIdentifier | EcdsaParams {\n if (alg.startsWith(\"RS\")) return { name: \"RSASSA-PKCS1-v1_5\" };\n if (alg.startsWith(\"ES\")) return { name: \"ECDSA\", hash: ALG_HASH[alg] };\n throw new JwtError(`not an asymmetric algorithm: ${alg}`, \"algorithm\");\n}\n\nfunction subtleImportAlgo(alg: Algorithm): RsaHashedImportParams | EcKeyImportParams {\n if (alg.startsWith(\"RS\")) return { name: \"RSASSA-PKCS1-v1_5\", hash: ALG_HASH[alg] };\n const curve = alg === \"ES256\" ? \"P-256\" : alg === \"ES384\" ? \"P-384\" : \"P-521\";\n return { name: \"ECDSA\", namedCurve: curve };\n}\n\nexport interface JwtPayload {\n iss?: string;\n sub?: string;\n aud?: string | string[];\n exp?: number;\n nbf?: number;\n iat?: number;\n jti?: string;\n [key: string]: unknown;\n}\n\nexport interface SignOptions {\n algorithm?: Algorithm;\n /** Seconds from now until expiry, e.g. 3600. */\n expiresIn?: number;\n issuer?: string;\n audience?: string | string[];\n subject?: string;\n /** `kid` header — which key signed this (for rotation / JWKS). */\n keyId?: string;\n}\n\nasync function signBytes(alg: Algorithm, key: SigningKey, input: string): Promise<Uint8Array> {\n if (alg.startsWith(\"HS\")) {\n if (typeof key !== \"string\" && !(key instanceof Uint8Array))\n throw new JwtError(\"HMAC algorithms need a string/bytes secret\", \"algorithm\");\n return hmac(key, input, ALG_HASH[alg]);\n }\n if (!(typeof CryptoKey !== \"undefined\" && key instanceof CryptoKey))\n throw new JwtError(`${alg} needs a CryptoKey — use importPkcs8 / importJwk`, \"algorithm\");\n const sig = await getSubtle().sign(subtleSignAlgo(alg), key, enc.encode(input) as unknown as BufferSource);\n return new Uint8Array(sig);\n}\n\nconst enc = new TextEncoder();\nconst dec = new TextDecoder();\n\nfunction b64urlJson(obj: unknown): string {\n return toBase64url(enc.encode(JSON.stringify(obj)));\n}\n\nfunction nowSec(): number {\n return Math.floor(Date.now() / 1000);\n}\n\n/** Sign a JWT. Pass an HMAC secret (HS*) or a Web Crypto private key (RS and ES). */\nexport async function sign(\n payload: JwtPayload,\n secret: SigningKey,\n opts: SignOptions = {},\n): Promise<string> {\n const alg = opts.algorithm ?? \"HS256\";\n const iat = nowSec();\n const body: JwtPayload = { iat, ...payload };\n if (opts.expiresIn !== undefined) body.exp = iat + opts.expiresIn;\n if (opts.issuer) body.iss = opts.issuer;\n if (opts.audience) body.aud = opts.audience;\n if (opts.subject) body.sub = opts.subject;\n\n const header: Record<string, unknown> = { alg, typ: \"JWT\" };\n if (opts.keyId) header.kid = opts.keyId;\n const signingInput = `${b64urlJson(header)}.${b64urlJson(body)}`;\n const sig = await signBytes(alg, secret, signingInput);\n return `${signingInput}.${toBase64url(sig)}`;\n}\n\nexport interface VerifyOptions {\n algorithms?: Algorithm[];\n issuer?: string;\n audience?: string | string[];\n /** Allowed clock skew in seconds. Default 0. */\n clockTolerance?: number;\n}\n\n/** Decode a JWT without verifying (never trust the result for auth). */\nexport function decode(token: string): { header: Record<string, unknown>; payload: JwtPayload } {\n const parts = token.split(\".\");\n if (parts.length !== 3) throw new JwtError(\"malformed token\", \"malformed\");\n return {\n header: JSON.parse(dec.decode(fromBase64url(parts[0]!))),\n payload: JSON.parse(dec.decode(fromBase64url(parts[1]!))),\n };\n}\n\n/**\n * Verify a JWT's signature and claims. Throws {@link JwtError} on any failure.\n * `key` is an HMAC secret, a Web Crypto public key (RS and ES), or a resolver\n * (e.g. from {@link createRemoteJWKS}) that returns the key for the token header.\n */\nexport async function verify<T extends JwtPayload = JwtPayload>(\n token: string,\n key: SigningKey | KeyResolver,\n opts: VerifyOptions = {},\n): Promise<T> {\n const parts = token.split(\".\");\n if (parts.length !== 3) throw new JwtError(\"malformed token\", \"malformed\");\n const [h, p, s] = parts as [string, string, string];\n\n let header: { alg?: Algorithm; kid?: string };\n try {\n header = JSON.parse(dec.decode(fromBase64url(h)));\n } catch {\n throw new JwtError(\"malformed header\", \"malformed\");\n }\n const alg = header.alg;\n if (!alg || !(alg in ALG_HASH)) throw new JwtError(`unsupported algorithm ${alg}`, \"algorithm\");\n if (opts.algorithms && !opts.algorithms.includes(alg))\n throw new JwtError(`algorithm ${alg} not allowed`, \"algorithm\");\n\n const resolved = typeof key === \"function\" ? await key({ alg, kid: header.kid }) : key;\n\n const signingInput = `${h}.${p}`;\n const sig = fromBase64url(s);\n let valid: boolean;\n if (alg.startsWith(\"HS\")) {\n if (typeof resolved !== \"string\" && !(resolved instanceof Uint8Array))\n throw new JwtError(\"HMAC algorithms need a string/bytes secret\", \"algorithm\");\n valid = constantTimeEqual(await hmac(resolved, signingInput, ALG_HASH[alg]), sig);\n } else {\n if (!(typeof CryptoKey !== \"undefined\" && resolved instanceof CryptoKey))\n throw new JwtError(`${alg} needs a CryptoKey — use importSpki / importJwk / createRemoteJWKS`, \"algorithm\");\n valid = await getSubtle().verify(\n subtleSignAlgo(alg),\n resolved,\n sig as unknown as BufferSource,\n enc.encode(signingInput) as unknown as BufferSource,\n );\n }\n if (!valid) throw new JwtError(\"signature verification failed\", \"signature\");\n\n let payload: T;\n try {\n payload = JSON.parse(dec.decode(fromBase64url(p)));\n } catch {\n throw new JwtError(\"malformed payload\", \"malformed\");\n }\n\n const now = nowSec();\n const skew = opts.clockTolerance ?? 0;\n if (payload.exp !== undefined && now > payload.exp + skew)\n throw new JwtError(\"token expired\", \"expired\");\n if (payload.nbf !== undefined && now + skew < payload.nbf)\n throw new JwtError(\"token not yet active\", \"not_active\");\n if (opts.issuer && payload.iss !== opts.issuer)\n throw new JwtError(\"issuer mismatch\", \"issuer\");\n if (opts.audience) {\n const want = Array.isArray(opts.audience) ? opts.audience : [opts.audience];\n const have = Array.isArray(payload.aud) ? payload.aud : payload.aud ? [payload.aud] : [];\n if (!want.some((a) => have.includes(a))) throw new JwtError(\"audience mismatch\", \"audience\");\n }\n return payload;\n}\n\n/* ------------------------------ asymmetric keys & JWKS ------------------------------ */\n\nfunction pemToDer(pem: string): Uint8Array {\n const b64 = pem.replace(/-----BEGIN [^-]+-----/, \"\").replace(/-----END [^-]+-----/, \"\").replace(/\\s+/g, \"\");\n return b64ToBytes(b64);\n}\n\n/** Import a PKCS#8 PEM private key for signing (RS and ES). */\nexport function importPkcs8(pem: string, alg: Algorithm): Promise<CryptoKey> {\n return getSubtle().importKey(\"pkcs8\", pemToDer(pem) as unknown as BufferSource, subtleImportAlgo(alg), false, [\"sign\"]);\n}\n\n/** Import an SPKI PEM public key for verification (RS and ES). */\nexport function importSpki(pem: string, alg: Algorithm): Promise<CryptoKey> {\n return getSubtle().importKey(\"spki\", pemToDer(pem) as unknown as BufferSource, subtleImportAlgo(alg), false, [\"verify\"]);\n}\n\n/** Import a JWK (public or private) for verify/sign. */\nexport function importJwk(jwk: JsonWebKey, alg: Algorithm): Promise<CryptoKey> {\n const usage: KeyUsage[] = jwk.d ? [\"sign\"] : [\"verify\"];\n return getSubtle().importKey(\"jwk\", jwk, subtleImportAlgo(alg), false, usage);\n}\n\nexport interface JwksOptions {\n /** Cache the key set this long (ms). Default 600000 (10 min). */\n cacheMaxAgeMs?: number;\n /** Custom fetch (tests / edge). */\n fetch?: typeof fetch;\n}\n\n/**\n * A cached remote JWKS resolver for {@link verify}. Fetches the JSON Web Key\n * Set once, caches it, and returns the right public key by the token's `kid`.\n * @example const jwks = createRemoteJWKS(\"https://issuer/.well-known/jwks.json\");\n * const payload = await verify(token, jwks, { algorithms: [\"RS256\"] });\n */\nexport function createRemoteJWKS(url: string, opts: JwksOptions = {}): KeyResolver {\n const ttl = opts.cacheMaxAgeMs ?? 600000;\n const f = opts.fetch ?? (typeof fetch !== \"undefined\" ? fetch.bind(globalThis) : undefined);\n if (!f) throw new JwtError(\"no global fetch — pass opts.fetch\", \"signature\");\n let cache: { keys: JsonWebKey[]; at: number } | null = null;\n const imported = new Map<string, CryptoKey>();\n\n return async ({ alg, kid }) => {\n if (!cache || Date.now() - cache.at > ttl) {\n const res = await f(url);\n if (!res.ok) throw new JwtError(`JWKS fetch failed: ${res.status}`, \"signature\");\n const data = (await res.json()) as { keys?: JsonWebKey[] };\n cache = { keys: data.keys ?? [], at: Date.now() };\n imported.clear();\n }\n const jwk = kid ? cache.keys.find((k) => (k as { kid?: string }).kid === kid) : cache.keys[0];\n if (!jwk) throw new JwtError(`no JWK found for kid \"${kid}\"`, \"signature\");\n const keyAlg = ((jwk as { alg?: string }).alg as Algorithm) ?? alg;\n const cacheKey = `${keyAlg}:${kid ?? \"\"}`;\n let ck = imported.get(cacheKey);\n if (!ck) {\n ck = await importJwk(jwk, keyAlg);\n imported.set(cacheKey, ck);\n }\n return ck;\n };\n}\n\n/* ------------------------------ refresh-token flow ------------------------------ */\n\nexport interface TokenPair {\n accessToken: string;\n refreshToken: string;\n /** The refresh token's `jti` — persist it to detect reuse on rotation. */\n refreshJti: string;\n}\n\nexport interface IssuePairOptions {\n algorithm?: Algorithm;\n /** Access-token lifetime in seconds. Default 900 (15 min). */\n accessTtl?: number;\n /** Refresh-token lifetime in seconds. Default 2592000 (30 days). */\n refreshTtl?: number;\n issuer?: string;\n audience?: string | string[];\n subject?: string;\n keyId?: string;\n}\n\n/** Issue a short-lived access token + a long-lived refresh token (with a `jti`). */\nexport async function issueTokenPair(\n payload: JwtPayload,\n secret: SigningKey,\n opts: IssuePairOptions = {},\n): Promise<TokenPair> {\n const subject = opts.subject ?? (payload.sub as string | undefined);\n const common = { algorithm: opts.algorithm, issuer: opts.issuer, audience: opts.audience, subject, keyId: opts.keyId };\n const accessToken = await sign(payload, secret, { ...common, expiresIn: opts.accessTtl ?? 900 });\n const refreshJti = randomToken(16);\n const refreshToken = await sign({ typ: \"refresh\", jti: refreshJti }, secret, {\n ...common,\n expiresIn: opts.refreshTtl ?? 2592000,\n });\n return { accessToken, refreshToken, refreshJti };\n}\n\n/** Verify a refresh token (must carry `typ: \"refresh\"`). */\nexport async function verifyRefreshToken<T extends JwtPayload = JwtPayload>(\n token: string,\n key: SigningKey | KeyResolver,\n opts: VerifyOptions = {},\n): Promise<T> {\n const payload = await verify<T>(token, key, opts);\n if (payload.typ !== \"refresh\") throw new JwtError(\"not a refresh token\", \"malformed\");\n return payload;\n}\n\nexport interface RotateOptions extends IssuePairOptions, VerifyOptions {\n /** New access-token payload. Defaults to `{ sub }` from the refresh token. */\n payload?: JwtPayload;\n /** Reuse detection: return true if this refresh `jti` was already used/revoked. */\n isUsed?: (jti: string) => boolean | Promise<boolean>;\n}\n\n/**\n * Rotate a refresh token: verify it, optionally detect reuse, and issue a fresh\n * pair. Returns the new pair plus `usedJti` (persist it as spent).\n */\nexport async function rotateRefreshToken(\n refreshToken: string,\n secret: SigningKey,\n opts: RotateOptions = {},\n): Promise<TokenPair & { usedJti?: string }> {\n const claims = await verifyRefreshToken(refreshToken, secret, opts);\n const jti = claims.jti;\n if (jti && opts.isUsed && (await opts.isUsed(jti)))\n throw new JwtError(\"refresh token reuse detected\", \"reuse\");\n const pair = await issueTokenPair(opts.payload ?? { sub: claims.sub }, secret, {\n ...opts,\n subject: opts.subject ?? (claims.sub as string | undefined),\n });\n return { ...pair, usedJti: jti };\n}\n\n/* ------------------------------ opaque tokens ------------------------------ */\n\n/** A cryptographically-random URL-safe token (default 32 bytes → 43 chars). */\nexport function randomToken(bytes = 32): string {\n return toBase64url(randomBytes(bytes));\n}\n\n/** A CSRF token (alias of a 32-byte random token). */\nexport function csrfToken(): string {\n return randomToken(32);\n}\n\n/* ------------------------------ adapters ------------------------------ */\n\n/** Anything we can read a header off: a Fetch Request/Headers, or Node `req.headers`. */\nexport type HeadersLike =\n | Request\n | Headers\n | { headers: Headers | Record<string, string | string[] | undefined> }\n | Record<string, string | string[] | undefined>;\n\nfunction readHeader(src: HeadersLike, name: string): string | undefined {\n const lower = name.toLowerCase();\n const h: unknown = (src as { headers?: unknown }).headers ?? src;\n if (h && typeof (h as Headers).get === \"function\") return (h as Headers).get(name) ?? undefined;\n const rec = h as Record<string, string | string[] | undefined>;\n const v = rec[name] ?? rec[lower];\n return Array.isArray(v) ? v[0] : v ?? undefined;\n}\n\n/** Extract a bearer token from an `Authorization` header (or a named cookie). */\nexport function extractBearer(src: HeadersLike, opts: { cookieName?: string } = {}): string | undefined {\n const auth = readHeader(src, \"authorization\");\n if (auth) {\n const m = /^Bearer\\s+(.+)$/i.exec(auth.trim());\n if (m) return m[1];\n }\n if (opts.cookieName) {\n const cookie = readHeader(src, \"cookie\");\n if (cookie) {\n const found = cookie.split(/;\\s*/).find((c) => c.startsWith(`${opts.cookieName}=`));\n if (found) return decodeURIComponent(found.slice(opts.cookieName.length + 1));\n }\n }\n return undefined;\n}\n\n/** Read the bearer token from a request and verify it. Throws {@link JwtError}. */\nexport async function authenticate<T extends JwtPayload = JwtPayload>(\n src: HeadersLike,\n secret: SigningKey | KeyResolver,\n opts: VerifyOptions & { cookieName?: string } = {},\n): Promise<T> {\n const token = extractBearer(src, { cookieName: opts.cookieName });\n if (!token) throw new JwtError(\"no bearer token\", \"malformed\");\n return verify<T>(token, secret, opts);\n}\n\nexport interface AuthCookieOptions {\n name?: string;\n maxAge?: number; // seconds\n path?: string;\n domain?: string;\n secure?: boolean;\n httpOnly?: boolean;\n sameSite?: \"Strict\" | \"Lax\" | \"None\";\n}\n\n/** Build a hardened `Set-Cookie` value carrying an auth token. */\nexport function toAuthCookie(token: string, opts: AuthCookieOptions = {}): string {\n const name = opts.name ?? \"token\";\n const parts = [`${name}=${encodeURIComponent(token)}`, `Path=${opts.path ?? \"/\"}`, `SameSite=${opts.sameSite ?? \"Lax\"}`];\n if (opts.maxAge !== undefined) parts.push(`Max-Age=${opts.maxAge}`);\n if (opts.domain) parts.push(`Domain=${opts.domain}`);\n if (opts.httpOnly !== false) parts.push(\"HttpOnly\");\n if (opts.secure !== false) parts.push(\"Secure\");\n return parts.join(\"; \");\n}\n\n/** Build a `Set-Cookie` value that clears the auth cookie. */\nexport function clearAuthCookie(opts: Pick<AuthCookieOptions, \"name\" | \"path\" | \"domain\"> = {}): string {\n const name = opts.name ?? \"token\";\n const parts = [`${name}=`, `Path=${opts.path ?? \"/\"}`, \"Max-Age=0\"];\n if (opts.domain) parts.push(`Domain=${opts.domain}`);\n return parts.join(\"; \");\n}\n\n/** Minimal Express-style middleware: verifies the bearer token → `req.user`, else 401. */\nexport function expressJwt<T extends JwtPayload = JwtPayload>(\n secret: SigningKey | KeyResolver,\n opts: VerifyOptions & { cookieName?: string; userKey?: string } = {},\n) {\n const userKey = opts.userKey ?? \"user\";\n return async (\n req: { headers: Record<string, string | string[] | undefined>; [k: string]: unknown },\n res: { status: (n: number) => { json: (b: unknown) => unknown } },\n next: (err?: unknown) => void,\n ): Promise<void> => {\n try {\n req[userKey] = await authenticate<T>(req, secret, opts);\n next();\n } catch (err) {\n const code = err instanceof JwtError ? err.code : \"malformed\";\n res.status(401).json({ error: \"unauthorized\", code });\n }\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @lacspace/jwt\n * JSON Web Tokens (HMAC) + secure random tokens — done safely.\n *\n * Sign and verify HS256/HS384/HS512 JWTs over Web Crypto (isomorphic), with\n * strict expiry / not-before / issuer / audience checks and constant-time\n * signature comparison. Plus opaque secure tokens and CSRF tokens.\n *\n * Zero dependencies (bar @lacspace/crypto) · isomorphic · fully typed.\n */\n\nimport { hmac, toBase64url, fromBase64url, constantTimeEqual, randomBytes, type HashAlgorithm } from \"@lacspace/crypto\";\n\nexport type Algorithm =\n | \"HS256\" | \"HS384\" | \"HS512\"\n | \"RS256\" | \"RS384\" | \"RS512\"\n | \"ES256\" | \"ES384\" | \"ES512\";\n\nconst ALG_HASH: Record<Algorithm, HashAlgorithm> = {\n HS256: \"SHA-256\", HS384: \"SHA-384\", HS512: \"SHA-512\",\n RS256: \"SHA-256\", RS384: \"SHA-384\", RS512: \"SHA-512\",\n ES256: \"SHA-256\", ES384: \"SHA-384\", ES512: \"SHA-512\",\n};\n\n/** A signing/verification key: an HMAC secret, or a Web Crypto key for RS and ES. */\nexport type SigningKey = string | Uint8Array | CryptoKey;\n/** Resolve a verification key from the token header (e.g. by `kid`). */\nexport type KeyResolver = (header: { alg: Algorithm; kid?: string }) => SigningKey | Promise<SigningKey>;\n\nexport class JwtError extends Error {\n constructor(\n message: string,\n public code:\n | \"malformed\"\n | \"signature\"\n | \"expired\"\n | \"not_active\"\n | \"issuer\"\n | \"audience\"\n | \"algorithm\"\n | \"reuse\",\n ) {\n super(message);\n this.name = \"JwtError\";\n }\n}\n\nfunction getSubtle(): SubtleCrypto {\n const c = (globalThis as { crypto?: Crypto }).crypto;\n if (!c || !c.subtle) throw new JwtError(\"Web Crypto API unavailable for asymmetric JWT\", \"algorithm\");\n return c.subtle;\n}\n\nfunction b64ToBytes(b64: string): Uint8Array {\n if (typeof atob !== \"undefined\") {\n const bin = atob(b64);\n const out = new Uint8Array(bin.length);\n for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);\n return out;\n }\n return new Uint8Array(Buffer.from(b64, \"base64\"));\n}\n\nfunction subtleSignAlgo(alg: Algorithm): AlgorithmIdentifier | EcdsaParams {\n if (alg.startsWith(\"RS\")) return { name: \"RSASSA-PKCS1-v1_5\" };\n if (alg.startsWith(\"ES\")) return { name: \"ECDSA\", hash: ALG_HASH[alg] };\n throw new JwtError(`not an asymmetric algorithm: ${alg}`, \"algorithm\");\n}\n\nfunction subtleImportAlgo(alg: Algorithm): RsaHashedImportParams | EcKeyImportParams {\n if (alg.startsWith(\"RS\")) return { name: \"RSASSA-PKCS1-v1_5\", hash: ALG_HASH[alg] };\n const curve = alg === \"ES256\" ? \"P-256\" : alg === \"ES384\" ? \"P-384\" : \"P-521\";\n return { name: \"ECDSA\", namedCurve: curve };\n}\n\nexport interface JwtPayload {\n iss?: string;\n sub?: string;\n aud?: string | string[];\n exp?: number;\n nbf?: number;\n iat?: number;\n jti?: string;\n [key: string]: unknown;\n}\n\nexport interface SignOptions {\n algorithm?: Algorithm;\n /** Seconds from now until expiry, e.g. 3600. */\n expiresIn?: number;\n issuer?: string;\n audience?: string | string[];\n subject?: string;\n /** `kid` header — which key signed this (for rotation / JWKS). */\n keyId?: string;\n}\n\nasync function signBytes(alg: Algorithm, key: SigningKey, input: string): Promise<Uint8Array> {\n if (alg.startsWith(\"HS\")) {\n if (typeof key !== \"string\" && !(key instanceof Uint8Array))\n throw new JwtError(\"HMAC algorithms need a string/bytes secret\", \"algorithm\");\n return hmac(key, input, ALG_HASH[alg]);\n }\n if (!(typeof CryptoKey !== \"undefined\" && key instanceof CryptoKey))\n throw new JwtError(`${alg} needs a CryptoKey — use importPkcs8 / importJwk`, \"algorithm\");\n const sig = await getSubtle().sign(subtleSignAlgo(alg), key, enc.encode(input) as unknown as BufferSource);\n return new Uint8Array(sig);\n}\n\nconst enc = new TextEncoder();\nconst dec = new TextDecoder();\n\nfunction b64urlJson(obj: unknown): string {\n return toBase64url(enc.encode(JSON.stringify(obj)));\n}\n\nfunction nowSec(): number {\n return Math.floor(Date.now() / 1000);\n}\n\n/** Sign a JWT. Pass an HMAC secret (HS*) or a Web Crypto private key (RS and ES). */\nexport async function sign(\n payload: JwtPayload,\n secret: SigningKey,\n opts: SignOptions = {},\n): Promise<string> {\n const alg = opts.algorithm ?? \"HS256\";\n const iat = nowSec();\n const body: JwtPayload = { iat, ...payload };\n if (opts.expiresIn !== undefined) body.exp = iat + opts.expiresIn;\n if (opts.issuer) body.iss = opts.issuer;\n if (opts.audience) body.aud = opts.audience;\n if (opts.subject) body.sub = opts.subject;\n\n const header: Record<string, unknown> = { alg, typ: \"JWT\" };\n if (opts.keyId) header.kid = opts.keyId;\n const signingInput = `${b64urlJson(header)}.${b64urlJson(body)}`;\n const sig = await signBytes(alg, secret, signingInput);\n return `${signingInput}.${toBase64url(sig)}`;\n}\n\nexport interface VerifyOptions {\n algorithms?: Algorithm[];\n issuer?: string;\n audience?: string | string[];\n /** Allowed clock skew in seconds. Default 0. */\n clockTolerance?: number;\n /** If set, reject unless the token's `typ` claim equals this (e.g. \"refresh\"). */\n requireTyp?: string;\n}\n\n/** Decode a JWT without verifying (never trust the result for auth). */\nexport function decode(token: string): { header: Record<string, unknown>; payload: JwtPayload } {\n const parts = token.split(\".\");\n if (parts.length !== 3) throw new JwtError(\"malformed token\", \"malformed\");\n return {\n header: JSON.parse(dec.decode(fromBase64url(parts[0]!))),\n payload: JSON.parse(dec.decode(fromBase64url(parts[1]!))),\n };\n}\n\n/**\n * Verify a JWT's signature and claims. Throws {@link JwtError} on any failure.\n * `key` is an HMAC secret, a Web Crypto public key (RS and ES), or a resolver\n * (e.g. from {@link createRemoteJWKS}) that returns the key for the token header.\n */\nexport async function verify<T extends JwtPayload = JwtPayload>(\n token: string,\n key: SigningKey | KeyResolver,\n opts: VerifyOptions = {},\n): Promise<T> {\n const parts = token.split(\".\");\n if (parts.length !== 3) throw new JwtError(\"malformed token\", \"malformed\");\n const [h, p, s] = parts as [string, string, string];\n\n let header: { alg?: Algorithm; kid?: string };\n try {\n header = JSON.parse(dec.decode(fromBase64url(h)));\n } catch {\n throw new JwtError(\"malformed header\", \"malformed\");\n }\n const alg = header.alg;\n if (!alg || !(alg in ALG_HASH)) throw new JwtError(`unsupported algorithm ${alg}`, \"algorithm\");\n if (opts.algorithms && !opts.algorithms.includes(alg))\n throw new JwtError(`algorithm ${alg} not allowed`, \"algorithm\");\n\n const resolved = typeof key === \"function\" ? await key({ alg, kid: header.kid }) : key;\n\n const signingInput = `${h}.${p}`;\n const sig = fromBase64url(s);\n let valid: boolean;\n if (alg.startsWith(\"HS\")) {\n if (typeof resolved !== \"string\" && !(resolved instanceof Uint8Array))\n throw new JwtError(\"HMAC algorithms need a string/bytes secret\", \"algorithm\");\n valid = constantTimeEqual(await hmac(resolved, signingInput, ALG_HASH[alg]), sig);\n } else {\n if (!(typeof CryptoKey !== \"undefined\" && resolved instanceof CryptoKey))\n throw new JwtError(`${alg} needs a CryptoKey — use importSpki / importJwk / createRemoteJWKS`, \"algorithm\");\n valid = await getSubtle().verify(\n subtleSignAlgo(alg),\n resolved,\n sig as unknown as BufferSource,\n enc.encode(signingInput) as unknown as BufferSource,\n );\n }\n if (!valid) throw new JwtError(\"signature verification failed\", \"signature\");\n\n let payload: T;\n try {\n payload = JSON.parse(dec.decode(fromBase64url(p)));\n } catch {\n throw new JwtError(\"malformed payload\", \"malformed\");\n }\n\n const now = nowSec();\n const skew = opts.clockTolerance ?? 0;\n if (payload.exp !== undefined && now > payload.exp + skew)\n throw new JwtError(\"token expired\", \"expired\");\n if (payload.nbf !== undefined && now + skew < payload.nbf)\n throw new JwtError(\"token not yet active\", \"not_active\");\n if (opts.issuer && payload.iss !== opts.issuer)\n throw new JwtError(\"issuer mismatch\", \"issuer\");\n if (opts.audience) {\n const want = Array.isArray(opts.audience) ? opts.audience : [opts.audience];\n const have = Array.isArray(payload.aud) ? payload.aud : payload.aud ? [payload.aud] : [];\n if (!want.some((a) => have.includes(a))) throw new JwtError(\"audience mismatch\", \"audience\");\n }\n if (opts.requireTyp !== undefined && payload.typ !== opts.requireTyp)\n throw new JwtError(`token type mismatch (expected ${opts.requireTyp})`, \"malformed\");\n return payload;\n}\n\n/* ------------------------------ asymmetric keys & JWKS ------------------------------ */\n\nfunction pemToDer(pem: string): Uint8Array {\n const b64 = pem.replace(/-----BEGIN [^-]+-----/, \"\").replace(/-----END [^-]+-----/, \"\").replace(/\\s+/g, \"\");\n return b64ToBytes(b64);\n}\n\n/** Import a PKCS#8 PEM private key for signing (RS and ES). */\nexport function importPkcs8(pem: string, alg: Algorithm): Promise<CryptoKey> {\n return getSubtle().importKey(\"pkcs8\", pemToDer(pem) as unknown as BufferSource, subtleImportAlgo(alg), false, [\"sign\"]);\n}\n\n/** Import an SPKI PEM public key for verification (RS and ES). */\nexport function importSpki(pem: string, alg: Algorithm): Promise<CryptoKey> {\n return getSubtle().importKey(\"spki\", pemToDer(pem) as unknown as BufferSource, subtleImportAlgo(alg), false, [\"verify\"]);\n}\n\n/** Import a JWK (public or private) for verify/sign. */\nexport function importJwk(jwk: JsonWebKey, alg: Algorithm): Promise<CryptoKey> {\n const usage: KeyUsage[] = jwk.d ? [\"sign\"] : [\"verify\"];\n return getSubtle().importKey(\"jwk\", jwk, subtleImportAlgo(alg), false, usage);\n}\n\nexport interface JwksOptions {\n /** Cache the key set this long (ms). Default 600000 (10 min). */\n cacheMaxAgeMs?: number;\n /** Custom fetch (tests / edge). */\n fetch?: typeof fetch;\n}\n\n/**\n * A cached remote JWKS resolver for {@link verify}. Fetches the JSON Web Key\n * Set once, caches it, and returns the right public key by the token's `kid`.\n * @example const jwks = createRemoteJWKS(\"https://issuer/.well-known/jwks.json\");\n * const payload = await verify(token, jwks, { algorithms: [\"RS256\"] });\n */\nexport function createRemoteJWKS(url: string, opts: JwksOptions = {}): KeyResolver {\n const ttl = opts.cacheMaxAgeMs ?? 600000;\n const f = opts.fetch ?? (typeof fetch !== \"undefined\" ? fetch.bind(globalThis) : undefined);\n if (!f) throw new JwtError(\"no global fetch — pass opts.fetch\", \"signature\");\n let cache: { keys: JsonWebKey[]; at: number } | null = null;\n const imported = new Map<string, CryptoKey>();\n\n return async ({ alg, kid }) => {\n if (!cache || Date.now() - cache.at > ttl) {\n const res = await f(url);\n if (!res.ok) throw new JwtError(`JWKS fetch failed: ${res.status}`, \"signature\");\n const data = (await res.json()) as { keys?: JsonWebKey[] };\n cache = { keys: data.keys ?? [], at: Date.now() };\n imported.clear();\n }\n const jwk = kid ? cache.keys.find((k) => (k as { kid?: string }).kid === kid) : cache.keys[0];\n if (!jwk) throw new JwtError(`no JWK found for kid \"${kid}\"`, \"signature\");\n const keyAlg = ((jwk as { alg?: string }).alg as Algorithm) ?? alg;\n const cacheKey = `${keyAlg}:${kid ?? \"\"}`;\n let ck = imported.get(cacheKey);\n if (!ck) {\n ck = await importJwk(jwk, keyAlg);\n imported.set(cacheKey, ck);\n }\n return ck;\n };\n}\n\n/* ------------------------------ refresh-token flow ------------------------------ */\n\nexport interface TokenPair {\n accessToken: string;\n refreshToken: string;\n /** The refresh token's `jti` — persist it to detect reuse on rotation. */\n refreshJti: string;\n}\n\nexport interface IssuePairOptions {\n algorithm?: Algorithm;\n /** Access-token lifetime in seconds. Default 900 (15 min). */\n accessTtl?: number;\n /** Refresh-token lifetime in seconds. Default 2592000 (30 days). */\n refreshTtl?: number;\n issuer?: string;\n audience?: string | string[];\n subject?: string;\n keyId?: string;\n}\n\n/** Issue a short-lived access token + a long-lived refresh token (with a `jti`). */\nexport async function issueTokenPair(\n payload: JwtPayload,\n secret: SigningKey,\n opts: IssuePairOptions = {},\n): Promise<TokenPair> {\n const subject = opts.subject ?? (payload.sub as string | undefined);\n const common = { algorithm: opts.algorithm, issuer: opts.issuer, audience: opts.audience, subject, keyId: opts.keyId };\n const accessPayload = payload.typ !== undefined ? payload : { typ: \"access\", ...payload };\n const accessToken = await sign(accessPayload, secret, { ...common, expiresIn: opts.accessTtl ?? 900 });\n const refreshJti = randomToken(16);\n const refreshToken = await sign({ typ: \"refresh\", jti: refreshJti }, secret, {\n ...common,\n expiresIn: opts.refreshTtl ?? 2592000,\n });\n return { accessToken, refreshToken, refreshJti };\n}\n\n/** Verify a refresh token (must carry `typ: \"refresh\"`). */\nexport async function verifyRefreshToken<T extends JwtPayload = JwtPayload>(\n token: string,\n key: SigningKey | KeyResolver,\n opts: VerifyOptions = {},\n): Promise<T> {\n const payload = await verify<T>(token, key, opts);\n if (payload.typ !== \"refresh\") throw new JwtError(\"not a refresh token\", \"malformed\");\n return payload;\n}\n\nexport interface RotateOptions extends IssuePairOptions, VerifyOptions {\n /** New access-token payload. Defaults to `{ sub }` from the refresh token. */\n payload?: JwtPayload;\n /** Reuse detection: return true if this refresh `jti` was already used/revoked. */\n isUsed?: (jti: string) => boolean | Promise<boolean>;\n}\n\n/**\n * Rotate a refresh token: verify it, optionally detect reuse, and issue a fresh\n * pair. Returns the new pair plus `usedJti` (persist it as spent).\n */\nexport async function rotateRefreshToken(\n refreshToken: string,\n secret: SigningKey,\n opts: RotateOptions = {},\n): Promise<TokenPair & { usedJti?: string }> {\n const claims = await verifyRefreshToken(refreshToken, secret, opts);\n const jti = claims.jti;\n if (jti && opts.isUsed && (await opts.isUsed(jti)))\n throw new JwtError(\"refresh token reuse detected\", \"reuse\");\n const pair = await issueTokenPair(opts.payload ?? { sub: claims.sub }, secret, {\n ...opts,\n subject: opts.subject ?? (claims.sub as string | undefined),\n });\n return { ...pair, usedJti: jti };\n}\n\n/* ------------------------------ opaque tokens ------------------------------ */\n\n/** A cryptographically-random URL-safe token (default 32 bytes → 43 chars). */\nexport function randomToken(bytes = 32): string {\n return toBase64url(randomBytes(bytes));\n}\n\n/** A CSRF token (alias of a 32-byte random token). */\nexport function csrfToken(): string {\n return randomToken(32);\n}\n\n/* ------------------------------ adapters ------------------------------ */\n\n/** Anything we can read a header off: a Fetch Request/Headers, or Node `req.headers`. */\nexport type HeadersLike =\n | Request\n | Headers\n | { headers: Headers | Record<string, string | string[] | undefined> }\n | Record<string, string | string[] | undefined>;\n\nfunction readHeader(src: HeadersLike, name: string): string | undefined {\n const lower = name.toLowerCase();\n const h: unknown = (src as { headers?: unknown }).headers ?? src;\n if (h && typeof (h as Headers).get === \"function\") return (h as Headers).get(name) ?? undefined;\n const rec = h as Record<string, string | string[] | undefined>;\n const v = rec[name] ?? rec[lower];\n return Array.isArray(v) ? v[0] : v ?? undefined;\n}\n\n/** Extract a bearer token from an `Authorization` header (or a named cookie). */\nexport function extractBearer(src: HeadersLike, opts: { cookieName?: string } = {}): string | undefined {\n const auth = readHeader(src, \"authorization\");\n if (auth) {\n const m = /^Bearer\\s+(.+)$/i.exec(auth.trim());\n if (m) return m[1];\n }\n if (opts.cookieName) {\n const cookie = readHeader(src, \"cookie\");\n if (cookie) {\n const found = cookie.split(/;\\s*/).find((c) => c.startsWith(`${opts.cookieName}=`));\n if (found) return decodeURIComponent(found.slice(opts.cookieName.length + 1));\n }\n }\n return undefined;\n}\n\n/** Read the bearer token from a request and verify it. Throws {@link JwtError}. */\nexport async function authenticate<T extends JwtPayload = JwtPayload>(\n src: HeadersLike,\n secret: SigningKey | KeyResolver,\n opts: VerifyOptions & { cookieName?: string } = {},\n): Promise<T> {\n const token = extractBearer(src, { cookieName: opts.cookieName });\n if (!token) throw new JwtError(\"no bearer token\", \"malformed\");\n return verify<T>(token, secret, opts);\n}\n\nexport interface AuthCookieOptions {\n name?: string;\n maxAge?: number; // seconds\n path?: string;\n domain?: string;\n secure?: boolean;\n httpOnly?: boolean;\n sameSite?: \"Strict\" | \"Lax\" | \"None\";\n}\n\n/** Build a hardened `Set-Cookie` value carrying an auth token. */\nexport function toAuthCookie(token: string, opts: AuthCookieOptions = {}): string {\n const name = opts.name ?? \"token\";\n const parts = [`${name}=${encodeURIComponent(token)}`, `Path=${opts.path ?? \"/\"}`, `SameSite=${opts.sameSite ?? \"Lax\"}`];\n if (opts.maxAge !== undefined) parts.push(`Max-Age=${opts.maxAge}`);\n if (opts.domain) parts.push(`Domain=${opts.domain}`);\n if (opts.httpOnly !== false) parts.push(\"HttpOnly\");\n if (opts.secure !== false) parts.push(\"Secure\");\n return parts.join(\"; \");\n}\n\n/** Build a `Set-Cookie` value that clears the auth cookie. */\nexport function clearAuthCookie(opts: Pick<AuthCookieOptions, \"name\" | \"path\" | \"domain\"> = {}): string {\n const name = opts.name ?? \"token\";\n const parts = [`${name}=`, `Path=${opts.path ?? \"/\"}`, \"Max-Age=0\"];\n if (opts.domain) parts.push(`Domain=${opts.domain}`);\n return parts.join(\"; \");\n}\n\n/** Minimal Express-style middleware: verifies the bearer token → `req.user`, else 401. */\nexport function expressJwt<T extends JwtPayload = JwtPayload>(\n secret: SigningKey | KeyResolver,\n opts: VerifyOptions & { cookieName?: string; userKey?: string } = {},\n) {\n const userKey = opts.userKey ?? \"user\";\n return async (\n req: { headers: Record<string, string | string[] | undefined>; [k: string]: unknown },\n res: { status: (n: number) => { json: (b: unknown) => unknown } },\n next: (err?: unknown) => void,\n ): Promise<void> => {\n try {\n req[userKey] = await authenticate<T>(req, secret, opts);\n next();\n } catch (err) {\n const code = err instanceof JwtError ? err.code : \"malformed\";\n res.status(401).json({ error: \"unauthorized\", code });\n }\n };\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lacspace/jwt",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "JSON Web Tokens (HS256/384/512) with strict expiry/issuer/audience checks + secure random & CSRF tokens. Isomorphic over Web Crypto — Node, edge, browser.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -35,7 +35,16 @@
35
35
  "csrf",
36
36
  "web-crypto",
37
37
  "edge",
38
- "typescript"
38
+ "typescript",
39
+ "jose",
40
+ "rs256",
41
+ "es256",
42
+ "hs384",
43
+ "hs512",
44
+ "jwks",
45
+ "refresh-token",
46
+ "token-rotation",
47
+ "sign"
39
48
  ],
40
49
  "author": "Lacspace <contact@lacspace.com>",
41
50
  "license": "SEE LICENSE IN LICENSE",
@@ -49,9 +58,12 @@
49
58
  "url": "https://github.com/lacspace/npm-packages/issues"
50
59
  },
51
60
  "engines": {
52
- "node": ">=18"
61
+ "node": ">=20"
53
62
  },
54
63
  "dependencies": {
55
64
  "@lacspace/crypto": "^1.0.0"
65
+ },
66
+ "publishConfig": {
67
+ "access": "public"
56
68
  }
57
69
  }