@tern-secure/backend 1.2.0-canary.v20251003171521 → 1.2.0-canary.v20251008131428
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/admin/index.mjs +531 -22
- package/dist/admin/index.mjs.map +1 -1
- package/dist/auth/getauth.d.ts +8 -0
- package/dist/auth/getauth.d.ts.map +1 -1
- package/dist/auth/index.js +136 -200
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/index.mjs +4 -67
- package/dist/auth/index.mjs.map +1 -1
- package/dist/{chunk-4SGWLAJG.mjs → chunk-3ZLDOHI2.mjs} +4 -68
- package/dist/chunk-3ZLDOHI2.mjs.map +1 -0
- package/dist/{chunk-WZYVAHZ3.mjs → chunk-SVZUAXAW.mjs} +115 -1
- package/dist/chunk-SVZUAXAW.mjs.map +1 -0
- package/dist/chunk-VSYYHCUV.mjs +71 -0
- package/dist/chunk-VSYYHCUV.mjs.map +1 -0
- package/dist/{chunk-YKIA5EBF.mjs → chunk-YGNTGJTP.mjs} +94 -4
- package/dist/chunk-YGNTGJTP.mjs.map +1 -0
- package/dist/fireRestApi/endpoints/TokenApi.d.ts +2 -2
- package/dist/fireRestApi/endpoints/TokenApi.d.ts.map +1 -1
- package/dist/index.d.ts +12 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +359 -92
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +84 -7
- package/dist/index.mjs.map +1 -1
- package/dist/jwt/customJwt.d.ts +11 -0
- package/dist/jwt/customJwt.d.ts.map +1 -0
- package/dist/jwt/index.d.ts +1 -0
- package/dist/jwt/index.d.ts.map +1 -1
- package/dist/jwt/index.js +104 -0
- package/dist/jwt/index.js.map +1 -1
- package/dist/jwt/index.mjs +7 -1
- package/dist/jwt/index.mjs.map +1 -1
- package/dist/tokens/cookie.d.ts +5 -0
- package/dist/tokens/cookie.d.ts.map +1 -0
- package/dist/tokens/request.d.ts.map +1 -1
- package/dist/tokens/types.d.ts +1 -12
- package/dist/tokens/types.d.ts.map +1 -1
- package/dist/utils/errors.d.ts +12 -0
- package/dist/utils/errors.d.ts.map +1 -1
- package/dist/utils/options.d.ts +3 -3
- package/dist/utils/options.d.ts.map +1 -1
- package/package.json +3 -3
- package/dist/chunk-4SGWLAJG.mjs.map +0 -1
- package/dist/chunk-WZYVAHZ3.mjs.map +0 -1
- package/dist/chunk-XTYEXHJD.mjs +0 -554
- package/dist/chunk-XTYEXHJD.mjs.map +0 -1
- package/dist/chunk-YKIA5EBF.mjs.map +0 -1
- package/dist/tokens/sessionConfig.d.ts +0 -14
- package/dist/tokens/sessionConfig.d.ts.map +0 -1
package/dist/index.js
CHANGED
|
@@ -376,11 +376,22 @@ var SignUpApi = class extends AbstractAPI {
|
|
|
376
376
|
var TokenApi = class extends AbstractAPI {
|
|
377
377
|
async refreshToken(apiKey, params) {
|
|
378
378
|
this.requireApiKey(apiKey);
|
|
379
|
-
const { ...restParams } = params;
|
|
379
|
+
const { refresh_token, request_origin, ...restParams } = params;
|
|
380
|
+
const headers = {};
|
|
381
|
+
if (request_origin) {
|
|
382
|
+
headers["Referer"] = request_origin;
|
|
383
|
+
}
|
|
384
|
+
const bodyParams = {
|
|
385
|
+
grant_type: "refresh_token",
|
|
386
|
+
refresh_token,
|
|
387
|
+
...restParams
|
|
388
|
+
};
|
|
380
389
|
return this.request({
|
|
381
390
|
endpoint: "refreshToken",
|
|
382
391
|
method: "POST",
|
|
383
|
-
|
|
392
|
+
apiKey,
|
|
393
|
+
bodyParams,
|
|
394
|
+
headerParams: headers
|
|
384
395
|
});
|
|
385
396
|
}
|
|
386
397
|
async exchangeCustomForIdAndRefreshTokens(apiKey, params, options) {
|
|
@@ -594,7 +605,117 @@ function createFireApi(options) {
|
|
|
594
605
|
};
|
|
595
606
|
}
|
|
596
607
|
|
|
608
|
+
// src/jwt/customJwt.ts
|
|
609
|
+
var import_jose = require("jose");
|
|
610
|
+
var CustomTokenError = class extends Error {
|
|
611
|
+
constructor(message, code) {
|
|
612
|
+
super(message);
|
|
613
|
+
this.code = code;
|
|
614
|
+
this.name = "CustomTokenError";
|
|
615
|
+
}
|
|
616
|
+
};
|
|
617
|
+
var RESERVED_CLAIMS = [
|
|
618
|
+
"acr",
|
|
619
|
+
"amr",
|
|
620
|
+
"at_hash",
|
|
621
|
+
"aud",
|
|
622
|
+
"auth_time",
|
|
623
|
+
"azp",
|
|
624
|
+
"cnf",
|
|
625
|
+
"c_hash",
|
|
626
|
+
"exp",
|
|
627
|
+
"firebase",
|
|
628
|
+
"iat",
|
|
629
|
+
"iss",
|
|
630
|
+
"jti",
|
|
631
|
+
"nbf",
|
|
632
|
+
"nonce",
|
|
633
|
+
"sub"
|
|
634
|
+
];
|
|
635
|
+
async function createCustomTokenJwt(uid, developerClaims) {
|
|
636
|
+
try {
|
|
637
|
+
const privateKey = process.env.FIREBASE_PRIVATE_KEY;
|
|
638
|
+
const clientEmail = process.env.FIREBASE_CLIENT_EMAIL;
|
|
639
|
+
if (!privateKey || !clientEmail) {
|
|
640
|
+
return {
|
|
641
|
+
errors: [
|
|
642
|
+
new CustomTokenError(
|
|
643
|
+
"Missing FIREBASE_PRIVATE_KEY or FIREBASE_CLIENT_EMAIL environment variables",
|
|
644
|
+
"MISSING_ENV_VARS"
|
|
645
|
+
)
|
|
646
|
+
]
|
|
647
|
+
};
|
|
648
|
+
}
|
|
649
|
+
if (!uid || typeof uid !== "string") {
|
|
650
|
+
return {
|
|
651
|
+
errors: [new CustomTokenError("uid must be a non-empty string", "INVALID_UID")]
|
|
652
|
+
};
|
|
653
|
+
}
|
|
654
|
+
if (uid.length > 128) {
|
|
655
|
+
return {
|
|
656
|
+
errors: [new CustomTokenError("uid must not exceed 128 characters", "UID_TOO_LONG")]
|
|
657
|
+
};
|
|
658
|
+
}
|
|
659
|
+
if (developerClaims) {
|
|
660
|
+
for (const claim of Object.keys(developerClaims)) {
|
|
661
|
+
if (RESERVED_CLAIMS.includes(claim)) {
|
|
662
|
+
return {
|
|
663
|
+
errors: [new CustomTokenError(`Custom claim '${claim}' is reserved`, "RESERVED_CLAIM")]
|
|
664
|
+
};
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
const expiresIn = 3600;
|
|
669
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
670
|
+
const parsedPrivateKey = await (0, import_jose.importPKCS8)(privateKey.replace(/\\n/g, "\n"), "RS256");
|
|
671
|
+
const payload = {
|
|
672
|
+
iss: clientEmail,
|
|
673
|
+
sub: clientEmail,
|
|
674
|
+
aud: "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit",
|
|
675
|
+
iat: now,
|
|
676
|
+
exp: now + expiresIn,
|
|
677
|
+
uid,
|
|
678
|
+
...developerClaims
|
|
679
|
+
};
|
|
680
|
+
const jwt = await new import_jose.SignJWT(payload).setProtectedHeader({ alg: "RS256", typ: "JWT" }).setIssuedAt(now).setExpirationTime(now + expiresIn).setIssuer(clientEmail).setSubject(clientEmail).setAudience(
|
|
681
|
+
"https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit"
|
|
682
|
+
).sign(parsedPrivateKey);
|
|
683
|
+
return {
|
|
684
|
+
data: jwt
|
|
685
|
+
};
|
|
686
|
+
} catch (error) {
|
|
687
|
+
const message = error instanceof Error ? error.message : "Unknown error occurred";
|
|
688
|
+
return {
|
|
689
|
+
errors: [
|
|
690
|
+
new CustomTokenError(`Failed to create custom token: ${message}`, "TOKEN_CREATION_FAILED")
|
|
691
|
+
]
|
|
692
|
+
};
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
async function createCustomToken(uid, developerClaims) {
|
|
696
|
+
const { data, errors } = await createCustomTokenJwt(uid, developerClaims);
|
|
697
|
+
if (errors) {
|
|
698
|
+
throw errors[0];
|
|
699
|
+
}
|
|
700
|
+
return data;
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
// src/jwt/verifyJwt.ts
|
|
704
|
+
var import_jose3 = require("jose");
|
|
705
|
+
|
|
597
706
|
// src/utils/errors.ts
|
|
707
|
+
var RefreshTokenErrorReason = {
|
|
708
|
+
NonEligibleNoCookie: "non-eligible-no-refresh-cookie",
|
|
709
|
+
NonEligibleNonGet: "non-eligible-non-get",
|
|
710
|
+
InvalidSessionToken: "invalid-session-token",
|
|
711
|
+
MissingApiClient: "missing-api-client",
|
|
712
|
+
MissingIdToken: "missing-id-token",
|
|
713
|
+
MissingSessionToken: "missing-session-token",
|
|
714
|
+
MissingRefreshToken: "missing-refresh-token",
|
|
715
|
+
ExpiredIdTokenDecodeFailed: "expired-id-token-decode-failed",
|
|
716
|
+
ExpiredSessionTokenDecodeFailed: "expired-session-token-decode-failed",
|
|
717
|
+
FetchError: "fetch-error"
|
|
718
|
+
};
|
|
598
719
|
var TokenVerificationErrorReason = {
|
|
599
720
|
TokenExpired: "token-expired",
|
|
600
721
|
TokenInvalid: "token-invalid",
|
|
@@ -629,88 +750,6 @@ var TokenVerificationError = class _TokenVerificationError extends Error {
|
|
|
629
750
|
}
|
|
630
751
|
};
|
|
631
752
|
|
|
632
|
-
// src/utils/options.ts
|
|
633
|
-
var defaultOptions = {
|
|
634
|
-
apiKey: void 0,
|
|
635
|
-
apiUrl: void 0,
|
|
636
|
-
apiVersion: void 0
|
|
637
|
-
};
|
|
638
|
-
function mergePreDefinedOptions(userOptions = {}) {
|
|
639
|
-
return {
|
|
640
|
-
...defaultOptions,
|
|
641
|
-
...userOptions
|
|
642
|
-
};
|
|
643
|
-
}
|
|
644
|
-
|
|
645
|
-
// src/tokens/c-authenticateRequestProcessor.ts
|
|
646
|
-
var RequestProcessorContext = class {
|
|
647
|
-
constructor(ternSecureRequest, options) {
|
|
648
|
-
this.ternSecureRequest = ternSecureRequest;
|
|
649
|
-
this.options = options;
|
|
650
|
-
this.initHeaderValues();
|
|
651
|
-
this.initCookieValues();
|
|
652
|
-
this.initUrlValues();
|
|
653
|
-
Object.assign(this, options);
|
|
654
|
-
this.ternUrl = this.ternSecureRequest.ternUrl;
|
|
655
|
-
}
|
|
656
|
-
get request() {
|
|
657
|
-
return this.ternSecureRequest;
|
|
658
|
-
}
|
|
659
|
-
initHeaderValues() {
|
|
660
|
-
this.sessionTokenInHeader = this.parseAuthorizationHeader(
|
|
661
|
-
this.getHeader(constants.Headers.Authorization)
|
|
662
|
-
);
|
|
663
|
-
this.origin = this.getHeader(constants.Headers.Origin);
|
|
664
|
-
this.host = this.getHeader(constants.Headers.Host);
|
|
665
|
-
this.forwardedHost = this.getHeader(constants.Headers.ForwardedHost);
|
|
666
|
-
this.forwardedProto = this.getHeader(constants.Headers.CloudFrontForwardedProto) || this.getHeader(constants.Headers.ForwardedProto);
|
|
667
|
-
this.referrer = this.getHeader(constants.Headers.Referrer);
|
|
668
|
-
this.userAgent = this.getHeader(constants.Headers.UserAgent);
|
|
669
|
-
this.secFetchDest = this.getHeader(constants.Headers.SecFetchDest);
|
|
670
|
-
this.accept = this.getHeader(constants.Headers.Accept);
|
|
671
|
-
}
|
|
672
|
-
initCookieValues() {
|
|
673
|
-
const isProduction = process.env.NODE_ENV === "production";
|
|
674
|
-
const defaultPrefix = isProduction ? "__HOST-" : "__dev_";
|
|
675
|
-
this.sessionTokenInCookie = this.getCookie(constants.Cookies.Session);
|
|
676
|
-
this.idTokenInCookie = this.getCookie(`${defaultPrefix}${constants.Cookies.IdToken}`);
|
|
677
|
-
this.refreshTokenInCookie = this.getCookie(`${defaultPrefix}${constants.Cookies.Refresh}`);
|
|
678
|
-
this.csrfTokenInCookie = this.getCookie(constants.Cookies.CsrfToken);
|
|
679
|
-
this.customTokenInCookie = this.getCookie(constants.Cookies.Custom);
|
|
680
|
-
}
|
|
681
|
-
initUrlValues() {
|
|
682
|
-
this.method = this.ternSecureRequest.method;
|
|
683
|
-
this.pathSegments = this.ternSecureRequest.ternUrl.pathname.split("/").filter(Boolean);
|
|
684
|
-
this.endpoint = this.pathSegments[2];
|
|
685
|
-
this.subEndpoint = this.pathSegments[3];
|
|
686
|
-
}
|
|
687
|
-
getHeader(name) {
|
|
688
|
-
return this.ternSecureRequest.headers.get(name) || void 0;
|
|
689
|
-
}
|
|
690
|
-
getCookie(name) {
|
|
691
|
-
return this.ternSecureRequest.cookies.get(name) || void 0;
|
|
692
|
-
}
|
|
693
|
-
parseAuthorizationHeader(authorizationHeader) {
|
|
694
|
-
if (!authorizationHeader) {
|
|
695
|
-
return void 0;
|
|
696
|
-
}
|
|
697
|
-
const [scheme, token] = authorizationHeader.split(" ", 2);
|
|
698
|
-
if (!token) {
|
|
699
|
-
return scheme;
|
|
700
|
-
}
|
|
701
|
-
if (scheme === "Bearer") {
|
|
702
|
-
return token;
|
|
703
|
-
}
|
|
704
|
-
return void 0;
|
|
705
|
-
}
|
|
706
|
-
};
|
|
707
|
-
var createRequestProcessor = (ternSecureRequest, options) => {
|
|
708
|
-
return new RequestProcessorContext(ternSecureRequest, options);
|
|
709
|
-
};
|
|
710
|
-
|
|
711
|
-
// src/jwt/verifyJwt.ts
|
|
712
|
-
var import_jose2 = require("jose");
|
|
713
|
-
|
|
714
753
|
// src/utils/rfc4648.ts
|
|
715
754
|
var base64url = {
|
|
716
755
|
parse(string, opts) {
|
|
@@ -788,10 +827,10 @@ function stringify(data, encoding, opts = {}) {
|
|
|
788
827
|
}
|
|
789
828
|
|
|
790
829
|
// src/jwt/cryptoKeys.ts
|
|
791
|
-
var
|
|
830
|
+
var import_jose2 = require("jose");
|
|
792
831
|
async function importKey(key, algorithm) {
|
|
793
832
|
if (typeof key === "object") {
|
|
794
|
-
const result = await (0,
|
|
833
|
+
const result = await (0, import_jose2.importJWK)(key, algorithm);
|
|
795
834
|
if (result instanceof Uint8Array) {
|
|
796
835
|
throw new Error("Unexpected Uint8Array result from JWK import");
|
|
797
836
|
}
|
|
@@ -799,13 +838,13 @@ async function importKey(key, algorithm) {
|
|
|
799
838
|
}
|
|
800
839
|
const keyString = key.trim();
|
|
801
840
|
if (keyString.includes("-----BEGIN CERTIFICATE-----")) {
|
|
802
|
-
return await (0,
|
|
841
|
+
return await (0, import_jose2.importX509)(keyString, algorithm);
|
|
803
842
|
}
|
|
804
843
|
if (keyString.includes("-----BEGIN PUBLIC KEY-----")) {
|
|
805
|
-
return await (0,
|
|
844
|
+
return await (0, import_jose2.importSPKI)(keyString, algorithm);
|
|
806
845
|
}
|
|
807
846
|
try {
|
|
808
|
-
return await (0,
|
|
847
|
+
return await (0, import_jose2.importSPKI)(keyString, algorithm);
|
|
809
848
|
} catch (error) {
|
|
810
849
|
throw new Error(
|
|
811
850
|
`Unsupported key format. Supported formats: X.509 certificate (PEM), SPKI (PEM), JWK (JSON object or string). Error: ${error}`
|
|
@@ -888,7 +927,7 @@ async function verifySignature(jwt, key) {
|
|
|
888
927
|
const joseAlgorithm = header.alg || "RS256";
|
|
889
928
|
try {
|
|
890
929
|
const publicKey = await importKey(key, joseAlgorithm);
|
|
891
|
-
const { payload } = await (0,
|
|
930
|
+
const { payload } = await (0, import_jose3.jwtVerify)(raw.text, publicKey);
|
|
892
931
|
return { data: payload };
|
|
893
932
|
} catch (error) {
|
|
894
933
|
return {
|
|
@@ -903,8 +942,8 @@ async function verifySignature(jwt, key) {
|
|
|
903
942
|
}
|
|
904
943
|
function ternDecodeJwt(token) {
|
|
905
944
|
try {
|
|
906
|
-
const header = (0,
|
|
907
|
-
const payload = (0,
|
|
945
|
+
const header = (0, import_jose3.decodeProtectedHeader)(token);
|
|
946
|
+
const payload = (0, import_jose3.decodeJwt)(token);
|
|
908
947
|
const tokenParts = (token || "").toString().split(".");
|
|
909
948
|
if (tokenParts.length !== 3) {
|
|
910
949
|
return {
|
|
@@ -1097,12 +1136,223 @@ async function verifyToken(token, options) {
|
|
|
1097
1136
|
}
|
|
1098
1137
|
}
|
|
1099
1138
|
|
|
1139
|
+
// src/auth/getauth.ts
|
|
1140
|
+
var API_KEY_ERROR = "API Key is required";
|
|
1141
|
+
var NO_DATA_ERROR = "No token data received";
|
|
1142
|
+
function parseFirebaseResponse(data) {
|
|
1143
|
+
if (typeof data === "string") {
|
|
1144
|
+
try {
|
|
1145
|
+
return JSON.parse(data);
|
|
1146
|
+
} catch (error) {
|
|
1147
|
+
throw new Error(`Failed to parse Firebase response: ${error}`);
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
return data;
|
|
1151
|
+
}
|
|
1152
|
+
function getAuth(options) {
|
|
1153
|
+
const { apiKey } = options;
|
|
1154
|
+
const firebaseApiKey = options.firebaseConfig?.apiKey;
|
|
1155
|
+
const effectiveApiKey = apiKey || firebaseApiKey;
|
|
1156
|
+
async function refreshExpiredIdToken(refreshToken, opts) {
|
|
1157
|
+
if (!effectiveApiKey) {
|
|
1158
|
+
return { data: null, error: new Error(API_KEY_ERROR) };
|
|
1159
|
+
}
|
|
1160
|
+
const response = await options.apiClient?.tokens.refreshToken(effectiveApiKey, {
|
|
1161
|
+
refresh_token: refreshToken,
|
|
1162
|
+
request_origin: opts.referer
|
|
1163
|
+
});
|
|
1164
|
+
if (!response?.data) {
|
|
1165
|
+
return {
|
|
1166
|
+
data: null,
|
|
1167
|
+
error: new Error(NO_DATA_ERROR)
|
|
1168
|
+
};
|
|
1169
|
+
}
|
|
1170
|
+
const parsedData = parseFirebaseResponse(response.data);
|
|
1171
|
+
return {
|
|
1172
|
+
data: {
|
|
1173
|
+
idToken: parsedData.id_token,
|
|
1174
|
+
refreshToken: parsedData.refresh_token
|
|
1175
|
+
},
|
|
1176
|
+
error: null
|
|
1177
|
+
};
|
|
1178
|
+
}
|
|
1179
|
+
async function customForIdAndRefreshToken(customToken, opts) {
|
|
1180
|
+
if (!effectiveApiKey) {
|
|
1181
|
+
throw new Error("API Key is required to create custom token");
|
|
1182
|
+
}
|
|
1183
|
+
const response = await options.apiClient?.tokens.exchangeCustomForIdAndRefreshTokens(
|
|
1184
|
+
effectiveApiKey,
|
|
1185
|
+
{
|
|
1186
|
+
token: customToken,
|
|
1187
|
+
returnSecureToken: true
|
|
1188
|
+
},
|
|
1189
|
+
{
|
|
1190
|
+
referer: opts.referer
|
|
1191
|
+
}
|
|
1192
|
+
);
|
|
1193
|
+
if (!response?.data) {
|
|
1194
|
+
throw new Error("No data received from Firebase token exchange");
|
|
1195
|
+
}
|
|
1196
|
+
const parsedData = parseFirebaseResponse(response.data);
|
|
1197
|
+
return {
|
|
1198
|
+
idToken: parsedData.idToken,
|
|
1199
|
+
refreshToken: parsedData.refreshToken
|
|
1200
|
+
};
|
|
1201
|
+
}
|
|
1202
|
+
async function createCustomIdAndRefreshToken(idToken, opts) {
|
|
1203
|
+
const decoded = await verifyToken(idToken, options);
|
|
1204
|
+
const { data, errors } = decoded;
|
|
1205
|
+
if (errors) {
|
|
1206
|
+
throw errors[0];
|
|
1207
|
+
}
|
|
1208
|
+
const customToken = await createCustomToken(data.uid, {
|
|
1209
|
+
emailVerified: data.email_verified,
|
|
1210
|
+
source_sign_in_provider: data.firebase.sign_in_provider
|
|
1211
|
+
});
|
|
1212
|
+
const idAndRefreshTokens = await customForIdAndRefreshToken(customToken, {
|
|
1213
|
+
referer: opts.referer
|
|
1214
|
+
});
|
|
1215
|
+
return {
|
|
1216
|
+
...idAndRefreshTokens,
|
|
1217
|
+
customToken
|
|
1218
|
+
};
|
|
1219
|
+
}
|
|
1220
|
+
return {
|
|
1221
|
+
customForIdAndRefreshToken,
|
|
1222
|
+
createCustomIdAndRefreshToken,
|
|
1223
|
+
refreshExpiredIdToken
|
|
1224
|
+
};
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
// src/utils/options.ts
|
|
1228
|
+
var defaultOptions = {
|
|
1229
|
+
apiKey: void 0,
|
|
1230
|
+
apiUrl: void 0,
|
|
1231
|
+
apiVersion: void 0
|
|
1232
|
+
};
|
|
1233
|
+
function mergePreDefinedOptions(userOptions = {}) {
|
|
1234
|
+
return {
|
|
1235
|
+
...defaultOptions,
|
|
1236
|
+
...userOptions
|
|
1237
|
+
};
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1240
|
+
// src/tokens/c-authenticateRequestProcessor.ts
|
|
1241
|
+
var RequestProcessorContext = class {
|
|
1242
|
+
constructor(ternSecureRequest, options) {
|
|
1243
|
+
this.ternSecureRequest = ternSecureRequest;
|
|
1244
|
+
this.options = options;
|
|
1245
|
+
this.initHeaderValues();
|
|
1246
|
+
this.initCookieValues();
|
|
1247
|
+
this.initUrlValues();
|
|
1248
|
+
Object.assign(this, options);
|
|
1249
|
+
this.ternUrl = this.ternSecureRequest.ternUrl;
|
|
1250
|
+
}
|
|
1251
|
+
get request() {
|
|
1252
|
+
return this.ternSecureRequest;
|
|
1253
|
+
}
|
|
1254
|
+
initHeaderValues() {
|
|
1255
|
+
this.sessionTokenInHeader = this.parseAuthorizationHeader(
|
|
1256
|
+
this.getHeader(constants.Headers.Authorization)
|
|
1257
|
+
);
|
|
1258
|
+
this.origin = this.getHeader(constants.Headers.Origin);
|
|
1259
|
+
this.host = this.getHeader(constants.Headers.Host);
|
|
1260
|
+
this.forwardedHost = this.getHeader(constants.Headers.ForwardedHost);
|
|
1261
|
+
this.forwardedProto = this.getHeader(constants.Headers.CloudFrontForwardedProto) || this.getHeader(constants.Headers.ForwardedProto);
|
|
1262
|
+
this.referrer = this.getHeader(constants.Headers.Referrer);
|
|
1263
|
+
this.userAgent = this.getHeader(constants.Headers.UserAgent);
|
|
1264
|
+
this.secFetchDest = this.getHeader(constants.Headers.SecFetchDest);
|
|
1265
|
+
this.accept = this.getHeader(constants.Headers.Accept);
|
|
1266
|
+
}
|
|
1267
|
+
initCookieValues() {
|
|
1268
|
+
const isProduction = process.env.NODE_ENV === "production";
|
|
1269
|
+
const defaultPrefix = isProduction ? "__HOST-" : "__dev_";
|
|
1270
|
+
this.sessionTokenInCookie = this.getCookie(constants.Cookies.Session);
|
|
1271
|
+
this.idTokenInCookie = this.getCookie(`${defaultPrefix}${constants.Cookies.IdToken}`);
|
|
1272
|
+
this.refreshTokenInCookie = this.getCookie(`${defaultPrefix}${constants.Cookies.Refresh}`);
|
|
1273
|
+
this.csrfTokenInCookie = this.getCookie(constants.Cookies.CsrfToken);
|
|
1274
|
+
this.customTokenInCookie = this.getCookie(constants.Cookies.Custom);
|
|
1275
|
+
}
|
|
1276
|
+
initUrlValues() {
|
|
1277
|
+
this.method = this.ternSecureRequest.method;
|
|
1278
|
+
this.pathSegments = this.ternSecureRequest.ternUrl.pathname.split("/").filter(Boolean);
|
|
1279
|
+
this.endpoint = this.pathSegments[2];
|
|
1280
|
+
this.subEndpoint = this.pathSegments[3];
|
|
1281
|
+
}
|
|
1282
|
+
getHeader(name) {
|
|
1283
|
+
return this.ternSecureRequest.headers.get(name) || void 0;
|
|
1284
|
+
}
|
|
1285
|
+
getCookie(name) {
|
|
1286
|
+
return this.ternSecureRequest.cookies.get(name) || void 0;
|
|
1287
|
+
}
|
|
1288
|
+
parseAuthorizationHeader(authorizationHeader) {
|
|
1289
|
+
if (!authorizationHeader) {
|
|
1290
|
+
return void 0;
|
|
1291
|
+
}
|
|
1292
|
+
const [scheme, token] = authorizationHeader.split(" ", 2);
|
|
1293
|
+
if (!token) {
|
|
1294
|
+
return scheme;
|
|
1295
|
+
}
|
|
1296
|
+
if (scheme === "Bearer") {
|
|
1297
|
+
return token;
|
|
1298
|
+
}
|
|
1299
|
+
return void 0;
|
|
1300
|
+
}
|
|
1301
|
+
};
|
|
1302
|
+
var createRequestProcessor = (ternSecureRequest, options) => {
|
|
1303
|
+
return new RequestProcessorContext(ternSecureRequest, options);
|
|
1304
|
+
};
|
|
1305
|
+
|
|
1306
|
+
// src/tokens/cookie.ts
|
|
1307
|
+
var import_cookie2 = require("@tern-secure/shared/cookie");
|
|
1308
|
+
|
|
1100
1309
|
// src/tokens/request.ts
|
|
1101
1310
|
function hasAuthorizationHeader(request) {
|
|
1102
1311
|
return request.headers.has("Authorization");
|
|
1103
1312
|
}
|
|
1313
|
+
function isRequestForRefresh(error, context, request) {
|
|
1314
|
+
return error.reason === TokenVerificationErrorReason.TokenExpired && !!context.refreshTokenInCookie && request.method === "GET";
|
|
1315
|
+
}
|
|
1104
1316
|
async function authenticateRequest(request, options) {
|
|
1105
1317
|
const context = createRequestProcessor(createTernSecureRequest(request), options);
|
|
1318
|
+
const { refreshTokenInCookie } = context;
|
|
1319
|
+
const { refreshExpiredIdToken } = getAuth(options);
|
|
1320
|
+
async function refreshToken() {
|
|
1321
|
+
if (!refreshTokenInCookie) {
|
|
1322
|
+
return {
|
|
1323
|
+
data: null,
|
|
1324
|
+
error: {
|
|
1325
|
+
message: "No refresh token available",
|
|
1326
|
+
reason: AuthErrorReason.SessionTokenMissing
|
|
1327
|
+
}
|
|
1328
|
+
};
|
|
1329
|
+
}
|
|
1330
|
+
return await refreshExpiredIdToken(refreshTokenInCookie, {
|
|
1331
|
+
referer: context.ternUrl.origin
|
|
1332
|
+
});
|
|
1333
|
+
}
|
|
1334
|
+
async function handleRefresh() {
|
|
1335
|
+
const { data: refreshedData, error } = await refreshToken();
|
|
1336
|
+
if (!refreshedData) {
|
|
1337
|
+
return { data: null, error };
|
|
1338
|
+
}
|
|
1339
|
+
const headers = new Headers();
|
|
1340
|
+
const { idToken } = refreshedData;
|
|
1341
|
+
const maxAge = 3600;
|
|
1342
|
+
const cookiePrefix = (0, import_cookie2.getCookiePrefix)();
|
|
1343
|
+
const idTokenCookieName = (0, import_cookie2.getCookieName)(constants.Cookies.IdToken, cookiePrefix);
|
|
1344
|
+
const baseCookieAttributes = "HttpOnly; Secure; SameSite=Strict; Path=/";
|
|
1345
|
+
const idTokenCookie = `${idTokenCookieName}=${idToken}; ${baseCookieAttributes};`;
|
|
1346
|
+
headers.append("Set-Cookie", idTokenCookie);
|
|
1347
|
+
const { data: decoded, errors } = await verifyToken(idToken, options);
|
|
1348
|
+
if (errors) {
|
|
1349
|
+
return {
|
|
1350
|
+
data: null,
|
|
1351
|
+
error: errors ? errors[0] : new Error("Failed to verify refreshed token")
|
|
1352
|
+
};
|
|
1353
|
+
}
|
|
1354
|
+
return { data: { decoded, token: idToken, headers }, error: null };
|
|
1355
|
+
}
|
|
1106
1356
|
async function authenticateRequestWithTokenInCookie() {
|
|
1107
1357
|
try {
|
|
1108
1358
|
const { data, errors } = await verifyToken(context.idTokenInCookie, options);
|
|
@@ -1133,6 +1383,23 @@ async function authenticateRequest(request, options) {
|
|
|
1133
1383
|
return signedOut(AuthErrorReason.UnexpectedError);
|
|
1134
1384
|
}
|
|
1135
1385
|
let refreshError;
|
|
1386
|
+
if (isRequestForRefresh(err, context, request)) {
|
|
1387
|
+
const { data, error } = await handleRefresh();
|
|
1388
|
+
if (data) {
|
|
1389
|
+
return signedIn(data.decoded, data.headers, data.token);
|
|
1390
|
+
}
|
|
1391
|
+
if (error?.cause?.reason) {
|
|
1392
|
+
refreshError = error.cause.reason;
|
|
1393
|
+
}
|
|
1394
|
+
} else {
|
|
1395
|
+
if (request.method !== "GET") {
|
|
1396
|
+
refreshError = RefreshTokenErrorReason.NonEligibleNonGet;
|
|
1397
|
+
} else if (!context.refreshTokenInCookie) {
|
|
1398
|
+
refreshError = RefreshTokenErrorReason.NonEligibleNoCookie;
|
|
1399
|
+
} else {
|
|
1400
|
+
refreshError = null;
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1136
1403
|
err.tokenCarrier = tokenCarrier;
|
|
1137
1404
|
return signedOut(err.reason, err.getFullMessage());
|
|
1138
1405
|
}
|