@tern-secure/backend 1.2.0-canary.v20251003134325 → 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.js +7 -3
- package/dist/admin/index.js.map +1 -1
- package/dist/admin/index.mjs +531 -22
- package/dist/admin/index.mjs.map +1 -1
- package/dist/admin/nextSessionTernSecure.d.ts.map +1 -1
- package/dist/auth/getauth.d.ts +16 -2
- package/dist/auth/getauth.d.ts.map +1 -1
- package/dist/auth/index.js +166 -213
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/index.mjs +4 -51
- 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 +6 -3
- package/dist/fireRestApi/endpoints/TokenApi.d.ts.map +1 -1
- package/dist/fireRestApi/request.d.ts +3 -3
- package/dist/fireRestApi/request.d.ts.map +1 -1
- package/dist/index.d.ts +12 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +405 -103
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +130 -18
- 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-NEPV6OWI.mjs +0 -550
- package/dist/chunk-NEPV6OWI.mjs.map +0 -1
- package/dist/chunk-WZYVAHZ3.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,20 +376,36 @@ 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
|
-
async exchangeCustomForIdAndRefreshTokens(apiKey, params) {
|
|
397
|
+
async exchangeCustomForIdAndRefreshTokens(apiKey, params, options) {
|
|
387
398
|
this.requireApiKey(apiKey);
|
|
399
|
+
const headers = {};
|
|
400
|
+
if (options?.referer) {
|
|
401
|
+
headers["Referer"] = options.referer;
|
|
402
|
+
}
|
|
388
403
|
return this.request({
|
|
389
404
|
endpoint: "signInWithCustomToken",
|
|
390
405
|
method: "POST",
|
|
391
406
|
apiKey,
|
|
392
|
-
bodyParams: params
|
|
407
|
+
bodyParams: params,
|
|
408
|
+
headerParams: headers
|
|
393
409
|
});
|
|
394
410
|
}
|
|
395
411
|
};
|
|
@@ -461,8 +477,10 @@ function createRequest(options) {
|
|
|
461
477
|
data: null,
|
|
462
478
|
errors: [
|
|
463
479
|
{
|
|
464
|
-
|
|
465
|
-
|
|
480
|
+
domain: "none",
|
|
481
|
+
reason: "invalid_parameter",
|
|
482
|
+
message: "Firebase API key is required",
|
|
483
|
+
code: "400"
|
|
466
484
|
}
|
|
467
485
|
]
|
|
468
486
|
};
|
|
@@ -517,8 +535,10 @@ function createRequest(options) {
|
|
|
517
535
|
data: null,
|
|
518
536
|
errors: [
|
|
519
537
|
{
|
|
520
|
-
|
|
521
|
-
|
|
538
|
+
domain: "none",
|
|
539
|
+
reason: "request_failed",
|
|
540
|
+
message: error.message || "An unexpected error occurred",
|
|
541
|
+
code: "500"
|
|
522
542
|
}
|
|
523
543
|
]
|
|
524
544
|
};
|
|
@@ -534,16 +554,42 @@ function createRequest(options) {
|
|
|
534
554
|
return requestFn;
|
|
535
555
|
}
|
|
536
556
|
function parseErrors(data) {
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
557
|
+
let parsedData = data;
|
|
558
|
+
if (typeof data === "string") {
|
|
559
|
+
try {
|
|
560
|
+
parsedData = JSON.parse(data);
|
|
561
|
+
} catch (error) {
|
|
562
|
+
return [];
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
if (!parsedData || typeof parsedData !== "object") {
|
|
566
|
+
return [];
|
|
567
|
+
}
|
|
568
|
+
if ("error" in parsedData && typeof parsedData.error === "object" && parsedData.error !== null) {
|
|
569
|
+
const errorObj = parsedData.error;
|
|
570
|
+
if ("errors" in errorObj && Array.isArray(errorObj.errors) && errorObj.errors.length > 0) {
|
|
571
|
+
return errorObj.errors.map((err) => parseError({
|
|
572
|
+
code: errorObj.code || "unknown_error",
|
|
573
|
+
message: err.message || "Unknown error",
|
|
574
|
+
domain: err.domain,
|
|
575
|
+
reason: err.reason
|
|
576
|
+
}));
|
|
577
|
+
}
|
|
578
|
+
return [parseError({
|
|
579
|
+
code: errorObj.code?.toString() || "unknown_error",
|
|
580
|
+
message: errorObj.message || "Unknown error",
|
|
581
|
+
domain: errorObj.domain || "unknown",
|
|
582
|
+
reason: errorObj.reason || errorObj.code?.toString() || "unknown_error"
|
|
583
|
+
})];
|
|
540
584
|
}
|
|
541
585
|
return [];
|
|
542
586
|
}
|
|
543
587
|
function parseError(error) {
|
|
544
588
|
return {
|
|
545
|
-
|
|
546
|
-
|
|
589
|
+
domain: error.domain,
|
|
590
|
+
reason: error.reason,
|
|
591
|
+
message: error.message,
|
|
592
|
+
code: error.code
|
|
547
593
|
};
|
|
548
594
|
}
|
|
549
595
|
|
|
@@ -559,7 +605,117 @@ function createFireApi(options) {
|
|
|
559
605
|
};
|
|
560
606
|
}
|
|
561
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
|
+
|
|
562
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
|
+
};
|
|
563
719
|
var TokenVerificationErrorReason = {
|
|
564
720
|
TokenExpired: "token-expired",
|
|
565
721
|
TokenInvalid: "token-invalid",
|
|
@@ -594,88 +750,6 @@ var TokenVerificationError = class _TokenVerificationError extends Error {
|
|
|
594
750
|
}
|
|
595
751
|
};
|
|
596
752
|
|
|
597
|
-
// src/utils/options.ts
|
|
598
|
-
var defaultOptions = {
|
|
599
|
-
apiKey: void 0,
|
|
600
|
-
apiUrl: void 0,
|
|
601
|
-
apiVersion: void 0
|
|
602
|
-
};
|
|
603
|
-
function mergePreDefinedOptions(userOptions = {}) {
|
|
604
|
-
return {
|
|
605
|
-
...defaultOptions,
|
|
606
|
-
...userOptions
|
|
607
|
-
};
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
// src/tokens/c-authenticateRequestProcessor.ts
|
|
611
|
-
var RequestProcessorContext = class {
|
|
612
|
-
constructor(ternSecureRequest, options) {
|
|
613
|
-
this.ternSecureRequest = ternSecureRequest;
|
|
614
|
-
this.options = options;
|
|
615
|
-
this.initHeaderValues();
|
|
616
|
-
this.initCookieValues();
|
|
617
|
-
this.initUrlValues();
|
|
618
|
-
Object.assign(this, options);
|
|
619
|
-
this.ternUrl = this.ternSecureRequest.ternUrl;
|
|
620
|
-
}
|
|
621
|
-
get request() {
|
|
622
|
-
return this.ternSecureRequest;
|
|
623
|
-
}
|
|
624
|
-
initHeaderValues() {
|
|
625
|
-
this.sessionTokenInHeader = this.parseAuthorizationHeader(
|
|
626
|
-
this.getHeader(constants.Headers.Authorization)
|
|
627
|
-
);
|
|
628
|
-
this.origin = this.getHeader(constants.Headers.Origin);
|
|
629
|
-
this.host = this.getHeader(constants.Headers.Host);
|
|
630
|
-
this.forwardedHost = this.getHeader(constants.Headers.ForwardedHost);
|
|
631
|
-
this.forwardedProto = this.getHeader(constants.Headers.CloudFrontForwardedProto) || this.getHeader(constants.Headers.ForwardedProto);
|
|
632
|
-
this.referrer = this.getHeader(constants.Headers.Referrer);
|
|
633
|
-
this.userAgent = this.getHeader(constants.Headers.UserAgent);
|
|
634
|
-
this.secFetchDest = this.getHeader(constants.Headers.SecFetchDest);
|
|
635
|
-
this.accept = this.getHeader(constants.Headers.Accept);
|
|
636
|
-
}
|
|
637
|
-
initCookieValues() {
|
|
638
|
-
const isProduction = process.env.NODE_ENV === "production";
|
|
639
|
-
const defaultPrefix = isProduction ? "__HOST-" : "__dev_";
|
|
640
|
-
this.sessionTokenInCookie = this.getCookie(constants.Cookies.Session);
|
|
641
|
-
this.idTokenInCookie = this.getCookie(`${defaultPrefix}${constants.Cookies.IdToken}`);
|
|
642
|
-
this.refreshTokenInCookie = this.getCookie(`${defaultPrefix}${constants.Cookies.Refresh}`);
|
|
643
|
-
this.csrfTokenInCookie = this.getCookie(constants.Cookies.CsrfToken);
|
|
644
|
-
this.customTokenInCookie = this.getCookie(constants.Cookies.Custom);
|
|
645
|
-
}
|
|
646
|
-
initUrlValues() {
|
|
647
|
-
this.method = this.ternSecureRequest.method;
|
|
648
|
-
this.pathSegments = this.ternSecureRequest.ternUrl.pathname.split("/").filter(Boolean);
|
|
649
|
-
this.endpoint = this.pathSegments[2];
|
|
650
|
-
this.subEndpoint = this.pathSegments[3];
|
|
651
|
-
}
|
|
652
|
-
getHeader(name) {
|
|
653
|
-
return this.ternSecureRequest.headers.get(name) || void 0;
|
|
654
|
-
}
|
|
655
|
-
getCookie(name) {
|
|
656
|
-
return this.ternSecureRequest.cookies.get(name) || void 0;
|
|
657
|
-
}
|
|
658
|
-
parseAuthorizationHeader(authorizationHeader) {
|
|
659
|
-
if (!authorizationHeader) {
|
|
660
|
-
return void 0;
|
|
661
|
-
}
|
|
662
|
-
const [scheme, token] = authorizationHeader.split(" ", 2);
|
|
663
|
-
if (!token) {
|
|
664
|
-
return scheme;
|
|
665
|
-
}
|
|
666
|
-
if (scheme === "Bearer") {
|
|
667
|
-
return token;
|
|
668
|
-
}
|
|
669
|
-
return void 0;
|
|
670
|
-
}
|
|
671
|
-
};
|
|
672
|
-
var createRequestProcessor = (ternSecureRequest, options) => {
|
|
673
|
-
return new RequestProcessorContext(ternSecureRequest, options);
|
|
674
|
-
};
|
|
675
|
-
|
|
676
|
-
// src/jwt/verifyJwt.ts
|
|
677
|
-
var import_jose2 = require("jose");
|
|
678
|
-
|
|
679
753
|
// src/utils/rfc4648.ts
|
|
680
754
|
var base64url = {
|
|
681
755
|
parse(string, opts) {
|
|
@@ -753,10 +827,10 @@ function stringify(data, encoding, opts = {}) {
|
|
|
753
827
|
}
|
|
754
828
|
|
|
755
829
|
// src/jwt/cryptoKeys.ts
|
|
756
|
-
var
|
|
830
|
+
var import_jose2 = require("jose");
|
|
757
831
|
async function importKey(key, algorithm) {
|
|
758
832
|
if (typeof key === "object") {
|
|
759
|
-
const result = await (0,
|
|
833
|
+
const result = await (0, import_jose2.importJWK)(key, algorithm);
|
|
760
834
|
if (result instanceof Uint8Array) {
|
|
761
835
|
throw new Error("Unexpected Uint8Array result from JWK import");
|
|
762
836
|
}
|
|
@@ -764,13 +838,13 @@ async function importKey(key, algorithm) {
|
|
|
764
838
|
}
|
|
765
839
|
const keyString = key.trim();
|
|
766
840
|
if (keyString.includes("-----BEGIN CERTIFICATE-----")) {
|
|
767
|
-
return await (0,
|
|
841
|
+
return await (0, import_jose2.importX509)(keyString, algorithm);
|
|
768
842
|
}
|
|
769
843
|
if (keyString.includes("-----BEGIN PUBLIC KEY-----")) {
|
|
770
|
-
return await (0,
|
|
844
|
+
return await (0, import_jose2.importSPKI)(keyString, algorithm);
|
|
771
845
|
}
|
|
772
846
|
try {
|
|
773
|
-
return await (0,
|
|
847
|
+
return await (0, import_jose2.importSPKI)(keyString, algorithm);
|
|
774
848
|
} catch (error) {
|
|
775
849
|
throw new Error(
|
|
776
850
|
`Unsupported key format. Supported formats: X.509 certificate (PEM), SPKI (PEM), JWK (JSON object or string). Error: ${error}`
|
|
@@ -853,7 +927,7 @@ async function verifySignature(jwt, key) {
|
|
|
853
927
|
const joseAlgorithm = header.alg || "RS256";
|
|
854
928
|
try {
|
|
855
929
|
const publicKey = await importKey(key, joseAlgorithm);
|
|
856
|
-
const { payload } = await (0,
|
|
930
|
+
const { payload } = await (0, import_jose3.jwtVerify)(raw.text, publicKey);
|
|
857
931
|
return { data: payload };
|
|
858
932
|
} catch (error) {
|
|
859
933
|
return {
|
|
@@ -868,8 +942,8 @@ async function verifySignature(jwt, key) {
|
|
|
868
942
|
}
|
|
869
943
|
function ternDecodeJwt(token) {
|
|
870
944
|
try {
|
|
871
|
-
const header = (0,
|
|
872
|
-
const payload = (0,
|
|
945
|
+
const header = (0, import_jose3.decodeProtectedHeader)(token);
|
|
946
|
+
const payload = (0, import_jose3.decodeJwt)(token);
|
|
873
947
|
const tokenParts = (token || "").toString().split(".");
|
|
874
948
|
if (tokenParts.length !== 3) {
|
|
875
949
|
return {
|
|
@@ -1062,12 +1136,223 @@ async function verifyToken(token, options) {
|
|
|
1062
1136
|
}
|
|
1063
1137
|
}
|
|
1064
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
|
+
|
|
1065
1309
|
// src/tokens/request.ts
|
|
1066
1310
|
function hasAuthorizationHeader(request) {
|
|
1067
1311
|
return request.headers.has("Authorization");
|
|
1068
1312
|
}
|
|
1313
|
+
function isRequestForRefresh(error, context, request) {
|
|
1314
|
+
return error.reason === TokenVerificationErrorReason.TokenExpired && !!context.refreshTokenInCookie && request.method === "GET";
|
|
1315
|
+
}
|
|
1069
1316
|
async function authenticateRequest(request, options) {
|
|
1070
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
|
+
}
|
|
1071
1356
|
async function authenticateRequestWithTokenInCookie() {
|
|
1072
1357
|
try {
|
|
1073
1358
|
const { data, errors } = await verifyToken(context.idTokenInCookie, options);
|
|
@@ -1098,6 +1383,23 @@ async function authenticateRequest(request, options) {
|
|
|
1098
1383
|
return signedOut(AuthErrorReason.UnexpectedError);
|
|
1099
1384
|
}
|
|
1100
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
|
+
}
|
|
1101
1403
|
err.tokenCarrier = tokenCarrier;
|
|
1102
1404
|
return signedOut(err.reason, err.getFullMessage());
|
|
1103
1405
|
}
|