@periodic/tungsten 1.0.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/dist/index.mjs ADDED
@@ -0,0 +1,529 @@
1
+ import { SignJWT, jwtVerify } from 'jose';
2
+ import { timingSafeEqual, createHmac } from 'crypto';
3
+ import { sha256 } from '@noble/hashes/sha256';
4
+
5
+ // src/core/jwt.ts
6
+ function parseDuration(duration) {
7
+ if (typeof duration === "number") return duration;
8
+ const match = duration.match(/^(\d+)([smhd])$/);
9
+ if (!match) throw new Error(`Invalid duration format: ${duration}`);
10
+ const value = parseInt(match[1], 10);
11
+ const unit = match[2];
12
+ const multipliers = {
13
+ s: 1,
14
+ m: 60,
15
+ h: 3600,
16
+ d: 86400
17
+ };
18
+ return value * multipliers[unit];
19
+ }
20
+ async function signAccessToken(payload, options) {
21
+ const { expiresIn, issuer, audience, keyProvider, kid } = options;
22
+ const signingKey = await keyProvider.getSigningKey(kid);
23
+ const expiresInSeconds = parseDuration(expiresIn);
24
+ let key;
25
+ if (signingKey.algorithm === "HS256") {
26
+ key = typeof signingKey.key === "string" ? new TextEncoder().encode(signingKey.key) : signingKey.key;
27
+ } else {
28
+ const privateKey = typeof signingKey.key === "string" ? signingKey.key : new TextDecoder().decode(signingKey.key);
29
+ key = await crypto.subtle.importKey(
30
+ "pkcs8",
31
+ new TextEncoder().encode(privateKey),
32
+ { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" },
33
+ false,
34
+ ["sign"]
35
+ );
36
+ }
37
+ const jwt = new SignJWT(payload).setProtectedHeader({ alg: signingKey.algorithm, kid: signingKey.kid }).setIssuedAt().setExpirationTime(Math.floor(Date.now() / 1e3) + expiresInSeconds);
38
+ if (issuer) jwt.setIssuer(issuer);
39
+ if (audience) jwt.setAudience(audience);
40
+ if (payload.sub) jwt.setSubject(payload.sub);
41
+ return jwt.sign(key);
42
+ }
43
+ async function verifyAccessToken(token, options) {
44
+ const { keyProvider, issuer, audience, clockTolerance = 60 } = options;
45
+ let header;
46
+ try {
47
+ const parts = token.split(".");
48
+ if (parts.length !== 3) throw new Error("Invalid JWT format");
49
+ header = JSON.parse(Buffer.from(parts[0], "base64url").toString());
50
+ } catch {
51
+ throw new Error("Invalid JWT header");
52
+ }
53
+ const kid = header.kid;
54
+ if (!kid) throw new Error("Missing kid in JWT header");
55
+ const verificationKey = await keyProvider.getVerificationKey(kid);
56
+ let key;
57
+ if (verificationKey.algorithm === "HS256") {
58
+ key = typeof verificationKey.key === "string" ? new TextEncoder().encode(verificationKey.key) : verificationKey.key;
59
+ } else {
60
+ const publicKey = typeof verificationKey.key === "string" ? verificationKey.key : new TextDecoder().decode(verificationKey.key);
61
+ key = await crypto.subtle.importKey(
62
+ "spki",
63
+ new TextEncoder().encode(publicKey),
64
+ { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" },
65
+ false,
66
+ ["verify"]
67
+ );
68
+ }
69
+ const verified = await jwtVerify(token, key, {
70
+ issuer,
71
+ audience,
72
+ clockTolerance
73
+ });
74
+ return verified.payload;
75
+ }
76
+ async function rotateRefreshToken(oldRefreshToken, options) {
77
+ const { keyProvider, onTokenReused } = options;
78
+ let payload;
79
+ try {
80
+ payload = await verifyAccessToken(oldRefreshToken, { keyProvider });
81
+ } catch {
82
+ throw new Error("Invalid refresh token");
83
+ }
84
+ if (payload.reused) {
85
+ if (onTokenReused) {
86
+ await onTokenReused(payload.jti || "unknown");
87
+ }
88
+ throw new Error("Refresh token reuse detected");
89
+ }
90
+ const jti = generateJTI();
91
+ const newPayload = {
92
+ sub: payload.sub,
93
+ jti
94
+ };
95
+ const accessToken = await signAccessToken(newPayload, {
96
+ expiresIn: "15m",
97
+ keyProvider
98
+ });
99
+ const refreshToken = await signAccessToken(
100
+ { ...newPayload, type: "refresh" },
101
+ {
102
+ expiresIn: "7d",
103
+ keyProvider
104
+ }
105
+ );
106
+ return {
107
+ accessToken,
108
+ refreshToken,
109
+ payload: newPayload
110
+ };
111
+ }
112
+ function generateJTI() {
113
+ const bytes = crypto.getRandomValues(new Uint8Array(16));
114
+ return Buffer.from(bytes).toString("base64url");
115
+ }
116
+ function timingSafeCompare(a, b) {
117
+ if (a.length !== b.length) return false;
118
+ const bufA = Buffer.from(a);
119
+ const bufB = Buffer.from(b);
120
+ return timingSafeEqual(bufA, bufB);
121
+ }
122
+
123
+ // src/core/password.ts
124
+ var ARGON2_OPTIONS = {
125
+ // argon2id = 2, hardcoded to avoid ESM named export resolution issues
126
+ // with native Node addons. argon2.argon2id is undefined when imported
127
+ // via dynamic ESM import due to CJS/ESM interop limitations.
128
+ type: 2,
129
+ memoryCost: 65536,
130
+ // 64 MB - OWASP recommended minimum
131
+ timeCost: 3,
132
+ // 3 iterations
133
+ parallelism: 4,
134
+ // 4 threads
135
+ hashLength: 32
136
+ // 256-bit output
137
+ };
138
+ async function getArgon2() {
139
+ const mod = await import('argon2');
140
+ const argon2 = mod.default ?? mod;
141
+ if (typeof argon2.hash !== "function" || typeof argon2.verify !== "function") {
142
+ throw new Error("[tungsten] argon2 module loaded incorrectly - hash/verify not available");
143
+ }
144
+ return argon2;
145
+ }
146
+ async function hashPassword(password) {
147
+ if (!password || password.length === 0) {
148
+ throw new Error("Password cannot be empty");
149
+ }
150
+ if (password.length > 128) {
151
+ throw new Error("Password exceeds maximum length of 128 characters");
152
+ }
153
+ try {
154
+ const argon2 = await getArgon2();
155
+ return await argon2.hash(password, ARGON2_OPTIONS);
156
+ } catch (err) {
157
+ if (err instanceof Error && (err.message === "Password cannot be empty" || err.message === "Password exceeds maximum length of 128 characters")) {
158
+ throw err;
159
+ }
160
+ throw new Error("Password hashing failed");
161
+ }
162
+ }
163
+ async function verifyPassword(password, hash) {
164
+ if (!password || !hash) return false;
165
+ try {
166
+ const argon2 = await getArgon2();
167
+ return await argon2.verify(hash, password);
168
+ } catch {
169
+ return false;
170
+ }
171
+ }
172
+ async function needsRehash(hash) {
173
+ try {
174
+ const argon2 = await getArgon2();
175
+ return argon2.needsRehash(hash, ARGON2_OPTIONS);
176
+ } catch {
177
+ return true;
178
+ }
179
+ }
180
+ var DEFAULT_KEY_LENGTH = 32;
181
+ var ENCODING = "base64url";
182
+ function generateApiKey(options = {}) {
183
+ const { prefix = "", length = DEFAULT_KEY_LENGTH } = options;
184
+ if (length < 16) {
185
+ throw new Error("API key length must be at least 16 bytes");
186
+ }
187
+ const bytes = crypto.getRandomValues(new Uint8Array(length));
188
+ const key = Buffer.from(bytes).toString(ENCODING);
189
+ return prefix ? `${prefix}${key}` : key;
190
+ }
191
+ function hashApiKey(apiKey) {
192
+ if (!apiKey || apiKey.length === 0) {
193
+ throw new Error("API key cannot be empty");
194
+ }
195
+ const hash = sha256(new TextEncoder().encode(apiKey));
196
+ return Buffer.from(hash).toString("hex");
197
+ }
198
+ function verifyApiKey(apiKey, hash) {
199
+ if (!apiKey || !hash) {
200
+ return false;
201
+ }
202
+ try {
203
+ const computedHash = hashApiKey(apiKey);
204
+ return timingSafeCompare(computedHash, hash);
205
+ } catch {
206
+ return false;
207
+ }
208
+ }
209
+ function extractPrefix(apiKey) {
210
+ const match = apiKey.match(/^([a-z_]+)_/i);
211
+ return match ? match[0] : "";
212
+ }
213
+ var TOKEN_LENGTH = 32;
214
+ function generateOpaqueToken() {
215
+ const bytes = crypto.getRandomValues(new Uint8Array(TOKEN_LENGTH));
216
+ return Buffer.from(bytes).toString("base64url");
217
+ }
218
+ function hashOpaqueToken(token) {
219
+ if (!token || token.length === 0) {
220
+ throw new Error("Token cannot be empty");
221
+ }
222
+ const hash = sha256(new TextEncoder().encode(token));
223
+ return Buffer.from(hash).toString("hex");
224
+ }
225
+ var DEFAULT_PERIOD = 30;
226
+ var DEFAULT_DIGITS = 6;
227
+ var DEFAULT_ALGORITHM = "SHA1";
228
+ var DEFAULT_WINDOW = 1;
229
+ function generateTOTPSecret() {
230
+ const bytes = crypto.getRandomValues(new Uint8Array(20));
231
+ return base32Encode(bytes);
232
+ }
233
+ function generateTOTP(secret, options = {}) {
234
+ const {
235
+ period = DEFAULT_PERIOD,
236
+ digits = DEFAULT_DIGITS,
237
+ algorithm = DEFAULT_ALGORITHM
238
+ } = options;
239
+ const counter = Math.floor(Date.now() / 1e3 / period);
240
+ return generateHOTP(secret, counter, { digits, algorithm });
241
+ }
242
+ function verifyTOTP(code, secret, options = {}) {
243
+ const {
244
+ period = DEFAULT_PERIOD,
245
+ digits = DEFAULT_DIGITS,
246
+ algorithm = DEFAULT_ALGORITHM,
247
+ window = DEFAULT_WINDOW
248
+ } = options;
249
+ const currentCounter = Math.floor(Date.now() / 1e3 / period);
250
+ for (let i = -window; i <= window; i++) {
251
+ const counter = currentCounter + i;
252
+ const expectedCode = generateHOTP(secret, counter, { digits, algorithm });
253
+ if (timingSafeEqual2(code, expectedCode)) {
254
+ return { valid: true, delta: i };
255
+ }
256
+ }
257
+ return { valid: false };
258
+ }
259
+ function generateHOTP(secret, counter, options) {
260
+ const { digits, algorithm } = options;
261
+ const secretBytes = base32Decode(secret);
262
+ const counterBuffer = Buffer.allocUnsafe(8);
263
+ counterBuffer.writeBigUInt64BE(BigInt(counter));
264
+ const hmac = createHmac(algorithm.toLowerCase(), secretBytes);
265
+ hmac.update(counterBuffer);
266
+ const hash = hmac.digest();
267
+ const offset = hash[hash.length - 1] & 15;
268
+ const binary = (hash[offset] & 127) << 24 | (hash[offset + 1] & 255) << 16 | (hash[offset + 2] & 255) << 8 | hash[offset + 3] & 255;
269
+ const otp = binary % Math.pow(10, digits);
270
+ return otp.toString().padStart(digits, "0");
271
+ }
272
+ function base32Encode(data) {
273
+ const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
274
+ let bits = 0;
275
+ let value = 0;
276
+ let output = "";
277
+ for (let i = 0; i < data.length; i++) {
278
+ value = value << 8 | data[i];
279
+ bits += 8;
280
+ while (bits >= 5) {
281
+ output += alphabet[value >>> bits - 5 & 31];
282
+ bits -= 5;
283
+ }
284
+ }
285
+ if (bits > 0) {
286
+ output += alphabet[value << 5 - bits & 31];
287
+ }
288
+ return output;
289
+ }
290
+ function base32Decode(encoded) {
291
+ const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
292
+ const cleanInput = encoded.toUpperCase().replace(/[^A-Z2-7]/g, "");
293
+ let bits = 0;
294
+ let value = 0;
295
+ const output = [];
296
+ for (let i = 0; i < cleanInput.length; i++) {
297
+ const idx = alphabet.indexOf(cleanInput[i]);
298
+ if (idx === -1) continue;
299
+ value = value << 5 | idx;
300
+ bits += 5;
301
+ if (bits >= 8) {
302
+ output.push(value >>> bits - 8 & 255);
303
+ bits -= 8;
304
+ }
305
+ }
306
+ return Buffer.from(output);
307
+ }
308
+ function timingSafeEqual2(a, b) {
309
+ if (a.length !== b.length) return false;
310
+ let result = 0;
311
+ for (let i = 0; i < a.length; i++) {
312
+ result |= a.charCodeAt(i) ^ b.charCodeAt(i);
313
+ }
314
+ return result === 0;
315
+ }
316
+ var DEFAULT_ALGORITHM2 = "sha256";
317
+ function signPayload(payload, secret, algorithm = DEFAULT_ALGORITHM2) {
318
+ if (!secret || secret.length === 0) {
319
+ throw new Error("Secret cannot be empty");
320
+ }
321
+ const data = typeof payload === "string" ? payload : JSON.stringify(payload);
322
+ const hmac = createHmac(algorithm, secret);
323
+ hmac.update(data);
324
+ return hmac.digest("hex");
325
+ }
326
+ function verifySignature(payload, signature, secret, algorithm = DEFAULT_ALGORITHM2) {
327
+ if (!signature || !secret) {
328
+ return false;
329
+ }
330
+ try {
331
+ const expectedSignature = signPayload(payload, secret, algorithm);
332
+ return timingSafeCompare(signature, expectedSignature);
333
+ } catch {
334
+ return false;
335
+ }
336
+ }
337
+ function signRequest(payload, secret, timestamp) {
338
+ const ts = timestamp ?? Math.floor(Date.now() / 1e3);
339
+ const data = typeof payload === "string" ? payload : JSON.stringify(payload);
340
+ const signedData = `${ts}.${data}`;
341
+ return {
342
+ signature: signPayload(signedData, secret),
343
+ timestamp: ts
344
+ };
345
+ }
346
+ function verifyRequest(payload, signature, timestamp, secret, maxAge = 300) {
347
+ const now = Math.floor(Date.now() / 1e3);
348
+ const age = now - timestamp;
349
+ if (age > maxAge || age < 0) {
350
+ return false;
351
+ }
352
+ const data = typeof payload === "string" ? payload : JSON.stringify(payload);
353
+ const signedData = `${timestamp}.${data}`;
354
+ return verifySignature(signedData, signature, secret);
355
+ }
356
+
357
+ // src/core/cookie.ts
358
+ function getSecureCookieDefaults(isProduction = true) {
359
+ return {
360
+ httpOnly: true,
361
+ secure: isProduction,
362
+ sameSite: "lax",
363
+ path: "/"
364
+ };
365
+ }
366
+ function serializeCookie(name, value, options) {
367
+ const parts = [`${name}=${encodeURIComponent(value)}`];
368
+ if (options.domain) {
369
+ parts.push(`Domain=${options.domain}`);
370
+ }
371
+ if (options.path) {
372
+ parts.push(`Path=${options.path}`);
373
+ }
374
+ if (options.maxAge !== void 0) {
375
+ parts.push(`Max-Age=${options.maxAge}`);
376
+ }
377
+ if (options.httpOnly) {
378
+ parts.push("HttpOnly");
379
+ }
380
+ if (options.secure) {
381
+ parts.push("Secure");
382
+ }
383
+ if (options.sameSite) {
384
+ const sameSite = options.sameSite.charAt(0).toUpperCase() + options.sameSite.slice(1);
385
+ parts.push(`SameSite=${sameSite}`);
386
+ }
387
+ return parts.join("; ");
388
+ }
389
+ function parseCookie(cookieHeader) {
390
+ const cookies = {};
391
+ if (!cookieHeader) return cookies;
392
+ const pairs = cookieHeader.split(";");
393
+ for (const pair of pairs) {
394
+ const [name, ...rest] = pair.split("=");
395
+ if (name && rest.length > 0) {
396
+ const trimmedName = name.trim();
397
+ const value = rest.join("=").trim();
398
+ cookies[trimmedName] = decodeURIComponent(value);
399
+ }
400
+ }
401
+ return cookies;
402
+ }
403
+ function deleteCookie(name, options = {}) {
404
+ return serializeCookie(name, "", {
405
+ ...options,
406
+ maxAge: 0
407
+ });
408
+ }
409
+
410
+ // src/core/key-provider.ts
411
+ var SimpleKeyProvider = class {
412
+ signingKey;
413
+ verificationKey;
414
+ constructor(secret, algorithm = "HS256", kid = "default") {
415
+ this.signingKey = {
416
+ kid,
417
+ algorithm,
418
+ key: secret
419
+ };
420
+ this.verificationKey = {
421
+ algorithm,
422
+ key: secret
423
+ };
424
+ }
425
+ async getSigningKey(_kid) {
426
+ return this.signingKey;
427
+ }
428
+ async getVerificationKey(_kid) {
429
+ return this.verificationKey;
430
+ }
431
+ };
432
+ var RotatingKeyProvider = class {
433
+ keys;
434
+ currentKid;
435
+ constructor(initialKey) {
436
+ this.keys = /* @__PURE__ */ new Map();
437
+ this.currentKid = initialKey.kid;
438
+ this.addKey(initialKey.kid, initialKey.secret, initialKey.algorithm);
439
+ }
440
+ /**
441
+ * Add a new key to the rotation
442
+ */
443
+ addKey(kid, secret, algorithm) {
444
+ this.keys.set(kid, {
445
+ signing: { kid, algorithm, key: secret },
446
+ verification: { algorithm, key: secret }
447
+ });
448
+ }
449
+ /**
450
+ * Set the current signing key
451
+ */
452
+ setCurrentKey(kid) {
453
+ if (!this.keys.has(kid)) {
454
+ throw new Error(`Key with kid "${kid}" not found`);
455
+ }
456
+ this.currentKid = kid;
457
+ }
458
+ /**
459
+ * Remove an old key from rotation
460
+ */
461
+ removeKey(kid) {
462
+ if (kid === this.currentKid) {
463
+ throw new Error("Cannot remove the current signing key");
464
+ }
465
+ this.keys.delete(kid);
466
+ }
467
+ async getSigningKey(kid) {
468
+ const keyId = kid ?? this.currentKid;
469
+ const keyPair = this.keys.get(keyId);
470
+ if (!keyPair) {
471
+ throw new Error(`Signing key with kid "${keyId}" not found`);
472
+ }
473
+ return keyPair.signing;
474
+ }
475
+ async getVerificationKey(kid) {
476
+ const keyPair = this.keys.get(kid);
477
+ if (!keyPair) {
478
+ throw new Error(`Verification key with kid "${kid}" not found`);
479
+ }
480
+ return keyPair.verification;
481
+ }
482
+ /**
483
+ * Get all available key IDs
484
+ */
485
+ getKeyIds() {
486
+ return Array.from(this.keys.keys());
487
+ }
488
+ };
489
+ var MultiTenantKeyProvider = class {
490
+ tenants;
491
+ constructor() {
492
+ this.tenants = /* @__PURE__ */ new Map();
493
+ }
494
+ /**
495
+ * Register a tenant with its key provider
496
+ */
497
+ registerTenant(tenantId, keyProvider) {
498
+ this.tenants.set(tenantId, keyProvider);
499
+ }
500
+ /**
501
+ * Unregister a tenant
502
+ */
503
+ unregisterTenant(tenantId) {
504
+ this.tenants.delete(tenantId);
505
+ }
506
+ async getSigningKey(kid) {
507
+ if (!kid) {
508
+ throw new Error("kid is required for multi-tenant key provider");
509
+ }
510
+ const [tenantId] = kid.split(":");
511
+ const provider = this.tenants.get(tenantId);
512
+ if (!provider) {
513
+ throw new Error(`No key provider registered for tenant "${tenantId}"`);
514
+ }
515
+ return provider.getSigningKey(kid);
516
+ }
517
+ async getVerificationKey(kid) {
518
+ const [tenantId] = kid.split(":");
519
+ const provider = this.tenants.get(tenantId);
520
+ if (!provider) {
521
+ throw new Error(`No key provider registered for tenant "${tenantId}"`);
522
+ }
523
+ return provider.getVerificationKey(kid);
524
+ }
525
+ };
526
+
527
+ export { MultiTenantKeyProvider, RotatingKeyProvider, SimpleKeyProvider, deleteCookie, extractPrefix, generateApiKey, generateOpaqueToken, generateTOTP, generateTOTPSecret, getSecureCookieDefaults, hashApiKey, hashOpaqueToken, hashPassword, needsRehash, parseCookie, rotateRefreshToken, serializeCookie, signAccessToken, signPayload, signRequest, timingSafeCompare, verifyAccessToken, verifyApiKey, verifyPassword, verifyRequest, verifySignature, verifyTOTP };
528
+ //# sourceMappingURL=index.mjs.map
529
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/jwt.ts","../src/core/password.ts","../src/core/api-key.ts","../src/core/opaque-token.ts","../src/core/totp.ts","../src/core/hmac.ts","../src/core/cookie.ts","../src/core/key-provider.ts"],"names":["sha256","timingSafeEqual","DEFAULT_ALGORITHM","createHmac"],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * JWT access and refresh token utilities with HS256/RS256 support\n */\n\nimport { SignJWT, jwtVerify, JWTPayload as JoseJWTPayload } from 'jose';\nimport { timingSafeEqual } from 'crypto';\nimport type {\n JWTPayload,\n SignAccessTokenOptions,\n VerifyAccessTokenOptions,\n RotateRefreshTokenOptions,\n RefreshTokenRotationResult,\n} from './types';\n\n/**\n * Parse duration string to seconds\n * @example \"15m\" -> 900, \"7d\" -> 604800\n */\nfunction parseDuration(duration: string | number): number {\n if (typeof duration === 'number') return duration;\n\n const match = duration.match(/^(\\d+)([smhd])$/);\n if (!match) throw new Error(`Invalid duration format: ${duration}`);\n\n const value = parseInt(match[1], 10);\n const unit = match[2];\n\n const multipliers: Record<string, number> = {\n s: 1,\n m: 60,\n h: 3600,\n d: 86400,\n };\n\n return value * multipliers[unit];\n}\n\n/**\n * Sign an access token with JWT\n */\nexport async function signAccessToken(\n payload: JWTPayload,\n options: SignAccessTokenOptions\n): Promise<string> {\n const { expiresIn, issuer, audience, keyProvider, kid } = options;\n\n const signingKey = await keyProvider.getSigningKey(kid);\n const expiresInSeconds = parseDuration(expiresIn);\n\n let key: Uint8Array | CryptoKey;\n\n if (signingKey.algorithm === 'HS256') {\n key =\n typeof signingKey.key === 'string'\n ? new TextEncoder().encode(signingKey.key)\n : signingKey.key;\n } else {\n const privateKey =\n typeof signingKey.key === 'string'\n ? signingKey.key\n : new TextDecoder().decode(signingKey.key);\n\n key = await crypto.subtle.importKey(\n 'pkcs8',\n new TextEncoder().encode(privateKey),\n { name: 'RSASSA-PKCS1-v1_5', hash: 'SHA-256' },\n false,\n ['sign']\n );\n }\n\n const jwt = new SignJWT(payload as unknown as JoseJWTPayload)\n .setProtectedHeader({ alg: signingKey.algorithm, kid: signingKey.kid })\n .setIssuedAt()\n .setExpirationTime(Math.floor(Date.now() / 1000) + expiresInSeconds);\n\n if (issuer) jwt.setIssuer(issuer);\n if (audience) jwt.setAudience(audience);\n if (payload.sub) jwt.setSubject(payload.sub);\n\n return jwt.sign(key);\n}\n\n/**\n * Verify an access token\n */\nexport async function verifyAccessToken(\n token: string,\n options: VerifyAccessTokenOptions\n): Promise<JWTPayload> {\n const { keyProvider, issuer, audience, clockTolerance = 60 } = options;\n\n let header;\n try {\n const parts = token.split('.');\n if (parts.length !== 3) throw new Error('Invalid JWT format');\n header = JSON.parse(Buffer.from(parts[0], 'base64url').toString());\n } catch {\n throw new Error('Invalid JWT header');\n }\n\n const kid = header.kid as string;\n if (!kid) throw new Error('Missing kid in JWT header');\n\n const verificationKey = await keyProvider.getVerificationKey(kid);\n\n let key: Uint8Array | CryptoKey;\n\n if (verificationKey.algorithm === 'HS256') {\n key =\n typeof verificationKey.key === 'string'\n ? new TextEncoder().encode(verificationKey.key)\n : verificationKey.key;\n } else {\n const publicKey =\n typeof verificationKey.key === 'string'\n ? verificationKey.key\n : new TextDecoder().decode(verificationKey.key);\n\n key = await crypto.subtle.importKey(\n 'spki',\n new TextEncoder().encode(publicKey),\n { name: 'RSASSA-PKCS1-v1_5', hash: 'SHA-256' },\n false,\n ['verify']\n );\n }\n\n const verified = await jwtVerify(token, key, {\n issuer,\n audience,\n clockTolerance,\n });\n\n return verified.payload as JWTPayload;\n}\n\n/**\n * Rotate refresh token with replay protection\n */\nexport async function rotateRefreshToken(\n oldRefreshToken: string,\n options: RotateRefreshTokenOptions\n): Promise<RefreshTokenRotationResult> {\n const { keyProvider, onTokenReused } = options;\n\n let payload: JWTPayload;\n try {\n payload = await verifyAccessToken(oldRefreshToken, { keyProvider });\n } catch {\n throw new Error('Invalid refresh token');\n }\n\n if (payload.reused) {\n if (onTokenReused) {\n await onTokenReused(payload.jti || 'unknown');\n }\n throw new Error('Refresh token reuse detected');\n }\n\n const jti = generateJTI();\n const newPayload: JWTPayload = {\n sub: payload.sub,\n jti,\n };\n\n const accessToken = await signAccessToken(newPayload, {\n expiresIn: '15m',\n keyProvider,\n });\n\n const refreshToken = await signAccessToken(\n { ...newPayload, type: 'refresh' },\n {\n expiresIn: '7d',\n keyProvider,\n }\n );\n\n return {\n accessToken,\n refreshToken,\n payload: newPayload,\n };\n}\n\n/**\n * Generate unique JWT ID\n */\nfunction generateJTI(): string {\n const bytes = crypto.getRandomValues(new Uint8Array(16));\n return Buffer.from(bytes).toString('base64url');\n}\n\n/**\n * Timing-safe string comparison\n */\nexport function timingSafeCompare(a: string, b: string): boolean {\n if (a.length !== b.length) return false;\n const bufA = Buffer.from(a);\n const bufB = Buffer.from(b);\n return timingSafeEqual(bufA, bufB);\n}\n","/**\n * Argon2id password hashing with secure defaults\n */\n\nconst ARGON2_OPTIONS = {\n // argon2id = 2, hardcoded to avoid ESM named export resolution issues\n // with native Node addons. argon2.argon2id is undefined when imported\n // via dynamic ESM import due to CJS/ESM interop limitations.\n type: 2 as 0 | 1 | 2,\n memoryCost: 65536, // 64 MB - OWASP recommended minimum\n timeCost: 3, // 3 iterations\n parallelism: 4, // 4 threads\n hashLength: 32, // 256-bit output\n};\n\nasync function getArgon2() {\n const mod = await import('argon2');\n const argon2 = mod.default ?? mod;\n if (typeof argon2.hash !== 'function' || typeof argon2.verify !== 'function') {\n throw new Error('[tungsten] argon2 module loaded incorrectly - hash/verify not available');\n }\n return argon2 as typeof mod;\n}\n\nexport async function hashPassword(password: string): Promise<string> {\n if (!password || password.length === 0) {\n throw new Error('Password cannot be empty');\n }\n if (password.length > 128) {\n throw new Error('Password exceeds maximum length of 128 characters');\n }\n try {\n const argon2 = await getArgon2();\n return await argon2.hash(password, ARGON2_OPTIONS);\n } catch (err) {\n // Re-throw validation errors as-is\n if (\n err instanceof Error &&\n (err.message === 'Password cannot be empty' ||\n err.message === 'Password exceeds maximum length of 128 characters')\n ) {\n throw err;\n }\n // Wrap internal argon2 errors to avoid leaking implementation details\n throw new Error('Password hashing failed');\n }\n}\n\nexport async function verifyPassword(password: string, hash: string): Promise<boolean> {\n if (!password || !hash) return false;\n try {\n const argon2 = await getArgon2();\n return await argon2.verify(hash, password);\n } catch {\n return false;\n }\n}\n\nexport async function needsRehash(hash: string): Promise<boolean> {\n try {\n const argon2 = await getArgon2();\n return argon2.needsRehash(hash, ARGON2_OPTIONS);\n } catch {\n return true;\n }\n}\n","/**\n * API key generation and verification utilities\n */\n\nimport { sha256 } from '@noble/hashes/sha256';\nimport { timingSafeCompare } from './jwt';\nimport type { GenerateApiKeyOptions } from './types';\n\nconst DEFAULT_KEY_LENGTH = 32;\nconst ENCODING = 'base64url';\n\n/**\n * Generate a cryptographically secure API key\n * @param options - Generation options\n * @returns API key with optional prefix\n */\nexport function generateApiKey(options: GenerateApiKeyOptions = {}): string {\n const { prefix = '', length = DEFAULT_KEY_LENGTH } = options;\n\n if (length < 16) {\n throw new Error('API key length must be at least 16 bytes');\n }\n\n const bytes = crypto.getRandomValues(new Uint8Array(length));\n const key = Buffer.from(bytes).toString(ENCODING);\n\n return prefix ? `${prefix}${key}` : key;\n}\n\n/**\n * Hash an API key for storage\n * @param apiKey - Plain API key\n * @returns Hashed key (hex encoded)\n */\nexport function hashApiKey(apiKey: string): string {\n if (!apiKey || apiKey.length === 0) {\n throw new Error('API key cannot be empty');\n }\n\n const hash = sha256(new TextEncoder().encode(apiKey));\n return Buffer.from(hash).toString('hex');\n}\n\n/**\n * Verify an API key against a stored hash\n * @param apiKey - Plain API key\n * @param hash - Stored hash\n * @returns True if key matches hash\n */\nexport function verifyApiKey(apiKey: string, hash: string): boolean {\n if (!apiKey || !hash) {\n return false;\n }\n\n try {\n const computedHash = hashApiKey(apiKey);\n return timingSafeCompare(computedHash, hash);\n } catch {\n return false;\n }\n}\n\n/**\n * Extract prefix from an API key\n * @param apiKey - API key with potential prefix\n * @returns Prefix or empty string\n */\nexport function extractPrefix(apiKey: string): string {\n const match = apiKey.match(/^([a-z_]+)_/i);\n return match ? match[0] : '';\n}\n","/**\n * Opaque token generation for session identifiers\n */\n\nimport { sha256 } from '@noble/hashes/sha256';\n\nconst TOKEN_LENGTH = 32;\n\n/**\n * Generate a cryptographically secure opaque token\n * @returns Base64url encoded token\n */\nexport function generateOpaqueToken(): string {\n const bytes = crypto.getRandomValues(new Uint8Array(TOKEN_LENGTH));\n return Buffer.from(bytes).toString('base64url');\n}\n\n/**\n * Hash an opaque token for storage\n * @param token - Plain opaque token\n * @returns Hashed token (hex encoded)\n */\nexport function hashOpaqueToken(token: string): string {\n if (!token || token.length === 0) {\n throw new Error('Token cannot be empty');\n }\n\n const hash = sha256(new TextEncoder().encode(token));\n return Buffer.from(hash).toString('hex');\n}\n","/**\n * TOTP (Time-based One-Time Password) implementation (RFC 6238)\n */\n\nimport { createHmac } from 'crypto';\nimport type { TOTPOptions, TOTPVerificationResult } from './types';\n\nconst DEFAULT_PERIOD = 30;\nconst DEFAULT_DIGITS = 6;\nconst DEFAULT_ALGORITHM = 'SHA1';\nconst DEFAULT_WINDOW = 1;\n\n/**\n * Generate a TOTP secret\n * @returns Base32 encoded secret\n */\nexport function generateTOTPSecret(): string {\n const bytes = crypto.getRandomValues(new Uint8Array(20));\n return base32Encode(bytes);\n}\n\n/**\n * Generate a TOTP code\n * @param secret - Base32 encoded secret\n * @param options - TOTP options\n * @returns 6-digit TOTP code\n */\nexport function generateTOTP(secret: string, options: TOTPOptions = {}): string {\n const {\n period = DEFAULT_PERIOD,\n digits = DEFAULT_DIGITS,\n algorithm = DEFAULT_ALGORITHM,\n } = options;\n\n const counter = Math.floor(Date.now() / 1000 / period);\n return generateHOTP(secret, counter, { digits, algorithm });\n}\n\n/**\n * Verify a TOTP code\n * @param code - User-provided code\n * @param secret - Base32 encoded secret\n * @param options - TOTP options\n * @returns Verification result with delta\n */\nexport function verifyTOTP(\n code: string,\n secret: string,\n options: TOTPOptions = {}\n): TOTPVerificationResult {\n const {\n period = DEFAULT_PERIOD,\n digits = DEFAULT_DIGITS,\n algorithm = DEFAULT_ALGORITHM,\n window = DEFAULT_WINDOW,\n } = options;\n\n const currentCounter = Math.floor(Date.now() / 1000 / period);\n\n for (let i = -window; i <= window; i++) {\n const counter = currentCounter + i;\n const expectedCode = generateHOTP(secret, counter, { digits, algorithm });\n\n if (timingSafeEqual(code, expectedCode)) {\n return { valid: true, delta: i };\n }\n }\n\n return { valid: false };\n}\n\n/**\n * Generate HOTP (HMAC-based One-Time Password)\n */\nfunction generateHOTP(\n secret: string,\n counter: number,\n options: { digits: number; algorithm: string }\n): string {\n const { digits, algorithm } = options;\n\n const secretBytes = base32Decode(secret);\n const counterBuffer = Buffer.allocUnsafe(8);\n counterBuffer.writeBigUInt64BE(BigInt(counter));\n\n const hmac = createHmac(algorithm.toLowerCase(), secretBytes);\n hmac.update(counterBuffer);\n const hash = hmac.digest();\n\n const offset = hash[hash.length - 1] & 0x0f;\n const binary =\n ((hash[offset] & 0x7f) << 24) |\n ((hash[offset + 1] & 0xff) << 16) |\n ((hash[offset + 2] & 0xff) << 8) |\n (hash[offset + 3] & 0xff);\n\n const otp = binary % Math.pow(10, digits);\n return otp.toString().padStart(digits, '0');\n}\n\n/**\n * Base32 encode\n */\nfunction base32Encode(data: Uint8Array): string {\n const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';\n let bits = 0;\n let value = 0;\n let output = '';\n\n for (let i = 0; i < data.length; i++) {\n value = (value << 8) | data[i];\n bits += 8;\n\n while (bits >= 5) {\n output += alphabet[(value >>> (bits - 5)) & 31];\n bits -= 5;\n }\n }\n\n if (bits > 0) {\n output += alphabet[(value << (5 - bits)) & 31];\n }\n\n return output;\n}\n\n/**\n * Base32 decode\n */\nfunction base32Decode(encoded: string): Buffer {\n const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';\n const cleanInput = encoded.toUpperCase().replace(/[^A-Z2-7]/g, '');\n\n let bits = 0;\n let value = 0;\n const output: number[] = [];\n\n for (let i = 0; i < cleanInput.length; i++) {\n const idx = alphabet.indexOf(cleanInput[i]);\n if (idx === -1) continue;\n\n value = (value << 5) | idx;\n bits += 5;\n\n if (bits >= 8) {\n output.push((value >>> (bits - 8)) & 255);\n bits -= 8;\n }\n }\n\n return Buffer.from(output);\n}\n\n/**\n * Timing-safe string equality\n */\nfunction timingSafeEqual(a: string, b: string): boolean {\n if (a.length !== b.length) return false;\n\n let result = 0;\n for (let i = 0; i < a.length; i++) {\n result |= a.charCodeAt(i) ^ b.charCodeAt(i);\n }\n return result === 0;\n}\n","/**\n * HMAC request signing and verification\n */\n\nimport { createHmac } from 'crypto';\nimport { timingSafeCompare } from './jwt';\n\nconst DEFAULT_ALGORITHM = 'sha256';\n\n/**\n * Sign a payload with HMAC\n * @param payload - Data to sign (string or object)\n * @param secret - Signing secret\n * @param algorithm - HMAC algorithm (default: sha256)\n * @returns Hex-encoded signature\n */\nexport function signPayload(\n payload: string | Record<string, unknown>,\n secret: string,\n algorithm: string = DEFAULT_ALGORITHM\n): string {\n if (!secret || secret.length === 0) {\n throw new Error('Secret cannot be empty');\n }\n\n const data = typeof payload === 'string' ? payload : JSON.stringify(payload);\n\n const hmac = createHmac(algorithm, secret);\n hmac.update(data);\n return hmac.digest('hex');\n}\n\n/**\n * Verify a payload signature\n * @param payload - Data to verify\n * @param signature - Expected signature\n * @param secret - Signing secret\n * @param algorithm - HMAC algorithm (default: sha256)\n * @returns True if signature is valid\n */\nexport function verifySignature(\n payload: string | Record<string, unknown>,\n signature: string,\n secret: string,\n algorithm: string = DEFAULT_ALGORITHM\n): boolean {\n if (!signature || !secret) {\n return false;\n }\n\n try {\n const expectedSignature = signPayload(payload, secret, algorithm);\n return timingSafeCompare(signature, expectedSignature);\n } catch {\n return false;\n }\n}\n\n/**\n * Sign a request with timestamp to prevent replay attacks\n * @param payload - Request data\n * @param secret - Signing secret\n * @param timestamp - Unix timestamp (default: current time)\n * @returns Object with signature and timestamp\n */\nexport function signRequest(\n payload: string | Record<string, unknown>,\n secret: string,\n timestamp?: number\n): { signature: string; timestamp: number } {\n const ts = timestamp ?? Math.floor(Date.now() / 1000);\n const data = typeof payload === 'string' ? payload : JSON.stringify(payload);\n const signedData = `${ts}.${data}`;\n\n return {\n signature: signPayload(signedData, secret),\n timestamp: ts,\n };\n}\n\n/**\n * Verify a signed request with timestamp validation\n * @param payload - Request data\n * @param signature - Request signature\n * @param timestamp - Request timestamp\n * @param secret - Signing secret\n * @param maxAge - Maximum age in seconds (default: 300)\n * @returns True if signature and timestamp are valid\n */\nexport function verifyRequest(\n payload: string | Record<string, unknown>,\n signature: string,\n timestamp: number,\n secret: string,\n maxAge: number = 300\n): boolean {\n const now = Math.floor(Date.now() / 1000);\n const age = now - timestamp;\n\n if (age > maxAge || age < 0) {\n return false;\n }\n\n const data = typeof payload === 'string' ? payload : JSON.stringify(payload);\n const signedData = `${timestamp}.${data}`;\n\n return verifySignature(signedData, signature, secret);\n}\n","/**\n * Cookie configuration helpers\n */\n\nimport type { CookieOptions } from './types';\n\n/**\n * Get secure cookie defaults for production\n */\nexport function getSecureCookieDefaults(isProduction: boolean = true): Partial<CookieOptions> {\n return {\n httpOnly: true,\n secure: isProduction,\n sameSite: 'lax',\n path: '/',\n };\n}\n\n/**\n * Generate Set-Cookie header value\n */\nexport function serializeCookie(name: string, value: string, options: CookieOptions): string {\n const parts: string[] = [`${name}=${encodeURIComponent(value)}`];\n\n if (options.domain) {\n parts.push(`Domain=${options.domain}`);\n }\n\n if (options.path) {\n parts.push(`Path=${options.path}`);\n }\n\n if (options.maxAge !== undefined) {\n parts.push(`Max-Age=${options.maxAge}`);\n }\n\n if (options.httpOnly) {\n parts.push('HttpOnly');\n }\n\n if (options.secure) {\n parts.push('Secure');\n }\n\n if (options.sameSite) {\n const sameSite = options.sameSite.charAt(0).toUpperCase() + options.sameSite.slice(1);\n parts.push(`SameSite=${sameSite}`);\n }\n\n return parts.join('; ');\n}\n\n/**\n * Parse Cookie header value\n */\nexport function parseCookie(cookieHeader: string): Record<string, string> {\n const cookies: Record<string, string> = {};\n\n if (!cookieHeader) return cookies;\n\n const pairs = cookieHeader.split(';');\n\n for (const pair of pairs) {\n const [name, ...rest] = pair.split('=');\n if (name && rest.length > 0) {\n const trimmedName = name.trim();\n const value = rest.join('=').trim();\n cookies[trimmedName] = decodeURIComponent(value);\n }\n }\n\n return cookies;\n}\n\n/**\n * Create a cookie deletion header\n */\nexport function deleteCookie(name: string, options: Partial<CookieOptions> = {}): string {\n return serializeCookie(name, '', {\n ...options,\n name,\n maxAge: 0,\n });\n}\n","/**\n * Key provider implementations for multi-tenant JWT signing\n */\n\nimport type { KeyProvider, SigningKey, VerificationKey } from './types';\n\n/**\n * Simple in-memory key provider for single-tenant applications\n */\nexport class SimpleKeyProvider implements KeyProvider {\n private signingKey: SigningKey;\n private verificationKey: VerificationKey;\n\n constructor(secret: string, algorithm: 'HS256' | 'RS256' = 'HS256', kid: string = 'default') {\n this.signingKey = {\n kid,\n algorithm,\n key: secret,\n };\n\n this.verificationKey = {\n algorithm,\n key: secret,\n };\n }\n\n async getSigningKey(_kid?: string): Promise<SigningKey> {\n return this.signingKey;\n }\n\n async getVerificationKey(_kid: string): Promise<VerificationKey> {\n return this.verificationKey;\n }\n}\n\n/**\n * Rotating key provider with multiple keys\n */\nexport class RotatingKeyProvider implements KeyProvider {\n private keys: Map<string, { signing: SigningKey; verification: VerificationKey }>;\n private currentKid: string;\n\n constructor(initialKey: { kid: string; secret: string; algorithm: 'HS256' | 'RS256' }) {\n this.keys = new Map();\n this.currentKid = initialKey.kid;\n\n this.addKey(initialKey.kid, initialKey.secret, initialKey.algorithm);\n }\n\n /**\n * Add a new key to the rotation\n */\n addKey(kid: string, secret: string, algorithm: 'HS256' | 'RS256'): void {\n this.keys.set(kid, {\n signing: { kid, algorithm, key: secret },\n verification: { algorithm, key: secret },\n });\n }\n\n /**\n * Set the current signing key\n */\n setCurrentKey(kid: string): void {\n if (!this.keys.has(kid)) {\n throw new Error(`Key with kid \"${kid}\" not found`);\n }\n this.currentKid = kid;\n }\n\n /**\n * Remove an old key from rotation\n */\n removeKey(kid: string): void {\n if (kid === this.currentKid) {\n throw new Error('Cannot remove the current signing key');\n }\n this.keys.delete(kid);\n }\n\n async getSigningKey(kid?: string): Promise<SigningKey> {\n const keyId = kid ?? this.currentKid;\n const keyPair = this.keys.get(keyId);\n\n if (!keyPair) {\n throw new Error(`Signing key with kid \"${keyId}\" not found`);\n }\n\n return keyPair.signing;\n }\n\n async getVerificationKey(kid: string): Promise<VerificationKey> {\n const keyPair = this.keys.get(kid);\n\n if (!keyPair) {\n throw new Error(`Verification key with kid \"${kid}\" not found`);\n }\n\n return keyPair.verification;\n }\n\n /**\n * Get all available key IDs\n */\n getKeyIds(): string[] {\n return Array.from(this.keys.keys());\n }\n}\n\n/**\n * Multi-tenant key provider\n */\nexport class MultiTenantKeyProvider implements KeyProvider {\n private tenants: Map<string, KeyProvider>;\n\n constructor() {\n this.tenants = new Map();\n }\n\n /**\n * Register a tenant with its key provider\n */\n registerTenant(tenantId: string, keyProvider: KeyProvider): void {\n this.tenants.set(tenantId, keyProvider);\n }\n\n /**\n * Unregister a tenant\n */\n unregisterTenant(tenantId: string): void {\n this.tenants.delete(tenantId);\n }\n\n async getSigningKey(kid?: string): Promise<SigningKey> {\n if (!kid) {\n throw new Error('kid is required for multi-tenant key provider');\n }\n\n const [tenantId] = kid.split(':');\n const provider = this.tenants.get(tenantId);\n\n if (!provider) {\n throw new Error(`No key provider registered for tenant \"${tenantId}\"`);\n }\n\n return provider.getSigningKey(kid);\n }\n\n async getVerificationKey(kid: string): Promise<VerificationKey> {\n const [tenantId] = kid.split(':');\n const provider = this.tenants.get(tenantId);\n\n if (!provider) {\n throw new Error(`No key provider registered for tenant \"${tenantId}\"`);\n }\n\n return provider.getVerificationKey(kid);\n }\n}\n"]}