@tern-secure/backend 1.2.0-canary.v20251108045933 → 1.2.0-canary.v20251127221555

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 (74) hide show
  1. package/dist/admin/index.d.ts +1 -0
  2. package/dist/admin/index.d.ts.map +1 -1
  3. package/dist/admin/index.js +40 -0
  4. package/dist/admin/index.js.map +1 -1
  5. package/dist/admin/index.mjs +40 -2
  6. package/dist/admin/index.mjs.map +1 -1
  7. package/dist/admin/user.d.ts +16 -0
  8. package/dist/admin/user.d.ts.map +1 -0
  9. package/dist/auth/constants.d.ts +6 -0
  10. package/dist/auth/constants.d.ts.map +1 -0
  11. package/dist/auth/credential.d.ts +27 -0
  12. package/dist/auth/credential.d.ts.map +1 -0
  13. package/dist/auth/getauth.d.ts +1 -0
  14. package/dist/auth/getauth.d.ts.map +1 -1
  15. package/dist/auth/index.js +234 -28
  16. package/dist/auth/index.js.map +1 -1
  17. package/dist/auth/index.mjs +3 -3
  18. package/dist/auth/utils.d.ts +3 -0
  19. package/dist/auth/utils.d.ts.map +1 -0
  20. package/dist/{chunk-MS6L7M3C.mjs → chunk-DJLDUW7J.mjs} +174 -12
  21. package/dist/chunk-DJLDUW7J.mjs.map +1 -0
  22. package/dist/{chunk-ASGV4MFO.mjs → chunk-GFH5CXQR.mjs} +2 -2
  23. package/dist/{chunk-DDUNOEIM.mjs → chunk-NXYWC6YO.mjs} +278 -116
  24. package/dist/chunk-NXYWC6YO.mjs.map +1 -0
  25. package/dist/{chunk-DFAJCSBJ.mjs → chunk-WIVOBOZR.mjs} +2 -1
  26. package/dist/chunk-WIVOBOZR.mjs.map +1 -0
  27. package/dist/constants.d.ts +1 -0
  28. package/dist/constants.d.ts.map +1 -1
  29. package/dist/fireRestApi/createFireApi.d.ts +4 -2
  30. package/dist/fireRestApi/createFireApi.d.ts.map +1 -1
  31. package/dist/fireRestApi/endpointUrl.d.ts +2 -1
  32. package/dist/fireRestApi/endpointUrl.d.ts.map +1 -1
  33. package/dist/fireRestApi/endpoints/AppCheckApi.d.ts +23 -0
  34. package/dist/fireRestApi/endpoints/AppCheckApi.d.ts.map +1 -0
  35. package/dist/fireRestApi/endpoints/SignInApi.d.ts +11 -0
  36. package/dist/fireRestApi/endpoints/SignInApi.d.ts.map +1 -0
  37. package/dist/fireRestApi/endpoints/TokenApi.d.ts +3 -1
  38. package/dist/fireRestApi/endpoints/TokenApi.d.ts.map +1 -1
  39. package/dist/fireRestApi/endpoints/UserData.d.ts.map +1 -1
  40. package/dist/fireRestApi/endpoints/index.d.ts +2 -0
  41. package/dist/fireRestApi/endpoints/index.d.ts.map +1 -1
  42. package/dist/fireRestApi/request.d.ts.map +1 -1
  43. package/dist/fireRestApi/resources/EmailAddress.d.ts +7 -0
  44. package/dist/fireRestApi/resources/EmailAddress.d.ts.map +1 -0
  45. package/dist/fireRestApi/resources/JSON.d.ts +4 -0
  46. package/dist/fireRestApi/resources/JSON.d.ts.map +1 -1
  47. package/dist/index.js +421 -43
  48. package/dist/index.js.map +1 -1
  49. package/dist/index.mjs +190 -19
  50. package/dist/index.mjs.map +1 -1
  51. package/dist/jwt/index.d.ts +1 -0
  52. package/dist/jwt/index.d.ts.map +1 -1
  53. package/dist/jwt/index.js +51 -19
  54. package/dist/jwt/index.js.map +1 -1
  55. package/dist/jwt/index.mjs +8 -132
  56. package/dist/jwt/index.mjs.map +1 -1
  57. package/dist/jwt/signJwt.d.ts +8 -0
  58. package/dist/jwt/signJwt.d.ts.map +1 -1
  59. package/dist/jwt/verifyJwt.d.ts.map +1 -1
  60. package/dist/tokens/authstate.d.ts.map +1 -1
  61. package/dist/tokens/c-authenticateRequestProcessor.d.ts +1 -0
  62. package/dist/tokens/c-authenticateRequestProcessor.d.ts.map +1 -1
  63. package/dist/tokens/request.d.ts.map +1 -1
  64. package/dist/tokens/types.d.ts +2 -1
  65. package/dist/tokens/types.d.ts.map +1 -1
  66. package/dist/tokens/verify.d.ts +2 -2
  67. package/dist/tokens/verify.d.ts.map +1 -1
  68. package/dist/utils/admin-init.d.ts +1 -0
  69. package/dist/utils/admin-init.d.ts.map +1 -1
  70. package/package.json +3 -3
  71. package/dist/chunk-DDUNOEIM.mjs.map +0 -1
  72. package/dist/chunk-DFAJCSBJ.mjs.map +0 -1
  73. package/dist/chunk-MS6L7M3C.mjs.map +0 -1
  74. /package/dist/{chunk-ASGV4MFO.mjs.map → chunk-GFH5CXQR.mjs.map} +0 -0
package/dist/index.js CHANGED
@@ -78,6 +78,7 @@ var QueryParameters = {
78
78
  };
79
79
  var Headers2 = {
80
80
  Accept: "accept",
81
+ AppCheckToken: "x-firebase-appcheck",
81
82
  AuthMessage: "x-ternsecure-auth-message",
82
83
  Authorization: "authorization",
83
84
  AuthReason: "x-ternsecure-auth-reason",
@@ -371,6 +372,84 @@ var AbstractAPI = class {
371
372
  }
372
373
  };
373
374
 
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()}`
381
+ };
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");
387
+ }
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;
415
+ }
416
+ }
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");
421
+ }
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;
449
+ }
450
+ }
451
+ };
452
+
374
453
  // src/fireRestApi/endpoints/EmailApi.ts
375
454
  var EmailApi = class extends AbstractAPI {
376
455
  async verifyEmailVerification(apiKey, params) {
@@ -424,6 +503,30 @@ var PasswordApi = class extends AbstractAPI {
424
503
  }
425
504
  };
426
505
 
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);
526
+ }
527
+ }
528
+ };
529
+
427
530
  // src/fireRestApi/endpoints/SignInTokenApi.ts
428
531
  var SignInTokenApi = class extends AbstractAPI {
429
532
  async createCustomToken(apiKey, params) {
@@ -464,11 +567,14 @@ var SignUpApi = class extends AbstractAPI {
464
567
  var TokenApi = class extends AbstractAPI {
465
568
  async refreshToken(apiKey, params) {
466
569
  this.requireApiKey(apiKey);
467
- const { refresh_token, request_origin, ...restParams } = params;
570
+ const { refresh_token, request_origin, app_check_token, ...restParams } = params;
468
571
  const headers = {};
469
572
  if (request_origin) {
470
573
  headers["Referer"] = request_origin;
471
574
  }
575
+ if (app_check_token) {
576
+ headers["X-Firebase-AppCheck"] = app_check_token;
577
+ }
472
578
  const bodyParams = {
473
579
  grant_type: "refresh_token",
474
580
  refresh_token,
@@ -488,13 +594,23 @@ var TokenApi = class extends AbstractAPI {
488
594
  if (options?.referer) {
489
595
  headers["Referer"] = options.referer;
490
596
  }
491
- return this.request({
597
+ if (options?.appCheckToken) {
598
+ headers["X-Firebase-AppCheck"] = options.appCheckToken;
599
+ }
600
+ const response = await this.request({
492
601
  endpoint: "signInWithCustomToken",
493
602
  method: "POST",
494
603
  apiKey,
495
604
  bodyParams: params,
496
605
  headerParams: headers
497
606
  });
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;
498
614
  }
499
615
  };
500
616
 
@@ -556,6 +672,9 @@ var signInWithPassword = (apiKey) => {
556
672
  var signUpEndpoint = (apiKey) => {
557
673
  return `https://identitytoolkit.googleapis.com/v1/accounts:signUp?key=${apiKey}`;
558
674
  };
675
+ var sendOobCode = (apiKey) => {
676
+ return `https://identitytoolkit.googleapis.com/v1/accounts:sendOobCode?key=${apiKey}`;
677
+ };
559
678
  var getCustomTokenEndpoint = (apiKey) => {
560
679
  if (useEmulator() && FIREBASE_AUTH_EMULATOR_HOST) {
561
680
  let protocol = "http://";
@@ -566,9 +685,6 @@ var getCustomTokenEndpoint = (apiKey) => {
566
685
  }
567
686
  return `https://identitytoolkit.googleapis.com/v1/accounts:signInWithCustomToken?key=${apiKey}`;
568
687
  };
569
- var passwordResetEndpoint = (apiKey) => {
570
- return `https://identitytoolkit.googleapis.com/v1/accounts:resetPassword?key=${apiKey}`;
571
- };
572
688
 
573
689
  // src/fireRestApi/request.ts
574
690
  var FIREBASE_ENDPOINT_MAP = {
@@ -576,8 +692,8 @@ var FIREBASE_ENDPOINT_MAP = {
576
692
  signInWithPassword,
577
693
  signUp: signUpEndpoint,
578
694
  signInWithCustomToken: getCustomTokenEndpoint,
579
- passwordReset: passwordResetEndpoint,
580
- sendOobCode: signInWithPassword,
695
+ passwordReset: sendOobCode,
696
+ sendOobCode,
581
697
  lookup: lookupEndpoint
582
698
  };
583
699
  function createRequest(options) {
@@ -626,8 +742,18 @@ function createRequest(options) {
626
742
  ...body
627
743
  });
628
744
  }
629
- const isJSONResponse = res?.headers && res.headers?.get(constants.Headers.ContentType) === constants.ContentTypes.Json;
630
- const responseBody = await (isJSONResponse ? res.json() : res.text());
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
+ }
631
757
  if (!res.ok) {
632
758
  return {
633
759
  data: null,
@@ -708,9 +834,11 @@ function parseError(error) {
708
834
  function createFireApi(options) {
709
835
  const request = createRequest(options);
710
836
  return {
837
+ appCheck: new AppCheckApi(request),
711
838
  email: new EmailApi(request),
712
839
  password: new PasswordApi(request),
713
- signIn: new SignInTokenApi(request),
840
+ signIn: new SignInApi(request),
841
+ signInToken: new SignInTokenApi(request),
714
842
  signUp: new SignUpApi(request),
715
843
  tokens: new TokenApi(request),
716
844
  userData: new UserData(request)
@@ -1069,33 +1197,44 @@ async function verifySignature(jwt, key) {
1069
1197
  }
1070
1198
  }
1071
1199
  function ternDecodeJwt(token) {
1072
- const header = (0, import_jose3.decodeProtectedHeader)(token);
1073
- const payload = (0, import_jose3.decodeJwt)(token);
1074
- const tokenParts = (token || "").toString().split(".");
1075
- if (tokenParts.length !== 3) {
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) {
1205
+ return {
1206
+ errors: [
1207
+ new TokenVerificationError({
1208
+ reason: TokenVerificationErrorReason.TokenInvalid,
1209
+ message: "Invalid JWT format"
1210
+ })
1211
+ ]
1212
+ };
1213
+ }
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) {
1076
1229
  return {
1077
1230
  errors: [
1078
1231
  new TokenVerificationError({
1079
1232
  reason: TokenVerificationErrorReason.TokenInvalid,
1080
- message: "Invalid JWT format"
1233
+ message: `${error.message || "Invalid Token or Protected Header formatting"} (Token length: ${token?.length}, First 10 chars: ${token?.substring(0, 10)}...)`
1081
1234
  })
1082
1235
  ]
1083
1236
  };
1084
1237
  }
1085
- const [rawHeader, rawPayload, rawSignature] = tokenParts;
1086
- const signature = base64url.parse(rawSignature, { loose: true });
1087
- const data = {
1088
- header,
1089
- payload,
1090
- signature,
1091
- raw: {
1092
- header: rawHeader,
1093
- payload: rawPayload,
1094
- signature: rawSignature,
1095
- text: token
1096
- }
1097
- };
1098
- return { data };
1099
1238
  }
1100
1239
  async function verifyJwt(token, options) {
1101
1240
  const { key } = options;
@@ -1253,6 +1392,143 @@ async function verifyToken(token, options) {
1253
1392
  }
1254
1393
  }
1255
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
+ };
1404
+ }
1405
+
1406
+ // src/jwt/jwt.ts
1407
+ var import_jose4 = require("jose");
1408
+
1409
+ // src/jwt/signJwt.ts
1410
+ var import_jose5 = require("jose");
1411
+ var ALGORITHM_RS256 = "RS256";
1412
+ async function ternSignJwt(opts) {
1413
+ const { payload, privateKey, keyId } = opts;
1414
+ let key;
1415
+ try {
1416
+ key = await (0, import_jose5.importPKCS8)(privateKey, ALGORITHM_RS256);
1417
+ } catch (error) {
1418
+ throw new TokenVerificationError({
1419
+ message: `Failed to import private key: ${error.message}`,
1420
+ reason: TokenVerificationErrorReason.TokenInvalid
1421
+ });
1422
+ }
1423
+ return new import_jose5.SignJWT(payload).setProtectedHeader({ alg: ALGORITHM_RS256, kid: keyId }).sign(key);
1424
+ }
1425
+
1426
+ // src/jwt/index.ts
1427
+ var ternDecodeJwt2 = createJwtGuard(ternDecodeJwt);
1428
+
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;
1435
+
1436
+ // src/auth/utils.ts
1437
+ async function getDetailFromResponse(response) {
1438
+ const json = await response.json();
1439
+ if (!json) {
1440
+ return "Missing error payload";
1441
+ }
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 + ")";
1445
+ }
1446
+ return detail;
1447
+ }
1448
+ async function fetchJson(url, init) {
1449
+ return (await fetchAny(url, init)).json();
1450
+ }
1451
+ async function fetchAny(url, init) {
1452
+ const response = await fetch(url, init);
1453
+ if (!response.ok) {
1454
+ throw new Error(await getDetailFromResponse(response));
1455
+ }
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");
1465
+ }
1466
+ return {
1467
+ accessToken: json.access_token,
1468
+ expirationTime: Date.now() + json.expires_in * 1e3
1469
+ };
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;
1479
+ }
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
1491
+ });
1492
+ };
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
1528
+ });
1529
+ };
1530
+ };
1531
+
1256
1532
  // src/auth/getauth.ts
1257
1533
  var API_KEY_ERROR = "API Key is required";
1258
1534
  var NO_DATA_ERROR = "No token data received";
@@ -1267,9 +1543,17 @@ function parseFirebaseResponse(data) {
1267
1543
  return data;
1268
1544
  }
1269
1545
  function getAuth(options) {
1270
- const { apiKey } = options;
1546
+ const { apiKey, firebaseAdminConfig } = options;
1271
1547
  const firebaseApiKey = options.firebaseConfig?.apiKey;
1272
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
+ }
1273
1557
  async function getUserData(idToken, localId) {
1274
1558
  if (!effectiveApiKey) {
1275
1559
  throw new Error(API_KEY_ERROR);
@@ -1311,23 +1595,23 @@ function getAuth(options) {
1311
1595
  if (!effectiveApiKey) {
1312
1596
  throw new Error("API Key is required to create custom token");
1313
1597
  }
1314
- const response = await options.apiClient?.tokens.exchangeCustomForIdAndRefreshTokens(
1598
+ const data = await options.apiClient?.tokens.exchangeCustomForIdAndRefreshTokens(
1315
1599
  effectiveApiKey,
1316
1600
  {
1317
1601
  token: customToken,
1318
1602
  returnSecureToken: true
1319
1603
  },
1320
1604
  {
1321
- referer: opts.referer
1605
+ referer: opts.referer,
1606
+ appCheckToken: opts.appCheckToken
1322
1607
  }
1323
1608
  );
1324
- if (!response?.data) {
1609
+ if (!data) {
1325
1610
  throw new Error("No data received from Firebase token exchange");
1326
1611
  }
1327
- const parsedData = parseFirebaseResponse(response.data);
1328
1612
  return {
1329
- idToken: parsedData.idToken,
1330
- refreshToken: parsedData.refreshToken
1613
+ idToken: data.idToken,
1614
+ refreshToken: data.refreshToken
1331
1615
  };
1332
1616
  }
1333
1617
  async function createCustomIdAndRefreshToken(idToken, opts) {
@@ -1341,7 +1625,8 @@ function getAuth(options) {
1341
1625
  source_sign_in_provider: data.firebase.sign_in_provider
1342
1626
  });
1343
1627
  const idAndRefreshTokens = await customForIdAndRefreshToken(customToken, {
1344
- referer: opts.referer
1628
+ referer: opts.referer,
1629
+ appCheckToken: opts.appCheckToken
1345
1630
  });
1346
1631
  const decodedCustomIdToken = await verifyToken(idAndRefreshTokens.idToken, options);
1347
1632
  if (decodedCustomIdToken.errors) {
@@ -1353,11 +1638,60 @@ function getAuth(options) {
1353
1638
  auth_time: decodedCustomIdToken.data.auth_time
1354
1639
  };
1355
1640
  }
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
+ }
1653
+ 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
+ }
1678
+ return {
1679
+ data: {
1680
+ token: appCheckResponse.token,
1681
+ ttl: appCheckResponse.ttl
1682
+ },
1683
+ error: null
1684
+ };
1685
+ } catch (error) {
1686
+ return { data: null, error };
1687
+ }
1688
+ }
1356
1689
  return {
1357
1690
  getUserData,
1358
1691
  customForIdAndRefreshToken,
1359
1692
  createCustomIdAndRefreshToken,
1360
- refreshExpiredIdToken
1693
+ refreshExpiredIdToken,
1694
+ exchangeAppCheckToken
1361
1695
  };
1362
1696
  }
1363
1697
 
@@ -1388,6 +1722,7 @@ var RequestProcessorContext = class {
1388
1722
  this.userAgent = this.getHeader(constants.Headers.UserAgent);
1389
1723
  this.secFetchDest = this.getHeader(constants.Headers.SecFetchDest);
1390
1724
  this.accept = this.getHeader(constants.Headers.Accept);
1725
+ this.appCheckToken = this.getHeader(constants.Headers.AppCheckToken);
1391
1726
  }
1392
1727
  initCookieValues() {
1393
1728
  const isProduction = process.env.NODE_ENV === "production";
@@ -1451,14 +1786,13 @@ function isRequestForRefresh(error, context, request) {
1451
1786
  }
1452
1787
  async function authenticateRequest(request, options) {
1453
1788
  const context = createRequestProcessor(createTernSecureRequest(request), options);
1454
- const { refreshTokenInCookie } = context;
1789
+ const { refreshTokenInCookie, appCheckToken } = context;
1455
1790
  const { refreshExpiredIdToken } = getAuth(options);
1456
1791
  function checkSessionTimeout(authTimeValue) {
1457
1792
  const defaultMaxAgeSeconds = convertToSeconds("5 days");
1458
1793
  const REAUTH_PERIOD_SECONDS = context.session?.maxAge ? convertToSeconds(context.session.maxAge) : defaultMaxAgeSeconds;
1459
1794
  const currentTime = Math.floor(Date.now() / 1e3);
1460
1795
  const authAge = currentTime - authTimeValue;
1461
- console.log("Current time:", currentTime, "Auth age:", authAge, "Reauth period (s):", REAUTH_PERIOD_SECONDS);
1462
1796
  if (authTimeValue > 0 && authAge > REAUTH_PERIOD_SECONDS) {
1463
1797
  return signedOut(context, AuthErrorReason.AuthTimeout, "Authentication expired");
1464
1798
  }
@@ -1475,7 +1809,8 @@ async function authenticateRequest(request, options) {
1475
1809
  };
1476
1810
  }
1477
1811
  return await refreshExpiredIdToken(refreshTokenInCookie, {
1478
- referer: context.ternUrl.origin
1812
+ referer: context.ternUrl.origin,
1813
+ appCheckToken
1479
1814
  });
1480
1815
  }
1481
1816
  async function handleRefresh() {
@@ -1577,7 +1912,24 @@ async function authenticateRequest(request, options) {
1577
1912
  if (errors) {
1578
1913
  throw errors[0];
1579
1914
  }
1580
- const signedInRequestState = signedIn(context, data, void 0, context.idTokenInCookie);
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);
1581
1933
  return signedInRequestState;
1582
1934
  } catch (err) {
1583
1935
  return handleError(err, "cookie");
@@ -1591,7 +1943,23 @@ async function authenticateRequest(request, options) {
1591
1943
  if (errors) {
1592
1944
  throw errors[0];
1593
1945
  }
1594
- const signedInRequestState = signedIn(context, data, void 0, sessionTokenInHeader);
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);
1595
1963
  return signedInRequestState;
1596
1964
  } catch (err) {
1597
1965
  return handleError(err, "header");
@@ -1605,6 +1973,16 @@ async function authenticateRequest(request, options) {
1605
1973
  if (isRequestForRefresh(err, context, request)) {
1606
1974
  const { data, error } = await handleRefresh();
1607
1975
  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
+ }
1608
1986
  return signedIn(context, data.decoded, data.headers, data.token);
1609
1987
  }
1610
1988
  if (error?.cause?.reason) {