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

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 +10 -70
  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 +1052 -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 +819 -394
  34. package/dist/auth/index.js.map +1 -1
  35. package/dist/auth/index.mjs +5 -3
  36. package/dist/chunk-3OGMNIOJ.mjs +174 -0
  37. package/dist/chunk-3OGMNIOJ.mjs.map +1 -0
  38. package/dist/{chunk-GFH5CXQR.mjs → chunk-AW5OXT7N.mjs} +2 -2
  39. package/dist/chunk-IEJQ7F4A.mjs +778 -0
  40. package/dist/chunk-IEJQ7F4A.mjs.map +1 -0
  41. package/dist/{chunk-NXYWC6YO.mjs → chunk-TUYCJY35.mjs} +182 -6
  42. package/dist/chunk-TUYCJY35.mjs.map +1 -0
  43. package/dist/constants.d.ts +10 -1
  44. package/dist/constants.d.ts.map +1 -1
  45. package/dist/fireRestApi/endpoints/AppCheckApi.d.ts.map +1 -1
  46. package/dist/index.d.ts +4 -1
  47. package/dist/index.d.ts.map +1 -1
  48. package/dist/index.js +1570 -1183
  49. package/dist/index.js.map +1 -1
  50. package/dist/index.mjs +99 -137
  51. package/dist/index.mjs.map +1 -1
  52. package/dist/jwt/crypto-signer.d.ts +21 -0
  53. package/dist/jwt/crypto-signer.d.ts.map +1 -0
  54. package/dist/jwt/index.d.ts +2 -1
  55. package/dist/jwt/index.d.ts.map +1 -1
  56. package/dist/jwt/index.js +119 -2
  57. package/dist/jwt/index.js.map +1 -1
  58. package/dist/jwt/index.mjs +7 -3
  59. package/dist/jwt/signJwt.d.ts +8 -2
  60. package/dist/jwt/signJwt.d.ts.map +1 -1
  61. package/dist/jwt/types.d.ts +6 -0
  62. package/dist/jwt/types.d.ts.map +1 -1
  63. package/dist/jwt/verifyJwt.d.ts +7 -1
  64. package/dist/jwt/verifyJwt.d.ts.map +1 -1
  65. package/dist/tokens/authstate.d.ts +2 -0
  66. package/dist/tokens/authstate.d.ts.map +1 -1
  67. package/dist/tokens/c-authenticateRequestProcessor.d.ts +2 -2
  68. package/dist/tokens/c-authenticateRequestProcessor.d.ts.map +1 -1
  69. package/dist/tokens/keys.d.ts.map +1 -1
  70. package/dist/tokens/request.d.ts.map +1 -1
  71. package/dist/tokens/types.d.ts +6 -4
  72. package/dist/tokens/types.d.ts.map +1 -1
  73. package/dist/utils/config.d.ts.map +1 -1
  74. package/dist/{auth/utils.d.ts → utils/fetcher.d.ts} +2 -1
  75. package/dist/utils/fetcher.d.ts.map +1 -0
  76. package/dist/utils/mapDecode.d.ts +2 -1
  77. package/dist/utils/mapDecode.d.ts.map +1 -1
  78. package/dist/utils/token-generator.d.ts +4 -0
  79. package/dist/utils/token-generator.d.ts.map +1 -0
  80. package/package.json +13 -3
  81. package/dist/auth/constants.d.ts +0 -6
  82. package/dist/auth/constants.d.ts.map +0 -1
  83. package/dist/auth/utils.d.ts.map +0 -1
  84. package/dist/chunk-DJLDUW7J.mjs +0 -414
  85. package/dist/chunk-DJLDUW7J.mjs.map +0 -1
  86. package/dist/chunk-NXYWC6YO.mjs.map +0 -1
  87. package/dist/chunk-WIVOBOZR.mjs +0 -86
  88. package/dist/chunk-WIVOBOZR.mjs.map +0 -1
  89. package/dist/utils/gemini_admin-init.d.ts +0 -10
  90. package/dist/utils/gemini_admin-init.d.ts.map +0 -1
  91. /package/dist/{chunk-GFH5CXQR.mjs.map → chunk-AW5OXT7N.mjs.map} +0 -0
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,1059 +459,1143 @@ 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({
459
- endpoint: "sendOobCode",
460
- method: "POST",
461
- bodyParams: restParams
462
- });
463
- }
464
- async confirmEmailVerification(apiKey, params) {
465
- this.requireApiKey(apiKey);
466
- const { ...restParams } = params;
467
- return this.request({
468
- endpoint: "sendOobCode",
469
- method: "POST",
470
- bodyParams: restParams
471
- });
568
+ if (bits >= encoding.bits || 255 & buffer << 8 - bits) {
569
+ throw new SyntaxError("Unexpected end of data");
472
570
  }
473
- };
474
-
475
- // src/fireRestApi/endpoints/PasswordApi.ts
476
- var PasswordApi = class extends AbstractAPI {
477
- async verifyPasswordResetCode(apiKey, params) {
478
- this.requireApiKey(apiKey);
479
- const { ...restParams } = params;
480
- return this.request({
481
- endpoint: "passwordReset",
482
- method: "POST",
483
- bodyParams: restParams
484
- });
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
+ }
485
586
  }
486
- async confirmPasswordReset(apiKey, params) {
487
- this.requireApiKey(apiKey);
488
- const { ...restParams } = params;
489
- return this.request({
490
- endpoint: "passwordReset",
491
- method: "POST",
492
- bodyParams: restParams
493
- });
587
+ if (bits) {
588
+ out += encoding.chars[mask & buffer << encoding.bits - bits];
494
589
  }
495
- async changePassword(apiKey, params) {
496
- this.requireApiKey(apiKey);
497
- const { ...restParams } = params;
498
- return this.request({
499
- endpoint: "passwordReset",
500
- method: "POST",
501
- bodyParams: restParams
502
- });
590
+ if (pad) {
591
+ while (out.length * encoding.bits & 7) {
592
+ out += "=";
593
+ }
503
594
  }
504
- };
595
+ return out;
596
+ }
505
597
 
506
- // src/fireRestApi/endpoints/SignInApi.ts
507
- var SignInApi = class extends AbstractAPI {
508
- async resetPasswordEmail(apiKey, params) {
509
- try {
510
- this.requireApiKey(apiKey);
511
- const { ...restParams } = params;
512
- const response = await this.request({
513
- endpoint: "sendOobCode",
514
- method: "POST",
515
- apiKey,
516
- bodyParams: restParams
517
- });
518
- if (response.errors) {
519
- const errorMessage = response.errors[0]?.message || "Failed to send reset password email";
520
- throw new Error(errorMessage);
521
- }
522
- return response.data;
523
- } catch (error) {
524
- const contextualMessage = `Failed to send reset password email: ${error instanceof Error ? error.message : "Unknown error"}`;
525
- throw new Error(contextualMessage);
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");
526
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
+ );
527
621
  }
622
+ }
623
+
624
+ // src/jwt/algorithms.ts
625
+ var algToHash = {
626
+ RS256: "SHA-256",
627
+ RS384: "SHA-384",
628
+ RS512: "SHA-512"
528
629
  };
630
+ var algs = Object.keys(algToHash);
529
631
 
530
- // src/fireRestApi/endpoints/SignInTokenApi.ts
531
- var SignInTokenApi = class extends AbstractAPI {
532
- async createCustomToken(apiKey, params) {
533
- try {
534
- this.requireApiKey(apiKey);
535
- const { ...restParams } = params;
536
- const response = await this.request({
537
- endpoint: "signInWithCustomToken",
538
- method: "POST",
539
- bodyParams: restParams
540
- });
541
- if (response.errors) {
542
- const errorMessage = response.errors[0]?.message || "Failed to create custom token";
543
- throw new Error(errorMessage);
544
- }
545
- return response.data;
546
- } catch (error) {
547
- const contextualMessage = `Failed to create custom token: ${error instanceof Error ? error.message : "Unknown error"}`;
548
- throw new Error(contextualMessage);
549
- }
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
+ });
550
642
  }
551
643
  };
552
-
553
- // src/fireRestApi/endpoints/SignUpApi.ts
554
- var SignUpApi = class extends AbstractAPI {
555
- async createCustomToken(apiKey, params) {
556
- this.requireApiKey(apiKey);
557
- const { ...restParams } = params;
558
- return this.request({
559
- endpoint: "signUp",
560
- method: "POST",
561
- bodyParams: restParams
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)}.`
562
649
  });
563
650
  }
564
651
  };
565
-
566
- // src/fireRestApi/endpoints/TokenApi.ts
567
- var TokenApi = class extends AbstractAPI {
568
- async refreshToken(apiKey, params) {
569
- this.requireApiKey(apiKey);
570
- const { refresh_token, request_origin, app_check_token, ...restParams } = params;
571
- const headers = {};
572
- if (request_origin) {
573
- headers["Referer"] = request_origin;
574
- }
575
- if (app_check_token) {
576
- headers["X-Firebase-AppCheck"] = app_check_token;
577
- }
578
- const bodyParams = {
579
- grant_type: "refresh_token",
580
- refresh_token,
581
- ...restParams
582
- };
583
- return this.request({
584
- endpoint: "refreshToken",
585
- method: "POST",
586
- apiKey,
587
- bodyParams,
588
- headerParams: headers
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.`
589
657
  });
590
658
  }
591
- async exchangeCustomForIdAndRefreshTokens(apiKey, params, options) {
592
- this.requireApiKey(apiKey);
593
- const headers = {};
594
- if (options?.referer) {
595
- headers["Referer"] = options.referer;
596
- }
597
- if (options?.appCheckToken) {
598
- headers["X-Firebase-AppCheck"] = options.appCheckToken;
599
- }
600
- const response = await this.request({
601
- endpoint: "signInWithCustomToken",
602
- method: "POST",
603
- apiKey,
604
- bodyParams: params,
605
- headerParams: headers
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()}.`
606
667
  });
607
- if (response.errors) {
608
- throw new Error(response.errors[0].message);
609
- }
610
- if (!response.data) {
611
- throw new Error("No data received from Firebase token exchange");
612
- }
613
- return response.data;
614
668
  }
615
669
  };
616
-
617
- // src/fireRestApi/endpoints/UserData.ts
618
- var UserData = class extends AbstractAPI {
619
- async getUserData(apiKey, params, options) {
620
- this.requireApiKey(apiKey);
621
- const { ...restParams } = params;
622
- const headers = {};
623
- if (options?.referer) {
624
- headers["Referer"] = options.referer;
625
- }
626
- return this.request({
627
- endpoint: "lookup",
628
- method: "POST",
629
- apiKey,
630
- bodyParams: restParams,
631
- headerParams: headers
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()};`
632
688
  });
633
689
  }
634
690
  };
635
691
 
636
- // src/runtime.ts
637
- var import_crypto = require("#crypto");
638
- var globalFetch = fetch.bind(globalThis);
639
- var runtime = {
640
- crypto: import_crypto.webcrypto,
641
- get fetch() {
642
- return process.env.NODE_ENV === "test" ? fetch : globalFetch;
643
- },
644
- AbortController: globalThis.AbortController,
645
- Blob: globalThis.Blob,
646
- FormData: globalThis.FormData,
647
- Headers: globalThis.Headers,
648
- Request: globalThis.Request,
649
- Response: globalThis.Response
650
- };
651
-
652
- // src/fireRestApi/emulator.ts
653
- var FIREBASE_AUTH_EMULATOR_HOST = process.env.FIREBASE_AUTH_EMULATOR_HOST;
654
- function emulatorHost() {
655
- if (typeof process === "undefined") return void 0;
656
- return FIREBASE_AUTH_EMULATOR_HOST;
657
- }
658
- function useEmulator() {
659
- return !!emulatorHost();
660
- }
661
-
662
- // src/fireRestApi/endpointUrl.ts
663
- var lookupEndpoint = (apiKey) => {
664
- return `https://identitytoolkit.googleapis.com/v1/accounts:lookup?key=${apiKey}`;
665
- };
666
- var getRefreshTokenEndpoint = (apiKey) => {
667
- return `https://securetoken.googleapis.com/v1/token?key=${apiKey}`;
668
- };
669
- var signInWithPassword = (apiKey) => {
670
- return `https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=${apiKey}`;
671
- };
672
- var signUpEndpoint = (apiKey) => {
673
- return `https://identitytoolkit.googleapis.com/v1/accounts:signUp?key=${apiKey}`;
674
- };
675
- var sendOobCode = (apiKey) => {
676
- return `https://identitytoolkit.googleapis.com/v1/accounts:sendOobCode?key=${apiKey}`;
677
- };
678
- var getCustomTokenEndpoint = (apiKey) => {
679
- if (useEmulator() && FIREBASE_AUTH_EMULATOR_HOST) {
680
- let protocol = "http://";
681
- if (FIREBASE_AUTH_EMULATOR_HOST.startsWith("http://")) {
682
- protocol = "";
683
- }
684
- return `${protocol}${FIREBASE_AUTH_EMULATOR_HOST}/identitytoolkit.googleapis.com/v1/accounts:signInWithCustomToken?key=${apiKey}`;
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
+ };
685
710
  }
686
- return `https://identitytoolkit.googleapis.com/v1/accounts:signInWithCustomToken?key=${apiKey}`;
687
- };
688
-
689
- // src/fireRestApi/request.ts
690
- var FIREBASE_ENDPOINT_MAP = {
691
- refreshToken: getRefreshTokenEndpoint,
692
- signInWithPassword,
693
- signUp: signUpEndpoint,
694
- signInWithCustomToken: getCustomTokenEndpoint,
695
- passwordReset: sendOobCode,
696
- sendOobCode,
697
- lookup: lookupEndpoint
698
- };
699
- function createRequest(options) {
700
- const requestFn = async (requestOptions) => {
701
- const { endpoint, method, apiKey, queryParams, headerParams, bodyParams, formData } = requestOptions;
702
- if (!apiKey) {
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) {
703
718
  return {
704
- data: null,
705
719
  errors: [
706
- {
707
- domain: "none",
708
- reason: "invalid_parameter",
709
- message: "Firebase API key is required",
710
- code: "400"
711
- }
720
+ new TokenVerificationError({
721
+ reason: TokenVerificationErrorReason.TokenInvalid,
722
+ message: "Invalid JWT format"
723
+ })
712
724
  ]
713
725
  };
714
726
  }
715
- const endpointUrl = FIREBASE_ENDPOINT_MAP[endpoint](apiKey);
716
- const finalUrl = new URL(endpointUrl);
717
- if (queryParams) {
718
- Object.entries(queryParams).forEach(([key, value]) => {
719
- if (value) {
720
- [value].flat().forEach((v) => finalUrl.searchParams.append(key, v));
721
- }
722
- });
723
- }
724
- const headers = {
725
- ...headerParams
726
- };
727
- let res;
728
- try {
729
- if (formData) {
730
- res = await runtime.fetch(finalUrl.href, {
731
- method,
732
- headers,
733
- body: formData
734
- });
735
- } else {
736
- headers["Content-Type"] = "application/json";
737
- const hasBody = method !== "GET" && bodyParams && Object.keys(bodyParams).length > 0;
738
- const body = hasBody ? { body: JSON.stringify(bodyParams) } : null;
739
- res = await runtime.fetch(finalUrl.href, {
740
- method,
741
- headers,
742
- ...body
743
- });
744
- }
745
- const isJSONResponse = res?.headers && res.headers?.get(constants.Headers.ContentType)?.includes(constants.ContentTypes.Json);
746
- let responseBody;
747
- try {
748
- const text = await res.text();
749
- try {
750
- responseBody = JSON.parse(text);
751
- } catch {
752
- responseBody = text;
753
- }
754
- } catch (e) {
755
- responseBody = null;
756
- }
757
- if (!res.ok) {
758
- return {
759
- data: null,
760
- errors: parseErrors(responseBody),
761
- status: res?.status,
762
- statusText: res?.statusText
763
- };
764
- }
765
- return {
766
- data: responseBody,
767
- errors: null
768
- };
769
- } catch (error) {
770
- if (error instanceof Error) {
771
- return {
772
- data: null,
773
- errors: [
774
- {
775
- domain: "none",
776
- reason: "request_failed",
777
- message: error.message || "An unexpected error occurred",
778
- code: "500"
779
- }
780
- ]
781
- };
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
782
738
  }
783
- return {
784
- data: null,
785
- errors: parseErrors(error),
786
- status: res?.status,
787
- statusText: res?.statusText
788
- };
789
- }
790
- };
791
- return requestFn;
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
+ }
792
751
  }
793
- function parseErrors(data) {
794
- let parsedData = data;
795
- if (typeof data === "string") {
796
- try {
797
- parsedData = JSON.parse(data);
798
- } catch (error) {
799
- return [];
800
- }
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 };
801
758
  }
802
- if (!parsedData || typeof parsedData !== "object") {
803
- return [];
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] };
804
767
  }
805
- if ("error" in parsedData && typeof parsedData.error === "object" && parsedData.error !== null) {
806
- const errorObj = parsedData.error;
807
- if ("errors" in errorObj && Array.isArray(errorObj.errors) && errorObj.errors.length > 0) {
808
- return errorObj.errors.map((err) => parseError({
809
- code: errorObj.code || "unknown_error",
810
- message: err.message || "Unknown error",
811
- domain: err.domain,
812
- reason: err.reason
813
- }));
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
+ });
814
877
  }
815
- return [parseError({
816
- code: errorObj.code?.toString() || "unknown_error",
817
- message: errorObj.message || "Unknown error",
818
- domain: errorObj.domain || "unknown",
819
- reason: errorObj.reason || errorObj.code?.toString() || "unknown_error"
820
- })];
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
+ });
821
891
  }
822
- return [];
823
- }
824
- function parseError(error) {
825
- return {
826
- domain: error.domain,
827
- reason: error.reason,
828
- message: error.message,
829
- code: error.code
830
- };
892
+ return cert;
831
893
  }
832
-
833
- // src/fireRestApi/createFireApi.ts
834
- function createFireApi(options) {
835
- const request = createRequest(options);
836
- return {
837
- appCheck: new AppCheckApi(request),
838
- email: new EmailApi(request),
839
- password: new PasswordApi(request),
840
- signIn: new SignInApi(request),
841
- signInToken: new SignInTokenApi(request),
842
- signUp: new SignUpApi(request),
843
- tokens: new TokenApi(request),
844
- userData: new UserData(request)
845
- };
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;
846
908
  }
847
-
848
- // src/utils/options.ts
849
- var defaultOptions = {
850
- apiKey: void 0,
851
- apiUrl: void 0,
852
- apiVersion: void 0
853
- };
854
- function mergePreDefinedOptions(userOptions = {}) {
855
- return {
856
- ...defaultOptions,
857
- ...userOptions
858
- };
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;
859
917
  }
860
918
 
861
- // src/tokens/request.ts
862
- var import_ms = require("@tern-secure/shared/ms");
863
-
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";
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
+ };
871
936
  }
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
937
  try {
893
- const privateKey = process.env.FIREBASE_PRIVATE_KEY;
894
- const clientEmail = process.env.FIREBASE_CLIENT_EMAIL;
895
- if (!privateKey || !clientEmail) {
938
+ const key = options.jwtKey || await loadJWKFromRemote({ ...options, kid });
939
+ if (!key) {
896
940
  return {
897
941
  errors: [
898
- new CustomTokenError(
899
- "Missing FIREBASE_PRIVATE_KEY or FIREBASE_CLIENT_EMAIL environment variables",
900
- "MISSING_ENV_VARS"
901
- )
942
+ new TokenVerificationError({
943
+ reason: TokenVerificationErrorReason.TokenInvalid,
944
+ message: `No public key found for kid "${kid}".`
945
+ })
902
946
  ]
903
947
  };
904
948
  }
905
- if (!uid || typeof uid !== "string") {
906
- return {
907
- errors: [new CustomTokenError("uid must be a non-empty string", "INVALID_UID")]
908
- };
949
+ return await verifyJwt(token, { ...options, key });
950
+ } catch (error) {
951
+ if (error instanceof TokenVerificationError) {
952
+ return { errors: [error] };
909
953
  }
910
- if (uid.length > 128) {
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();
911
1001
  return {
912
- errors: [new CustomTokenError("uid must not exceed 128 characters", "UID_TOO_LONG")]
1002
+ token: data.token,
1003
+ ttl: data.ttl
913
1004
  };
1005
+ } catch (error) {
1006
+ console.warn("[ternsecure - appcheck api]unexpected error:", error);
1007
+ throw error;
914
1008
  }
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
- }
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");
923
1014
  }
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
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}`
941
1019
  };
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
- ]
1020
+ const body = {
1021
+ customToken,
1022
+ limitedUse
948
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({
1052
+ endpoint: "sendOobCode",
1053
+ method: "POST",
1054
+ bodyParams: restParams
1055
+ });
949
1056
  }
950
- }
951
- async function createCustomToken(uid, developerClaims) {
952
- const { data, errors } = await createCustomTokenJwt(uid, developerClaims);
953
- if (errors) {
954
- throw errors[0];
1057
+ async confirmEmailVerification(apiKey, params) {
1058
+ this.requireApiKey(apiKey);
1059
+ const { ...restParams } = params;
1060
+ return this.request({
1061
+ endpoint: "sendOobCode",
1062
+ method: "POST",
1063
+ bodyParams: restParams
1064
+ });
955
1065
  }
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
1066
  };
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;
1067
+
1068
+ // src/fireRestApi/endpoints/PasswordApi.ts
1069
+ var PasswordApi = class extends AbstractAPI {
1070
+ async verifyPasswordResetCode(apiKey, params) {
1071
+ this.requireApiKey(apiKey);
1072
+ const { ...restParams } = params;
1073
+ return this.request({
1074
+ endpoint: "passwordReset",
1075
+ method: "POST",
1076
+ bodyParams: restParams
1077
+ });
1003
1078
  }
1004
- getFullMessage() {
1005
- return `${[this.message].filter((m) => m).join(" ")} (reason=${this.reason}, token-carrier=${this.tokenCarrier})`;
1079
+ async confirmPasswordReset(apiKey, params) {
1080
+ this.requireApiKey(apiKey);
1081
+ const { ...restParams } = params;
1082
+ return this.request({
1083
+ endpoint: "passwordReset",
1084
+ method: "POST",
1085
+ bodyParams: restParams
1086
+ });
1087
+ }
1088
+ async changePassword(apiKey, params) {
1089
+ this.requireApiKey(apiKey);
1090
+ const { ...restParams } = params;
1091
+ return this.request({
1092
+ endpoint: "passwordReset",
1093
+ method: "POST",
1094
+ bodyParams: restParams
1095
+ });
1006
1096
  }
1007
1097
  };
1008
1098
 
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);
1099
+ // src/fireRestApi/endpoints/SignInApi.ts
1100
+ var SignInApi = class extends AbstractAPI {
1101
+ async resetPasswordEmail(apiKey, params) {
1102
+ try {
1103
+ this.requireApiKey(apiKey);
1104
+ const { ...restParams } = params;
1105
+ const response = await this.request({
1106
+ endpoint: "sendOobCode",
1107
+ method: "POST",
1108
+ apiKey,
1109
+ bodyParams: restParams
1110
+ });
1111
+ if (response.errors) {
1112
+ const errorMessage = response.errors[0]?.message || "Failed to send reset password email";
1113
+ throw new Error(errorMessage);
1114
+ }
1115
+ return response.data;
1116
+ } catch (error) {
1117
+ const contextualMessage = `Failed to send reset password email: ${error instanceof Error ? error.message : "Unknown error"}`;
1118
+ throw new Error(contextualMessage);
1119
+ }
1016
1120
  }
1017
1121
  };
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;
1122
+
1123
+ // src/fireRestApi/endpoints/SignInTokenApi.ts
1124
+ var SignInTokenApi = class extends AbstractAPI {
1125
+ async createCustomToken(apiKey, params) {
1126
+ try {
1127
+ this.requireApiKey(apiKey);
1128
+ const { ...restParams } = params;
1129
+ const response = await this.request({
1130
+ endpoint: "signInWithCustomToken",
1131
+ method: "POST",
1132
+ bodyParams: restParams
1133
+ });
1134
+ if (response.errors) {
1135
+ const errorMessage = response.errors[0]?.message || "Failed to create custom token";
1136
+ throw new Error(errorMessage);
1137
+ }
1138
+ return response.data;
1139
+ } catch (error) {
1140
+ const contextualMessage = `Failed to create custom token: ${error instanceof Error ? error.message : "Unknown error"}`;
1141
+ throw new Error(contextualMessage);
1027
1142
  }
1028
1143
  }
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
- }
1144
+ };
1145
+
1146
+ // src/fireRestApi/endpoints/SignUpApi.ts
1147
+ var SignUpApi = class extends AbstractAPI {
1148
+ async createCustomToken(apiKey, params) {
1149
+ this.requireApiKey(apiKey);
1150
+ const { ...restParams } = params;
1151
+ return this.request({
1152
+ endpoint: "signUp",
1153
+ method: "POST",
1154
+ bodyParams: restParams
1155
+ });
1038
1156
  }
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]);
1157
+ };
1158
+
1159
+ // src/fireRestApi/endpoints/TokenApi.ts
1160
+ var TokenApi = class extends AbstractAPI {
1161
+ async refreshToken(apiKey, params) {
1162
+ this.requireApiKey(apiKey);
1163
+ const { refresh_token, request_origin, app_check_token, ...restParams } = params;
1164
+ const headers = {};
1165
+ if (request_origin) {
1166
+ headers["Referer"] = request_origin;
1047
1167
  }
1048
- buffer = buffer << encoding.bits | value;
1049
- bits += encoding.bits;
1050
- if (bits >= 8) {
1051
- bits -= 8;
1052
- out[written++] = 255 & buffer >> bits;
1168
+ if (app_check_token) {
1169
+ headers["X-Firebase-AppCheck"] = app_check_token;
1053
1170
  }
1171
+ const bodyParams = {
1172
+ grant_type: "refresh_token",
1173
+ refresh_token,
1174
+ ...restParams
1175
+ };
1176
+ return this.request({
1177
+ endpoint: "refreshToken",
1178
+ method: "POST",
1179
+ apiKey,
1180
+ bodyParams,
1181
+ headerParams: headers
1182
+ });
1054
1183
  }
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];
1184
+ async exchangeCustomForIdAndRefreshTokens(apiKey, params, options) {
1185
+ this.requireApiKey(apiKey);
1186
+ const headers = {};
1187
+ if (options?.referer) {
1188
+ headers["Referer"] = options.referer;
1072
1189
  }
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 += "=";
1190
+ if (options?.appCheckToken) {
1191
+ headers["X-Firebase-AppCheck"] = options.appCheckToken;
1080
1192
  }
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");
1193
+ const response = await this.request({
1194
+ endpoint: "signInWithCustomToken",
1195
+ method: "POST",
1196
+ apiKey,
1197
+ bodyParams: params,
1198
+ headerParams: headers
1199
+ });
1200
+ if (response.errors) {
1201
+ throw new Error(response.errors[0].message);
1092
1202
  }
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
- );
1203
+ if (!response.data) {
1204
+ throw new Error("No data received from Firebase token exchange");
1205
+ }
1206
+ return response.data;
1108
1207
  }
1109
- }
1110
-
1111
- // src/jwt/algorithms.ts
1112
- var algToHash = {
1113
- RS256: "SHA-256",
1114
- RS384: "SHA-384",
1115
- RS512: "SHA-512"
1116
1208
  };
1117
- var algs = Object.keys(algToHash);
1118
1209
 
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.`
1210
+ // src/fireRestApi/endpoints/UserData.ts
1211
+ var UserData = class extends AbstractAPI {
1212
+ async getUserData(apiKey, params, options) {
1213
+ this.requireApiKey(apiKey);
1214
+ const { ...restParams } = params;
1215
+ const headers = {};
1216
+ if (options?.referer) {
1217
+ headers["Referer"] = options.referer;
1218
+ }
1219
+ return this.request({
1220
+ endpoint: "lookup",
1221
+ method: "POST",
1222
+ apiKey,
1223
+ bodyParams: restParams,
1224
+ headerParams: headers
1128
1225
  });
1129
1226
  }
1130
1227
  };
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
- }
1228
+
1229
+ // src/runtime.ts
1230
+ var import_crypto = require("#crypto");
1231
+ var globalFetch = fetch.bind(globalThis);
1232
+ var runtime = {
1233
+ crypto: import_crypto.webcrypto,
1234
+ get fetch() {
1235
+ return process.env.NODE_ENV === "test" ? fetch : globalFetch;
1236
+ },
1237
+ AbortController: globalThis.AbortController,
1238
+ Blob: globalThis.Blob,
1239
+ FormData: globalThis.FormData,
1240
+ Headers: globalThis.Headers,
1241
+ Request: globalThis.Request,
1242
+ Response: globalThis.Response
1138
1243
  };
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
- }
1244
+
1245
+ // src/fireRestApi/emulator.ts
1246
+ var FIREBASE_AUTH_EMULATOR_HOST = process.env.FIREBASE_AUTH_EMULATOR_HOST;
1247
+ function emulatorHost() {
1248
+ if (typeof process === "undefined") return void 0;
1249
+ return FIREBASE_AUTH_EMULATOR_HOST;
1250
+ }
1251
+ function useEmulator() {
1252
+ return !!emulatorHost();
1253
+ }
1254
+
1255
+ // src/fireRestApi/endpointUrl.ts
1256
+ var lookupEndpoint = (apiKey) => {
1257
+ return `https://identitytoolkit.googleapis.com/v1/accounts:lookup?key=${apiKey}`;
1156
1258
  };
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
- });
1259
+ var getRefreshTokenEndpoint = (apiKey) => {
1260
+ return `https://securetoken.googleapis.com/v1/token?key=${apiKey}`;
1261
+ };
1262
+ var signInWithPassword = (apiKey) => {
1263
+ return `https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=${apiKey}`;
1264
+ };
1265
+ var signUpEndpoint = (apiKey) => {
1266
+ return `https://identitytoolkit.googleapis.com/v1/accounts:signUp?key=${apiKey}`;
1267
+ };
1268
+ var sendOobCode = (apiKey) => {
1269
+ return `https://identitytoolkit.googleapis.com/v1/accounts:sendOobCode?key=${apiKey}`;
1270
+ };
1271
+ var getCustomTokenEndpoint = (apiKey) => {
1272
+ if (useEmulator() && FIREBASE_AUTH_EMULATOR_HOST) {
1273
+ let protocol = "http://";
1274
+ if (FIREBASE_AUTH_EMULATOR_HOST.startsWith("http://")) {
1275
+ protocol = "";
1276
+ }
1277
+ return `${protocol}${FIREBASE_AUTH_EMULATOR_HOST}/identitytoolkit.googleapis.com/v1/accounts:signInWithCustomToken?key=${apiKey}`;
1176
1278
  }
1279
+ return `https://identitytoolkit.googleapis.com/v1/accounts:signInWithCustomToken?key=${apiKey}`;
1177
1280
  };
1178
1281
 
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
- ]
1282
+ // src/fireRestApi/request.ts
1283
+ var FIREBASE_ENDPOINT_MAP = {
1284
+ refreshToken: getRefreshTokenEndpoint,
1285
+ signInWithPassword,
1286
+ signUp: signUpEndpoint,
1287
+ signInWithCustomToken: getCustomTokenEndpoint,
1288
+ passwordReset: sendOobCode,
1289
+ sendOobCode,
1290
+ lookup: lookupEndpoint
1291
+ };
1292
+ function createRequest(options) {
1293
+ const requestFn = async (requestOptions) => {
1294
+ const { endpoint, method, apiKey, queryParams, headerParams, bodyParams, formData } = requestOptions;
1295
+ if (!apiKey) {
1296
+ return {
1297
+ data: null,
1298
+ errors: [
1299
+ {
1300
+ domain: "none",
1301
+ reason: "invalid_parameter",
1302
+ message: "Firebase API key is required",
1303
+ code: "400"
1304
+ }
1305
+ ]
1306
+ };
1307
+ }
1308
+ const endpointUrl = FIREBASE_ENDPOINT_MAP[endpoint](apiKey);
1309
+ const finalUrl = new URL(endpointUrl);
1310
+ if (queryParams) {
1311
+ Object.entries(queryParams).forEach(([key, value]) => {
1312
+ if (value) {
1313
+ [value].flat().forEach((v) => finalUrl.searchParams.append(key, v));
1314
+ }
1315
+ });
1316
+ }
1317
+ const headers = {
1318
+ ...headerParams
1196
1319
  };
1197
- }
1198
- }
1199
- function ternDecodeJwt(token) {
1200
- 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) {
1320
+ let res;
1321
+ try {
1322
+ if (formData) {
1323
+ res = await runtime.fetch(finalUrl.href, {
1324
+ method,
1325
+ headers,
1326
+ body: formData
1327
+ });
1328
+ } else {
1329
+ headers["Content-Type"] = "application/json";
1330
+ const hasBody = method !== "GET" && bodyParams && Object.keys(bodyParams).length > 0;
1331
+ const body = hasBody ? { body: JSON.stringify(bodyParams) } : null;
1332
+ res = await runtime.fetch(finalUrl.href, {
1333
+ method,
1334
+ headers,
1335
+ ...body
1336
+ });
1337
+ }
1338
+ const isJSONResponse = res?.headers && res.headers?.get(constants.Headers.ContentType)?.includes(constants.ContentTypes.Json);
1339
+ let responseBody;
1340
+ try {
1341
+ const text = await res.text();
1342
+ try {
1343
+ responseBody = JSON.parse(text);
1344
+ } catch {
1345
+ responseBody = text;
1346
+ }
1347
+ } catch (e) {
1348
+ responseBody = null;
1349
+ }
1350
+ if (!res.ok) {
1351
+ return {
1352
+ data: null,
1353
+ errors: parseErrors(responseBody),
1354
+ status: res?.status,
1355
+ statusText: res?.statusText
1356
+ };
1357
+ }
1358
+ return {
1359
+ data: responseBody,
1360
+ errors: null
1361
+ };
1362
+ } catch (error) {
1363
+ if (error instanceof Error) {
1364
+ return {
1365
+ data: null,
1366
+ errors: [
1367
+ {
1368
+ domain: "none",
1369
+ reason: "request_failed",
1370
+ message: error.message || "An unexpected error occurred",
1371
+ code: "500"
1372
+ }
1373
+ ]
1374
+ };
1375
+ }
1205
1376
  return {
1206
- errors: [
1207
- new TokenVerificationError({
1208
- reason: TokenVerificationErrorReason.TokenInvalid,
1209
- message: "Invalid JWT format"
1210
- })
1211
- ]
1377
+ data: null,
1378
+ errors: parseErrors(error),
1379
+ status: res?.status,
1380
+ statusText: res?.statusText
1212
1381
  };
1213
1382
  }
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
1225
- }
1226
- };
1227
- return { data };
1228
- } catch (error) {
1229
- return {
1230
- 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
- })
1235
- ]
1236
- };
1237
- }
1383
+ };
1384
+ return requestFn;
1238
1385
  }
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);
1243
- if (errors) {
1244
- return { errors };
1386
+ function parseErrors(data) {
1387
+ let parsedData = data;
1388
+ if (typeof data === "string") {
1389
+ try {
1390
+ parsedData = JSON.parse(data);
1391
+ } catch (error) {
1392
+ return [];
1393
+ }
1245
1394
  }
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] };
1395
+ if (!parsedData || typeof parsedData !== "object") {
1396
+ return [];
1254
1397
  }
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
- };
1398
+ if ("error" in parsedData && typeof parsedData.error === "object" && parsedData.error !== null) {
1399
+ const errorObj = parsedData.error;
1400
+ if ("errors" in errorObj && Array.isArray(errorObj.errors) && errorObj.errors.length > 0) {
1401
+ return errorObj.errors.map((err) => parseError({
1402
+ code: errorObj.code || "unknown_error",
1403
+ message: err.message || "Unknown error",
1404
+ domain: err.domain,
1405
+ reason: err.reason
1406
+ }));
1407
+ }
1408
+ return [parseError({
1409
+ code: errorObj.code?.toString() || "unknown_error",
1410
+ message: errorObj.message || "Unknown error",
1411
+ domain: errorObj.domain || "unknown",
1412
+ reason: errorObj.reason || errorObj.code?.toString() || "unknown_error"
1413
+ })];
1265
1414
  }
1266
- const decodedIdToken = mapJwtPayloadToDecodedIdToken(verifiedPayload);
1267
- return { data: decodedIdToken };
1268
- }
1269
-
1270
- // src/tokens/keys.ts
1271
- var cache = {};
1272
- var lastUpdatedAt = 0;
1273
- var googleExpiresAt = 0;
1274
- function getFromCache(kid) {
1275
- return cache[kid];
1415
+ return [];
1276
1416
  }
1277
- function getCacheValues() {
1278
- return Object.values(cache);
1417
+ function parseError(error) {
1418
+ return {
1419
+ domain: error.domain,
1420
+ reason: error.reason,
1421
+ message: error.message,
1422
+ code: error.code
1423
+ };
1279
1424
  }
1280
- function setInCache(kid, certificate, shouldExpire = true) {
1281
- cache[kid] = certificate;
1282
- lastUpdatedAt = shouldExpire ? Date.now() : -1;
1425
+
1426
+ // src/fireRestApi/createFireApi.ts
1427
+ function createFireApi(options) {
1428
+ const request = createRequest(options);
1429
+ return {
1430
+ appCheck: new AppCheckApi(request),
1431
+ email: new EmailApi(request),
1432
+ password: new PasswordApi(request),
1433
+ signIn: new SignInApi(request),
1434
+ signInToken: new SignInTokenApi(request),
1435
+ signUp: new SignUpApi(request),
1436
+ tokens: new TokenApi(request),
1437
+ userData: new UserData(request)
1438
+ };
1283
1439
  }
1284
- async function fetchPublicKeys(keyUrl) {
1285
- const url = new URL(keyUrl);
1286
- const response = await fetch(url);
1287
- if (!response.ok) {
1288
- throw new TokenVerificationError({
1289
- message: `Error loading public keys from ${url.href} with code=${response.status} `,
1290
- reason: TokenVerificationErrorReason.TokenInvalid
1291
- });
1292
- }
1293
- const data = await response.json();
1294
- const expiresAt = getExpiresAt(response);
1440
+
1441
+ // src/utils/options.ts
1442
+ var defaultOptions = {
1443
+ apiKey: void 0,
1444
+ apiUrl: void 0,
1445
+ apiVersion: void 0
1446
+ };
1447
+ function mergePreDefinedOptions(userOptions = {}) {
1295
1448
  return {
1296
- keys: data,
1297
- expiresAt
1449
+ ...defaultOptions,
1450
+ ...userOptions
1298
1451
  };
1299
1452
  }
1300
- async function loadJWKFromRemote({
1301
- keyURL = GOOGLE_PUBLIC_KEYS_URL,
1302
- skipJwksCache,
1303
- kid
1304
- }) {
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
- });
1453
+
1454
+ // src/tokens/request.ts
1455
+ var import_ms = require("@tern-secure/shared/ms");
1456
+
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];
1312
1463
  }
1313
- googleExpiresAt = expiresAt;
1314
- Object.entries(keys).forEach(([keyId, cert2]) => {
1315
- setInCache(keyId, cert2);
1316
- });
1317
- }
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
1325
- });
1326
- }
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 = {};
1341
- }
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;
1348
- }
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;
1464
+ return data;
1465
+ };
1352
1466
  }
1353
1467
 
1354
- // src/tokens/verify.ts
1355
- async function verifyToken(token, options) {
1356
- const { data: decodedResult, errors } = ternDecodeJwt(token);
1357
- if (errors) {
1358
- return { errors };
1359
- }
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
- ]
1370
- };
1468
+ // src/jwt/jwt.ts
1469
+ var import_jose3 = require("jose");
1470
+
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";
1371
1478
  }
1372
- try {
1373
- const key = options.jwtKey || await loadJWKFromRemote({ ...options, kid });
1374
- if (!key) {
1479
+ };
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) {
1499
+ try {
1500
+ const privateKey = process.env.FIREBASE_PRIVATE_KEY;
1501
+ const clientEmail = process.env.FIREBASE_CLIENT_EMAIL;
1502
+ if (!privateKey || !clientEmail) {
1375
1503
  return {
1376
1504
  errors: [
1377
- new TokenVerificationError({
1378
- reason: TokenVerificationErrorReason.TokenInvalid,
1379
- message: `No public key found for kid "${kid}".`
1380
- })
1505
+ new CustomTokenError(
1506
+ "Missing FIREBASE_PRIVATE_KEY or FIREBASE_CLIENT_EMAIL environment variables",
1507
+ "MISSING_ENV_VARS"
1508
+ )
1381
1509
  ]
1382
1510
  };
1383
1511
  }
1384
- return await verifyJwt(token, { ...options, key });
1385
- } catch (error) {
1386
- if (error instanceof TokenVerificationError) {
1387
- return { errors: [error] };
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
+ }
1529
+ }
1388
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);
1389
1546
  return {
1390
- errors: [error]
1547
+ data: jwt
1548
+ };
1549
+ } catch (error) {
1550
+ const message = error instanceof Error ? error.message : "Unknown error occurred";
1551
+ return {
1552
+ errors: [
1553
+ new CustomTokenError(`Failed to create custom token: ${message}`, "TOKEN_CREATION_FAILED")
1554
+ ]
1391
1555
  };
1392
1556
  }
1393
1557
  }
1394
-
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];
1401
- }
1402
- return data;
1403
- };
1558
+ async function createCustomToken(uid, developerClaims) {
1559
+ const { data, errors } = await createCustomTokenJwt(uid, developerClaims);
1560
+ if (errors) {
1561
+ throw errors[0];
1562
+ }
1563
+ return data;
1404
1564
  }
1405
1565
 
1406
- // src/jwt/jwt.ts
1407
- var import_jose4 = require("jose");
1408
-
1409
1566
  // src/jwt/signJwt.ts
1410
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;
1580
+ }
1581
+ async function fetchText(url, init) {
1582
+ return (await fetchAny(url, init)).text();
1583
+ }
1584
+ async function fetchJson(url, init) {
1585
+ return (await fetchAny(url, init)).json();
1586
+ }
1587
+ async function fetchAny(url, init) {
1588
+ const response = await fetch(url, init);
1589
+ if (!response.ok) {
1590
+ throw new Error(await getDetailFromResponse(response));
1591
+ }
1592
+ return response;
1593
+ }
1594
+
1595
+ // src/jwt/types.ts
1411
1596
  var ALGORITHM_RS256 = "RS256";
1597
+
1598
+ // src/jwt/signJwt.ts
1412
1599
  async function ternSignJwt(opts) {
1413
1600
  const { payload, privateKey, keyId } = opts;
1414
1601
  let key;
@@ -1422,112 +1609,382 @@ async function ternSignJwt(opts) {
1422
1609
  }
1423
1610
  return new import_jose5.SignJWT(payload).setProtectedHeader({ alg: ALGORITHM_RS256, kid: keyId }).sign(key);
1424
1611
  }
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
1623
+ }) {
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;
1656
+ }
1657
+ return ternSignJwt({ payload, privateKey: this.credential.privateKey });
1658
+ }
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
1680
+ });
1681
+ }
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);
1696
+ }
1697
+ };
1425
1698
 
1426
1699
  // src/jwt/index.ts
1427
1700
  var ternDecodeJwt2 = createJwtGuard(ternDecodeJwt);
1428
1701
 
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;
1702
+ // src/utils/token-generator.ts
1703
+ function cryptoSignerFromCredential(credential, tenantId, serviceAccountId) {
1704
+ if (credential instanceof ServiceAccountManager) {
1705
+ return new ServiceAccountSigner(credential, tenantId);
1706
+ }
1707
+ return new IAMSigner(credential, tenantId, serviceAccountId);
1708
+ }
1435
1709
 
1436
- // src/auth/utils.ts
1437
- async function getDetailFromResponse(response) {
1438
- const json = await response.json();
1439
- if (!json) {
1440
- return "Missing error payload";
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;
1441
1720
  }
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 + ")";
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}`
1731
+ };
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();
1743
+ return {
1744
+ token: data.token,
1745
+ ttl: data.ttl
1746
+ };
1747
+ } catch (error) {
1748
+ console.warn("[ternsecure - appcheck api]unexpected error:", error);
1749
+ throw error;
1750
+ }
1445
1751
  }
1446
- return detail;
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");
1756
+ }
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
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
+ }
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;
1447
1803
  }
1448
- async function fetchJson(url, init) {
1449
- return (await fetchAny(url, init)).json();
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
+ };
1844
+
1845
+ // src/app-check/serverAppCheck.ts
1846
+ var import_redis = require("@upstash/redis");
1847
+
1848
+ // src/utils/admin-init.ts
1849
+ var import_firebase_admin = __toESM(require("firebase-admin"));
1850
+ var import_app_check = require("firebase-admin/app-check");
1851
+
1852
+ // src/utils/config.ts
1853
+ var loadAdminConfig = () => ({
1854
+ projectId: process.env.FIREBASE_PROJECT_ID || "",
1855
+ clientEmail: process.env.FIREBASE_CLIENT_EMAIL || "",
1856
+ privateKey: process.env.FIREBASE_PRIVATE_KEY || ""
1857
+ });
1858
+ var validateAdminConfig = (config) => {
1859
+ const requiredFields = [
1860
+ "projectId",
1861
+ "clientEmail",
1862
+ "privateKey"
1863
+ ];
1864
+ const errors = [];
1865
+ requiredFields.forEach((field) => {
1866
+ if (!config[field]) {
1867
+ errors.push(`Missing required field: FIREBASE_${String(field).toUpperCase()}`);
1868
+ }
1869
+ });
1870
+ return {
1871
+ isValid: errors.length === 0,
1872
+ errors,
1873
+ config
1874
+ };
1875
+ };
1876
+ var initializeAdminConfig = () => {
1877
+ const config = loadAdminConfig();
1878
+ const validationResult = validateAdminConfig(config);
1879
+ if (!validationResult.isValid) {
1880
+ throw new Error(
1881
+ `Firebase Admin configuration validation failed:
1882
+ ${validationResult.errors.join("\n")}`
1883
+ );
1884
+ }
1885
+ return config;
1886
+ };
1887
+
1888
+ // src/utils/admin-init.ts
1889
+ if (!import_firebase_admin.default.apps.length) {
1890
+ try {
1891
+ const config = initializeAdminConfig();
1892
+ import_firebase_admin.default.initializeApp({
1893
+ credential: import_firebase_admin.default.credential.cert({
1894
+ ...config,
1895
+ privateKey: config.privateKey.replace(/\\n/g, "\n")
1896
+ })
1897
+ });
1898
+ } catch (error) {
1899
+ console.error("Firebase admin initialization error", error);
1900
+ }
1450
1901
  }
1451
- async function fetchAny(url, init) {
1452
- const response = await fetch(url, init);
1453
- if (!response.ok) {
1454
- throw new Error(await getDetailFromResponse(response));
1902
+ var adminTernSecureAuth = import_firebase_admin.default.auth();
1903
+ var adminTernSecureDb = import_firebase_admin.default.firestore();
1904
+ var TernSecureTenantManager = import_firebase_admin.default.auth().tenantManager();
1905
+ var appCheckAdmin = (0, import_app_check.getAppCheck)();
1906
+
1907
+ // src/app-check/verifier.ts
1908
+ var import_jose6 = require("jose");
1909
+ var getPublicKey = async (header, keyURL) => {
1910
+ const jswksUrl = new URL(keyURL);
1911
+ const getKey = (0, import_jose6.createRemoteJWKSet)(jswksUrl);
1912
+ return getKey(header);
1913
+ };
1914
+ var verifyAppCheckToken = async (token, options) => {
1915
+ const { data: decodedResult, errors } = ternDecodeJwt(token);
1916
+ if (errors) {
1917
+ throw errors[0];
1918
+ }
1919
+ const { header } = decodedResult;
1920
+ const { kid } = header;
1921
+ if (!kid) {
1922
+ return {
1923
+ errors: [
1924
+ new TokenVerificationError({
1925
+ reason: TokenVerificationErrorReason.TokenInvalid,
1926
+ message: 'JWT "kid" header is missing.'
1927
+ })
1928
+ ]
1929
+ };
1930
+ }
1931
+ try {
1932
+ const getPublicKeyForToken = () => getPublicKey(header, options.keyURL || "");
1933
+ return await verifyAppCheckJwt(token, { ...options, key: getPublicKeyForToken });
1934
+ } catch (error) {
1935
+ if (error instanceof TokenVerificationError) {
1936
+ return { errors: [error] };
1937
+ }
1938
+ return {
1939
+ errors: [error]
1940
+ };
1455
1941
  }
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");
1942
+ };
1943
+ var AppcheckTokenVerifier = class {
1944
+ constructor(credential) {
1945
+ this.credential = credential;
1465
1946
  }
1466
- return {
1467
- accessToken: json.access_token,
1468
- expirationTime: Date.now() + json.expires_in * 1e3
1947
+ verifyToken = async (token, projectId, options) => {
1948
+ const { data, errors } = await verifyAppCheckToken(token, options);
1949
+ if (errors) {
1950
+ throw errors[0];
1951
+ }
1952
+ return data;
1469
1953
  };
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;
1954
+ };
1955
+
1956
+ // src/app-check/index.ts
1957
+ var JWKS_URL = "https://firebaseappcheck.googleapis.com/v1/jwks";
1958
+ var AppCheck = class {
1959
+ client;
1960
+ tokenGenerator;
1961
+ appCheckTokenVerifier;
1962
+ limitedUse;
1963
+ constructor(credential, tenantId, limitedUse) {
1964
+ this.client = new AppCheckApi2(credential);
1965
+ this.tokenGenerator = new AppCheckTokenGenerator(
1966
+ cryptoSignerFromCredential(credential, tenantId)
1967
+ );
1968
+ this.appCheckTokenVerifier = new AppcheckTokenVerifier(credential);
1969
+ this.limitedUse = limitedUse;
1479
1970
  }
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
1971
+ createToken = (projectId, appId, options) => {
1972
+ return this.tokenGenerator.createCustomToken(appId, options).then((customToken) => {
1973
+ return this.client.exchangeToken({ customToken, projectId, appId });
1491
1974
  });
1492
1975
  };
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
1976
+ verifyToken = async (appCheckToken, projectId, options) => {
1977
+ return this.appCheckTokenVerifier.verifyToken(appCheckToken, projectId, { keyURL: JWKS_URL, ...options }).then((decodedToken) => {
1978
+ return {
1979
+ appId: decodedToken.app_id,
1980
+ token: decodedToken
1981
+ };
1528
1982
  });
1529
1983
  };
1530
1984
  };
1985
+ function getAppCheck2(serviceAccount, tenantId, limitedUse) {
1986
+ return new AppCheck(new ServiceAccountManager(serviceAccount), tenantId, limitedUse);
1987
+ }
1531
1988
 
1532
1989
  // src/auth/getauth.ts
1533
1990
  var API_KEY_ERROR = "API Key is required";
@@ -1543,17 +2000,8 @@ function parseFirebaseResponse(data) {
1543
2000
  return data;
1544
2001
  }
1545
2002
  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
- }
2003
+ const { apiKey } = options;
2004
+ const effectiveApiKey = apiKey || process.env.NEXT_PUBLIC_FIREBASE_API_KEY;
1557
2005
  async function getUserData(idToken, localId) {
1558
2006
  if (!effectiveApiKey) {
1559
2007
  throw new Error(API_KEY_ERROR);
@@ -1638,43 +2086,12 @@ function getAuth(options) {
1638
2086
  auth_time: decodedCustomIdToken.data.auth_time
1639
2087
  };
1640
2088
  }
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
- }
2089
+ async function createAppCheckToken() {
2090
+ const adminConfig = loadAdminConfig();
2091
+ const appId = process.env.NEXT_PUBLIC_FIREBASE_APP_ID || "";
2092
+ const appCheck = getAppCheck2(adminConfig, options.tenantId);
1653
2093
  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
- }
2094
+ const appCheckResponse = await appCheck.createToken(adminConfig.projectId, appId);
1678
2095
  return {
1679
2096
  data: {
1680
2097
  token: appCheckResponse.token,
@@ -1686,89 +2103,100 @@ function getAuth(options) {
1686
2103
  return { data: null, error };
1687
2104
  }
1688
2105
  }
2106
+ async function verifyAppCheckToken2(token) {
2107
+ const adminConfig = loadAdminConfig();
2108
+ const appCheck = getAppCheck2(adminConfig, options.tenantId);
2109
+ try {
2110
+ const decodedToken = await appCheck.verifyToken(token, adminConfig.projectId, {});
2111
+ return {
2112
+ data: decodedToken,
2113
+ error: null
2114
+ };
2115
+ } catch (error) {
2116
+ return { data: null, error };
2117
+ }
2118
+ }
1689
2119
  return {
1690
2120
  getUserData,
1691
2121
  customForIdAndRefreshToken,
1692
2122
  createCustomIdAndRefreshToken,
1693
2123
  refreshExpiredIdToken,
1694
- exchangeAppCheckToken
2124
+ createAppCheckToken,
2125
+ verifyAppCheckToken: verifyAppCheckToken2
1695
2126
  };
1696
2127
  }
1697
2128
 
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;
2129
+ // src/auth/credential.ts
2130
+ var accessTokenCache = /* @__PURE__ */ new Map();
2131
+ async function requestAccessToken(urlString, init) {
2132
+ const json = await fetchJson(urlString, init);
2133
+ if (!json.access_token || !json.expires_in) {
2134
+ throw new Error("Invalid access token response");
1752
2135
  }
1753
- getCookie(name) {
1754
- return this.ternSecureRequest.cookies.get(name) || void 0;
2136
+ return {
2137
+ accessToken: json.access_token,
2138
+ expirationTime: Date.now() + json.expires_in * 1e3
2139
+ };
2140
+ }
2141
+ var ServiceAccountManager = class {
2142
+ projectId;
2143
+ privateKey;
2144
+ clientEmail;
2145
+ constructor(serviceAccount) {
2146
+ this.projectId = serviceAccount.projectId;
2147
+ this.privateKey = serviceAccount.privateKey;
2148
+ this.clientEmail = serviceAccount.clientEmail;
1755
2149
  }
1756
- parseAuthorizationHeader(authorizationHeader) {
1757
- if (!authorizationHeader) {
1758
- return void 0;
1759
- }
1760
- const [scheme, token] = authorizationHeader.split(" ", 2);
1761
- if (!token) {
1762
- return scheme;
2150
+ fetchAccessToken = async (url) => {
2151
+ const token = await this.createJwt();
2152
+ const postData = "grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=" + token;
2153
+ return requestAccessToken(url, {
2154
+ method: "POST",
2155
+ headers: {
2156
+ "Content-Type": "application/x-www-form-urlencoded",
2157
+ Authorization: `Bearer ${token}`,
2158
+ Accept: "application/json"
2159
+ },
2160
+ body: postData
2161
+ });
2162
+ };
2163
+ fetchAndCacheAccessToken = async (url) => {
2164
+ const accessToken = await this.fetchAccessToken(url);
2165
+ accessTokenCache.set(this.projectId, accessToken);
2166
+ return accessToken;
2167
+ };
2168
+ getAccessToken = async (refresh) => {
2169
+ const url = `https://${GOOGLE_AUTH_TOKEN_HOST}${GOOGLE_AUTH_TOKEN_PATH}`;
2170
+ if (refresh) {
2171
+ return this.fetchAndCacheAccessToken(url);
1763
2172
  }
1764
- if (scheme === "Bearer") {
1765
- return token;
2173
+ const cachedResponse = accessTokenCache.get(this.projectId);
2174
+ if (!cachedResponse || cachedResponse.expirationTime - Date.now() <= TOKEN_EXPIRY_THRESHOLD_MILLIS) {
2175
+ return this.fetchAndCacheAccessToken(url);
1766
2176
  }
1767
- return void 0;
1768
- }
1769
- };
1770
- var createRequestProcessor = (ternSecureRequest, options) => {
1771
- return new RequestProcessorContext(ternSecureRequest, options);
2177
+ return cachedResponse;
2178
+ };
2179
+ createJwt = async () => {
2180
+ const iat = Math.floor(Date.now() / 1e3);
2181
+ const payload = {
2182
+ aud: GOOGLE_TOKEN_AUDIENCE,
2183
+ iat,
2184
+ exp: iat + ONE_HOUR_IN_SECONDS,
2185
+ iss: this.clientEmail,
2186
+ sub: this.clientEmail,
2187
+ scope: [
2188
+ "https://www.googleapis.com/auth/cloud-platform",
2189
+ "https://www.googleapis.com/auth/firebase.database",
2190
+ "https://www.googleapis.com/auth/firebase.messaging",
2191
+ "https://www.googleapis.com/auth/identitytoolkit",
2192
+ "https://www.googleapis.com/auth/userinfo.email"
2193
+ ].join(" ")
2194
+ };
2195
+ return ternSignJwt({
2196
+ payload,
2197
+ privateKey: this.privateKey
2198
+ });
2199
+ };
1772
2200
  };
1773
2201
 
1774
2202
  // src/tokens/cookie.ts
@@ -1786,7 +2214,7 @@ function isRequestForRefresh(error, context, request) {
1786
2214
  }
1787
2215
  async function authenticateRequest(request, options) {
1788
2216
  const context = createRequestProcessor(createTernSecureRequest(request), options);
1789
- const { refreshTokenInCookie, appCheckToken } = context;
2217
+ const { refreshTokenInCookie } = context;
1790
2218
  const { refreshExpiredIdToken } = getAuth(options);
1791
2219
  function checkSessionTimeout(authTimeValue) {
1792
2220
  const defaultMaxAgeSeconds = convertToSeconds("5 days");
@@ -1809,8 +2237,7 @@ async function authenticateRequest(request, options) {
1809
2237
  };
1810
2238
  }
1811
2239
  return await refreshExpiredIdToken(refreshTokenInCookie, {
1812
- referer: context.ternUrl.origin,
1813
- appCheckToken
2240
+ referer: context.ternUrl.origin
1814
2241
  });
1815
2242
  }
1816
2243
  async function handleRefresh() {
@@ -1912,24 +2339,7 @@ async function authenticateRequest(request, options) {
1912
2339
  if (errors) {
1913
2340
  throw errors[0];
1914
2341
  }
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);
2342
+ const signedInRequestState = signedIn(context, data, void 0, context.idTokenInCookie);
1933
2343
  return signedInRequestState;
1934
2344
  } catch (err) {
1935
2345
  return handleError(err, "cookie");
@@ -1943,23 +2353,7 @@ async function authenticateRequest(request, options) {
1943
2353
  if (errors) {
1944
2354
  throw errors[0];
1945
2355
  }
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);
2356
+ const signedInRequestState = signedIn(context, data, void 0, sessionTokenInHeader);
1963
2357
  return signedInRequestState;
1964
2358
  } catch (err) {
1965
2359
  return handleError(err, "header");
@@ -1973,17 +2367,8 @@ async function authenticateRequest(request, options) {
1973
2367
  if (isRequestForRefresh(err, context, request)) {
1974
2368
  const { data, error } = await handleRefresh();
1975
2369
  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);
2370
+ const signedInState = signedIn(context, data.decoded, data.headers, data.token);
2371
+ return signedInState;
1987
2372
  }
1988
2373
  if (error?.cause?.reason) {
1989
2374
  refreshError = error.cause.reason;
@@ -2161,7 +2546,7 @@ var PostgresAdapter = class {
2161
2546
  };
2162
2547
 
2163
2548
  // src/adapters/RedisAdapter.ts
2164
- var import_redis = require("@upstash/redis");
2549
+ var import_redis2 = require("@upstash/redis");
2165
2550
  var TTLCache = class {
2166
2551
  cache = /* @__PURE__ */ new Map();
2167
2552
  defaultTTL;
@@ -2218,7 +2603,7 @@ var RedisAdapter = class {
2218
2603
  cache;
2219
2604
  keyPrefix;
2220
2605
  constructor(config) {
2221
- this.redis = new import_redis.Redis({
2606
+ this.redis = new import_redis2.Redis({
2222
2607
  url: config.url,
2223
2608
  token: config.token
2224
2609
  });
@@ -2300,6 +2685,7 @@ function validateCheckRevokedOptions(options) {
2300
2685
  createAdapter,
2301
2686
  createBackendInstanceClient,
2302
2687
  createRedirect,
2688
+ createRequestProcessor,
2303
2689
  createTernSecureRequest,
2304
2690
  disableDebugLogging,
2305
2691
  enableDebugLogging,
@@ -2307,6 +2693,7 @@ function validateCheckRevokedOptions(options) {
2307
2693
  signedIn,
2308
2694
  signedInAuthObject,
2309
2695
  signedOutAuthObject,
2310
- validateCheckRevokedOptions
2696
+ validateCheckRevokedOptions,
2697
+ verifyToken
2311
2698
  });
2312
2699
  //# sourceMappingURL=index.js.map