@tern-secure/backend 1.2.0-canary.v20251127235234 → 1.2.0-canary.v20251202164451

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.
Files changed (91) hide show
  1. package/dist/adapters/index.d.ts +1 -1
  2. package/dist/adapters/index.d.ts.map +1 -1
  3. package/dist/adapters/types.d.ts +42 -0
  4. package/dist/adapters/types.d.ts.map +1 -1
  5. package/dist/admin/index.d.ts +1 -1
  6. package/dist/admin/index.d.ts.map +1 -1
  7. package/dist/admin/index.js +8 -1
  8. package/dist/admin/index.js.map +1 -1
  9. package/dist/admin/index.mjs +24 -598
  10. package/dist/admin/index.mjs.map +1 -1
  11. package/dist/app-check/AppCheckApi.d.ts +14 -0
  12. package/dist/app-check/AppCheckApi.d.ts.map +1 -0
  13. package/dist/app-check/generator.d.ts +9 -0
  14. package/dist/app-check/generator.d.ts.map +1 -0
  15. package/dist/app-check/index.d.ts +18 -0
  16. package/dist/app-check/index.d.ts.map +1 -0
  17. package/dist/app-check/index.js +1135 -0
  18. package/dist/app-check/index.js.map +1 -0
  19. package/dist/app-check/index.mjs +13 -0
  20. package/dist/app-check/index.mjs.map +1 -0
  21. package/dist/app-check/serverAppCheck.d.ts +33 -0
  22. package/dist/app-check/serverAppCheck.d.ts.map +1 -0
  23. package/dist/app-check/types.d.ts +21 -0
  24. package/dist/app-check/types.d.ts.map +1 -0
  25. package/dist/app-check/verifier.d.ts +16 -0
  26. package/dist/app-check/verifier.d.ts.map +1 -0
  27. package/dist/auth/credential.d.ts +5 -5
  28. package/dist/auth/credential.d.ts.map +1 -1
  29. package/dist/auth/getauth.d.ts +2 -1
  30. package/dist/auth/getauth.d.ts.map +1 -1
  31. package/dist/auth/index.d.ts +2 -0
  32. package/dist/auth/index.d.ts.map +1 -1
  33. package/dist/auth/index.js +902 -394
  34. package/dist/auth/index.js.map +1 -1
  35. package/dist/auth/index.mjs +5 -3
  36. package/dist/chunk-34QENCWP.mjs +784 -0
  37. package/dist/chunk-34QENCWP.mjs.map +1 -0
  38. package/dist/{chunk-NXYWC6YO.mjs → chunk-TUYCJY35.mjs} +182 -6
  39. package/dist/chunk-TUYCJY35.mjs.map +1 -0
  40. package/dist/chunk-UCSJDX6Y.mjs +778 -0
  41. package/dist/chunk-UCSJDX6Y.mjs.map +1 -0
  42. package/dist/constants.d.ts +10 -1
  43. package/dist/constants.d.ts.map +1 -1
  44. package/dist/fireRestApi/endpoints/AppCheckApi.d.ts.map +1 -1
  45. package/dist/index.d.ts +4 -1
  46. package/dist/index.d.ts.map +1 -1
  47. package/dist/index.js +1275 -856
  48. package/dist/index.js.map +1 -1
  49. package/dist/index.mjs +97 -137
  50. package/dist/index.mjs.map +1 -1
  51. package/dist/jwt/crypto-signer.d.ts +21 -0
  52. package/dist/jwt/crypto-signer.d.ts.map +1 -0
  53. package/dist/jwt/index.d.ts +2 -1
  54. package/dist/jwt/index.d.ts.map +1 -1
  55. package/dist/jwt/index.js +119 -2
  56. package/dist/jwt/index.js.map +1 -1
  57. package/dist/jwt/index.mjs +7 -3
  58. package/dist/jwt/signJwt.d.ts +8 -2
  59. package/dist/jwt/signJwt.d.ts.map +1 -1
  60. package/dist/jwt/types.d.ts +6 -0
  61. package/dist/jwt/types.d.ts.map +1 -1
  62. package/dist/jwt/verifyJwt.d.ts +7 -1
  63. package/dist/jwt/verifyJwt.d.ts.map +1 -1
  64. package/dist/tokens/authstate.d.ts +2 -0
  65. package/dist/tokens/authstate.d.ts.map +1 -1
  66. package/dist/tokens/c-authenticateRequestProcessor.d.ts +2 -2
  67. package/dist/tokens/c-authenticateRequestProcessor.d.ts.map +1 -1
  68. package/dist/tokens/keys.d.ts.map +1 -1
  69. package/dist/tokens/request.d.ts.map +1 -1
  70. package/dist/tokens/types.d.ts +6 -4
  71. package/dist/tokens/types.d.ts.map +1 -1
  72. package/dist/utils/config.d.ts.map +1 -1
  73. package/dist/{auth/utils.d.ts → utils/fetcher.d.ts} +2 -1
  74. package/dist/utils/fetcher.d.ts.map +1 -0
  75. package/dist/utils/mapDecode.d.ts +2 -1
  76. package/dist/utils/mapDecode.d.ts.map +1 -1
  77. package/dist/utils/token-generator.d.ts +4 -0
  78. package/dist/utils/token-generator.d.ts.map +1 -0
  79. package/package.json +13 -3
  80. package/dist/auth/constants.d.ts +0 -6
  81. package/dist/auth/constants.d.ts.map +0 -1
  82. package/dist/auth/utils.d.ts.map +0 -1
  83. package/dist/chunk-DJLDUW7J.mjs +0 -414
  84. package/dist/chunk-DJLDUW7J.mjs.map +0 -1
  85. package/dist/chunk-GFH5CXQR.mjs +0 -71
  86. package/dist/chunk-GFH5CXQR.mjs.map +0 -1
  87. package/dist/chunk-NXYWC6YO.mjs.map +0 -1
  88. package/dist/chunk-WIVOBOZR.mjs +0 -86
  89. package/dist/chunk-WIVOBOZR.mjs.map +0 -1
  90. package/dist/utils/gemini_admin-init.d.ts +0 -10
  91. package/dist/utils/gemini_admin-init.d.ts.map +0 -1
package/dist/index.js CHANGED
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __export = (target, all) => {
7
9
  for (var name in all)
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
15
17
  }
16
18
  return to;
17
19
  };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
18
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
29
 
20
30
  // src/index.ts
@@ -28,6 +38,7 @@ __export(index_exports, {
28
38
  createAdapter: () => createAdapter,
29
39
  createBackendInstanceClient: () => createBackendInstanceClient,
30
40
  createRedirect: () => createRedirect,
41
+ createRequestProcessor: () => createRequestProcessor,
31
42
  createTernSecureRequest: () => createTernSecureRequest,
32
43
  disableDebugLogging: () => disableDebugLogging,
33
44
  enableDebugLogging: () => enableDebugLogging,
@@ -35,15 +46,25 @@ __export(index_exports, {
35
46
  signedIn: () => signedIn,
36
47
  signedInAuthObject: () => signedInAuthObject,
37
48
  signedOutAuthObject: () => signedOutAuthObject,
38
- validateCheckRevokedOptions: () => validateCheckRevokedOptions
49
+ validateCheckRevokedOptions: () => validateCheckRevokedOptions,
50
+ verifyToken: () => verifyToken
39
51
  });
40
52
  module.exports = __toCommonJS(index_exports);
41
53
 
42
54
  // src/constants.ts
43
55
  var GOOGLE_PUBLIC_KEYS_URL = "https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com";
56
+ var FIREBASE_APP_CHECK_AUDIENCE = "https://firebaseappcheck.googleapis.com/google.firebase.appcheck.v1.TokenExchangeService";
44
57
  var MAX_CACHE_LAST_UPDATED_AT_SECONDS = 5 * 60;
45
58
  var DEFAULT_CACHE_DURATION = 3600 * 1e3;
46
59
  var CACHE_CONTROL_REGEX = /max-age=(\d+)/;
60
+ var TOKEN_EXPIRY_THRESHOLD_MILLIS = 5 * 60 * 1e3;
61
+ var GOOGLE_TOKEN_AUDIENCE = "https://accounts.google.com/o/oauth2/token";
62
+ var GOOGLE_AUTH_TOKEN_HOST = "accounts.google.com";
63
+ var GOOGLE_AUTH_TOKEN_PATH = "/o/oauth2/token";
64
+ var ONE_HOUR_IN_SECONDS = 60 * 60;
65
+ var ONE_MINUTE_IN_SECONDS = 60;
66
+ var ONE_MINUTE_IN_MILLIS = ONE_MINUTE_IN_SECONDS * 1e3;
67
+ var ONE_DAY_IN_MILLIS = 24 * 60 * 60 * 1e3;
47
68
  var Attributes = {
48
69
  AuthToken: "__ternsecureAuthToken",
49
70
  AuthSignature: "__ternsecureAuthSignature",
@@ -78,7 +99,7 @@ var QueryParameters = {
78
99
  };
79
100
  var Headers2 = {
80
101
  Accept: "accept",
81
- AppCheckToken: "x-firebase-appcheck",
102
+ AppCheckToken: "x-ternsecure-appcheck",
82
103
  AuthMessage: "x-ternsecure-auth-message",
83
104
  Authorization: "authorization",
84
105
  AuthReason: "x-ternsecure-auth-reason",
@@ -238,12 +259,93 @@ var createTernSecureRequest = (...args) => {
238
259
  return args[0] instanceof TernSecureRequest ? args[0] : new TernSecureRequest(...args);
239
260
  };
240
261
 
262
+ // src/tokens/c-authenticateRequestProcessor.ts
263
+ var RequestProcessorContext = class {
264
+ constructor(ternSecureRequest, options) {
265
+ this.ternSecureRequest = ternSecureRequest;
266
+ this.options = options;
267
+ this.initHeaderValues();
268
+ this.initCookieValues();
269
+ this.initHandshakeValues();
270
+ this.initUrlValues();
271
+ Object.assign(this, options);
272
+ this.ternUrl = this.ternSecureRequest.ternUrl;
273
+ }
274
+ get request() {
275
+ return this.ternSecureRequest;
276
+ }
277
+ initHeaderValues() {
278
+ this.sessionTokenInHeader = this.parseAuthorizationHeader(
279
+ this.getHeader(constants.Headers.Authorization)
280
+ );
281
+ this.origin = this.getHeader(constants.Headers.Origin);
282
+ this.host = this.getHeader(constants.Headers.Host);
283
+ this.forwardedHost = this.getHeader(constants.Headers.ForwardedHost);
284
+ this.forwardedProto = this.getHeader(constants.Headers.CloudFrontForwardedProto) || this.getHeader(constants.Headers.ForwardedProto);
285
+ this.referrer = this.getHeader(constants.Headers.Referrer);
286
+ this.userAgent = this.getHeader(constants.Headers.UserAgent);
287
+ this.secFetchDest = this.getHeader(constants.Headers.SecFetchDest);
288
+ this.accept = this.getHeader(constants.Headers.Accept);
289
+ this.appCheckToken = this.getHeader(constants.Headers.AppCheckToken);
290
+ }
291
+ initCookieValues() {
292
+ const isProduction = process.env.NODE_ENV === "production";
293
+ const defaultPrefix = isProduction ? "__HOST-" : "__dev_";
294
+ this.sessionTokenInCookie = this.getCookie(constants.Cookies.Session);
295
+ this.idTokenInCookie = this.getCookie(`${defaultPrefix}${constants.Cookies.IdToken}`);
296
+ this.refreshTokenInCookie = this.getCookie(`${defaultPrefix}${constants.Cookies.Refresh}`);
297
+ this.csrfTokenInCookie = this.getCookie(constants.Cookies.CsrfToken);
298
+ this.customTokenInCookie = this.getCookie(constants.Cookies.Custom);
299
+ this.ternAuth = Number.parseInt(this.getCookie(constants.Cookies.TernAut) || "0", 10);
300
+ }
301
+ initHandshakeValues() {
302
+ this.handshakeToken = this.getQueryParam(constants.QueryParameters.Handshake) || this.getCookie(constants.Cookies.Handshake);
303
+ this.handshakeNonce = this.getQueryParam(constants.QueryParameters.HandshakeNonce) || this.getCookie(constants.Cookies.HandshakeNonce);
304
+ }
305
+ initUrlValues() {
306
+ this.method = this.ternSecureRequest.method;
307
+ this.pathSegments = this.ternSecureRequest.ternUrl.pathname.split("/").filter(Boolean);
308
+ this.endpoint = this.pathSegments[2];
309
+ this.subEndpoint = this.pathSegments[3];
310
+ }
311
+ getQueryParam(name) {
312
+ return this.ternSecureRequest.ternUrl.searchParams.get(name);
313
+ }
314
+ getHeader(name) {
315
+ return this.ternSecureRequest.headers.get(name) || void 0;
316
+ }
317
+ getCookie(name) {
318
+ return this.ternSecureRequest.cookies.get(name) || void 0;
319
+ }
320
+ parseAuthorizationHeader(authorizationHeader) {
321
+ if (!authorizationHeader) {
322
+ return void 0;
323
+ }
324
+ const [scheme, token] = authorizationHeader.split(" ", 2);
325
+ if (!token) {
326
+ return scheme;
327
+ }
328
+ if (scheme === "Bearer") {
329
+ return token;
330
+ }
331
+ return void 0;
332
+ }
333
+ };
334
+ var createRequestProcessor = (ternSecureRequest, options) => {
335
+ return new RequestProcessorContext(ternSecureRequest, options);
336
+ };
337
+
241
338
  // src/utils/mapDecode.ts
242
339
  function mapJwtPayloadToDecodedIdToken(payload) {
243
340
  const decodedIdToken = payload;
244
341
  decodedIdToken.uid = decodedIdToken.sub;
245
342
  return decodedIdToken;
246
343
  }
344
+ function mapJwtPayloadToDecodedAppCheckToken(payload) {
345
+ const decodedAppCheckToken = payload;
346
+ decodedAppCheckToken.app_id = decodedAppCheckToken.sub;
347
+ return decodedAppCheckToken;
348
+ }
247
349
 
248
350
  // src/tokens/authstate.ts
249
351
  var AuthStatus = {
@@ -333,6 +435,7 @@ function signedOut(authCtx, reason, message = "", headers = new Headers()) {
333
435
  isSignedIn: false,
334
436
  auth: () => signedOutAuthObject(),
335
437
  token: null,
438
+ appCheckToken: authCtx.appCheckToken,
336
439
  headers
337
440
  });
338
441
  }
@@ -356,106 +459,596 @@ var decorateHeaders = (requestState) => {
356
459
  } catch {
357
460
  }
358
461
  }
462
+ if (requestState.appCheckToken) {
463
+ try {
464
+ headers.set(constants.Headers.AppCheckToken, requestState.appCheckToken);
465
+ } catch {
466
+ }
467
+ }
359
468
  requestState.headers = headers;
360
469
  return requestState;
361
470
  };
362
471
 
363
- // src/fireRestApi/endpoints/AbstractApi.ts
364
- var AbstractAPI = class {
365
- constructor(request) {
366
- this.request = request;
472
+ // src/jwt/verifyJwt.ts
473
+ var import_jose2 = require("jose");
474
+
475
+ // src/utils/errors.ts
476
+ var RefreshTokenErrorReason = {
477
+ NonEligibleNoCookie: "non-eligible-no-refresh-cookie",
478
+ NonEligibleNonGet: "non-eligible-non-get",
479
+ InvalidSessionToken: "invalid-session-token",
480
+ MissingApiClient: "missing-api-client",
481
+ MissingIdToken: "missing-id-token",
482
+ MissingSessionToken: "missing-session-token",
483
+ MissingRefreshToken: "missing-refresh-token",
484
+ ExpiredIdTokenDecodeFailed: "expired-id-token-decode-failed",
485
+ ExpiredSessionTokenDecodeFailed: "expired-session-token-decode-failed",
486
+ FetchError: "fetch-error"
487
+ };
488
+ var TokenVerificationErrorReason = {
489
+ TokenExpired: "token-expired",
490
+ TokenInvalid: "token-invalid",
491
+ TokenInvalidAlgorithm: "token-invalid-algorithm",
492
+ TokenInvalidAuthorizedParties: "token-invalid-authorized-parties",
493
+ TokenInvalidSignature: "token-invalid-signature",
494
+ TokenNotActiveYet: "token-not-active-yet",
495
+ TokenIatInTheFuture: "token-iat-in-the-future",
496
+ TokenVerificationFailed: "token-verification-failed",
497
+ InvalidSecretKey: "secret-key-invalid",
498
+ LocalJWKMissing: "jwk-local-missing",
499
+ RemoteJWKFailedToLoad: "jwk-remote-failed-to-load",
500
+ RemoteJWKInvalid: "jwk-remote-invalid",
501
+ RemoteJWKMissing: "jwk-remote-missing",
502
+ JWKFailedToResolve: "jwk-failed-to-resolve",
503
+ JWKKidMismatch: "jwk-kid-mismatch"
504
+ };
505
+ var TokenVerificationError = class _TokenVerificationError extends Error {
506
+ reason;
507
+ tokenCarrier;
508
+ constructor({
509
+ message,
510
+ reason
511
+ }) {
512
+ super(message);
513
+ Object.setPrototypeOf(this, _TokenVerificationError.prototype);
514
+ this.reason = reason;
515
+ this.message = message;
367
516
  }
368
- requireApiKey(apiKey) {
369
- if (!apiKey) {
370
- throw new Error("A valid API key is required.");
371
- }
517
+ getFullMessage() {
518
+ return `${[this.message].filter((m) => m).join(" ")} (reason=${this.reason}, token-carrier=${this.tokenCarrier})`;
372
519
  }
373
520
  };
374
521
 
375
- // src/fireRestApi/endpoints/AppCheckApi.ts
376
- function getSdkVersion() {
377
- return "12.7.0";
378
- }
379
- var FIREBASE_APP_CHECK_CONFIG_HEADERS = {
380
- "X-Firebase-Client": `fire-admin-node/${getSdkVersion()}`
522
+ // src/utils/rfc4648.ts
523
+ var base64url = {
524
+ parse(string, opts) {
525
+ return parse2(string, base64UrlEncoding, opts);
526
+ },
527
+ stringify(data, opts) {
528
+ return stringify(data, base64UrlEncoding, opts);
529
+ }
381
530
  };
382
- var AppCheckApi = class extends AbstractAPI {
383
- async exchangeCustomToken(params) {
384
- const { projectId, appId, customToken, accessToken, limitedUse = false } = params;
385
- if (!projectId || !appId) {
386
- throw new Error("Project ID and App ID are required for App Check token exchange");
531
+ var base64UrlEncoding = {
532
+ chars: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_",
533
+ bits: 6
534
+ };
535
+ function parse2(string, encoding, opts = {}) {
536
+ if (!encoding.codes) {
537
+ encoding.codes = {};
538
+ for (let i = 0; i < encoding.chars.length; ++i) {
539
+ encoding.codes[encoding.chars[i]] = i;
387
540
  }
388
- const endpoint = `https://firebaseappcheck.googleapis.com/v1beta/projects/${projectId}/apps/${appId}:exchangeCustomToken`;
389
- const headers = {
390
- "Content-Type": "application/json",
391
- "Authorization": `Bearer ${accessToken}`
392
- };
393
- const body = {
394
- customToken,
395
- limitedUse
396
- };
397
- try {
398
- const response = await fetch(endpoint, {
399
- method: "POST",
400
- headers,
401
- body: JSON.stringify(body)
402
- });
403
- if (!response.ok) {
404
- const errorText = await response.text();
405
- throw new Error(`App Check token exchange failed: ${response.status} ${errorText}`);
406
- }
407
- const data = await response.json();
408
- return {
409
- token: data.token,
410
- ttl: data.ttl
411
- };
412
- } catch (error) {
413
- console.warn("[ternsecure - appcheck api]unexpected error:", error);
414
- throw error;
541
+ }
542
+ if (!opts.loose && string.length * encoding.bits & 7) {
543
+ throw new SyntaxError("Invalid padding");
544
+ }
545
+ let end = string.length;
546
+ while (string[end - 1] === "=") {
547
+ --end;
548
+ if (!opts.loose && !((string.length - end) * encoding.bits & 7)) {
549
+ throw new SyntaxError("Invalid padding");
415
550
  }
416
551
  }
417
- async exchangeDebugToken(params) {
418
- const { projectId, appId, customToken, accessToken, limitedUse = false } = params;
419
- if (!projectId || !appId) {
420
- throw new Error("Project ID and App ID are required for App Check token exchange");
552
+ const out = new (opts.out ?? Uint8Array)(end * encoding.bits / 8 | 0);
553
+ let bits = 0;
554
+ let buffer = 0;
555
+ let written = 0;
556
+ for (let i = 0; i < end; ++i) {
557
+ const value = encoding.codes[string[i]];
558
+ if (value === void 0) {
559
+ throw new SyntaxError("Invalid character " + string[i]);
421
560
  }
422
- const endpoint = `https://firebaseappcheck.googleapis.com/v1beta/projects/${projectId}/apps/${appId}:exchangeDebugToken`;
423
- const headers = {
424
- ...FIREBASE_APP_CHECK_CONFIG_HEADERS,
425
- "Authorization": `Bearer ${accessToken}`
426
- };
427
- const body = {
428
- customToken,
429
- limitedUse
430
- };
431
- try {
432
- const response = await fetch(endpoint, {
433
- method: "POST",
434
- headers,
435
- body: JSON.stringify(body)
436
- });
437
- if (!response.ok) {
438
- const errorText = await response.text();
439
- throw new Error(`App Check token exchange failed: ${response.status} ${errorText}`);
440
- }
441
- const data = await response.json();
442
- return {
443
- token: data.token,
444
- ttl: data.ttl
445
- };
446
- } catch (error) {
447
- console.warn("[ternsecure - appcheck api]unexpected error:", error);
448
- throw error;
561
+ buffer = buffer << encoding.bits | value;
562
+ bits += encoding.bits;
563
+ if (bits >= 8) {
564
+ bits -= 8;
565
+ out[written++] = 255 & buffer >> bits;
449
566
  }
450
567
  }
451
- };
452
-
453
- // src/fireRestApi/endpoints/EmailApi.ts
454
- var EmailApi = class extends AbstractAPI {
455
- async verifyEmailVerification(apiKey, params) {
456
- this.requireApiKey(apiKey);
457
- const { ...restParams } = params;
458
- return this.request({
568
+ if (bits >= encoding.bits || 255 & buffer << 8 - bits) {
569
+ throw new SyntaxError("Unexpected end of data");
570
+ }
571
+ return out;
572
+ }
573
+ function stringify(data, encoding, opts = {}) {
574
+ const { pad = true } = opts;
575
+ const mask = (1 << encoding.bits) - 1;
576
+ let out = "";
577
+ let bits = 0;
578
+ let buffer = 0;
579
+ for (let i = 0; i < data.length; ++i) {
580
+ buffer = buffer << 8 | 255 & data[i];
581
+ bits += 8;
582
+ while (bits > encoding.bits) {
583
+ bits -= encoding.bits;
584
+ out += encoding.chars[mask & buffer >> bits];
585
+ }
586
+ }
587
+ if (bits) {
588
+ out += encoding.chars[mask & buffer << encoding.bits - bits];
589
+ }
590
+ if (pad) {
591
+ while (out.length * encoding.bits & 7) {
592
+ out += "=";
593
+ }
594
+ }
595
+ return out;
596
+ }
597
+
598
+ // src/jwt/cryptoKeys.ts
599
+ var import_jose = require("jose");
600
+ async function importKey(key, algorithm) {
601
+ if (typeof key === "object") {
602
+ const result = await (0, import_jose.importJWK)(key, algorithm);
603
+ if (result instanceof Uint8Array) {
604
+ throw new Error("Unexpected Uint8Array result from JWK import");
605
+ }
606
+ return result;
607
+ }
608
+ const keyString = key.trim();
609
+ if (keyString.includes("-----BEGIN CERTIFICATE-----")) {
610
+ return await (0, import_jose.importX509)(keyString, algorithm);
611
+ }
612
+ if (keyString.includes("-----BEGIN PUBLIC KEY-----")) {
613
+ return await (0, import_jose.importSPKI)(keyString, algorithm);
614
+ }
615
+ try {
616
+ return await (0, import_jose.importSPKI)(keyString, algorithm);
617
+ } catch (error) {
618
+ throw new Error(
619
+ `Unsupported key format. Supported formats: X.509 certificate (PEM), SPKI (PEM), JWK (JSON object or string). Error: ${error}`
620
+ );
621
+ }
622
+ }
623
+
624
+ // src/jwt/algorithms.ts
625
+ var algToHash = {
626
+ RS256: "SHA-256",
627
+ RS384: "SHA-384",
628
+ RS512: "SHA-512"
629
+ };
630
+ var algs = Object.keys(algToHash);
631
+
632
+ // src/jwt/verifyContent.ts
633
+ var verifyHeaderKid = (kid) => {
634
+ if (typeof kid === "undefined") {
635
+ return;
636
+ }
637
+ if (typeof kid !== "string") {
638
+ throw new TokenVerificationError({
639
+ reason: TokenVerificationErrorReason.TokenInvalid,
640
+ message: `Invalid JWT kid ${JSON.stringify(kid)}. Expected a string.`
641
+ });
642
+ }
643
+ };
644
+ var verifySubClaim = (sub) => {
645
+ if (typeof sub !== "string") {
646
+ throw new TokenVerificationError({
647
+ reason: TokenVerificationErrorReason.TokenVerificationFailed,
648
+ message: `Subject claim (sub) is required and must be a string. Received ${JSON.stringify(sub)}.`
649
+ });
650
+ }
651
+ };
652
+ var verifyExpirationClaim = (exp, clockSkewInMs) => {
653
+ if (typeof exp !== "number") {
654
+ throw new TokenVerificationError({
655
+ reason: TokenVerificationErrorReason.TokenVerificationFailed,
656
+ message: `Invalid JWT expiry date (exp) claim ${JSON.stringify(exp)}. Expected a number.`
657
+ });
658
+ }
659
+ const currentDate = new Date(Date.now());
660
+ const expiryDate = /* @__PURE__ */ new Date(0);
661
+ expiryDate.setUTCSeconds(exp);
662
+ const expired = expiryDate.getTime() <= currentDate.getTime() - clockSkewInMs;
663
+ if (expired) {
664
+ throw new TokenVerificationError({
665
+ reason: TokenVerificationErrorReason.TokenExpired,
666
+ message: `JWT is expired. Expiry date: ${expiryDate.toUTCString()}, Current date: ${currentDate.toUTCString()}.`
667
+ });
668
+ }
669
+ };
670
+ var verifyIssuedAtClaim = (iat, clockSkewInMs) => {
671
+ if (typeof iat === "undefined") {
672
+ return;
673
+ }
674
+ if (typeof iat !== "number") {
675
+ throw new TokenVerificationError({
676
+ reason: TokenVerificationErrorReason.TokenVerificationFailed,
677
+ message: `Invalid JWT issued at date claim (iat) ${JSON.stringify(iat)}. Expected a number.`
678
+ });
679
+ }
680
+ const currentDate = new Date(Date.now());
681
+ const issuedAtDate = /* @__PURE__ */ new Date(0);
682
+ issuedAtDate.setUTCSeconds(iat);
683
+ const postIssued = issuedAtDate.getTime() > currentDate.getTime() + clockSkewInMs;
684
+ if (postIssued) {
685
+ throw new TokenVerificationError({
686
+ reason: TokenVerificationErrorReason.TokenIatInTheFuture,
687
+ message: `JWT issued at date claim (iat) is in the future. Issued at date: ${issuedAtDate.toUTCString()}; Current date: ${currentDate.toUTCString()};`
688
+ });
689
+ }
690
+ };
691
+
692
+ // src/jwt/verifyJwt.ts
693
+ var DEFAULT_CLOCK_SKEW_IN_MS = 5 * 1e3;
694
+ async function verifySignature(jwt, key) {
695
+ const { header, raw } = jwt;
696
+ const joseAlgorithm = header.alg || "RS256";
697
+ try {
698
+ const publicKey = await importKey(key, joseAlgorithm);
699
+ const { payload } = await (0, import_jose2.jwtVerify)(raw.text, publicKey);
700
+ return { data: payload };
701
+ } catch (error) {
702
+ return {
703
+ errors: [
704
+ new TokenVerificationError({
705
+ reason: TokenVerificationErrorReason.TokenInvalidSignature,
706
+ message: error.message
707
+ })
708
+ ]
709
+ };
710
+ }
711
+ }
712
+ function ternDecodeJwt(token) {
713
+ try {
714
+ const header = (0, import_jose2.decodeProtectedHeader)(token);
715
+ const payload = (0, import_jose2.decodeJwt)(token);
716
+ const tokenParts = (token || "").toString().split(".");
717
+ if (tokenParts.length !== 3) {
718
+ return {
719
+ errors: [
720
+ new TokenVerificationError({
721
+ reason: TokenVerificationErrorReason.TokenInvalid,
722
+ message: "Invalid JWT format"
723
+ })
724
+ ]
725
+ };
726
+ }
727
+ const [rawHeader, rawPayload, rawSignature] = tokenParts;
728
+ const signature = base64url.parse(rawSignature, { loose: true });
729
+ const data = {
730
+ header,
731
+ payload,
732
+ signature,
733
+ raw: {
734
+ header: rawHeader,
735
+ payload: rawPayload,
736
+ signature: rawSignature,
737
+ text: token
738
+ }
739
+ };
740
+ return { data };
741
+ } catch (error) {
742
+ return {
743
+ errors: [
744
+ new TokenVerificationError({
745
+ reason: TokenVerificationErrorReason.TokenInvalid,
746
+ message: `${error.message || "Invalid Token or Protected Header formatting"} (Token length: ${token?.length}, First 10 chars: ${token?.substring(0, 10)}...)`
747
+ })
748
+ ]
749
+ };
750
+ }
751
+ }
752
+ async function verifyJwt(token, options) {
753
+ const { key } = options;
754
+ const clockSkew = options.clockSkewInMs || DEFAULT_CLOCK_SKEW_IN_MS;
755
+ const { data: decoded, errors } = ternDecodeJwt(token);
756
+ if (errors) {
757
+ return { errors };
758
+ }
759
+ const { header, payload } = decoded;
760
+ try {
761
+ verifyHeaderKid(header.kid);
762
+ verifySubClaim(payload.sub);
763
+ verifyExpirationClaim(payload.exp, clockSkew);
764
+ verifyIssuedAtClaim(payload.iat, clockSkew);
765
+ } catch (error) {
766
+ return { errors: [error] };
767
+ }
768
+ const { data: verifiedPayload, errors: signatureErrors } = await verifySignature(decoded, key);
769
+ if (signatureErrors) {
770
+ return {
771
+ errors: [
772
+ new TokenVerificationError({
773
+ reason: TokenVerificationErrorReason.TokenInvalidSignature,
774
+ message: "Token signature verification failed."
775
+ })
776
+ ]
777
+ };
778
+ }
779
+ const decodedIdToken = mapJwtPayloadToDecodedIdToken(verifiedPayload);
780
+ return { data: decodedIdToken };
781
+ }
782
+ async function verifyAppCheckSignature(jwt, getPublicKey2) {
783
+ const { header, raw } = jwt;
784
+ const joseAlgorithm = header.alg || "RS256";
785
+ try {
786
+ const key = await getPublicKey2();
787
+ const { payload } = await (0, import_jose2.jwtVerify)(raw.text, key);
788
+ return { data: payload };
789
+ } catch (error) {
790
+ return {
791
+ errors: [
792
+ new TokenVerificationError({
793
+ reason: TokenVerificationErrorReason.TokenInvalidSignature,
794
+ message: error.message
795
+ })
796
+ ]
797
+ };
798
+ }
799
+ }
800
+ async function verifyAppCheckJwt(token, options) {
801
+ const { key: getPublicKey2 } = options;
802
+ const clockSkew = options.clockSkewInMs || DEFAULT_CLOCK_SKEW_IN_MS;
803
+ const { data: decoded, errors } = ternDecodeJwt(token);
804
+ if (errors) {
805
+ return { errors };
806
+ }
807
+ const { header, payload } = decoded;
808
+ try {
809
+ verifyHeaderKid(header.kid);
810
+ verifySubClaim(payload.sub);
811
+ verifyExpirationClaim(payload.exp, clockSkew);
812
+ verifyIssuedAtClaim(payload.iat, clockSkew);
813
+ } catch (error) {
814
+ return { errors: [error] };
815
+ }
816
+ const { data: verifiedPayload, errors: signatureErrors } = await verifyAppCheckSignature(
817
+ decoded,
818
+ getPublicKey2
819
+ );
820
+ if (signatureErrors) {
821
+ return {
822
+ errors: [
823
+ new TokenVerificationError({
824
+ reason: TokenVerificationErrorReason.TokenInvalidSignature,
825
+ message: "Token signature verification failed."
826
+ })
827
+ ]
828
+ };
829
+ }
830
+ const decodedAppCheckToken = mapJwtPayloadToDecodedAppCheckToken(verifiedPayload);
831
+ return { data: decodedAppCheckToken };
832
+ }
833
+
834
+ // src/tokens/keys.ts
835
+ var cache = {};
836
+ var lastUpdatedAt = 0;
837
+ var googleExpiresAt = 0;
838
+ function getFromCache(kid) {
839
+ return cache[kid];
840
+ }
841
+ function getCacheValues() {
842
+ return Object.values(cache);
843
+ }
844
+ function setInCache(kid, certificate, shouldExpire = true) {
845
+ cache[kid] = certificate;
846
+ lastUpdatedAt = shouldExpire ? Date.now() : -1;
847
+ }
848
+ async function fetchPublicKeys(keyUrl) {
849
+ const url = new URL(keyUrl);
850
+ const response = await fetch(url);
851
+ if (!response.ok) {
852
+ throw new TokenVerificationError({
853
+ message: `Error loading public keys from ${url.href} with code=${response.status} `,
854
+ reason: TokenVerificationErrorReason.TokenInvalid
855
+ });
856
+ }
857
+ const data = await response.json();
858
+ const expiresAt = getExpiresAt(response);
859
+ return {
860
+ keys: data,
861
+ expiresAt
862
+ };
863
+ }
864
+ async function loadJWKFromRemote({
865
+ keyURL,
866
+ skipJwksCache,
867
+ kid
868
+ }) {
869
+ const finalKeyURL = keyURL || GOOGLE_PUBLIC_KEYS_URL;
870
+ if (skipJwksCache || isCacheExpired() || !getFromCache(kid)) {
871
+ const { keys, expiresAt } = await fetchPublicKeys(finalKeyURL);
872
+ if (!keys || Object.keys(keys).length === 0) {
873
+ throw new TokenVerificationError({
874
+ message: `The JWKS endpoint ${finalKeyURL} returned no keys`,
875
+ reason: TokenVerificationErrorReason.RemoteJWKFailedToLoad
876
+ });
877
+ }
878
+ googleExpiresAt = expiresAt;
879
+ Object.entries(keys).forEach(([keyId, cert2]) => {
880
+ setInCache(keyId, cert2);
881
+ });
882
+ }
883
+ const cert = getFromCache(kid);
884
+ if (!cert) {
885
+ getCacheValues();
886
+ const availableKids = Object.keys(cache).sort().join(", ");
887
+ throw new TokenVerificationError({
888
+ message: `No public key found for kid "${kid}". Available kids: [${availableKids}]`,
889
+ reason: TokenVerificationErrorReason.TokenInvalid
890
+ });
891
+ }
892
+ return cert;
893
+ }
894
+ function isCacheExpired() {
895
+ const now = Date.now();
896
+ if (lastUpdatedAt === -1) {
897
+ return false;
898
+ }
899
+ const cacheAge = now - lastUpdatedAt;
900
+ const maxCacheAge = MAX_CACHE_LAST_UPDATED_AT_SECONDS * 1e3;
901
+ const localCacheExpired = cacheAge >= maxCacheAge;
902
+ const googleCacheExpired = now >= googleExpiresAt;
903
+ const isExpired = localCacheExpired || googleCacheExpired;
904
+ if (isExpired) {
905
+ cache = {};
906
+ }
907
+ return isExpired;
908
+ }
909
+ function getExpiresAt(res) {
910
+ const cacheControlHeader = res.headers.get("cache-control");
911
+ if (!cacheControlHeader) {
912
+ return Date.now() + DEFAULT_CACHE_DURATION;
913
+ }
914
+ const maxAgeMatch = cacheControlHeader.match(CACHE_CONTROL_REGEX);
915
+ const maxAge = maxAgeMatch ? parseInt(maxAgeMatch[1], 10) : DEFAULT_CACHE_DURATION / 1e3;
916
+ return Date.now() + maxAge * 1e3;
917
+ }
918
+
919
+ // src/tokens/verify.ts
920
+ async function verifyToken(token, options) {
921
+ const { data: decodedResult, errors } = ternDecodeJwt(token);
922
+ if (errors) {
923
+ return { errors };
924
+ }
925
+ const { header } = decodedResult;
926
+ const { kid } = header;
927
+ if (!kid) {
928
+ return {
929
+ errors: [
930
+ new TokenVerificationError({
931
+ reason: TokenVerificationErrorReason.TokenInvalid,
932
+ message: 'JWT "kid" header is missing.'
933
+ })
934
+ ]
935
+ };
936
+ }
937
+ try {
938
+ const key = options.jwtKey || await loadJWKFromRemote({ ...options, kid });
939
+ if (!key) {
940
+ return {
941
+ errors: [
942
+ new TokenVerificationError({
943
+ reason: TokenVerificationErrorReason.TokenInvalid,
944
+ message: `No public key found for kid "${kid}".`
945
+ })
946
+ ]
947
+ };
948
+ }
949
+ return await verifyJwt(token, { ...options, key });
950
+ } catch (error) {
951
+ if (error instanceof TokenVerificationError) {
952
+ return { errors: [error] };
953
+ }
954
+ return {
955
+ errors: [error]
956
+ };
957
+ }
958
+ }
959
+
960
+ // src/fireRestApi/endpoints/AbstractApi.ts
961
+ var AbstractAPI = class {
962
+ constructor(request) {
963
+ this.request = request;
964
+ }
965
+ requireApiKey(apiKey) {
966
+ if (!apiKey) {
967
+ throw new Error("A valid API key is required.");
968
+ }
969
+ }
970
+ };
971
+
972
+ // src/fireRestApi/endpoints/AppCheckApi.ts
973
+ function getSdkVersion() {
974
+ return "12.7.0";
975
+ }
976
+ var FIREBASE_APP_CHECK_CONFIG_HEADERS = {
977
+ "X-Firebase-Client": `fire-admin-node/${getSdkVersion()}`
978
+ };
979
+ var AppCheckApi = class extends AbstractAPI {
980
+ async exchangeCustomToken(params) {
981
+ const { projectId, appId, customToken, accessToken, limitedUse = false } = params;
982
+ if (!projectId || !appId) {
983
+ throw new Error("Project ID and App ID are required for App Check token exchange");
984
+ }
985
+ const endpoint = `https://firebaseappcheck.googleapis.com/v1beta/projects/${projectId}/apps/${appId}:exchangeCustomToken`;
986
+ const headers = {
987
+ "Content-Type": "application/json",
988
+ "Authorization": `Bearer ${accessToken}`
989
+ };
990
+ try {
991
+ const response = await fetch(endpoint, {
992
+ method: "POST",
993
+ headers,
994
+ body: JSON.stringify({ customToken, limitedUse })
995
+ });
996
+ if (!response.ok) {
997
+ const errorText = await response.text();
998
+ throw new Error(`App Check token exchange failed: ${response.status} ${errorText}`);
999
+ }
1000
+ const data = await response.json();
1001
+ return {
1002
+ token: data.token,
1003
+ ttl: data.ttl
1004
+ };
1005
+ } catch (error) {
1006
+ console.warn("[ternsecure - appcheck api]unexpected error:", error);
1007
+ throw error;
1008
+ }
1009
+ }
1010
+ async exchangeDebugToken(params) {
1011
+ const { projectId, appId, customToken, accessToken, limitedUse = false } = params;
1012
+ if (!projectId || !appId) {
1013
+ throw new Error("Project ID and App ID are required for App Check token exchange");
1014
+ }
1015
+ const endpoint = `https://firebaseappcheck.googleapis.com/v1beta/projects/${projectId}/apps/${appId}:exchangeDebugToken`;
1016
+ const headers = {
1017
+ ...FIREBASE_APP_CHECK_CONFIG_HEADERS,
1018
+ "Authorization": `Bearer ${accessToken}`
1019
+ };
1020
+ const body = {
1021
+ customToken,
1022
+ limitedUse
1023
+ };
1024
+ try {
1025
+ const response = await fetch(endpoint, {
1026
+ method: "POST",
1027
+ headers,
1028
+ body: JSON.stringify(body)
1029
+ });
1030
+ if (!response.ok) {
1031
+ const errorText = await response.text();
1032
+ throw new Error(`App Check token exchange failed: ${response.status} ${errorText}`);
1033
+ }
1034
+ const data = await response.json();
1035
+ return {
1036
+ token: data.token,
1037
+ ttl: data.ttl
1038
+ };
1039
+ } catch (error) {
1040
+ console.warn("[ternsecure - appcheck api]unexpected error:", error);
1041
+ throw error;
1042
+ }
1043
+ }
1044
+ };
1045
+
1046
+ // src/fireRestApi/endpoints/EmailApi.ts
1047
+ var EmailApi = class extends AbstractAPI {
1048
+ async verifyEmailVerification(apiKey, params) {
1049
+ this.requireApiKey(apiKey);
1050
+ const { ...restParams } = params;
1051
+ return this.request({
459
1052
  endpoint: "sendOobCode",
460
1053
  method: "POST",
461
1054
  bodyParams: restParams
@@ -861,673 +1454,569 @@ function mergePreDefinedOptions(userOptions = {}) {
861
1454
  // src/tokens/request.ts
862
1455
  var import_ms = require("@tern-secure/shared/ms");
863
1456
 
864
- // src/jwt/customJwt.ts
865
- var import_jose = require("jose");
866
- var CustomTokenError = class extends Error {
867
- constructor(message, code) {
868
- super(message);
869
- this.code = code;
870
- this.name = "CustomTokenError";
871
- }
872
- };
873
- var RESERVED_CLAIMS = [
874
- "acr",
875
- "amr",
876
- "at_hash",
877
- "aud",
878
- "auth_time",
879
- "azp",
880
- "cnf",
881
- "c_hash",
882
- "exp",
883
- "firebase",
884
- "iat",
885
- "iss",
886
- "jti",
887
- "nbf",
888
- "nonce",
889
- "sub"
890
- ];
891
- async function createCustomTokenJwt(uid, developerClaims) {
892
- try {
893
- const privateKey = process.env.FIREBASE_PRIVATE_KEY;
894
- const clientEmail = process.env.FIREBASE_CLIENT_EMAIL;
895
- if (!privateKey || !clientEmail) {
896
- return {
897
- errors: [
898
- new CustomTokenError(
899
- "Missing FIREBASE_PRIVATE_KEY or FIREBASE_CLIENT_EMAIL environment variables",
900
- "MISSING_ENV_VARS"
901
- )
902
- ]
903
- };
904
- }
905
- if (!uid || typeof uid !== "string") {
906
- return {
907
- errors: [new CustomTokenError("uid must be a non-empty string", "INVALID_UID")]
908
- };
909
- }
910
- if (uid.length > 128) {
911
- return {
912
- errors: [new CustomTokenError("uid must not exceed 128 characters", "UID_TOO_LONG")]
913
- };
914
- }
915
- if (developerClaims) {
916
- for (const claim of Object.keys(developerClaims)) {
917
- if (RESERVED_CLAIMS.includes(claim)) {
918
- return {
919
- errors: [new CustomTokenError(`Custom claim '${claim}' is reserved`, "RESERVED_CLAIM")]
920
- };
921
- }
922
- }
923
- }
924
- const expiresIn = 3600;
925
- const now = Math.floor(Date.now() / 1e3);
926
- const parsedPrivateKey = await (0, import_jose.importPKCS8)(privateKey.replace(/\\n/g, "\n"), "RS256");
927
- const payload = {
928
- iss: clientEmail,
929
- sub: clientEmail,
930
- aud: "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit",
931
- iat: now,
932
- exp: now + expiresIn,
933
- uid,
934
- ...developerClaims
935
- };
936
- const jwt = await new import_jose.SignJWT(payload).setProtectedHeader({ alg: "RS256", typ: "JWT" }).setIssuedAt(now).setExpirationTime(now + expiresIn).setIssuer(clientEmail).setSubject(clientEmail).setAudience(
937
- "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit"
938
- ).sign(parsedPrivateKey);
939
- return {
940
- data: jwt
941
- };
942
- } catch (error) {
943
- const message = error instanceof Error ? error.message : "Unknown error occurred";
944
- return {
945
- errors: [
946
- new CustomTokenError(`Failed to create custom token: ${message}`, "TOKEN_CREATION_FAILED")
947
- ]
948
- };
949
- }
950
- }
951
- async function createCustomToken(uid, developerClaims) {
952
- const { data, errors } = await createCustomTokenJwt(uid, developerClaims);
953
- if (errors) {
954
- throw errors[0];
955
- }
956
- return data;
957
- }
958
-
959
- // src/jwt/verifyJwt.ts
960
- var import_jose3 = require("jose");
961
-
962
- // src/utils/errors.ts
963
- var RefreshTokenErrorReason = {
964
- NonEligibleNoCookie: "non-eligible-no-refresh-cookie",
965
- NonEligibleNonGet: "non-eligible-non-get",
966
- InvalidSessionToken: "invalid-session-token",
967
- MissingApiClient: "missing-api-client",
968
- MissingIdToken: "missing-id-token",
969
- MissingSessionToken: "missing-session-token",
970
- MissingRefreshToken: "missing-refresh-token",
971
- ExpiredIdTokenDecodeFailed: "expired-id-token-decode-failed",
972
- ExpiredSessionTokenDecodeFailed: "expired-session-token-decode-failed",
973
- FetchError: "fetch-error"
974
- };
975
- var TokenVerificationErrorReason = {
976
- TokenExpired: "token-expired",
977
- TokenInvalid: "token-invalid",
978
- TokenInvalidAlgorithm: "token-invalid-algorithm",
979
- TokenInvalidAuthorizedParties: "token-invalid-authorized-parties",
980
- TokenInvalidSignature: "token-invalid-signature",
981
- TokenNotActiveYet: "token-not-active-yet",
982
- TokenIatInTheFuture: "token-iat-in-the-future",
983
- TokenVerificationFailed: "token-verification-failed",
984
- InvalidSecretKey: "secret-key-invalid",
985
- LocalJWKMissing: "jwk-local-missing",
986
- RemoteJWKFailedToLoad: "jwk-remote-failed-to-load",
987
- RemoteJWKInvalid: "jwk-remote-invalid",
988
- RemoteJWKMissing: "jwk-remote-missing",
989
- JWKFailedToResolve: "jwk-failed-to-resolve",
990
- JWKKidMismatch: "jwk-kid-mismatch"
991
- };
992
- var TokenVerificationError = class _TokenVerificationError extends Error {
993
- reason;
994
- tokenCarrier;
995
- constructor({
996
- message,
997
- reason
998
- }) {
999
- super(message);
1000
- Object.setPrototypeOf(this, _TokenVerificationError.prototype);
1001
- this.reason = reason;
1002
- this.message = message;
1003
- }
1004
- getFullMessage() {
1005
- return `${[this.message].filter((m) => m).join(" ")} (reason=${this.reason}, token-carrier=${this.tokenCarrier})`;
1006
- }
1007
- };
1008
-
1009
- // src/utils/rfc4648.ts
1010
- var base64url = {
1011
- parse(string, opts) {
1012
- return parse2(string, base64UrlEncoding, opts);
1013
- },
1014
- stringify(data, opts) {
1015
- return stringify(data, base64UrlEncoding, opts);
1016
- }
1017
- };
1018
- var base64UrlEncoding = {
1019
- chars: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_",
1020
- bits: 6
1021
- };
1022
- function parse2(string, encoding, opts = {}) {
1023
- if (!encoding.codes) {
1024
- encoding.codes = {};
1025
- for (let i = 0; i < encoding.chars.length; ++i) {
1026
- encoding.codes[encoding.chars[i]] = i;
1027
- }
1028
- }
1029
- if (!opts.loose && string.length * encoding.bits & 7) {
1030
- throw new SyntaxError("Invalid padding");
1031
- }
1032
- let end = string.length;
1033
- while (string[end - 1] === "=") {
1034
- --end;
1035
- if (!opts.loose && !((string.length - end) * encoding.bits & 7)) {
1036
- throw new SyntaxError("Invalid padding");
1037
- }
1038
- }
1039
- const out = new (opts.out ?? Uint8Array)(end * encoding.bits / 8 | 0);
1040
- let bits = 0;
1041
- let buffer = 0;
1042
- let written = 0;
1043
- for (let i = 0; i < end; ++i) {
1044
- const value = encoding.codes[string[i]];
1045
- if (value === void 0) {
1046
- throw new SyntaxError("Invalid character " + string[i]);
1047
- }
1048
- buffer = buffer << encoding.bits | value;
1049
- bits += encoding.bits;
1050
- if (bits >= 8) {
1051
- bits -= 8;
1052
- out[written++] = 255 & buffer >> bits;
1053
- }
1054
- }
1055
- if (bits >= encoding.bits || 255 & buffer << 8 - bits) {
1056
- throw new SyntaxError("Unexpected end of data");
1057
- }
1058
- return out;
1059
- }
1060
- function stringify(data, encoding, opts = {}) {
1061
- const { pad = true } = opts;
1062
- const mask = (1 << encoding.bits) - 1;
1063
- let out = "";
1064
- let bits = 0;
1065
- let buffer = 0;
1066
- for (let i = 0; i < data.length; ++i) {
1067
- buffer = buffer << 8 | 255 & data[i];
1068
- bits += 8;
1069
- while (bits > encoding.bits) {
1070
- bits -= encoding.bits;
1071
- out += encoding.chars[mask & buffer >> bits];
1072
- }
1073
- }
1074
- if (bits) {
1075
- out += encoding.chars[mask & buffer << encoding.bits - bits];
1076
- }
1077
- if (pad) {
1078
- while (out.length * encoding.bits & 7) {
1079
- out += "=";
1080
- }
1081
- }
1082
- return out;
1083
- }
1084
-
1085
- // src/jwt/cryptoKeys.ts
1086
- var import_jose2 = require("jose");
1087
- async function importKey(key, algorithm) {
1088
- if (typeof key === "object") {
1089
- const result = await (0, import_jose2.importJWK)(key, algorithm);
1090
- if (result instanceof Uint8Array) {
1091
- throw new Error("Unexpected Uint8Array result from JWK import");
1092
- }
1093
- return result;
1094
- }
1095
- const keyString = key.trim();
1096
- if (keyString.includes("-----BEGIN CERTIFICATE-----")) {
1097
- return await (0, import_jose2.importX509)(keyString, algorithm);
1098
- }
1099
- if (keyString.includes("-----BEGIN PUBLIC KEY-----")) {
1100
- return await (0, import_jose2.importSPKI)(keyString, algorithm);
1101
- }
1102
- try {
1103
- return await (0, import_jose2.importSPKI)(keyString, algorithm);
1104
- } catch (error) {
1105
- throw new Error(
1106
- `Unsupported key format. Supported formats: X.509 certificate (PEM), SPKI (PEM), JWK (JSON object or string). Error: ${error}`
1107
- );
1108
- }
1457
+ // src/jwt/guardReturn.ts
1458
+ function createJwtGuard(decodedFn) {
1459
+ return (...args) => {
1460
+ const { data, errors } = decodedFn(...args);
1461
+ if (errors) {
1462
+ throw errors[0];
1463
+ }
1464
+ return data;
1465
+ };
1109
1466
  }
1110
1467
 
1111
- // src/jwt/algorithms.ts
1112
- var algToHash = {
1113
- RS256: "SHA-256",
1114
- RS384: "SHA-384",
1115
- RS512: "SHA-512"
1116
- };
1117
- var algs = Object.keys(algToHash);
1468
+ // src/jwt/jwt.ts
1469
+ var import_jose3 = require("jose");
1118
1470
 
1119
- // src/jwt/verifyContent.ts
1120
- var verifyHeaderKid = (kid) => {
1121
- if (typeof kid === "undefined") {
1122
- return;
1123
- }
1124
- if (typeof kid !== "string") {
1125
- throw new TokenVerificationError({
1126
- reason: TokenVerificationErrorReason.TokenInvalid,
1127
- message: `Invalid JWT kid ${JSON.stringify(kid)}. Expected a string.`
1128
- });
1129
- }
1130
- };
1131
- var verifySubClaim = (sub) => {
1132
- if (typeof sub !== "string") {
1133
- throw new TokenVerificationError({
1134
- reason: TokenVerificationErrorReason.TokenVerificationFailed,
1135
- message: `Subject claim (sub) is required and must be a string. Received ${JSON.stringify(sub)}.`
1136
- });
1137
- }
1138
- };
1139
- var verifyExpirationClaim = (exp, clockSkewInMs) => {
1140
- if (typeof exp !== "number") {
1141
- throw new TokenVerificationError({
1142
- reason: TokenVerificationErrorReason.TokenVerificationFailed,
1143
- message: `Invalid JWT expiry date (exp) claim ${JSON.stringify(exp)}. Expected a number.`
1144
- });
1145
- }
1146
- const currentDate = new Date(Date.now());
1147
- const expiryDate = /* @__PURE__ */ new Date(0);
1148
- expiryDate.setUTCSeconds(exp);
1149
- const expired = expiryDate.getTime() <= currentDate.getTime() - clockSkewInMs;
1150
- if (expired) {
1151
- throw new TokenVerificationError({
1152
- reason: TokenVerificationErrorReason.TokenExpired,
1153
- message: `JWT is expired. Expiry date: ${expiryDate.toUTCString()}, Current date: ${currentDate.toUTCString()}.`
1154
- });
1155
- }
1156
- };
1157
- var verifyIssuedAtClaim = (iat, clockSkewInMs) => {
1158
- if (typeof iat === "undefined") {
1159
- return;
1160
- }
1161
- if (typeof iat !== "number") {
1162
- throw new TokenVerificationError({
1163
- reason: TokenVerificationErrorReason.TokenVerificationFailed,
1164
- message: `Invalid JWT issued at date claim (iat) ${JSON.stringify(iat)}. Expected a number.`
1165
- });
1166
- }
1167
- const currentDate = new Date(Date.now());
1168
- const issuedAtDate = /* @__PURE__ */ new Date(0);
1169
- issuedAtDate.setUTCSeconds(iat);
1170
- const postIssued = issuedAtDate.getTime() > currentDate.getTime() + clockSkewInMs;
1171
- if (postIssued) {
1172
- throw new TokenVerificationError({
1173
- reason: TokenVerificationErrorReason.TokenIatInTheFuture,
1174
- message: `JWT issued at date claim (iat) is in the future. Issued at date: ${issuedAtDate.toUTCString()}; Current date: ${currentDate.toUTCString()};`
1175
- });
1471
+ // src/jwt/customJwt.ts
1472
+ var import_jose4 = require("jose");
1473
+ var CustomTokenError = class extends Error {
1474
+ constructor(message, code) {
1475
+ super(message);
1476
+ this.code = code;
1477
+ this.name = "CustomTokenError";
1176
1478
  }
1177
1479
  };
1178
-
1179
- // src/jwt/verifyJwt.ts
1180
- var DEFAULT_CLOCK_SKEW_IN_MS = 5 * 1e3;
1181
- async function verifySignature(jwt, key) {
1182
- const { header, raw } = jwt;
1183
- const joseAlgorithm = header.alg || "RS256";
1184
- try {
1185
- const publicKey = await importKey(key, joseAlgorithm);
1186
- const { payload } = await (0, import_jose3.jwtVerify)(raw.text, publicKey);
1187
- return { data: payload };
1188
- } catch (error) {
1189
- return {
1190
- errors: [
1191
- new TokenVerificationError({
1192
- reason: TokenVerificationErrorReason.TokenInvalidSignature,
1193
- message: error.message
1194
- })
1195
- ]
1196
- };
1197
- }
1198
- }
1199
- function ternDecodeJwt(token) {
1480
+ var RESERVED_CLAIMS = [
1481
+ "acr",
1482
+ "amr",
1483
+ "at_hash",
1484
+ "aud",
1485
+ "auth_time",
1486
+ "azp",
1487
+ "cnf",
1488
+ "c_hash",
1489
+ "exp",
1490
+ "firebase",
1491
+ "iat",
1492
+ "iss",
1493
+ "jti",
1494
+ "nbf",
1495
+ "nonce",
1496
+ "sub"
1497
+ ];
1498
+ async function createCustomTokenJwt(uid, developerClaims) {
1200
1499
  try {
1201
- const header = (0, import_jose3.decodeProtectedHeader)(token);
1202
- const payload = (0, import_jose3.decodeJwt)(token);
1203
- const tokenParts = (token || "").toString().split(".");
1204
- if (tokenParts.length !== 3) {
1500
+ const privateKey = process.env.FIREBASE_PRIVATE_KEY;
1501
+ const clientEmail = process.env.FIREBASE_CLIENT_EMAIL;
1502
+ if (!privateKey || !clientEmail) {
1205
1503
  return {
1206
1504
  errors: [
1207
- new TokenVerificationError({
1208
- reason: TokenVerificationErrorReason.TokenInvalid,
1209
- message: "Invalid JWT format"
1210
- })
1505
+ new CustomTokenError(
1506
+ "Missing FIREBASE_PRIVATE_KEY or FIREBASE_CLIENT_EMAIL environment variables",
1507
+ "MISSING_ENV_VARS"
1508
+ )
1211
1509
  ]
1212
1510
  };
1213
1511
  }
1214
- const [rawHeader, rawPayload, rawSignature] = tokenParts;
1215
- const signature = base64url.parse(rawSignature, { loose: true });
1216
- const data = {
1217
- header,
1218
- payload,
1219
- signature,
1220
- raw: {
1221
- header: rawHeader,
1222
- payload: rawPayload,
1223
- signature: rawSignature,
1224
- text: token
1512
+ if (!uid || typeof uid !== "string") {
1513
+ return {
1514
+ errors: [new CustomTokenError("uid must be a non-empty string", "INVALID_UID")]
1515
+ };
1516
+ }
1517
+ if (uid.length > 128) {
1518
+ return {
1519
+ errors: [new CustomTokenError("uid must not exceed 128 characters", "UID_TOO_LONG")]
1520
+ };
1521
+ }
1522
+ if (developerClaims) {
1523
+ for (const claim of Object.keys(developerClaims)) {
1524
+ if (RESERVED_CLAIMS.includes(claim)) {
1525
+ return {
1526
+ errors: [new CustomTokenError(`Custom claim '${claim}' is reserved`, "RESERVED_CLAIM")]
1527
+ };
1528
+ }
1225
1529
  }
1530
+ }
1531
+ const expiresIn = 3600;
1532
+ const now = Math.floor(Date.now() / 1e3);
1533
+ const parsedPrivateKey = await (0, import_jose4.importPKCS8)(privateKey.replace(/\\n/g, "\n"), "RS256");
1534
+ const payload = {
1535
+ iss: clientEmail,
1536
+ sub: clientEmail,
1537
+ aud: "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit",
1538
+ iat: now,
1539
+ exp: now + expiresIn,
1540
+ uid,
1541
+ ...developerClaims
1542
+ };
1543
+ const jwt = await new import_jose4.SignJWT(payload).setProtectedHeader({ alg: "RS256", typ: "JWT" }).setIssuedAt(now).setExpirationTime(now + expiresIn).setIssuer(clientEmail).setSubject(clientEmail).setAudience(
1544
+ "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit"
1545
+ ).sign(parsedPrivateKey);
1546
+ return {
1547
+ data: jwt
1226
1548
  };
1227
- return { data };
1228
1549
  } catch (error) {
1550
+ const message = error instanceof Error ? error.message : "Unknown error occurred";
1229
1551
  return {
1230
1552
  errors: [
1231
- new TokenVerificationError({
1232
- reason: TokenVerificationErrorReason.TokenInvalid,
1233
- message: `${error.message || "Invalid Token or Protected Header formatting"} (Token length: ${token?.length}, First 10 chars: ${token?.substring(0, 10)}...)`
1234
- })
1553
+ new CustomTokenError(`Failed to create custom token: ${message}`, "TOKEN_CREATION_FAILED")
1235
1554
  ]
1236
1555
  };
1237
1556
  }
1238
1557
  }
1239
- async function verifyJwt(token, options) {
1240
- const { key } = options;
1241
- const clockSkew = options.clockSkewInMs || DEFAULT_CLOCK_SKEW_IN_MS;
1242
- const { data: decoded, errors } = ternDecodeJwt(token);
1558
+ async function createCustomToken(uid, developerClaims) {
1559
+ const { data, errors } = await createCustomTokenJwt(uid, developerClaims);
1243
1560
  if (errors) {
1244
- return { errors };
1245
- }
1246
- const { header, payload } = decoded;
1247
- try {
1248
- verifyHeaderKid(header.kid);
1249
- verifySubClaim(payload.sub);
1250
- verifyExpirationClaim(payload.exp, clockSkew);
1251
- verifyIssuedAtClaim(payload.iat, clockSkew);
1252
- } catch (error) {
1253
- return { errors: [error] };
1254
- }
1255
- const { data: verifiedPayload, errors: signatureErrors } = await verifySignature(decoded, key);
1256
- if (signatureErrors) {
1257
- return {
1258
- errors: [
1259
- new TokenVerificationError({
1260
- reason: TokenVerificationErrorReason.TokenInvalidSignature,
1261
- message: "Token signature verification failed."
1262
- })
1263
- ]
1264
- };
1561
+ throw errors[0];
1265
1562
  }
1266
- const decodedIdToken = mapJwtPayloadToDecodedIdToken(verifiedPayload);
1267
- return { data: decodedIdToken };
1563
+ return data;
1268
1564
  }
1269
1565
 
1270
- // src/tokens/keys.ts
1271
- var cache = {};
1272
- var lastUpdatedAt = 0;
1273
- var googleExpiresAt = 0;
1274
- function getFromCache(kid) {
1275
- return cache[kid];
1566
+ // src/jwt/signJwt.ts
1567
+ var import_jose5 = require("jose");
1568
+
1569
+ // src/utils/fetcher.ts
1570
+ async function getDetailFromResponse(response) {
1571
+ const json = await response.json();
1572
+ if (!json) {
1573
+ return "Missing error payload";
1574
+ }
1575
+ let detail = typeof json.error === "string" ? json.error : json.error?.message ?? "Missing error payload";
1576
+ if (json.error_description) {
1577
+ detail += " (" + json.error_description + ")";
1578
+ }
1579
+ return detail;
1276
1580
  }
1277
- function getCacheValues() {
1278
- return Object.values(cache);
1581
+ async function fetchText(url, init) {
1582
+ return (await fetchAny(url, init)).text();
1279
1583
  }
1280
- function setInCache(kid, certificate, shouldExpire = true) {
1281
- cache[kid] = certificate;
1282
- lastUpdatedAt = shouldExpire ? Date.now() : -1;
1584
+ async function fetchJson(url, init) {
1585
+ return (await fetchAny(url, init)).json();
1283
1586
  }
1284
- async function fetchPublicKeys(keyUrl) {
1285
- const url = new URL(keyUrl);
1286
- const response = await fetch(url);
1587
+ async function fetchAny(url, init) {
1588
+ const response = await fetch(url, init);
1287
1589
  if (!response.ok) {
1590
+ throw new Error(await getDetailFromResponse(response));
1591
+ }
1592
+ return response;
1593
+ }
1594
+
1595
+ // src/jwt/types.ts
1596
+ var ALGORITHM_RS256 = "RS256";
1597
+
1598
+ // src/jwt/signJwt.ts
1599
+ async function ternSignJwt(opts) {
1600
+ const { payload, privateKey, keyId } = opts;
1601
+ let key;
1602
+ try {
1603
+ key = await (0, import_jose5.importPKCS8)(privateKey, ALGORITHM_RS256);
1604
+ } catch (error) {
1288
1605
  throw new TokenVerificationError({
1289
- message: `Error loading public keys from ${url.href} with code=${response.status} `,
1606
+ message: `Failed to import private key: ${error.message}`,
1290
1607
  reason: TokenVerificationErrorReason.TokenInvalid
1291
1608
  });
1292
1609
  }
1293
- const data = await response.json();
1294
- const expiresAt = getExpiresAt(response);
1295
- return {
1296
- keys: data,
1297
- expiresAt
1298
- };
1610
+ return new import_jose5.SignJWT(payload).setProtectedHeader({ alg: ALGORITHM_RS256, kid: keyId }).sign(key);
1299
1611
  }
1300
- async function loadJWKFromRemote({
1301
- keyURL = GOOGLE_PUBLIC_KEYS_URL,
1302
- skipJwksCache,
1303
- kid
1612
+ function formatBase64(value) {
1613
+ return value.replace(/\//g, "_").replace(/\+/g, "-").replace(/=+$/, "");
1614
+ }
1615
+ function encodeSegment(segment) {
1616
+ const value = JSON.stringify(segment);
1617
+ return formatBase64(import_jose5.base64url.encode(value));
1618
+ }
1619
+ async function ternSignBlob({
1620
+ payload,
1621
+ serviceAccountId,
1622
+ accessToken
1304
1623
  }) {
1305
- if (skipJwksCache || isCacheExpired() || !getFromCache(kid)) {
1306
- const { keys, expiresAt } = await fetchPublicKeys(keyURL);
1307
- if (!keys || Object.keys(keys).length === 0) {
1308
- throw new TokenVerificationError({
1309
- message: `The JWKS endpoint ${keyURL} returned no keys`,
1310
- reason: TokenVerificationErrorReason.RemoteJWKFailedToLoad
1311
- });
1624
+ const url = `https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/${serviceAccountId}:signBlob`;
1625
+ const header = {
1626
+ alg: ALGORITHM_RS256,
1627
+ typ: "JWT"
1628
+ };
1629
+ const token = `${encodeSegment(header)}.${encodeSegment(payload)}`;
1630
+ const request = {
1631
+ method: "POST",
1632
+ headers: {
1633
+ Authorization: `Bearer ${accessToken}`
1634
+ },
1635
+ body: JSON.stringify({ payload: import_jose5.base64url.encode(token) })
1636
+ };
1637
+ const response = await fetchAny(url, request);
1638
+ const blob = await response.blob();
1639
+ const key = await blob.text();
1640
+ const { signedBlob } = JSON.parse(key);
1641
+ return `${token}.${formatBase64(signedBlob)}`;
1642
+ }
1643
+
1644
+ // src/jwt/crypto-signer.ts
1645
+ var ServiceAccountSigner = class {
1646
+ constructor(credential, tenantId) {
1647
+ this.credential = credential;
1648
+ this.tenantId = tenantId;
1649
+ }
1650
+ async getAccountId() {
1651
+ return Promise.resolve(this.credential.clientEmail);
1652
+ }
1653
+ async sign(payload) {
1654
+ if (this.tenantId) {
1655
+ payload.tenant_id = this.tenantId;
1312
1656
  }
1313
- googleExpiresAt = expiresAt;
1314
- Object.entries(keys).forEach(([keyId, cert2]) => {
1315
- setInCache(keyId, cert2);
1316
- });
1657
+ return ternSignJwt({ payload, privateKey: this.credential.privateKey });
1317
1658
  }
1318
- const cert = getFromCache(kid);
1319
- if (!cert) {
1320
- getCacheValues();
1321
- const availableKids = Object.keys(cache).sort().join(", ");
1322
- throw new TokenVerificationError({
1323
- message: `No public key found for kid "${kid}". Available kids: [${availableKids}]`,
1324
- reason: TokenVerificationErrorReason.TokenInvalid
1659
+ };
1660
+ var IAMSigner = class {
1661
+ algorithm = ALGORITHM_RS256;
1662
+ credential;
1663
+ tenantId;
1664
+ serviceAccountId;
1665
+ constructor(credential, tenantId, serviceAccountId) {
1666
+ this.credential = credential;
1667
+ this.tenantId = tenantId;
1668
+ this.serviceAccountId = serviceAccountId;
1669
+ }
1670
+ async sign(payload) {
1671
+ if (this.tenantId) {
1672
+ payload.tenant_id = this.tenantId;
1673
+ }
1674
+ const serviceAccount = await this.getAccountId();
1675
+ const accessToken = await this.credential.getAccessToken();
1676
+ return ternSignBlob({
1677
+ accessToken: accessToken.accessToken,
1678
+ serviceAccountId: serviceAccount,
1679
+ payload
1325
1680
  });
1326
1681
  }
1327
- return cert;
1328
- }
1329
- function isCacheExpired() {
1330
- const now = Date.now();
1331
- if (lastUpdatedAt === -1) {
1332
- return false;
1333
- }
1334
- const cacheAge = now - lastUpdatedAt;
1335
- const maxCacheAge = MAX_CACHE_LAST_UPDATED_AT_SECONDS * 1e3;
1336
- const localCacheExpired = cacheAge >= maxCacheAge;
1337
- const googleCacheExpired = now >= googleExpiresAt;
1338
- const isExpired = localCacheExpired || googleCacheExpired;
1339
- if (isExpired) {
1340
- cache = {};
1682
+ async getAccountId() {
1683
+ if (this.serviceAccountId) {
1684
+ return this.serviceAccountId;
1685
+ }
1686
+ const token = await this.credential.getAccessToken();
1687
+ const url = "http://metadata/computeMetadata/v1/instance/service-accounts/default/email";
1688
+ const request = {
1689
+ method: "GET",
1690
+ headers: {
1691
+ "Metadata-Flavor": "Google",
1692
+ Authorization: `Bearer ${token.accessToken}`
1693
+ }
1694
+ };
1695
+ return this.serviceAccountId = await fetchText(url, request);
1341
1696
  }
1342
- return isExpired;
1343
- }
1344
- function getExpiresAt(res) {
1345
- const cacheControlHeader = res.headers.get("cache-control");
1346
- if (!cacheControlHeader) {
1347
- return Date.now() + DEFAULT_CACHE_DURATION;
1697
+ };
1698
+
1699
+ // src/jwt/index.ts
1700
+ var ternDecodeJwt2 = createJwtGuard(ternDecodeJwt);
1701
+
1702
+ // src/utils/token-generator.ts
1703
+ function cryptoSignerFromCredential(credential, tenantId, serviceAccountId) {
1704
+ if (credential instanceof ServiceAccountManager) {
1705
+ return new ServiceAccountSigner(credential, tenantId);
1348
1706
  }
1349
- const maxAgeMatch = cacheControlHeader.match(CACHE_CONTROL_REGEX);
1350
- const maxAge = maxAgeMatch ? parseInt(maxAgeMatch[1], 10) : DEFAULT_CACHE_DURATION / 1e3;
1351
- return Date.now() + maxAge * 1e3;
1707
+ return new IAMSigner(credential, tenantId, serviceAccountId);
1352
1708
  }
1353
1709
 
1354
- // src/tokens/verify.ts
1355
- async function verifyToken(token, options) {
1356
- const { data: decodedResult, errors } = ternDecodeJwt(token);
1357
- if (errors) {
1358
- return { errors };
1710
+ // src/app-check/AppCheckApi.ts
1711
+ function getSdkVersion2() {
1712
+ return "12.7.0";
1713
+ }
1714
+ var FIREBASE_APP_CHECK_CONFIG_HEADERS2 = {
1715
+ "X-Firebase-Client": `fire-admin-node/${getSdkVersion2()}`
1716
+ };
1717
+ var AppCheckApi2 = class {
1718
+ constructor(credential) {
1719
+ this.credential = credential;
1359
1720
  }
1360
- const { header } = decodedResult;
1361
- const { kid } = header;
1362
- if (!kid) {
1363
- return {
1364
- errors: [
1365
- new TokenVerificationError({
1366
- reason: TokenVerificationErrorReason.TokenInvalid,
1367
- message: 'JWT "kid" header is missing.'
1368
- })
1369
- ]
1721
+ async exchangeToken(params) {
1722
+ const { projectId, appId, customToken, limitedUse = false } = params;
1723
+ const token = await this.credential.getAccessToken(false);
1724
+ if (!projectId || !appId) {
1725
+ throw new Error("Project ID and App ID are required for App Check token exchange");
1726
+ }
1727
+ const endpoint = `https://firebaseappcheck.googleapis.com/v1/projects/${projectId}/apps/${appId}:exchangeCustomToken`;
1728
+ const headers = {
1729
+ "Content-Type": "application/json",
1730
+ "Authorization": `Bearer ${token.accessToken}`
1370
1731
  };
1371
- }
1372
- try {
1373
- const key = options.jwtKey || await loadJWKFromRemote({ ...options, kid });
1374
- if (!key) {
1732
+ try {
1733
+ const response = await fetch(endpoint, {
1734
+ method: "POST",
1735
+ headers,
1736
+ body: JSON.stringify({ customToken, limitedUse })
1737
+ });
1738
+ if (!response.ok) {
1739
+ const errorText = await response.text();
1740
+ throw new Error(`App Check token exchange failed: ${response.status} ${errorText}`);
1741
+ }
1742
+ const data = await response.json();
1375
1743
  return {
1376
- errors: [
1377
- new TokenVerificationError({
1378
- reason: TokenVerificationErrorReason.TokenInvalid,
1379
- message: `No public key found for kid "${kid}".`
1380
- })
1381
- ]
1744
+ token: data.token,
1745
+ ttl: data.ttl
1382
1746
  };
1747
+ } catch (error) {
1748
+ console.warn("[ternsecure - appcheck api]unexpected error:", error);
1749
+ throw error;
1383
1750
  }
1384
- return await verifyJwt(token, { ...options, key });
1385
- } catch (error) {
1386
- if (error instanceof TokenVerificationError) {
1387
- return { errors: [error] };
1751
+ }
1752
+ async exchangeDebugToken(params) {
1753
+ const { projectId, appId, customToken, accessToken, limitedUse = false } = params;
1754
+ if (!projectId || !appId) {
1755
+ throw new Error("Project ID and App ID are required for App Check token exchange");
1388
1756
  }
1389
- return {
1390
- errors: [error]
1757
+ const endpoint = `https://firebaseappcheck.googleapis.com/v1beta/projects/${projectId}/apps/${appId}:exchangeDebugToken`;
1758
+ const headers = {
1759
+ ...FIREBASE_APP_CHECK_CONFIG_HEADERS2,
1760
+ "Authorization": `Bearer ${accessToken}`
1761
+ };
1762
+ const body = {
1763
+ customToken,
1764
+ limitedUse
1391
1765
  };
1766
+ try {
1767
+ const response = await fetch(endpoint, {
1768
+ method: "POST",
1769
+ headers,
1770
+ body: JSON.stringify(body)
1771
+ });
1772
+ if (!response.ok) {
1773
+ const errorText = await response.text();
1774
+ throw new Error(`App Check token exchange failed: ${response.status} ${errorText}`);
1775
+ }
1776
+ const data = await response.json();
1777
+ return {
1778
+ token: data.token,
1779
+ ttl: data.ttl
1780
+ };
1781
+ } catch (error) {
1782
+ console.warn("[ternsecure - appcheck api]unexpected error:", error);
1783
+ throw error;
1784
+ }
1392
1785
  }
1786
+ };
1787
+
1788
+ // src/app-check/generator.ts
1789
+ function transformMillisecondsToSecondsString(milliseconds) {
1790
+ let duration;
1791
+ const seconds = Math.floor(milliseconds / 1e3);
1792
+ const nanos = Math.floor((milliseconds - seconds * 1e3) * 1e6);
1793
+ if (nanos > 0) {
1794
+ let nanoString = nanos.toString();
1795
+ while (nanoString.length < 9) {
1796
+ nanoString = "0" + nanoString;
1797
+ }
1798
+ duration = `${seconds}.${nanoString}s`;
1799
+ } else {
1800
+ duration = `${seconds}s`;
1801
+ }
1802
+ return duration;
1393
1803
  }
1804
+ var AppCheckTokenGenerator = class {
1805
+ signer;
1806
+ constructor(signer) {
1807
+ this.signer = signer;
1808
+ }
1809
+ async createCustomToken(appId, options) {
1810
+ if (!appId) {
1811
+ throw new Error(
1812
+ "appId is invalid"
1813
+ );
1814
+ }
1815
+ let customOptions = {};
1816
+ if (typeof options !== "undefined") {
1817
+ customOptions = this.validateTokenOptions(options);
1818
+ }
1819
+ const account = await this.signer.getAccountId();
1820
+ const iat = Math.floor(Date.now() / 1e3);
1821
+ const body = {
1822
+ iss: account,
1823
+ sub: account,
1824
+ app_id: appId,
1825
+ aud: FIREBASE_APP_CHECK_AUDIENCE,
1826
+ exp: iat + ONE_MINUTE_IN_SECONDS * 5,
1827
+ iat,
1828
+ ...customOptions
1829
+ };
1830
+ return this.signer.sign(body);
1831
+ }
1832
+ validateTokenOptions(options) {
1833
+ if (typeof options.ttlMillis !== "undefined") {
1834
+ if (options.ttlMillis < ONE_MINUTE_IN_MILLIS * 30 || options.ttlMillis > ONE_DAY_IN_MILLIS * 7) {
1835
+ throw new Error(
1836
+ "ttlMillis must be a duration in milliseconds between 30 minutes and 7 days (inclusive)."
1837
+ );
1838
+ }
1839
+ return { ttl: transformMillisecondsToSecondsString(options.ttlMillis) };
1840
+ }
1841
+ return {};
1842
+ }
1843
+ };
1394
1844
 
1395
- // src/jwt/guardReturn.ts
1396
- function createJwtGuard(decodedFn) {
1397
- return (...args) => {
1398
- const { data, errors } = decodedFn(...args);
1399
- if (errors) {
1400
- throw errors[0];
1845
+ // src/app-check/serverAppCheck.ts
1846
+ var import_redis = require("@upstash/redis");
1847
+
1848
+ // src/admin/sessionTernSecure.ts
1849
+ var import_errors6 = require("@tern-secure/shared/errors");
1850
+
1851
+ // src/utils/admin-init.ts
1852
+ var import_firebase_admin = __toESM(require("firebase-admin"));
1853
+ var import_app_check = require("firebase-admin/app-check");
1854
+
1855
+ // src/utils/config.ts
1856
+ var loadAdminConfig = () => ({
1857
+ projectId: process.env.FIREBASE_PROJECT_ID || "",
1858
+ clientEmail: process.env.FIREBASE_CLIENT_EMAIL || "",
1859
+ privateKey: process.env.FIREBASE_PRIVATE_KEY || ""
1860
+ });
1861
+ var validateAdminConfig = (config) => {
1862
+ const requiredFields = [
1863
+ "projectId",
1864
+ "clientEmail",
1865
+ "privateKey"
1866
+ ];
1867
+ const errors = [];
1868
+ requiredFields.forEach((field) => {
1869
+ if (!config[field]) {
1870
+ errors.push(`Missing required field: FIREBASE_${String(field).toUpperCase()}`);
1401
1871
  }
1402
- return data;
1872
+ });
1873
+ return {
1874
+ isValid: errors.length === 0,
1875
+ errors,
1876
+ config
1403
1877
  };
1404
- }
1405
-
1406
- // src/jwt/jwt.ts
1407
- var import_jose4 = require("jose");
1878
+ };
1879
+ var initializeAdminConfig = () => {
1880
+ const config = loadAdminConfig();
1881
+ const validationResult = validateAdminConfig(config);
1882
+ if (!validationResult.isValid) {
1883
+ throw new Error(
1884
+ `Firebase Admin configuration validation failed:
1885
+ ${validationResult.errors.join("\n")}`
1886
+ );
1887
+ }
1888
+ return config;
1889
+ };
1408
1890
 
1409
- // src/jwt/signJwt.ts
1410
- var import_jose5 = require("jose");
1411
- var ALGORITHM_RS256 = "RS256";
1412
- async function ternSignJwt(opts) {
1413
- const { payload, privateKey, keyId } = opts;
1414
- let key;
1891
+ // src/utils/admin-init.ts
1892
+ if (!import_firebase_admin.default.apps.length) {
1415
1893
  try {
1416
- key = await (0, import_jose5.importPKCS8)(privateKey, ALGORITHM_RS256);
1417
- } catch (error) {
1418
- throw new TokenVerificationError({
1419
- message: `Failed to import private key: ${error.message}`,
1420
- reason: TokenVerificationErrorReason.TokenInvalid
1894
+ const config = initializeAdminConfig();
1895
+ import_firebase_admin.default.initializeApp({
1896
+ credential: import_firebase_admin.default.credential.cert({
1897
+ ...config,
1898
+ privateKey: config.privateKey.replace(/\\n/g, "\n")
1899
+ })
1421
1900
  });
1901
+ } catch (error) {
1902
+ console.error("Firebase admin initialization error", error);
1422
1903
  }
1423
- return new import_jose5.SignJWT(payload).setProtectedHeader({ alg: ALGORITHM_RS256, kid: keyId }).sign(key);
1424
1904
  }
1905
+ var adminTernSecureAuth = import_firebase_admin.default.auth();
1906
+ var adminTernSecureDb = import_firebase_admin.default.firestore();
1907
+ var TernSecureTenantManager = import_firebase_admin.default.auth().tenantManager();
1908
+ var appCheckAdmin = (0, import_app_check.getAppCheck)();
1425
1909
 
1426
- // src/jwt/index.ts
1427
- var ternDecodeJwt2 = createJwtGuard(ternDecodeJwt);
1910
+ // src/admin/sessionTernSecure.ts
1911
+ var DEFAULT_COOKIE_CONFIG = {
1912
+ DEFAULT_EXPIRES_IN_MS: 5 * 60 * 1e3,
1913
+ // 5 minutes
1914
+ DEFAULT_EXPIRES_IN_SECONDS: 5 * 60,
1915
+ REVOKE_REFRESH_TOKENS_ON_SIGNOUT: true
1916
+ };
1917
+ var DEFAULT_COOKIE_OPTIONS = {
1918
+ httpOnly: true,
1919
+ secure: process.env.NODE_ENV === "production",
1920
+ sameSite: "strict",
1921
+ path: "/"
1922
+ };
1428
1923
 
1429
- // src/auth/constants.ts
1430
- var TOKEN_EXPIRY_THRESHOLD_MILLIS = 5 * 60 * 1e3;
1431
- var GOOGLE_TOKEN_AUDIENCE = "https://accounts.google.com/o/oauth2/token";
1432
- var GOOGLE_AUTH_TOKEN_HOST = "accounts.google.com";
1433
- var GOOGLE_AUTH_TOKEN_PATH = "/o/oauth2/token";
1434
- var ONE_HOUR_IN_SECONDS = 60 * 60;
1924
+ // src/admin/nextSessionTernSecure.ts
1925
+ var import_cookie2 = require("@tern-secure/shared/cookie");
1926
+ var import_errors7 = require("@tern-secure/shared/errors");
1927
+ var import_headers = require("next/headers");
1928
+ var SESSION_CONSTANTS = {
1929
+ COOKIE_NAME: constants.Cookies.Session,
1930
+ DEFAULT_EXPIRES_IN_MS: 60 * 60 * 24 * 5 * 1e3,
1931
+ // 5 days
1932
+ DEFAULT_EXPIRES_IN_SECONDS: 60 * 60 * 24 * 5,
1933
+ REVOKE_REFRESH_TOKENS_ON_SIGNOUT: true
1934
+ };
1435
1935
 
1436
- // src/auth/utils.ts
1437
- async function getDetailFromResponse(response) {
1438
- const json = await response.json();
1439
- if (!json) {
1440
- return "Missing error payload";
1936
+ // src/admin/user.ts
1937
+ var import_errors8 = require("@tern-secure/shared/errors");
1938
+
1939
+ // src/app-check/verifier.ts
1940
+ var import_jose6 = require("jose");
1941
+ var getPublicKey = async (header, keyURL) => {
1942
+ const jswksUrl = new URL(keyURL);
1943
+ const getKey = (0, import_jose6.createRemoteJWKSet)(jswksUrl);
1944
+ return getKey(header);
1945
+ };
1946
+ var verifyAppCheckToken = async (token, options) => {
1947
+ const { data: decodedResult, errors } = ternDecodeJwt(token);
1948
+ if (errors) {
1949
+ throw errors[0];
1441
1950
  }
1442
- let detail = typeof json.error === "string" ? json.error : json.error?.message ?? "Missing error payload";
1443
- if (json.error_description) {
1444
- detail += " (" + json.error_description + ")";
1951
+ const { header } = decodedResult;
1952
+ const { kid } = header;
1953
+ if (!kid) {
1954
+ return {
1955
+ errors: [
1956
+ new TokenVerificationError({
1957
+ reason: TokenVerificationErrorReason.TokenInvalid,
1958
+ message: 'JWT "kid" header is missing.'
1959
+ })
1960
+ ]
1961
+ };
1445
1962
  }
1446
- return detail;
1447
- }
1448
- async function fetchJson(url, init) {
1449
- return (await fetchAny(url, init)).json();
1450
- }
1451
- async function fetchAny(url, init) {
1452
- const response = await fetch(url, init);
1453
- if (!response.ok) {
1454
- throw new Error(await getDetailFromResponse(response));
1963
+ try {
1964
+ const getPublicKeyForToken = () => getPublicKey(header, options.keyURL || "");
1965
+ return await verifyAppCheckJwt(token, { ...options, key: getPublicKeyForToken });
1966
+ } catch (error) {
1967
+ if (error instanceof TokenVerificationError) {
1968
+ return { errors: [error] };
1969
+ }
1970
+ return {
1971
+ errors: [error]
1972
+ };
1455
1973
  }
1456
- return response;
1457
- }
1458
-
1459
- // src/auth/credential.ts
1460
- var accessTokenCache = /* @__PURE__ */ new Map();
1461
- async function requestAccessToken(urlString, init) {
1462
- const json = await fetchJson(urlString, init);
1463
- if (!json.access_token || !json.expires_in) {
1464
- throw new Error("Invalid access token response");
1974
+ };
1975
+ var AppcheckTokenVerifier = class {
1976
+ constructor(credential) {
1977
+ this.credential = credential;
1465
1978
  }
1466
- return {
1467
- accessToken: json.access_token,
1468
- expirationTime: Date.now() + json.expires_in * 1e3
1979
+ verifyToken = async (token, projectId, options) => {
1980
+ const { data, errors } = await verifyAppCheckToken(token, options);
1981
+ if (errors) {
1982
+ throw errors[0];
1983
+ }
1984
+ return data;
1469
1985
  };
1470
- }
1471
- var ServiceAccountTokenManager = class {
1472
- projectId;
1473
- privateKey;
1474
- clientEmail;
1475
- constructor(serviceAccount) {
1476
- this.projectId = serviceAccount.projectId;
1477
- this.privateKey = serviceAccount.privateKey;
1478
- this.clientEmail = serviceAccount.clientEmail;
1986
+ };
1987
+
1988
+ // src/app-check/index.ts
1989
+ var JWKS_URL = "https://firebaseappcheck.googleapis.com/v1/jwks";
1990
+ var AppCheck = class {
1991
+ client;
1992
+ tokenGenerator;
1993
+ appCheckTokenVerifier;
1994
+ limitedUse;
1995
+ constructor(credential, tenantId, limitedUse) {
1996
+ this.client = new AppCheckApi2(credential);
1997
+ this.tokenGenerator = new AppCheckTokenGenerator(
1998
+ cryptoSignerFromCredential(credential, tenantId)
1999
+ );
2000
+ this.appCheckTokenVerifier = new AppcheckTokenVerifier(credential);
2001
+ this.limitedUse = limitedUse;
1479
2002
  }
1480
- fetchAccessToken = async (url) => {
1481
- const token = await this.createJwt();
1482
- const postData = "grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=" + token;
1483
- return requestAccessToken(url, {
1484
- method: "POST",
1485
- headers: {
1486
- "Content-Type": "application/x-www-form-urlencoded",
1487
- Authorization: `Bearer ${token}`,
1488
- Accept: "application/json"
1489
- },
1490
- body: postData
2003
+ createToken = (projectId, appId, options) => {
2004
+ return this.tokenGenerator.createCustomToken(appId, options).then((customToken) => {
2005
+ return this.client.exchangeToken({ customToken, projectId, appId });
1491
2006
  });
1492
2007
  };
1493
- fetchAndCacheAccessToken = async (url) => {
1494
- const accessToken = await this.fetchAccessToken(url);
1495
- accessTokenCache.set(this.projectId, accessToken);
1496
- return accessToken;
1497
- };
1498
- getAccessToken = async (refresh) => {
1499
- const url = `https://${GOOGLE_AUTH_TOKEN_HOST}${GOOGLE_AUTH_TOKEN_PATH}`;
1500
- if (refresh) {
1501
- return this.fetchAndCacheAccessToken(url);
1502
- }
1503
- const cachedResponse = accessTokenCache.get(this.projectId);
1504
- if (!cachedResponse || cachedResponse.expirationTime - Date.now() <= TOKEN_EXPIRY_THRESHOLD_MILLIS) {
1505
- return this.fetchAndCacheAccessToken(url);
1506
- }
1507
- return cachedResponse;
1508
- };
1509
- createJwt = async () => {
1510
- const iat = Math.floor(Date.now() / 1e3);
1511
- const payload = {
1512
- aud: GOOGLE_TOKEN_AUDIENCE,
1513
- iat,
1514
- exp: iat + ONE_HOUR_IN_SECONDS,
1515
- iss: this.clientEmail,
1516
- sub: this.clientEmail,
1517
- scope: [
1518
- "https://www.googleapis.com/auth/cloud-platform",
1519
- "https://www.googleapis.com/auth/firebase.database",
1520
- "https://www.googleapis.com/auth/firebase.messaging",
1521
- "https://www.googleapis.com/auth/identitytoolkit",
1522
- "https://www.googleapis.com/auth/userinfo.email"
1523
- ].join(" ")
1524
- };
1525
- return ternSignJwt({
1526
- payload,
1527
- privateKey: this.privateKey
2008
+ verifyToken = async (appCheckToken, projectId, options) => {
2009
+ return this.appCheckTokenVerifier.verifyToken(appCheckToken, projectId, { keyURL: JWKS_URL, ...options }).then((decodedToken) => {
2010
+ return {
2011
+ appId: decodedToken.app_id,
2012
+ token: decodedToken
2013
+ };
1528
2014
  });
1529
2015
  };
1530
2016
  };
2017
+ function getAppCheck2(serviceAccount, tenantId, limitedUse) {
2018
+ return new AppCheck(new ServiceAccountManager(serviceAccount), tenantId, limitedUse);
2019
+ }
1531
2020
 
1532
2021
  // src/auth/getauth.ts
1533
2022
  var API_KEY_ERROR = "API Key is required";
@@ -1543,17 +2032,8 @@ function parseFirebaseResponse(data) {
1543
2032
  return data;
1544
2033
  }
1545
2034
  function getAuth(options) {
1546
- const { apiKey, firebaseAdminConfig } = options;
1547
- const firebaseApiKey = options.firebaseConfig?.apiKey;
1548
- const effectiveApiKey = apiKey || firebaseApiKey;
1549
- let credential = null;
1550
- if (firebaseAdminConfig?.projectId && firebaseAdminConfig?.privateKey && firebaseAdminConfig?.clientEmail) {
1551
- credential = new ServiceAccountTokenManager({
1552
- projectId: firebaseAdminConfig.projectId,
1553
- privateKey: firebaseAdminConfig.privateKey,
1554
- clientEmail: firebaseAdminConfig.clientEmail
1555
- });
1556
- }
2035
+ const { apiKey } = options;
2036
+ const effectiveApiKey = apiKey || process.env.NEXT_PUBLIC_FIREBASE_API_KEY;
1557
2037
  async function getUserData(idToken, localId) {
1558
2038
  if (!effectiveApiKey) {
1559
2039
  throw new Error(API_KEY_ERROR);
@@ -1638,43 +2118,12 @@ function getAuth(options) {
1638
2118
  auth_time: decodedCustomIdToken.data.auth_time
1639
2119
  };
1640
2120
  }
1641
- async function exchangeAppCheckToken(idToken) {
1642
- if (!credential) {
1643
- return {
1644
- data: null,
1645
- error: new Error(
1646
- "Firebase Admin config must be provided to exchange App Check tokens."
1647
- )
1648
- };
1649
- }
1650
- if (!effectiveApiKey) {
1651
- return { data: null, error: new Error(API_KEY_ERROR) };
1652
- }
2121
+ async function createAppCheckToken() {
2122
+ const adminConfig = loadAdminConfig();
2123
+ const appId = process.env.NEXT_PUBLIC_FIREBASE_APP_ID || "";
2124
+ const appCheck = getAppCheck2(adminConfig, options.tenantId);
1653
2125
  try {
1654
- const decoded = await verifyToken(idToken, options);
1655
- if (decoded.errors) {
1656
- return { data: null, error: decoded.errors[0] };
1657
- }
1658
- const customToken = await createCustomToken(decoded.data.uid, {
1659
- emailVerified: decoded.data.email_verified,
1660
- source_sign_in_provider: decoded.data.firebase.sign_in_provider
1661
- });
1662
- const projectId = options.firebaseConfig?.projectId;
1663
- const appId = options.firebaseConfig?.appId;
1664
- if (!projectId || !appId) {
1665
- return { data: null, error: new Error("Project ID and App ID are required for App Check") };
1666
- }
1667
- const { accessToken } = await credential.getAccessToken();
1668
- const appCheckResponse = await options.apiClient?.appCheck.exchangeCustomToken({
1669
- accessToken,
1670
- projectId,
1671
- appId,
1672
- customToken,
1673
- limitedUse: false
1674
- });
1675
- if (!appCheckResponse?.token) {
1676
- return { data: null, error: new Error("Failed to exchange for App Check token") };
1677
- }
2126
+ const appCheckResponse = await appCheck.createToken(adminConfig.projectId, appId);
1678
2127
  return {
1679
2128
  data: {
1680
2129
  token: appCheckResponse.token,
@@ -1686,93 +2135,104 @@ function getAuth(options) {
1686
2135
  return { data: null, error };
1687
2136
  }
1688
2137
  }
2138
+ async function verifyAppCheckToken2(token) {
2139
+ const adminConfig = loadAdminConfig();
2140
+ const appCheck = getAppCheck2(adminConfig, options.tenantId);
2141
+ try {
2142
+ const decodedToken = await appCheck.verifyToken(token, adminConfig.projectId, {});
2143
+ return {
2144
+ data: decodedToken,
2145
+ error: null
2146
+ };
2147
+ } catch (error) {
2148
+ return { data: null, error };
2149
+ }
2150
+ }
1689
2151
  return {
1690
2152
  getUserData,
1691
2153
  customForIdAndRefreshToken,
1692
2154
  createCustomIdAndRefreshToken,
1693
2155
  refreshExpiredIdToken,
1694
- exchangeAppCheckToken
2156
+ createAppCheckToken,
2157
+ verifyAppCheckToken: verifyAppCheckToken2
1695
2158
  };
1696
2159
  }
1697
2160
 
1698
- // src/tokens/c-authenticateRequestProcessor.ts
1699
- var RequestProcessorContext = class {
1700
- constructor(ternSecureRequest, options) {
1701
- this.ternSecureRequest = ternSecureRequest;
1702
- this.options = options;
1703
- this.initHeaderValues();
1704
- this.initCookieValues();
1705
- this.initHandshakeValues();
1706
- this.initUrlValues();
1707
- Object.assign(this, options);
1708
- this.ternUrl = this.ternSecureRequest.ternUrl;
1709
- }
1710
- get request() {
1711
- return this.ternSecureRequest;
1712
- }
1713
- initHeaderValues() {
1714
- this.sessionTokenInHeader = this.parseAuthorizationHeader(
1715
- this.getHeader(constants.Headers.Authorization)
1716
- );
1717
- this.origin = this.getHeader(constants.Headers.Origin);
1718
- this.host = this.getHeader(constants.Headers.Host);
1719
- this.forwardedHost = this.getHeader(constants.Headers.ForwardedHost);
1720
- this.forwardedProto = this.getHeader(constants.Headers.CloudFrontForwardedProto) || this.getHeader(constants.Headers.ForwardedProto);
1721
- this.referrer = this.getHeader(constants.Headers.Referrer);
1722
- this.userAgent = this.getHeader(constants.Headers.UserAgent);
1723
- this.secFetchDest = this.getHeader(constants.Headers.SecFetchDest);
1724
- this.accept = this.getHeader(constants.Headers.Accept);
1725
- this.appCheckToken = this.getHeader(constants.Headers.AppCheckToken);
1726
- }
1727
- initCookieValues() {
1728
- const isProduction = process.env.NODE_ENV === "production";
1729
- const defaultPrefix = isProduction ? "__HOST-" : "__dev_";
1730
- this.sessionTokenInCookie = this.getCookie(constants.Cookies.Session);
1731
- this.idTokenInCookie = this.getCookie(`${defaultPrefix}${constants.Cookies.IdToken}`);
1732
- this.refreshTokenInCookie = this.getCookie(`${defaultPrefix}${constants.Cookies.Refresh}`);
1733
- this.csrfTokenInCookie = this.getCookie(constants.Cookies.CsrfToken);
1734
- this.customTokenInCookie = this.getCookie(constants.Cookies.Custom);
1735
- this.ternAuth = Number.parseInt(this.getCookie(constants.Cookies.TernAut) || "0", 10);
1736
- }
1737
- initHandshakeValues() {
1738
- this.handshakeToken = this.getQueryParam(constants.QueryParameters.Handshake) || this.getCookie(constants.Cookies.Handshake);
1739
- this.handshakeNonce = this.getQueryParam(constants.QueryParameters.HandshakeNonce) || this.getCookie(constants.Cookies.HandshakeNonce);
1740
- }
1741
- initUrlValues() {
1742
- this.method = this.ternSecureRequest.method;
1743
- this.pathSegments = this.ternSecureRequest.ternUrl.pathname.split("/").filter(Boolean);
1744
- this.endpoint = this.pathSegments[2];
1745
- this.subEndpoint = this.pathSegments[3];
1746
- }
1747
- getQueryParam(name) {
1748
- return this.ternSecureRequest.ternUrl.searchParams.get(name);
1749
- }
1750
- getHeader(name) {
1751
- return this.ternSecureRequest.headers.get(name) || void 0;
2161
+ // src/auth/credential.ts
2162
+ var accessTokenCache = /* @__PURE__ */ new Map();
2163
+ async function requestAccessToken(urlString, init) {
2164
+ const json = await fetchJson(urlString, init);
2165
+ if (!json.access_token || !json.expires_in) {
2166
+ throw new Error("Invalid access token response");
1752
2167
  }
1753
- getCookie(name) {
1754
- return this.ternSecureRequest.cookies.get(name) || void 0;
2168
+ return {
2169
+ accessToken: json.access_token,
2170
+ expirationTime: Date.now() + json.expires_in * 1e3
2171
+ };
2172
+ }
2173
+ var ServiceAccountManager = class {
2174
+ projectId;
2175
+ privateKey;
2176
+ clientEmail;
2177
+ constructor(serviceAccount) {
2178
+ this.projectId = serviceAccount.projectId;
2179
+ this.privateKey = serviceAccount.privateKey;
2180
+ this.clientEmail = serviceAccount.clientEmail;
1755
2181
  }
1756
- parseAuthorizationHeader(authorizationHeader) {
1757
- if (!authorizationHeader) {
1758
- return void 0;
1759
- }
1760
- const [scheme, token] = authorizationHeader.split(" ", 2);
1761
- if (!token) {
1762
- return scheme;
2182
+ fetchAccessToken = async (url) => {
2183
+ const token = await this.createJwt();
2184
+ const postData = "grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=" + token;
2185
+ return requestAccessToken(url, {
2186
+ method: "POST",
2187
+ headers: {
2188
+ "Content-Type": "application/x-www-form-urlencoded",
2189
+ Authorization: `Bearer ${token}`,
2190
+ Accept: "application/json"
2191
+ },
2192
+ body: postData
2193
+ });
2194
+ };
2195
+ fetchAndCacheAccessToken = async (url) => {
2196
+ const accessToken = await this.fetchAccessToken(url);
2197
+ accessTokenCache.set(this.projectId, accessToken);
2198
+ return accessToken;
2199
+ };
2200
+ getAccessToken = async (refresh) => {
2201
+ const url = `https://${GOOGLE_AUTH_TOKEN_HOST}${GOOGLE_AUTH_TOKEN_PATH}`;
2202
+ if (refresh) {
2203
+ return this.fetchAndCacheAccessToken(url);
1763
2204
  }
1764
- if (scheme === "Bearer") {
1765
- return token;
2205
+ const cachedResponse = accessTokenCache.get(this.projectId);
2206
+ if (!cachedResponse || cachedResponse.expirationTime - Date.now() <= TOKEN_EXPIRY_THRESHOLD_MILLIS) {
2207
+ return this.fetchAndCacheAccessToken(url);
1766
2208
  }
1767
- return void 0;
1768
- }
1769
- };
1770
- var createRequestProcessor = (ternSecureRequest, options) => {
1771
- return new RequestProcessorContext(ternSecureRequest, options);
2209
+ return cachedResponse;
2210
+ };
2211
+ createJwt = async () => {
2212
+ const iat = Math.floor(Date.now() / 1e3);
2213
+ const payload = {
2214
+ aud: GOOGLE_TOKEN_AUDIENCE,
2215
+ iat,
2216
+ exp: iat + ONE_HOUR_IN_SECONDS,
2217
+ iss: this.clientEmail,
2218
+ sub: this.clientEmail,
2219
+ scope: [
2220
+ "https://www.googleapis.com/auth/cloud-platform",
2221
+ "https://www.googleapis.com/auth/firebase.database",
2222
+ "https://www.googleapis.com/auth/firebase.messaging",
2223
+ "https://www.googleapis.com/auth/identitytoolkit",
2224
+ "https://www.googleapis.com/auth/userinfo.email"
2225
+ ].join(" ")
2226
+ };
2227
+ return ternSignJwt({
2228
+ payload,
2229
+ privateKey: this.privateKey
2230
+ });
2231
+ };
1772
2232
  };
1773
2233
 
1774
2234
  // src/tokens/cookie.ts
1775
- var import_cookie2 = require("@tern-secure/shared/cookie");
2235
+ var import_cookie3 = require("@tern-secure/shared/cookie");
1776
2236
 
1777
2237
  // src/tokens/request.ts
1778
2238
  function hasAuthorizationHeader(request) {
@@ -1784,9 +2244,9 @@ function convertToSeconds(value) {
1784
2244
  function isRequestForRefresh(error, context, request) {
1785
2245
  return error.reason === TokenVerificationErrorReason.TokenExpired && !!context.refreshTokenInCookie && request.method === "GET";
1786
2246
  }
1787
- async function authenticateRequest(request, options) {
2247
+ async function authenticateRequest2(request, options) {
1788
2248
  const context = createRequestProcessor(createTernSecureRequest(request), options);
1789
- const { refreshTokenInCookie, appCheckToken } = context;
2249
+ const { refreshTokenInCookie } = context;
1790
2250
  const { refreshExpiredIdToken } = getAuth(options);
1791
2251
  function checkSessionTimeout(authTimeValue) {
1792
2252
  const defaultMaxAgeSeconds = convertToSeconds("5 days");
@@ -1809,8 +2269,7 @@ async function authenticateRequest(request, options) {
1809
2269
  };
1810
2270
  }
1811
2271
  return await refreshExpiredIdToken(refreshTokenInCookie, {
1812
- referer: context.ternUrl.origin,
1813
- appCheckToken
2272
+ referer: context.ternUrl.origin
1814
2273
  });
1815
2274
  }
1816
2275
  async function handleRefresh() {
@@ -1821,8 +2280,8 @@ async function authenticateRequest(request, options) {
1821
2280
  const headers = new Headers();
1822
2281
  const { idToken } = refreshedData;
1823
2282
  const maxAge = 365 * 24 * 60 * 60;
1824
- const cookiePrefix = (0, import_cookie2.getCookiePrefix)();
1825
- const idTokenCookieName = (0, import_cookie2.getCookieName)(constants.Cookies.IdToken, cookiePrefix);
2283
+ const cookiePrefix = (0, import_cookie3.getCookiePrefix)();
2284
+ const idTokenCookieName = (0, import_cookie3.getCookieName)(constants.Cookies.IdToken, cookiePrefix);
1826
2285
  const baseCookieAttributes = `HttpOnly; Secure; SameSite=Strict; Max-Age=${maxAge}; Path=/`;
1827
2286
  const idTokenCookie = `${idTokenCookieName}=${idToken}; ${baseCookieAttributes};`;
1828
2287
  headers.append("Set-Cookie", idTokenCookie);
@@ -1912,24 +2371,7 @@ async function authenticateRequest(request, options) {
1912
2371
  if (errors) {
1913
2372
  throw errors[0];
1914
2373
  }
1915
- const { exchangeAppCheckToken } = getAuth(options);
1916
- let appCheckTokenValue;
1917
- try {
1918
- const idToken = context.idTokenInCookie || "";
1919
- const appCheckResult = await exchangeAppCheckToken(idToken);
1920
- console.log("[authenticateRequest] App Check exchange result:", appCheckResult);
1921
- if (appCheckResult.data?.token) {
1922
- appCheckTokenValue = appCheckResult.data.token;
1923
- }
1924
- } catch (error) {
1925
- console.warn("App Check token exchange failed:", error);
1926
- }
1927
- const headers = new Headers();
1928
- headers.set(
1929
- constants.Headers.AppCheckToken,
1930
- appCheckTokenValue || ""
1931
- );
1932
- const signedInRequestState = signedIn(context, data, headers, context.idTokenInCookie);
2374
+ const signedInRequestState = signedIn(context, data, void 0, context.idTokenInCookie);
1933
2375
  return signedInRequestState;
1934
2376
  } catch (err) {
1935
2377
  return handleError(err, "cookie");
@@ -1943,23 +2385,7 @@ async function authenticateRequest(request, options) {
1943
2385
  if (errors) {
1944
2386
  throw errors[0];
1945
2387
  }
1946
- const { exchangeAppCheckToken } = getAuth(options);
1947
- let appCheckTokenValue;
1948
- try {
1949
- const token = sessionTokenInHeader || "";
1950
- const appCheckResult = await exchangeAppCheckToken(token);
1951
- if (appCheckResult.data?.token) {
1952
- appCheckTokenValue = appCheckResult.data.token;
1953
- }
1954
- } catch (error) {
1955
- console.warn("App Check token exchange failed:", error);
1956
- }
1957
- const headers = new Headers();
1958
- headers.set(
1959
- constants.Headers.AppCheckToken,
1960
- appCheckTokenValue || ""
1961
- );
1962
- const signedInRequestState = signedIn(context, data, headers, sessionTokenInHeader);
2388
+ const signedInRequestState = signedIn(context, data, void 0, sessionTokenInHeader);
1963
2389
  return signedInRequestState;
1964
2390
  } catch (err) {
1965
2391
  return handleError(err, "header");
@@ -1973,17 +2399,8 @@ async function authenticateRequest(request, options) {
1973
2399
  if (isRequestForRefresh(err, context, request)) {
1974
2400
  const { data, error } = await handleRefresh();
1975
2401
  if (data) {
1976
- const { exchangeAppCheckToken } = getAuth(options);
1977
- let appCheckTokenValue;
1978
- try {
1979
- const appCheckResult = await exchangeAppCheckToken(data.token);
1980
- if (appCheckResult.data?.token) {
1981
- appCheckTokenValue = appCheckResult.data.token;
1982
- }
1983
- } catch (error2) {
1984
- console.warn("App Check token exchange failed in error handler:", error2);
1985
- }
1986
- return signedIn(context, data.decoded, data.headers, data.token);
2402
+ const signedInState = signedIn(context, data.decoded, data.headers, data.token);
2403
+ return signedInState;
1987
2404
  }
1988
2405
  if (error?.cause?.reason) {
1989
2406
  refreshError = error.cause.reason;
@@ -2012,7 +2429,7 @@ function createAuthenticateRequest(params) {
2012
2429
  const apiClient = params.apiClient;
2013
2430
  const handleAuthenticateRequest = (request, options = {}) => {
2014
2431
  const { apiUrl } = buildTimeOptions;
2015
- return authenticateRequest(request, { ...options, apiUrl, apiClient });
2432
+ return authenticateRequest2(request, { ...options, apiUrl, apiClient });
2016
2433
  };
2017
2434
  return {
2018
2435
  authenticateRequest: handleAuthenticateRequest
@@ -2161,7 +2578,7 @@ var PostgresAdapter = class {
2161
2578
  };
2162
2579
 
2163
2580
  // src/adapters/RedisAdapter.ts
2164
- var import_redis = require("@upstash/redis");
2581
+ var import_redis2 = require("@upstash/redis");
2165
2582
  var TTLCache = class {
2166
2583
  cache = /* @__PURE__ */ new Map();
2167
2584
  defaultTTL;
@@ -2218,7 +2635,7 @@ var RedisAdapter = class {
2218
2635
  cache;
2219
2636
  keyPrefix;
2220
2637
  constructor(config) {
2221
- this.redis = new import_redis.Redis({
2638
+ this.redis = new import_redis2.Redis({
2222
2639
  url: config.url,
2223
2640
  token: config.token
2224
2641
  });
@@ -2300,6 +2717,7 @@ function validateCheckRevokedOptions(options) {
2300
2717
  createAdapter,
2301
2718
  createBackendInstanceClient,
2302
2719
  createRedirect,
2720
+ createRequestProcessor,
2303
2721
  createTernSecureRequest,
2304
2722
  disableDebugLogging,
2305
2723
  enableDebugLogging,
@@ -2307,6 +2725,7 @@ function validateCheckRevokedOptions(options) {
2307
2725
  signedIn,
2308
2726
  signedInAuthObject,
2309
2727
  signedOutAuthObject,
2310
- validateCheckRevokedOptions
2728
+ validateCheckRevokedOptions,
2729
+ verifyToken
2311
2730
  });
2312
2731
  //# sourceMappingURL=index.js.map