@tern-secure/backend 1.2.0-canary.v20251030165007 → 1.2.0-canary.v20251125170702

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 (58) hide show
  1. package/dist/__tests__/request.test.d.ts +2 -0
  2. package/dist/__tests__/request.test.d.ts.map +1 -0
  3. package/dist/admin/index.d.ts +1 -0
  4. package/dist/admin/index.d.ts.map +1 -1
  5. package/dist/admin/index.js +68 -8
  6. package/dist/admin/index.js.map +1 -1
  7. package/dist/admin/index.mjs +53 -8
  8. package/dist/admin/index.mjs.map +1 -1
  9. package/dist/admin/nextSessionTernSecure.d.ts.map +1 -1
  10. package/dist/admin/sessionTernSecure.d.ts.map +1 -1
  11. package/dist/admin/user.d.ts +16 -0
  12. package/dist/admin/user.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 +49 -31
  16. package/dist/auth/index.js.map +1 -1
  17. package/dist/auth/index.mjs +3 -3
  18. package/dist/{chunk-IBABNFOK.mjs → chunk-ASGV4MFO.mjs} +2 -2
  19. package/dist/{chunk-5AP2WM3W.mjs → chunk-DDUNOEIM.mjs} +20 -31
  20. package/dist/chunk-DDUNOEIM.mjs.map +1 -0
  21. package/dist/{chunk-VY5FVZL2.mjs → chunk-DFAJCSBJ.mjs} +17 -3
  22. package/dist/chunk-DFAJCSBJ.mjs.map +1 -0
  23. package/dist/{chunk-A5G3CWO5.mjs → chunk-MS6L7M3C.mjs} +9 -4
  24. package/dist/chunk-MS6L7M3C.mjs.map +1 -0
  25. package/dist/constants.d.ts +13 -1
  26. package/dist/constants.d.ts.map +1 -1
  27. package/dist/fireRestApi/createFireApi.d.ts +3 -2
  28. package/dist/fireRestApi/createFireApi.d.ts.map +1 -1
  29. package/dist/fireRestApi/endpointUrl.d.ts +2 -1
  30. package/dist/fireRestApi/endpointUrl.d.ts.map +1 -1
  31. package/dist/fireRestApi/endpoints/SignInApi.d.ts +11 -0
  32. package/dist/fireRestApi/endpoints/SignInApi.d.ts.map +1 -0
  33. package/dist/fireRestApi/endpoints/index.d.ts +1 -0
  34. package/dist/fireRestApi/endpoints/index.d.ts.map +1 -1
  35. package/dist/fireRestApi/resources/EmailAddress.d.ts +7 -0
  36. package/dist/fireRestApi/resources/EmailAddress.d.ts.map +1 -0
  37. package/dist/fireRestApi/resources/JSON.d.ts +4 -0
  38. package/dist/fireRestApi/resources/JSON.d.ts.map +1 -1
  39. package/dist/index.js +186 -45
  40. package/dist/index.js.map +1 -1
  41. package/dist/index.mjs +151 -17
  42. package/dist/index.mjs.map +1 -1
  43. package/dist/jwt/index.js +19 -30
  44. package/dist/jwt/index.js.map +1 -1
  45. package/dist/jwt/index.mjs +1 -1
  46. package/dist/jwt/verifyJwt.d.ts.map +1 -1
  47. package/dist/tokens/authstate.d.ts +16 -4
  48. package/dist/tokens/authstate.d.ts.map +1 -1
  49. package/dist/tokens/c-authenticateRequestProcessor.d.ts +5 -0
  50. package/dist/tokens/c-authenticateRequestProcessor.d.ts.map +1 -1
  51. package/dist/tokens/request.d.ts.map +1 -1
  52. package/dist/tokens/types.d.ts +4 -0
  53. package/dist/tokens/types.d.ts.map +1 -1
  54. package/package.json +9 -7
  55. package/dist/chunk-5AP2WM3W.mjs.map +0 -1
  56. package/dist/chunk-A5G3CWO5.mjs.map +0 -1
  57. package/dist/chunk-VY5FVZL2.mjs.map +0 -1
  58. /package/dist/{chunk-IBABNFOK.mjs.map → chunk-ASGV4MFO.mjs.map} +0 -0
package/dist/index.mjs CHANGED
@@ -1,19 +1,20 @@
1
1
  import {
2
2
  createTernSecureRequest
3
- } from "./chunk-IBABNFOK.mjs";
3
+ } from "./chunk-ASGV4MFO.mjs";
4
4
  import {
5
5
  getAuth,
6
6
  verifyToken
7
- } from "./chunk-A5G3CWO5.mjs";
7
+ } from "./chunk-MS6L7M3C.mjs";
8
8
  import {
9
9
  constants
10
- } from "./chunk-VY5FVZL2.mjs";
10
+ } from "./chunk-DFAJCSBJ.mjs";
11
11
  import {
12
12
  RefreshTokenErrorReason,
13
13
  TokenVerificationError,
14
14
  TokenVerificationErrorReason,
15
- mapJwtPayloadToDecodedIdToken
16
- } from "./chunk-5AP2WM3W.mjs";
15
+ mapJwtPayloadToDecodedIdToken,
16
+ ternDecodeJwt
17
+ } from "./chunk-DDUNOEIM.mjs";
17
18
 
18
19
  // src/createRedirect.ts
19
20
  var buildUrl = (_baseUrl, _targetUrl, _returnBackUrl) => {
@@ -76,16 +77,20 @@ var createRedirect = (params) => {
76
77
  // src/tokens/authstate.ts
77
78
  var AuthStatus = {
78
79
  SignedIn: "signed-in",
79
- SignedOut: "signed-out"
80
+ SignedOut: "signed-out",
81
+ Handshake: "handshake"
80
82
  };
81
83
  var AuthErrorReason = {
82
- SessionTokenAndUATMissing: "session-token-and-uat-missing",
84
+ AuthTimeout: "auth-timeout",
85
+ SessionTokenAndAuthMissing: "session-token-and-aut-missing",
83
86
  SessionTokenMissing: "session-token-missing",
84
87
  SessionTokenExpired: "session-token-expired",
85
- SessionTokenIATBeforeClientUAT: "session-token-iat-before-client-uat",
88
+ SessionTokenIATBeforeTernAUT: "session-token-iat-before-tern-aut",
86
89
  SessionTokenNBF: "session-token-nbf",
87
90
  SessionTokenIatInTheFuture: "session-token-iat-in-the-future",
88
- ActiveOrganizationMismatch: "active-organization-mismatch",
91
+ SessionTokenWithoutTernAUT: "session-token-but-no-tern-uat",
92
+ TernAutWithoutSessionToken: "tern-aut-but-no-session-token",
93
+ SyncRequired: "sync-required",
89
94
  UnexpectedError: "unexpected-error"
90
95
  };
91
96
  function createHasAuthorization(decodedIdToken) {
@@ -137,6 +142,7 @@ function signedIn(authCtx, sessionClaims, headers = new Headers(), token) {
137
142
  const authObject = signedInAuthObject(token, sessionClaims);
138
143
  return {
139
144
  status: AuthStatus.SignedIn,
145
+ message: null,
140
146
  reason: null,
141
147
  signInUrl: authCtx.signInUrl || "",
142
148
  signUpUrl: authCtx.signUpUrl || "",
@@ -161,6 +167,12 @@ function signedOut(authCtx, reason, message = "", headers = new Headers()) {
161
167
  }
162
168
  var decorateHeaders = (requestState) => {
163
169
  const headers = new Headers(requestState.headers || {});
170
+ if (requestState.message) {
171
+ try {
172
+ headers.set(constants.Headers.AuthMessage, requestState.message);
173
+ } catch {
174
+ }
175
+ }
164
176
  if (requestState.reason) {
165
177
  try {
166
178
  headers.set(constants.Headers.AuthReason, requestState.reason);
@@ -242,6 +254,30 @@ var PasswordApi = class extends AbstractAPI {
242
254
  }
243
255
  };
244
256
 
257
+ // src/fireRestApi/endpoints/SignInApi.ts
258
+ var SignInApi = class extends AbstractAPI {
259
+ async resetPasswordEmail(apiKey, params) {
260
+ try {
261
+ this.requireApiKey(apiKey);
262
+ const { ...restParams } = params;
263
+ const response = await this.request({
264
+ endpoint: "sendOobCode",
265
+ method: "POST",
266
+ apiKey,
267
+ bodyParams: restParams
268
+ });
269
+ if (response.errors) {
270
+ const errorMessage = response.errors[0]?.message || "Failed to send reset password email";
271
+ throw new Error(errorMessage);
272
+ }
273
+ return response.data;
274
+ } catch (error) {
275
+ const contextualMessage = `Failed to send reset password email: ${error instanceof Error ? error.message : "Unknown error"}`;
276
+ throw new Error(contextualMessage);
277
+ }
278
+ }
279
+ };
280
+
245
281
  // src/fireRestApi/endpoints/SignInTokenApi.ts
246
282
  var SignInTokenApi = class extends AbstractAPI {
247
283
  async createCustomToken(apiKey, params) {
@@ -374,6 +410,9 @@ var signInWithPassword = (apiKey) => {
374
410
  var signUpEndpoint = (apiKey) => {
375
411
  return `https://identitytoolkit.googleapis.com/v1/accounts:signUp?key=${apiKey}`;
376
412
  };
413
+ var sendOobCode = (apiKey) => {
414
+ return `https://identitytoolkit.googleapis.com/v1/accounts:sendOobCode?key=${apiKey}`;
415
+ };
377
416
  var getCustomTokenEndpoint = (apiKey) => {
378
417
  if (useEmulator() && FIREBASE_AUTH_EMULATOR_HOST) {
379
418
  let protocol = "http://";
@@ -384,9 +423,6 @@ var getCustomTokenEndpoint = (apiKey) => {
384
423
  }
385
424
  return `https://identitytoolkit.googleapis.com/v1/accounts:signInWithCustomToken?key=${apiKey}`;
386
425
  };
387
- var passwordResetEndpoint = (apiKey) => {
388
- return `https://identitytoolkit.googleapis.com/v1/accounts:resetPassword?key=${apiKey}`;
389
- };
390
426
 
391
427
  // src/fireRestApi/request.ts
392
428
  var FIREBASE_ENDPOINT_MAP = {
@@ -394,8 +430,8 @@ var FIREBASE_ENDPOINT_MAP = {
394
430
  signInWithPassword,
395
431
  signUp: signUpEndpoint,
396
432
  signInWithCustomToken: getCustomTokenEndpoint,
397
- passwordReset: passwordResetEndpoint,
398
- sendOobCode: signInWithPassword,
433
+ passwordReset: sendOobCode,
434
+ sendOobCode,
399
435
  lookup: lookupEndpoint
400
436
  };
401
437
  function createRequest(options) {
@@ -528,7 +564,8 @@ function createFireApi(options) {
528
564
  return {
529
565
  email: new EmailApi(request),
530
566
  password: new PasswordApi(request),
531
- signIn: new SignInTokenApi(request),
567
+ signIn: new SignInApi(request),
568
+ signInToken: new SignInTokenApi(request),
532
569
  signUp: new SignUpApi(request),
533
570
  tokens: new TokenApi(request),
534
571
  userData: new UserData(request)
@@ -548,6 +585,9 @@ function mergePreDefinedOptions(userOptions = {}) {
548
585
  };
549
586
  }
550
587
 
588
+ // src/tokens/request.ts
589
+ import { ms } from "@tern-secure/shared/ms";
590
+
551
591
  // src/tokens/c-authenticateRequestProcessor.ts
552
592
  var RequestProcessorContext = class {
553
593
  constructor(ternSecureRequest, options) {
@@ -555,6 +595,7 @@ var RequestProcessorContext = class {
555
595
  this.options = options;
556
596
  this.initHeaderValues();
557
597
  this.initCookieValues();
598
+ this.initHandshakeValues();
558
599
  this.initUrlValues();
559
600
  Object.assign(this, options);
560
601
  this.ternUrl = this.ternSecureRequest.ternUrl;
@@ -583,6 +624,11 @@ var RequestProcessorContext = class {
583
624
  this.refreshTokenInCookie = this.getCookie(`${defaultPrefix}${constants.Cookies.Refresh}`);
584
625
  this.csrfTokenInCookie = this.getCookie(constants.Cookies.CsrfToken);
585
626
  this.customTokenInCookie = this.getCookie(constants.Cookies.Custom);
627
+ this.ternAuth = Number.parseInt(this.getCookie(constants.Cookies.TernAut) || "0", 10);
628
+ }
629
+ initHandshakeValues() {
630
+ this.handshakeToken = this.getQueryParam(constants.QueryParameters.Handshake) || this.getCookie(constants.Cookies.Handshake);
631
+ this.handshakeNonce = this.getQueryParam(constants.QueryParameters.HandshakeNonce) || this.getCookie(constants.Cookies.HandshakeNonce);
586
632
  }
587
633
  initUrlValues() {
588
634
  this.method = this.ternSecureRequest.method;
@@ -590,6 +636,9 @@ var RequestProcessorContext = class {
590
636
  this.endpoint = this.pathSegments[2];
591
637
  this.subEndpoint = this.pathSegments[3];
592
638
  }
639
+ getQueryParam(name) {
640
+ return this.ternSecureRequest.ternUrl.searchParams.get(name);
641
+ }
593
642
  getHeader(name) {
594
643
  return this.ternSecureRequest.headers.get(name) || void 0;
595
644
  }
@@ -621,6 +670,9 @@ import { getCookieName as getCookieNameEnvironment, getCookiePrefix } from "@ter
621
670
  function hasAuthorizationHeader(request) {
622
671
  return request.headers.has("Authorization");
623
672
  }
673
+ function convertToSeconds(value) {
674
+ return ms(value) / 1e3;
675
+ }
624
676
  function isRequestForRefresh(error, context, request) {
625
677
  return error.reason === TokenVerificationErrorReason.TokenExpired && !!context.refreshTokenInCookie && request.method === "GET";
626
678
  }
@@ -628,6 +680,16 @@ async function authenticateRequest(request, options) {
628
680
  const context = createRequestProcessor(createTernSecureRequest(request), options);
629
681
  const { refreshTokenInCookie } = context;
630
682
  const { refreshExpiredIdToken } = getAuth(options);
683
+ function checkSessionTimeout(authTimeValue) {
684
+ const defaultMaxAgeSeconds = convertToSeconds("5 days");
685
+ const REAUTH_PERIOD_SECONDS = context.session?.maxAge ? convertToSeconds(context.session.maxAge) : defaultMaxAgeSeconds;
686
+ const currentTime = Math.floor(Date.now() / 1e3);
687
+ const authAge = currentTime - authTimeValue;
688
+ if (authTimeValue > 0 && authAge > REAUTH_PERIOD_SECONDS) {
689
+ return signedOut(context, AuthErrorReason.AuthTimeout, "Authentication expired");
690
+ }
691
+ return null;
692
+ }
631
693
  async function refreshToken() {
632
694
  if (!refreshTokenInCookie) {
633
695
  return {
@@ -649,10 +711,10 @@ async function authenticateRequest(request, options) {
649
711
  }
650
712
  const headers = new Headers();
651
713
  const { idToken } = refreshedData;
652
- const maxAge = 3600;
714
+ const maxAge = 365 * 24 * 60 * 60;
653
715
  const cookiePrefix = getCookiePrefix();
654
716
  const idTokenCookieName = getCookieNameEnvironment(constants.Cookies.IdToken, cookiePrefix);
655
- const baseCookieAttributes = "HttpOnly; Secure; SameSite=Strict; Path=/";
717
+ const baseCookieAttributes = `HttpOnly; Secure; SameSite=Strict; Max-Age=${maxAge}; Path=/`;
656
718
  const idTokenCookie = `${idTokenCookieName}=${idToken}; ${baseCookieAttributes};`;
657
719
  headers.append("Set-Cookie", idTokenCookie);
658
720
  const { data: decoded, errors } = await verifyToken(idToken, options);
@@ -664,7 +726,78 @@ async function authenticateRequest(request, options) {
664
726
  }
665
727
  return { data: { decoded, token: idToken, headers }, error: null };
666
728
  }
729
+ async function handleLocalHandshakeWithErrorCheck(context2, reason, message, skipSessionCheck = false) {
730
+ const hasRefreshTokenInCookie = !!context2.refreshTokenInCookie;
731
+ if (!hasRefreshTokenInCookie) {
732
+ return signedOut(context2, reason, "Refresh token missing in cookie");
733
+ }
734
+ if (reason === AuthErrorReason.TernAutWithoutSessionToken) {
735
+ if (!skipSessionCheck) {
736
+ const sessionTimeoutResult = checkSessionTimeout(context2.ternAuth);
737
+ if (sessionTimeoutResult) {
738
+ return sessionTimeoutResult;
739
+ }
740
+ }
741
+ const { data, error } = await handleRefresh();
742
+ if (data) {
743
+ return signedIn(context2, data.decoded, data.headers, data.token);
744
+ }
745
+ return signedOut(context2, reason, "Failed to refresh idToken");
746
+ }
747
+ if (reason === AuthErrorReason.SessionTokenWithoutTernAUT || reason === AuthErrorReason.SessionTokenIATBeforeTernAUT) {
748
+ const { data, errors } = ternDecodeJwt(context2.idTokenInCookie);
749
+ if (errors) {
750
+ throw errors[0];
751
+ }
752
+ const authTime = data.payload.auth_time;
753
+ if (!authTime || typeof authTime !== "number") {
754
+ return signedOut(context2, reason, "Token missing auth_time");
755
+ }
756
+ if (!skipSessionCheck) {
757
+ const sessionTimeoutResult = checkSessionTimeout(authTime);
758
+ if (sessionTimeoutResult) {
759
+ return sessionTimeoutResult;
760
+ }
761
+ }
762
+ const { data: verifiedToken, errors: verifyErrors } = await verifyToken(context2.idTokenInCookie, options);
763
+ if (verifyErrors) {
764
+ throw verifyErrors[0];
765
+ }
766
+ const headers = new Headers();
767
+ const oneYearInSeconds = 365 * 24 * 60 * 60;
768
+ const ternAutCookie = `${constants.Cookies.TernAut}=${authTime}; Max-Age=${oneYearInSeconds}; Secure; SameSite=Strict; Path=/`;
769
+ headers.append("Set-Cookie", ternAutCookie);
770
+ return signedIn(context2, verifiedToken, headers, context2.idTokenInCookie);
771
+ }
772
+ return signedOut(context2, reason, message);
773
+ }
667
774
  async function authenticateRequestWithTokenInCookie() {
775
+ const hasTernAuth = context.ternAuth;
776
+ const hasIdTokenInCookie = !!context.idTokenInCookie;
777
+ if (!hasTernAuth && !hasIdTokenInCookie) {
778
+ return signedOut(context, AuthErrorReason.SessionTokenAndAuthMissing);
779
+ }
780
+ if (!hasTernAuth && hasIdTokenInCookie) {
781
+ return await handleLocalHandshakeWithErrorCheck(context, AuthErrorReason.SessionTokenWithoutTernAUT, "");
782
+ }
783
+ if (hasTernAuth && !hasIdTokenInCookie) {
784
+ return await handleLocalHandshakeWithErrorCheck(context, AuthErrorReason.TernAutWithoutSessionToken, "");
785
+ }
786
+ const sessionTimeoutResult = checkSessionTimeout(context.ternAuth);
787
+ if (sessionTimeoutResult) {
788
+ return sessionTimeoutResult;
789
+ }
790
+ const { data: decodedResult, errors: decodeErrors } = ternDecodeJwt(context.idTokenInCookie);
791
+ if (decodeErrors) {
792
+ return handleError(decodeErrors[0], "cookie");
793
+ }
794
+ const tokenIat = decodedResult.payload.iat;
795
+ if (!tokenIat) {
796
+ return signedOut(context, AuthErrorReason.SessionTokenMissing, "");
797
+ }
798
+ if (tokenIat < context.ternAuth) {
799
+ return await handleLocalHandshakeWithErrorCheck(context, AuthErrorReason.SessionTokenIATBeforeTernAUT, "", true);
800
+ }
668
801
  try {
669
802
  const { data, errors } = await verifyToken(context.idTokenInCookie, options);
670
803
  if (errors) {
@@ -675,6 +808,7 @@ async function authenticateRequest(request, options) {
675
808
  } catch (err) {
676
809
  return handleError(err, "cookie");
677
810
  }
811
+ return signedOut(context, AuthErrorReason.UnexpectedError);
678
812
  }
679
813
  async function authenticateRequestWithTokenInHeader() {
680
814
  const { sessionTokenInHeader } = context;