@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.
- package/dist/__tests__/request.test.d.ts +2 -0
- package/dist/__tests__/request.test.d.ts.map +1 -0
- package/dist/admin/index.d.ts +1 -0
- package/dist/admin/index.d.ts.map +1 -1
- package/dist/admin/index.js +68 -8
- package/dist/admin/index.js.map +1 -1
- package/dist/admin/index.mjs +53 -8
- package/dist/admin/index.mjs.map +1 -1
- package/dist/admin/nextSessionTernSecure.d.ts.map +1 -1
- package/dist/admin/sessionTernSecure.d.ts.map +1 -1
- package/dist/admin/user.d.ts +16 -0
- package/dist/admin/user.d.ts.map +1 -0
- package/dist/auth/getauth.d.ts +1 -0
- package/dist/auth/getauth.d.ts.map +1 -1
- package/dist/auth/index.js +49 -31
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/index.mjs +3 -3
- package/dist/{chunk-IBABNFOK.mjs → chunk-ASGV4MFO.mjs} +2 -2
- package/dist/{chunk-5AP2WM3W.mjs → chunk-DDUNOEIM.mjs} +20 -31
- package/dist/chunk-DDUNOEIM.mjs.map +1 -0
- package/dist/{chunk-VY5FVZL2.mjs → chunk-DFAJCSBJ.mjs} +17 -3
- package/dist/chunk-DFAJCSBJ.mjs.map +1 -0
- package/dist/{chunk-A5G3CWO5.mjs → chunk-MS6L7M3C.mjs} +9 -4
- package/dist/chunk-MS6L7M3C.mjs.map +1 -0
- package/dist/constants.d.ts +13 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/fireRestApi/createFireApi.d.ts +3 -2
- package/dist/fireRestApi/createFireApi.d.ts.map +1 -1
- package/dist/fireRestApi/endpointUrl.d.ts +2 -1
- package/dist/fireRestApi/endpointUrl.d.ts.map +1 -1
- package/dist/fireRestApi/endpoints/SignInApi.d.ts +11 -0
- package/dist/fireRestApi/endpoints/SignInApi.d.ts.map +1 -0
- package/dist/fireRestApi/endpoints/index.d.ts +1 -0
- package/dist/fireRestApi/endpoints/index.d.ts.map +1 -1
- package/dist/fireRestApi/resources/EmailAddress.d.ts +7 -0
- package/dist/fireRestApi/resources/EmailAddress.d.ts.map +1 -0
- package/dist/fireRestApi/resources/JSON.d.ts +4 -0
- package/dist/fireRestApi/resources/JSON.d.ts.map +1 -1
- package/dist/index.js +186 -45
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +151 -17
- package/dist/index.mjs.map +1 -1
- package/dist/jwt/index.js +19 -30
- package/dist/jwt/index.js.map +1 -1
- package/dist/jwt/index.mjs +1 -1
- package/dist/jwt/verifyJwt.d.ts.map +1 -1
- package/dist/tokens/authstate.d.ts +16 -4
- package/dist/tokens/authstate.d.ts.map +1 -1
- package/dist/tokens/c-authenticateRequestProcessor.d.ts +5 -0
- package/dist/tokens/c-authenticateRequestProcessor.d.ts.map +1 -1
- package/dist/tokens/request.d.ts.map +1 -1
- package/dist/tokens/types.d.ts +4 -0
- package/dist/tokens/types.d.ts.map +1 -1
- package/package.json +9 -7
- package/dist/chunk-5AP2WM3W.mjs.map +0 -1
- package/dist/chunk-A5G3CWO5.mjs.map +0 -1
- package/dist/chunk-VY5FVZL2.mjs.map +0 -1
- /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-
|
|
3
|
+
} from "./chunk-ASGV4MFO.mjs";
|
|
4
4
|
import {
|
|
5
5
|
getAuth,
|
|
6
6
|
verifyToken
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-MS6L7M3C.mjs";
|
|
8
8
|
import {
|
|
9
9
|
constants
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-DFAJCSBJ.mjs";
|
|
11
11
|
import {
|
|
12
12
|
RefreshTokenErrorReason,
|
|
13
13
|
TokenVerificationError,
|
|
14
14
|
TokenVerificationErrorReason,
|
|
15
|
-
mapJwtPayloadToDecodedIdToken
|
|
16
|
-
|
|
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
|
-
|
|
84
|
+
AuthTimeout: "auth-timeout",
|
|
85
|
+
SessionTokenAndAuthMissing: "session-token-and-aut-missing",
|
|
83
86
|
SessionTokenMissing: "session-token-missing",
|
|
84
87
|
SessionTokenExpired: "session-token-expired",
|
|
85
|
-
|
|
88
|
+
SessionTokenIATBeforeTernAUT: "session-token-iat-before-tern-aut",
|
|
86
89
|
SessionTokenNBF: "session-token-nbf",
|
|
87
90
|
SessionTokenIatInTheFuture: "session-token-iat-in-the-future",
|
|
88
|
-
|
|
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:
|
|
398
|
-
sendOobCode
|
|
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
|
|
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 =
|
|
714
|
+
const maxAge = 365 * 24 * 60 * 60;
|
|
653
715
|
const cookiePrefix = getCookiePrefix();
|
|
654
716
|
const idTokenCookieName = getCookieNameEnvironment(constants.Cookies.IdToken, cookiePrefix);
|
|
655
|
-
const baseCookieAttributes =
|
|
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;
|