@tern-secure/backend 1.2.0-canary.v20251202164451 → 1.2.0-canary.v20251202172616

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.
@@ -0,0 +1,142 @@
1
+ // src/constants.ts
2
+ var GOOGLE_PUBLIC_KEYS_URL = "https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com";
3
+ var FIREBASE_APP_CHECK_AUDIENCE = "https://firebaseappcheck.googleapis.com/google.firebase.appcheck.v1.TokenExchangeService";
4
+ var MAX_CACHE_LAST_UPDATED_AT_SECONDS = 5 * 60;
5
+ var DEFAULT_CACHE_DURATION = 3600 * 1e3;
6
+ var CACHE_CONTROL_REGEX = /max-age=(\d+)/;
7
+ var TOKEN_EXPIRY_THRESHOLD_MILLIS = 5 * 60 * 1e3;
8
+ var GOOGLE_TOKEN_AUDIENCE = "https://accounts.google.com/o/oauth2/token";
9
+ var GOOGLE_AUTH_TOKEN_HOST = "accounts.google.com";
10
+ var GOOGLE_AUTH_TOKEN_PATH = "/o/oauth2/token";
11
+ var ONE_HOUR_IN_SECONDS = 60 * 60;
12
+ var ONE_MINUTE_IN_SECONDS = 60;
13
+ var ONE_MINUTE_IN_MILLIS = ONE_MINUTE_IN_SECONDS * 1e3;
14
+ var ONE_DAY_IN_MILLIS = 24 * 60 * 60 * 1e3;
15
+ var Attributes = {
16
+ AuthToken: "__ternsecureAuthToken",
17
+ AuthSignature: "__ternsecureAuthSignature",
18
+ AuthStatus: "__ternsecureAuthStatus",
19
+ AuthReason: "__ternsecureAuthReason",
20
+ AuthMessage: "__ternsecureAuthMessage",
21
+ TernSecureUrl: "__ternsecureUrl"
22
+ };
23
+ var Cookies = {
24
+ Session: "__session",
25
+ CsrfToken: "__terncf",
26
+ IdToken: "TernSecure_[DEFAULT]",
27
+ Refresh: "TernSecureID_[DEFAULT]",
28
+ Custom: "__custom",
29
+ TernAut: "tern_aut",
30
+ Handshake: "__ternsecure_handshake",
31
+ DevBrowser: "__ternsecure_db_jwt",
32
+ RedirectCount: "__ternsecure_redirect_count",
33
+ HandshakeNonce: "__ternsecure_handshake_nonce"
34
+ };
35
+ var QueryParameters = {
36
+ TernSynced: "__tern_synced",
37
+ SuffixedCookies: "suffixed_cookies",
38
+ TernRedirectUrl: "__tern_redirect_url",
39
+ // use the reference to Cookies to indicate that it's the same value
40
+ DevBrowser: Cookies.DevBrowser,
41
+ Handshake: Cookies.Handshake,
42
+ HandshakeHelp: "__tern_help",
43
+ LegacyDevBrowser: "__dev_session",
44
+ HandshakeReason: "__tern_hs_reason",
45
+ HandshakeNonce: Cookies.HandshakeNonce
46
+ };
47
+ var Headers = {
48
+ Accept: "accept",
49
+ AppCheckToken: "x-ternsecure-appcheck",
50
+ AuthMessage: "x-ternsecure-auth-message",
51
+ Authorization: "authorization",
52
+ AuthReason: "x-ternsecure-auth-reason",
53
+ AuthSignature: "x-ternsecure-auth-signature",
54
+ AuthStatus: "x-ternsecure-auth-status",
55
+ AuthToken: "x-ternsecure-auth-token",
56
+ CacheControl: "cache-control",
57
+ TernSecureRedirectTo: "x-ternsecure-redirect-to",
58
+ TernSecureRequestData: "x-ternsecure-request-data",
59
+ TernSecureUrl: "x-ternsecure-url",
60
+ CloudFrontForwardedProto: "cloudfront-forwarded-proto",
61
+ ContentType: "content-type",
62
+ ContentSecurityPolicy: "content-security-policy",
63
+ ContentSecurityPolicyReportOnly: "content-security-policy-report-only",
64
+ EnableDebug: "x-ternsecure-debug",
65
+ ForwardedHost: "x-forwarded-host",
66
+ ForwardedPort: "x-forwarded-port",
67
+ ForwardedProto: "x-forwarded-proto",
68
+ Host: "host",
69
+ Location: "location",
70
+ Nonce: "x-nonce",
71
+ Origin: "origin",
72
+ Referrer: "referer",
73
+ SecFetchDest: "sec-fetch-dest",
74
+ UserAgent: "user-agent",
75
+ ReportingEndpoints: "reporting-endpoints"
76
+ };
77
+ var ContentTypes = {
78
+ Json: "application/json"
79
+ };
80
+ var constants = {
81
+ Attributes,
82
+ Cookies,
83
+ Headers,
84
+ ContentTypes,
85
+ QueryParameters
86
+ };
87
+
88
+ // src/utils/config.ts
89
+ var loadAdminConfig = () => ({
90
+ projectId: process.env.FIREBASE_PROJECT_ID || "",
91
+ clientEmail: process.env.FIREBASE_CLIENT_EMAIL || "",
92
+ privateKey: process.env.FIREBASE_PRIVATE_KEY || ""
93
+ });
94
+ var validateAdminConfig = (config) => {
95
+ const requiredFields = [
96
+ "projectId",
97
+ "clientEmail",
98
+ "privateKey"
99
+ ];
100
+ const errors = [];
101
+ requiredFields.forEach((field) => {
102
+ if (!config[field]) {
103
+ errors.push(`Missing required field: FIREBASE_${String(field).toUpperCase()}`);
104
+ }
105
+ });
106
+ return {
107
+ isValid: errors.length === 0,
108
+ errors,
109
+ config
110
+ };
111
+ };
112
+ var initializeAdminConfig = () => {
113
+ const config = loadAdminConfig();
114
+ const validationResult = validateAdminConfig(config);
115
+ if (!validationResult.isValid) {
116
+ throw new Error(
117
+ `Firebase Admin configuration validation failed:
118
+ ${validationResult.errors.join("\n")}`
119
+ );
120
+ }
121
+ return config;
122
+ };
123
+
124
+ export {
125
+ GOOGLE_PUBLIC_KEYS_URL,
126
+ FIREBASE_APP_CHECK_AUDIENCE,
127
+ MAX_CACHE_LAST_UPDATED_AT_SECONDS,
128
+ DEFAULT_CACHE_DURATION,
129
+ CACHE_CONTROL_REGEX,
130
+ TOKEN_EXPIRY_THRESHOLD_MILLIS,
131
+ GOOGLE_TOKEN_AUDIENCE,
132
+ GOOGLE_AUTH_TOKEN_HOST,
133
+ GOOGLE_AUTH_TOKEN_PATH,
134
+ ONE_HOUR_IN_SECONDS,
135
+ ONE_MINUTE_IN_SECONDS,
136
+ ONE_MINUTE_IN_MILLIS,
137
+ ONE_DAY_IN_MILLIS,
138
+ constants,
139
+ loadAdminConfig,
140
+ initializeAdminConfig
141
+ };
142
+ //# sourceMappingURL=chunk-4NYVEI6S.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/constants.ts","../src/utils/config.ts"],"sourcesContent":["export const GOOGLE_PUBLIC_KEYS_URL =\n 'https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com';\nexport const SESSION_COOKIE_PUBLIC_KEYS_URL =\n 'https://www.googleapis.com/identitytoolkit/v3/relyingparty/publicKeys';\n\nexport const FIREBASE_APP_CHECK_AUDIENCE =\n 'https://firebaseappcheck.googleapis.com/google.firebase.appcheck.v1.TokenExchangeService';\n\nexport const MAX_CACHE_LAST_UPDATED_AT_SECONDS = 5 * 60;\nexport const DEFAULT_CACHE_DURATION = 3600 * 1000; // 1 hour in milliseconds\nexport const CACHE_CONTROL_REGEX = /max-age=(\\d+)/;\n\nexport const TOKEN_EXPIRY_THRESHOLD_MILLIS = 5 * 60 * 1000;\nexport const GOOGLE_TOKEN_AUDIENCE = 'https://accounts.google.com/o/oauth2/token';\nexport const GOOGLE_AUTH_TOKEN_HOST = 'accounts.google.com';\nexport const GOOGLE_AUTH_TOKEN_PATH = '/o/oauth2/token';\nexport const ONE_HOUR_IN_SECONDS = 60 * 60;\n\nexport const ONE_MINUTE_IN_SECONDS = 60;\nexport const ONE_MINUTE_IN_MILLIS = ONE_MINUTE_IN_SECONDS * 1000;\nexport const ONE_DAY_IN_MILLIS = 24 * 60 * 60 * 1000;\n\nconst Attributes = {\n AuthToken: '__ternsecureAuthToken',\n AuthSignature: '__ternsecureAuthSignature',\n AuthStatus: '__ternsecureAuthStatus',\n AuthReason: '__ternsecureAuthReason',\n AuthMessage: '__ternsecureAuthMessage',\n TernSecureUrl: '__ternsecureUrl',\n} as const;\n\nconst Cookies = {\n Session: '__session',\n CsrfToken: '__terncf',\n IdToken: 'TernSecure_[DEFAULT]',\n Refresh: 'TernSecureID_[DEFAULT]',\n Custom: '__custom',\n TernAut: 'tern_aut',\n Handshake: '__ternsecure_handshake',\n DevBrowser: '__ternsecure_db_jwt',\n RedirectCount: '__ternsecure_redirect_count',\n HandshakeNonce: '__ternsecure_handshake_nonce',\n} as const;\n\n\nconst QueryParameters = {\n TernSynced: '__tern_synced',\n SuffixedCookies: 'suffixed_cookies',\n TernRedirectUrl: '__tern_redirect_url',\n // use the reference to Cookies to indicate that it's the same value\n DevBrowser: Cookies.DevBrowser,\n Handshake: Cookies.Handshake,\n HandshakeHelp: '__tern_help',\n LegacyDevBrowser: '__dev_session',\n HandshakeReason: '__tern_hs_reason',\n HandshakeNonce: Cookies.HandshakeNonce,\n} as const;\n\nconst Headers = {\n Accept: 'accept',\n AppCheckToken: 'x-ternsecure-appcheck',\n AuthMessage: 'x-ternsecure-auth-message',\n Authorization: 'authorization',\n AuthReason: 'x-ternsecure-auth-reason',\n AuthSignature: 'x-ternsecure-auth-signature',\n AuthStatus: 'x-ternsecure-auth-status',\n AuthToken: 'x-ternsecure-auth-token',\n CacheControl: 'cache-control',\n TernSecureRedirectTo: 'x-ternsecure-redirect-to',\n TernSecureRequestData: 'x-ternsecure-request-data',\n TernSecureUrl: 'x-ternsecure-url',\n CloudFrontForwardedProto: 'cloudfront-forwarded-proto',\n ContentType: 'content-type',\n ContentSecurityPolicy: 'content-security-policy',\n ContentSecurityPolicyReportOnly: 'content-security-policy-report-only',\n EnableDebug: 'x-ternsecure-debug',\n ForwardedHost: 'x-forwarded-host',\n ForwardedPort: 'x-forwarded-port',\n ForwardedProto: 'x-forwarded-proto',\n Host: 'host',\n Location: 'location',\n Nonce: 'x-nonce',\n Origin: 'origin',\n Referrer: 'referer',\n SecFetchDest: 'sec-fetch-dest',\n UserAgent: 'user-agent',\n ReportingEndpoints: 'reporting-endpoints',\n} as const;\n\nconst ContentTypes = {\n Json: 'application/json',\n} as const;\n\n/**\n * @internal\n */\nexport const constants = {\n Attributes,\n Cookies,\n Headers,\n ContentTypes,\n QueryParameters,\n} as const;\n\nexport type Constants = typeof constants;\n","import type { \r\n AdminConfigValidationResult, \r\n ConfigValidationResult, \r\n TernSecureAdminConfig, \r\n TernSecureConfig} from '@tern-secure/types'\r\n\r\n/**\r\n * Loads Firebase configuration from environment variables\r\n * @returns {TernSecureConfig} Firebase configuration object\r\n */\r\nexport const loadFireConfig = (): TernSecureConfig => ({\r\n apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY || '',\r\n authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN || '',\r\n databaseURL: process.env.NEXT_PUBLIC_FIREBASE_DATABASE_URL || undefined,\r\n projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID || '',\r\n storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET || '',\r\n messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID || '',\r\n appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID || '',\r\n measurementId: process.env.NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID || undefined,\r\n})\r\n\r\n/**\r\n * Validates Firebase configuration\r\n * @param {TernSecureConfig} config - Firebase configuration object\r\n * @throws {Error} If required configuration values are missing\r\n * @returns {TernSecureConfig} Validated configuration object\r\n */\r\nexport const validateConfig = (config: TernSecureConfig): ConfigValidationResult => {\r\n const requiredFields: (keyof TernSecureConfig)[] = [\r\n 'apiKey',\r\n 'authDomain',\r\n 'projectId',\r\n 'storageBucket',\r\n 'messagingSenderId',\r\n 'appId'\r\n ]\r\n\r\n const errors: string[] = []\r\n \r\n requiredFields.forEach(field => {\r\n if (!config[field]) {\r\n errors.push(`Missing required field: NEXT_PUBLIC_FIREBASE_${String(field).toUpperCase()}`)\r\n }\r\n })\r\n\r\n return {\r\n isValid: errors.length === 0,\r\n errors,\r\n config\r\n }\r\n}\r\n\r\n/**\r\n * Initializes configuration with validation\r\n * @throws {Error} If configuration is invalid\r\n */\r\nexport const initializeConfig = (): TernSecureConfig => {\r\n const config = loadFireConfig()\r\n const validationResult = validateConfig(config)\r\n\r\n if (!validationResult.isValid) {\r\n throw new Error(\r\n `Firebase configuration validation failed:\\n${validationResult.errors.join('\\n')}`\r\n )\r\n }\r\n\r\n return config\r\n}\r\n\r\n/**\r\n * Loads Firebase Admin configuration from environment variables\r\n * @returns {AdminConfig} Firebase Admin configuration object\r\n */\r\nexport const loadAdminConfig = (): TernSecureAdminConfig => ({\r\n projectId: process.env.FIREBASE_PROJECT_ID || '',\r\n clientEmail: process.env.FIREBASE_CLIENT_EMAIL || '',\r\n privateKey: process.env.FIREBASE_PRIVATE_KEY || '',\r\n})\r\n\r\n/**\r\n * Validates Firebase Admin configuration\r\n * @param {AdminConfig} config - Firebase Admin configuration object\r\n * @returns {ConfigValidationResult} Validation result\r\n */\r\nexport const validateAdminConfig = (config: TernSecureAdminConfig): AdminConfigValidationResult => {\r\n const requiredFields: (keyof TernSecureAdminConfig)[] = [\r\n 'projectId',\r\n 'clientEmail',\r\n 'privateKey'\r\n ]\r\n\r\n const errors: string[] = []\r\n \r\n requiredFields.forEach(field => {\r\n if (!config[field]) {\r\n errors.push(`Missing required field: FIREBASE_${String(field).toUpperCase()}`)\r\n }\r\n })\r\n\r\n return {\r\n isValid: errors.length === 0,\r\n errors,\r\n config\r\n }\r\n}\r\n\r\n/**\r\n * Initializes admin configuration with validation\r\n * @throws {Error} If configuration is invalid\r\n */\r\nexport const initializeAdminConfig = (): TernSecureAdminConfig => {\r\n const config = loadAdminConfig()\r\n const validationResult = validateAdminConfig(config)\r\n\r\n if (!validationResult.isValid) {\r\n throw new Error(\r\n `Firebase Admin configuration validation failed:\\n${validationResult.errors.join('\\n')}`\r\n )\r\n }\r\n\r\n return config\r\n}"],"mappings":";AAAO,IAAM,yBACX;AAIK,IAAM,8BACX;AAEK,IAAM,oCAAoC,IAAI;AAC9C,IAAM,yBAAyB,OAAO;AACtC,IAAM,sBAAsB;AAE5B,IAAM,gCAAgC,IAAI,KAAK;AAC/C,IAAM,wBAAwB;AAC9B,IAAM,yBAAyB;AAC/B,IAAM,yBAAyB;AAC/B,IAAM,sBAAsB,KAAK;AAEjC,IAAM,wBAAwB;AAC9B,IAAM,uBAAuB,wBAAwB;AACrD,IAAM,oBAAoB,KAAK,KAAK,KAAK;AAEhD,IAAM,aAAa;AAAA,EACjB,WAAW;AAAA,EACX,eAAe;AAAA,EACf,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,eAAe;AACjB;AAEA,IAAM,UAAU;AAAA,EACd,SAAS;AAAA,EACT,WAAW;AAAA,EACX,SAAS;AAAA,EACT,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,WAAW;AAAA,EACX,YAAY;AAAA,EACZ,eAAe;AAAA,EACf,gBAAgB;AAClB;AAGA,IAAM,kBAAkB;AAAA,EACtB,YAAY;AAAA,EACZ,iBAAiB;AAAA,EACjB,iBAAiB;AAAA;AAAA,EAEjB,YAAY,QAAQ;AAAA,EACpB,WAAW,QAAQ;AAAA,EACnB,eAAe;AAAA,EACf,kBAAkB;AAAA,EAClB,iBAAiB;AAAA,EACjB,gBAAgB,QAAQ;AAC1B;AAEA,IAAM,UAAU;AAAA,EACd,QAAQ;AAAA,EACR,eAAe;AAAA,EACf,aAAa;AAAA,EACb,eAAe;AAAA,EACf,YAAY;AAAA,EACZ,eAAe;AAAA,EACf,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,cAAc;AAAA,EACd,sBAAsB;AAAA,EACtB,uBAAuB;AAAA,EACvB,eAAe;AAAA,EACf,0BAA0B;AAAA,EAC1B,aAAa;AAAA,EACb,uBAAuB;AAAA,EACvB,iCAAiC;AAAA,EACjC,aAAa;AAAA,EACb,eAAe;AAAA,EACf,eAAe;AAAA,EACf,gBAAgB;AAAA,EAChB,MAAM;AAAA,EACN,UAAU;AAAA,EACV,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,UAAU;AAAA,EACV,cAAc;AAAA,EACd,WAAW;AAAA,EACX,oBAAoB;AACtB;AAEA,IAAM,eAAe;AAAA,EACnB,MAAM;AACR;AAKO,IAAM,YAAY;AAAA,EACvB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;;;AC7BO,IAAM,kBAAkB,OAA8B;AAAA,EAC3D,WAAW,QAAQ,IAAI,uBAAuB;AAAA,EAC9C,aAAa,QAAQ,IAAI,yBAAyB;AAAA,EAClD,YAAY,QAAQ,IAAI,wBAAwB;AAClD;AAOO,IAAM,sBAAsB,CAAC,WAA+D;AACjG,QAAM,iBAAkD;AAAA,IACtD;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,QAAM,SAAmB,CAAC;AAE1B,iBAAe,QAAQ,WAAS;AAC9B,QAAI,CAAC,OAAO,KAAK,GAAG;AAClB,aAAO,KAAK,oCAAoC,OAAO,KAAK,EAAE,YAAY,CAAC,EAAE;AAAA,IAC/E;AAAA,EACF,CAAC;AAED,SAAO;AAAA,IACL,SAAS,OAAO,WAAW;AAAA,IAC3B;AAAA,IACA;AAAA,EACF;AACF;AAMO,IAAM,wBAAwB,MAA6B;AAChE,QAAM,SAAS,gBAAgB;AAC/B,QAAM,mBAAmB,oBAAoB,MAAM;AAEnD,MAAI,CAAC,iBAAiB,SAAS;AAC7B,UAAM,IAAI;AAAA,MACR;AAAA,EAAoD,iBAAiB,OAAO,KAAK,IAAI,CAAC;AAAA,IACxF;AAAA,EACF;AAEA,SAAO;AACT;","names":[]}
@@ -0,0 +1,71 @@
1
+ import {
2
+ constants
3
+ } from "./chunk-4NYVEI6S.mjs";
4
+
5
+ // src/tokens/ternSecureRequest.ts
6
+ import { parse } from "cookie";
7
+
8
+ // src/tokens/ternUrl.ts
9
+ var TernUrl = class extends URL {
10
+ isCrossOrigin(other) {
11
+ return this.origin !== new URL(other.toString()).origin;
12
+ }
13
+ };
14
+ var createTernUrl = (...args) => {
15
+ return new TernUrl(...args);
16
+ };
17
+
18
+ // src/tokens/ternSecureRequest.ts
19
+ var TernSecureRequest = class extends Request {
20
+ ternUrl;
21
+ cookies;
22
+ constructor(input, init) {
23
+ const url = typeof input !== "string" && "url" in input ? input.url : String(input);
24
+ super(url, init || typeof input === "string" ? void 0 : input);
25
+ this.ternUrl = this.deriveUrlFromHeaders(this);
26
+ this.cookies = this.parseCookies(this);
27
+ }
28
+ toJSON() {
29
+ return {
30
+ url: this.ternUrl.href,
31
+ method: this.method,
32
+ headers: JSON.stringify(Object.fromEntries(this.headers)),
33
+ ternUrl: this.ternUrl.toString(),
34
+ cookies: JSON.stringify(Object.fromEntries(this.cookies))
35
+ };
36
+ }
37
+ deriveUrlFromHeaders(req) {
38
+ const initialUrl = new URL(req.url);
39
+ const forwardedProto = req.headers.get(constants.Headers.ForwardedProto);
40
+ const forwardedHost = req.headers.get(constants.Headers.ForwardedHost);
41
+ const host = req.headers.get(constants.Headers.Host);
42
+ const protocol = initialUrl.protocol;
43
+ const resolvedHost = this.getFirstValueFromHeader(forwardedHost) ?? host;
44
+ const resolvedProtocol = this.getFirstValueFromHeader(forwardedProto) ?? protocol?.replace(/[:/]/, "");
45
+ const origin = resolvedHost && resolvedProtocol ? `${resolvedProtocol}://${resolvedHost}` : initialUrl.origin;
46
+ if (origin === initialUrl.origin) {
47
+ return createTernUrl(initialUrl);
48
+ }
49
+ return createTernUrl(initialUrl.pathname + initialUrl.search, origin);
50
+ }
51
+ getFirstValueFromHeader(value) {
52
+ return value?.split(",")[0];
53
+ }
54
+ parseCookies(req) {
55
+ const cookiesRecord = parse(
56
+ this.decodeCookieValue(req.headers.get("cookie") || "")
57
+ );
58
+ return new Map(Object.entries(cookiesRecord));
59
+ }
60
+ decodeCookieValue(str) {
61
+ return str ? str.replace(/(%[0-9A-Z]{2})+/g, decodeURIComponent) : str;
62
+ }
63
+ };
64
+ var createTernSecureRequest = (...args) => {
65
+ return args[0] instanceof TernSecureRequest ? args[0] : new TernSecureRequest(...args);
66
+ };
67
+
68
+ export {
69
+ createTernSecureRequest
70
+ };
71
+ //# sourceMappingURL=chunk-PYNFU7M3.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/tokens/ternSecureRequest.ts","../src/tokens/ternUrl.ts"],"sourcesContent":["import { parse } from \"cookie\";\n\nimport { constants } from \"../constants\";\nimport type { TernUrl } from \"./ternUrl\";\nimport { createTernUrl } from \"./ternUrl\";\n\nclass TernSecureRequest extends Request {\n readonly ternUrl: TernUrl;\n readonly cookies: Map<string, string | undefined>;\n\n public constructor(\n input: TernSecureRequest | Request | RequestInfo,\n init?: RequestInit\n ) {\n const url =\n typeof input !== \"string\" && \"url\" in input ? input.url : String(input);\n super(url, init || typeof input === \"string\" ? undefined : input);\n this.ternUrl = this.deriveUrlFromHeaders(this);\n this.cookies = this.parseCookies(this);\n }\n\n public toJSON() {\n return {\n url: this.ternUrl.href,\n method: this.method,\n headers: JSON.stringify(Object.fromEntries(this.headers)),\n ternUrl: this.ternUrl.toString(),\n cookies: JSON.stringify(Object.fromEntries(this.cookies)),\n };\n }\n\n private deriveUrlFromHeaders(req: Request) {\n const initialUrl = new URL(req.url);\n const forwardedProto = req.headers.get(constants.Headers.ForwardedProto);\n const forwardedHost = req.headers.get(constants.Headers.ForwardedHost);\n const host = req.headers.get(constants.Headers.Host);\n const protocol = initialUrl.protocol;\n\n const resolvedHost = this.getFirstValueFromHeader(forwardedHost) ?? host;\n const resolvedProtocol =\n this.getFirstValueFromHeader(forwardedProto) ??\n protocol?.replace(/[:/]/, \"\");\n const origin =\n resolvedHost && resolvedProtocol\n ? `${resolvedProtocol}://${resolvedHost}`\n : initialUrl.origin;\n\n if (origin === initialUrl.origin) {\n return createTernUrl(initialUrl);\n }\n\n return createTernUrl(initialUrl.pathname + initialUrl.search, origin);\n }\n\n private getFirstValueFromHeader(value?: string | null) {\n return value?.split(\",\")[0];\n }\n\n private parseCookies(req: Request) {\n const cookiesRecord = parse(\n this.decodeCookieValue(req.headers.get(\"cookie\") || \"\")\n );\n return new Map(Object.entries(cookiesRecord));\n }\n\n private decodeCookieValue(str: string) {\n return str ? str.replace(/(%[0-9A-Z]{2})+/g, decodeURIComponent) : str;\n }\n}\n\nexport const createTernSecureRequest = (\n ...args: ConstructorParameters<typeof TernSecureRequest>\n): TernSecureRequest => {\n return args[0] instanceof TernSecureRequest\n ? args[0]\n : new TernSecureRequest(...args);\n};\n\nexport type { TernSecureRequest };\n","class TernUrl extends URL {\n public isCrossOrigin(other: URL | string) {\n return this.origin !== new URL(other.toString()).origin;\n }\n}\n\nexport type WithTernUrl<T> = T & {\n /**\n * When a NextJs app is hosted on a platform different from Vercel\n * or inside a container (Netlify, Fly.io, AWS Amplify, docker etc),\n * req.url is always set to `localhost:3000` instead of the actual host of the app.\n *\n */\n ternUrl: TernUrl;\n};\n\nexport const createTernUrl = (\n ...args: ConstructorParameters<typeof TernUrl>\n): TernUrl => {\n return new TernUrl(...args);\n};\n\nexport type { TernUrl };\n"],"mappings":";;;;;AAAA,SAAS,aAAa;;;ACAtB,IAAM,UAAN,cAAsB,IAAI;AAAA,EACjB,cAAc,OAAqB;AACxC,WAAO,KAAK,WAAW,IAAI,IAAI,MAAM,SAAS,CAAC,EAAE;AAAA,EACnD;AACF;AAYO,IAAM,gBAAgB,IACxB,SACS;AACZ,SAAO,IAAI,QAAQ,GAAG,IAAI;AAC5B;;;ADdA,IAAM,oBAAN,cAAgC,QAAQ;AAAA,EAC7B;AAAA,EACA;AAAA,EAEF,YACL,OACA,MACA;AACA,UAAM,MACJ,OAAO,UAAU,YAAY,SAAS,QAAQ,MAAM,MAAM,OAAO,KAAK;AACxE,UAAM,KAAK,QAAQ,OAAO,UAAU,WAAW,SAAY,KAAK;AAChE,SAAK,UAAU,KAAK,qBAAqB,IAAI;AAC7C,SAAK,UAAU,KAAK,aAAa,IAAI;AAAA,EACvC;AAAA,EAEO,SAAS;AACd,WAAO;AAAA,MACL,KAAK,KAAK,QAAQ;AAAA,MAClB,QAAQ,KAAK;AAAA,MACb,SAAS,KAAK,UAAU,OAAO,YAAY,KAAK,OAAO,CAAC;AAAA,MACxD,SAAS,KAAK,QAAQ,SAAS;AAAA,MAC/B,SAAS,KAAK,UAAU,OAAO,YAAY,KAAK,OAAO,CAAC;AAAA,IAC1D;AAAA,EACF;AAAA,EAEQ,qBAAqB,KAAc;AACzC,UAAM,aAAa,IAAI,IAAI,IAAI,GAAG;AAClC,UAAM,iBAAiB,IAAI,QAAQ,IAAI,UAAU,QAAQ,cAAc;AACvE,UAAM,gBAAgB,IAAI,QAAQ,IAAI,UAAU,QAAQ,aAAa;AACrE,UAAM,OAAO,IAAI,QAAQ,IAAI,UAAU,QAAQ,IAAI;AACnD,UAAM,WAAW,WAAW;AAE5B,UAAM,eAAe,KAAK,wBAAwB,aAAa,KAAK;AACpE,UAAM,mBACJ,KAAK,wBAAwB,cAAc,KAC3C,UAAU,QAAQ,QAAQ,EAAE;AAC9B,UAAM,SACJ,gBAAgB,mBACZ,GAAG,gBAAgB,MAAM,YAAY,KACrC,WAAW;AAEjB,QAAI,WAAW,WAAW,QAAQ;AAChC,aAAO,cAAc,UAAU;AAAA,IACjC;AAEA,WAAO,cAAc,WAAW,WAAW,WAAW,QAAQ,MAAM;AAAA,EACtE;AAAA,EAEQ,wBAAwB,OAAuB;AACrD,WAAO,OAAO,MAAM,GAAG,EAAE,CAAC;AAAA,EAC5B;AAAA,EAEQ,aAAa,KAAc;AACjC,UAAM,gBAAgB;AAAA,MACpB,KAAK,kBAAkB,IAAI,QAAQ,IAAI,QAAQ,KAAK,EAAE;AAAA,IACxD;AACA,WAAO,IAAI,IAAI,OAAO,QAAQ,aAAa,CAAC;AAAA,EAC9C;AAAA,EAEQ,kBAAkB,KAAa;AACrC,WAAO,MAAM,IAAI,QAAQ,oBAAoB,kBAAkB,IAAI;AAAA,EACrE;AACF;AAEO,IAAM,0BAA0B,IAClC,SACmB;AACtB,SAAO,KAAK,CAAC,aAAa,oBACtB,KAAK,CAAC,IACN,IAAI,kBAAkB,GAAG,IAAI;AACnC;","names":[]}
@@ -12,9 +12,9 @@ import {
12
12
  ONE_MINUTE_IN_MILLIS,
13
13
  ONE_MINUTE_IN_SECONDS,
14
14
  TOKEN_EXPIRY_THRESHOLD_MILLIS,
15
- appCheckAdmin,
15
+ initializeAdminConfig,
16
16
  loadAdminConfig
17
- } from "./chunk-34QENCWP.mjs";
17
+ } from "./chunk-4NYVEI6S.mjs";
18
18
  import {
19
19
  IAMSigner,
20
20
  ServiceAccountSigner,
@@ -542,10 +542,9 @@ var ServerAppCheckManager = class _ServerAppCheckManager {
542
542
  url: config.url,
543
543
  token: config.token
544
544
  });
545
- console.info("[AppCheck] Redis client initialized for token caching");
546
545
  } catch (error) {
547
546
  console.error("[AppCheck] Failed to initialize Redis client:", error);
548
- throw new Error('[AppCheck] Redis initialization failed. Install "@upstash/redis" package.');
547
+ throw new Error("[AppCheck] Redis initialization failed.");
549
548
  }
550
549
  };
551
550
  static getInstance(options) {
@@ -635,7 +634,9 @@ var ServerAppCheckManager = class _ServerAppCheckManager {
635
634
  generateAndCacheToken = async (appId) => {
636
635
  try {
637
636
  const now = Date.now();
638
- const appCheckToken = await appCheckAdmin.createToken(appId, {
637
+ const config = initializeAdminConfig();
638
+ const appCheck = getAppCheck(config);
639
+ const appCheckToken = await appCheck.createToken(config.projectId, appId, {
639
640
  ttlMillis: this.options.ttlMillis
640
641
  });
641
642
  const expiresAt = now + this.options.ttlMillis;
@@ -775,4 +776,4 @@ export {
775
776
  getAuth,
776
777
  ServiceAccountManager
777
778
  };
778
- //# sourceMappingURL=chunk-UCSJDX6Y.mjs.map
779
+ //# sourceMappingURL=chunk-ZGZR5TER.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/tokens/keys.ts","../src/tokens/verify.ts","../src/auth/getauth.ts","../src/auth/credential.ts","../src/utils/token-generator.ts","../src/app-check/AppCheckApi.ts","../src/app-check/generator.ts","../src/app-check/serverAppCheck.ts","../src/app-check/verifier.ts","../src/app-check/index.ts"],"sourcesContent":["import { type RemoteJWKSetOptions } from 'jose';\n\nimport {\n CACHE_CONTROL_REGEX,\n DEFAULT_CACHE_DURATION,\n GOOGLE_PUBLIC_KEYS_URL,\n MAX_CACHE_LAST_UPDATED_AT_SECONDS\n} from '../constants';\nimport { TokenVerificationError, TokenVerificationErrorReason } from '../utils/errors';\n\nexport type PublicKeys = { [key: string]: string };\n\ninterface PublicKeysResponse {\n keys: PublicKeys;\n expiresAt: number;\n}\n\nexport type LoadJWKFromRemoteOptions = RemoteJWKSetOptions & {\n kid: string;\n keyURL?: string;\n skipJwksCache?: boolean;\n};\n\ntype CertificateCache = Record<string, string>;\n\nlet cache: CertificateCache = {};\nlet lastUpdatedAt = 0;\nlet googleExpiresAt = 0;\n\nfunction getFromCache(kid: string) {\n return cache[kid];\n}\n\nfunction getCacheValues() {\n return Object.values(cache);\n}\n\nfunction setInCache(kid: string, certificate: string, shouldExpire = true) {\n cache[kid] = certificate;\n lastUpdatedAt = shouldExpire ? Date.now() : -1;\n}\n\nasync function fetchPublicKeys(keyUrl: string): Promise<PublicKeysResponse> {\n const url = new URL(keyUrl);\n const response = await fetch(url);\n if (!response.ok) {\n throw new TokenVerificationError({\n message: `Error loading public keys from ${url.href} with code=${response.status} `,\n reason: TokenVerificationErrorReason.TokenInvalid,\n });\n }\n\n const data = await response.json();\n const expiresAt = getExpiresAt(response);\n\n return {\n keys: data,\n expiresAt,\n };\n}\n\nexport async function loadJWKFromRemote({\n keyURL,\n skipJwksCache,\n kid,\n}: LoadJWKFromRemoteOptions): Promise<string> {\n const finalKeyURL = keyURL || GOOGLE_PUBLIC_KEYS_URL;\n if (skipJwksCache || isCacheExpired() || !getFromCache(kid)) {\n const { keys, expiresAt } = await fetchPublicKeys(finalKeyURL);\n\n if (!keys || Object.keys(keys).length === 0) {\n throw new TokenVerificationError({\n message: `The JWKS endpoint ${finalKeyURL} returned no keys`,\n reason: TokenVerificationErrorReason.RemoteJWKFailedToLoad,\n });\n }\n googleExpiresAt = expiresAt;\n\n Object.entries(keys).forEach(([keyId, cert]) => {\n setInCache(keyId, cert);\n });\n }\n const cert = getFromCache(kid);\n if (!cert) {\n getCacheValues();\n const availableKids = Object.keys(cache).sort().join(', ');\n\n throw new TokenVerificationError({\n message: `No public key found for kid \"${kid}\". Available kids: [${availableKids}]`,\n reason: TokenVerificationErrorReason.TokenInvalid,\n });\n }\n return cert;\n}\n\nfunction isCacheExpired() {\n const now = Date.now();\n if (lastUpdatedAt === -1) {\n return false;\n }\n\n const cacheAge = now - lastUpdatedAt;\n const maxCacheAge = MAX_CACHE_LAST_UPDATED_AT_SECONDS * 1000;\n const localCacheExpired = cacheAge >= maxCacheAge;\n const googleCacheExpired = now >= googleExpiresAt;\n\n const isExpired = localCacheExpired || googleCacheExpired;\n\n if (isExpired) {\n cache = {};\n }\n\n return isExpired;\n}\n\nfunction getExpiresAt(res: Response) {\n const cacheControlHeader = res.headers.get('cache-control');\n if (!cacheControlHeader) {\n return Date.now() + DEFAULT_CACHE_DURATION;\n }\n const maxAgeMatch = cacheControlHeader.match(CACHE_CONTROL_REGEX);\n const maxAge = maxAgeMatch ? parseInt(maxAgeMatch[1], 10) : DEFAULT_CACHE_DURATION / 1000;\n\n return Date.now() + maxAge * 1000;\n}\n\nexport const getCacheStats = () => ({\n localExpiry: lastUpdatedAt + MAX_CACHE_LAST_UPDATED_AT_SECONDS * 1000,\n googleExpiry: googleExpiresAt,\n cacheCount: Object.keys(cache).length,\n});\n","import type { DecodedIdToken, TernSecureAdminConfig, TernSecureConfig, TernSecureUserData} from '@tern-secure/types';\n\nimport type { JwtReturnType } from '../jwt/types';\nimport { ternDecodeJwt, verifyJwt, type VerifyJwtOptions } from '../jwt/verifyJwt';\nimport { TokenVerificationError, TokenVerificationErrorReason } from '../utils/errors';\nimport type { LoadJWKFromRemoteOptions } from './keys';\nimport { loadJWKFromRemote } from './keys';\n\nexport type VerifyTokenVOptions = Omit<VerifyJwtOptions, 'key'> & Omit<LoadJWKFromRemoteOptions, 'kid'> & {\n jwtKey?: string;\n};\n\nexport { TernSecureConfig, TernSecureAdminConfig, TernSecureUserData };\n\nexport async function verifyToken(\n token: string,\n options: VerifyTokenVOptions,\n): Promise<JwtReturnType<DecodedIdToken, TokenVerificationError>> {\n const { data: decodedResult, errors } = ternDecodeJwt(token);\n\n if (errors) {\n return { errors };\n }\n\n const { header } = decodedResult;\n const { kid } = header;\n\n if (!kid) {\n return {\n errors: [\n new TokenVerificationError({\n reason: TokenVerificationErrorReason.TokenInvalid,\n message: 'JWT \"kid\" header is missing.',\n }),\n ],\n };\n }\n\n try {\n const key = options.jwtKey || (await loadJWKFromRemote({ ...options, kid }));\n\n if (!key) {\n return {\n errors: [\n new TokenVerificationError({\n reason: TokenVerificationErrorReason.TokenInvalid,\n message: `No public key found for kid \"${kid}\".`,\n }),\n ],\n };\n }\n return await verifyJwt(token, { ...options, key });\n } catch (error) {\n if (error instanceof TokenVerificationError) {\n return { errors: [error] };\n }\n return {\n errors: [error as TokenVerificationError],\n };\n }\n}\n","import { getAppCheck } from '../app-check';\nimport { createCustomToken } from '../jwt/customJwt';\nimport type { AuthenticateRequestOptions, TernSecureUserData } from '../tokens/types';\nimport { verifyToken } from '../tokens/verify';\nimport { loadAdminConfig } from '../utils/config';\n\nexport interface IdAndRefreshTokens {\n idToken: string;\n refreshToken: string;\n}\n\nexport interface CustomTokens {\n auth_time: number;\n idToken: string;\n refreshToken: string;\n customToken: string;\n}\n\ninterface CustomForIdAndRefreshTokenOptions {\n tenantId?: string;\n appCheckToken?: string;\n referer?: string;\n}\n\ninterface FirebaseRefreshTokenResponse {\n kind: string;\n id_token: string;\n refresh_token: string;\n expires_in: string;\n isNewUser: boolean;\n}\n\ntype AuthResult<T = any> = { data: T; error: null } | { data: null; error: any };\n\nconst API_KEY_ERROR = 'API Key is required';\nconst NO_DATA_ERROR = 'No token data received';\n\nfunction parseFirebaseResponse<T>(data: unknown): T {\n if (typeof data === 'string') {\n try {\n return JSON.parse(data) as T;\n } catch (error) {\n throw new Error(`Failed to parse Firebase response: ${error}`);\n }\n }\n return data as T;\n}\n\nexport function getAuth(options: AuthenticateRequestOptions) {\n const { apiKey } = options;\n const effectiveApiKey = apiKey || process.env.NEXT_PUBLIC_FIREBASE_API_KEY;\n\n\n async function getUserData(idToken?: string, localId?: string): Promise<TernSecureUserData> {\n if (!effectiveApiKey) {\n throw new Error(API_KEY_ERROR);\n }\n const response = await options.apiClient?.userData.getUserData(effectiveApiKey, {\n idToken,\n localId,\n });\n\n if (!response?.data) {\n throw new Error(NO_DATA_ERROR);\n }\n\n const parsedData = parseFirebaseResponse<TernSecureUserData>(response.data);\n return parsedData;\n }\n\n async function refreshExpiredIdToken(\n refreshToken: string,\n opts: CustomForIdAndRefreshTokenOptions,\n ): Promise<AuthResult> {\n if (!effectiveApiKey) {\n return { data: null, error: new Error(API_KEY_ERROR) };\n }\n const response = await options.apiClient?.tokens.refreshToken(effectiveApiKey, {\n refresh_token: refreshToken,\n request_origin: opts.referer,\n });\n\n if (!response?.data) {\n return {\n data: null,\n error: new Error(NO_DATA_ERROR),\n };\n }\n\n const parsedData = parseFirebaseResponse<FirebaseRefreshTokenResponse>(response.data);\n\n return {\n data: {\n idToken: parsedData.id_token,\n refreshToken: parsedData.refresh_token,\n },\n error: null,\n };\n }\n\n async function customForIdAndRefreshToken(\n customToken: string,\n opts: CustomForIdAndRefreshTokenOptions,\n ): Promise<IdAndRefreshTokens> {\n if (!effectiveApiKey) {\n throw new Error('API Key is required to create custom token');\n }\n const data = await options.apiClient?.tokens.exchangeCustomForIdAndRefreshTokens(\n effectiveApiKey,\n {\n token: customToken,\n returnSecureToken: true,\n },\n {\n referer: opts.referer,\n appCheckToken: opts.appCheckToken,\n },\n );\n\n if (!data) {\n throw new Error('No data received from Firebase token exchange');\n }\n\n return {\n idToken: data.idToken,\n refreshToken: data.refreshToken,\n };\n }\n\n async function createCustomIdAndRefreshToken(\n idToken: string,\n opts: CustomForIdAndRefreshTokenOptions,\n ): Promise<CustomTokens> {\n const decoded = await verifyToken(idToken, options);\n const { data, errors } = decoded;\n if (errors) {\n throw errors[0];\n }\n\n //todo:\n /**\n * For sensitive applications, the auth_time should be checked before issuing the session cookie, minimizing the window of attack in case an ID token is stolen:\n */\n //if (new Date().getTime() / 1000 - data.auth_time < 5 * 60) {\n //proceed\n //}\n\n const customToken = await createCustomToken(data.uid, {\n emailVerified: data.email_verified,\n source_sign_in_provider: data.firebase.sign_in_provider,\n });\n\n const idAndRefreshTokens = await customForIdAndRefreshToken(customToken, {\n referer: opts.referer,\n appCheckToken: opts.appCheckToken,\n });\n\n const decodedCustomIdToken = await verifyToken(idAndRefreshTokens.idToken, options);\n if (decodedCustomIdToken.errors) {\n throw decodedCustomIdToken.errors[0];\n }\n\n return {\n ...idAndRefreshTokens,\n customToken,\n auth_time: decodedCustomIdToken.data.auth_time,\n };\n }\n\n async function createAppCheckToken(): Promise<AuthResult> {\n const adminConfig = loadAdminConfig();\n const appId = process.env.NEXT_PUBLIC_FIREBASE_APP_ID || '';\n const appCheck = getAppCheck(adminConfig, options.tenantId);\n try {\n const appCheckResponse = await appCheck.createToken(adminConfig.projectId, appId);\n\n return {\n data: {\n token: appCheckResponse.token,\n ttl: appCheckResponse.ttl,\n },\n error: null,\n };\n } catch (error) {\n return { data: null, error };\n }\n }\n\n async function verifyAppCheckToken(token: string): Promise<AuthResult> {\n const adminConfig = loadAdminConfig();\n const appCheck = getAppCheck(adminConfig, options.tenantId);\n try {\n const decodedToken = await appCheck.verifyToken(token, adminConfig.projectId, {});\n\n return {\n data: decodedToken,\n error: null,\n };\n } catch (error) {\n return { data: null, error };\n }\n }\n\n return {\n getUserData,\n customForIdAndRefreshToken,\n createCustomIdAndRefreshToken,\n refreshExpiredIdToken,\n createAppCheckToken,\n verifyAppCheckToken,\n };\n}\n","import type { JWTPayload } from '@tern-secure/types';\n\nimport {\n GOOGLE_AUTH_TOKEN_HOST,\n GOOGLE_AUTH_TOKEN_PATH,\n GOOGLE_TOKEN_AUDIENCE,\n ONE_HOUR_IN_SECONDS,\n TOKEN_EXPIRY_THRESHOLD_MILLIS\n} from '../constants'\nimport { ternSignJwt } from '../jwt';\nimport { fetchJson } from '../utils/fetcher';\n\n\nexport interface GoogleOAuthAccessToken {\n access_token: string;\n expires_in: number;\n}\n\nexport interface ServiceAccount {\n projectId: string;\n privateKey: string;\n clientEmail: string;\n}\n\nexport interface FirebaseAccessToken {\n accessToken: string;\n expirationTime: number;\n}\n\nconst accessTokenCache: Map<string, FirebaseAccessToken> = new Map();\n\nexport interface Credential {\n getAccessToken: (refresh?: boolean) => Promise<FirebaseAccessToken>;\n}\n\nasync function requestAccessToken(urlString: string, init: RequestInit): Promise<FirebaseAccessToken> {\n const json = await fetchJson(urlString, init);\n\n if (!json.access_token || !json.expires_in) {\n throw new Error('Invalid access token response');\n }\n\n return {\n accessToken: json.access_token,\n expirationTime: Date.now() + (json.expires_in * 1000),\n }\n}\n\nexport class ServiceAccountManager implements Credential {\n public readonly projectId: string;\n public readonly privateKey: string;\n public readonly clientEmail: string;\n\n constructor(serviceAccount: ServiceAccount) {\n this.projectId = serviceAccount.projectId;\n this.privateKey = serviceAccount.privateKey;\n this.clientEmail = serviceAccount.clientEmail;\n }\n\n private fetchAccessToken = async (url: string): Promise<FirebaseAccessToken> => {\n const token = await this.createJwt();\n const postData =\n 'grant_type=urn%3Aietf%3Aparams%3Aoauth%3A' +\n 'grant-type%3Ajwt-bearer&assertion=' +\n token;\n\n return requestAccessToken(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n Authorization: `Bearer ${token}`,\n Accept: 'application/json',\n },\n body: postData,\n })\n }\n\n private fetchAndCacheAccessToken = async (url: string): Promise<FirebaseAccessToken> => {\n const accessToken = await this.fetchAccessToken(url);\n accessTokenCache.set(this.projectId, accessToken);\n return accessToken;\n }\n\n public getAccessToken = async (refresh?: boolean): Promise<FirebaseAccessToken> => {\n const url = `https://${GOOGLE_AUTH_TOKEN_HOST}${GOOGLE_AUTH_TOKEN_PATH}`;\n\n if (refresh) {\n return this.fetchAndCacheAccessToken(url);\n }\n\n const cachedResponse = accessTokenCache.get(this.projectId);\n\n if (!cachedResponse || cachedResponse.expirationTime - Date.now() <= TOKEN_EXPIRY_THRESHOLD_MILLIS) {\n return this.fetchAndCacheAccessToken(url);\n }\n\n return cachedResponse;\n }\n\n private createJwt = async (): Promise<string> => {\n const iat = Math.floor(Date.now() / 1000);\n\n const payload = {\n aud: GOOGLE_TOKEN_AUDIENCE,\n iat,\n exp: iat + ONE_HOUR_IN_SECONDS,\n iss: this.clientEmail,\n sub: this.clientEmail,\n scope: [\n 'https://www.googleapis.com/auth/cloud-platform',\n 'https://www.googleapis.com/auth/firebase.database',\n 'https://www.googleapis.com/auth/firebase.messaging',\n 'https://www.googleapis.com/auth/identitytoolkit',\n 'https://www.googleapis.com/auth/userinfo.email'\n ].join(' ')\n } as JWTPayload;\n\n return ternSignJwt({\n payload,\n privateKey: this.privateKey,\n });\n }\n}\n","import type { Credential } from '../auth'\nimport { ServiceAccountManager } from '../auth'\nimport type { CryptoSigner } from '../jwt'\nimport { IAMSigner, ServiceAccountSigner } from '../jwt'\n\nexport function cryptoSignerFromCredential(\n credential: Credential,\n tenantId?: string,\n serviceAccountId?: string\n): CryptoSigner {\n if (credential instanceof ServiceAccountManager) {\n return new ServiceAccountSigner(credential, tenantId);\n }\n\n return new IAMSigner(credential, tenantId, serviceAccountId);\n}","import type { Credential } from '../auth'\nimport type { AppCheckParams, AppCheckToken } from './types'\n\nexport function getSdkVersion(): string {\n return '12.7.0';\n}\n\nconst FIREBASE_APP_CHECK_CONFIG_HEADERS = {\n 'X-Firebase-Client': `fire-admin-node/${getSdkVersion()}`\n};\n\n/**\n * App Check API for managing Firebase App Check tokens via REST\n * Firebase REST API endpoint: https://firebaseappcheck.googleapis.com/v1beta/projects/{projectId}/apps/{appId}:exchangeCustomToken\n */\nexport class AppCheckApi {\n constructor(private credential: Credential) { }\n\n public async exchangeToken(params: AppCheckParams): Promise<AppCheckToken> {\n const { projectId, appId, customToken, limitedUse = false } = params;\n const token = await this.credential.getAccessToken(false);\n if (!projectId || !appId) {\n throw new Error('Project ID and App ID are required for App Check token exchange');\n }\n\n const endpoint = `https://firebaseappcheck.googleapis.com/v1/projects/${projectId}/apps/${appId}:exchangeCustomToken`;\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${token.accessToken}`,\n };\n\n try {\n const response = await fetch(endpoint, {\n method: 'POST',\n headers,\n body: JSON.stringify({ customToken, limitedUse }),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`App Check token exchange failed: ${response.status} ${errorText}`);\n }\n\n const data = await response.json();\n return {\n token: data.token,\n ttl: data.ttl,\n };\n } catch (error) {\n console.warn('[ternsecure - appcheck api]unexpected error:', error);\n throw error;\n }\n }\n public async exchangeDebugToken(params: AppCheckParams): Promise<AppCheckToken> {\n const { projectId, appId, customToken, accessToken, limitedUse = false } = params;\n if (!projectId || !appId) {\n throw new Error('Project ID and App ID are required for App Check token exchange');\n }\n\n const endpoint = `https://firebaseappcheck.googleapis.com/v1beta/projects/${projectId}/apps/${appId}:exchangeDebugToken`;\n\n const headers: Record<string, string> = {\n ...FIREBASE_APP_CHECK_CONFIG_HEADERS,\n 'Authorization': `Bearer ${accessToken}`,\n };\n\n const body = {\n customToken,\n limitedUse,\n }\n\n try {\n const response = await fetch(endpoint, {\n method: 'POST',\n headers,\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`App Check token exchange failed: ${response.status} ${errorText}`);\n }\n\n const data = await response.json();\n return {\n token: data.token,\n ttl: data.ttl,\n };\n } catch (error) {\n console.warn('[ternsecure - appcheck api]unexpected error:', error);\n throw error;\n }\n }\n}\n","import {\n FIREBASE_APP_CHECK_AUDIENCE,\n ONE_DAY_IN_MILLIS,\n ONE_MINUTE_IN_MILLIS,\n ONE_MINUTE_IN_SECONDS\n} from '../constants'\nimport type { CryptoSigner } from '../jwt';\nimport type { AppCheckTokenOptions } from './types';\n\n\nfunction transformMillisecondsToSecondsString(milliseconds: number): string {\n let duration: string;\n const seconds = Math.floor(milliseconds / 1000);\n const nanos = Math.floor((milliseconds - seconds * 1000) * 1000000);\n if (nanos > 0) {\n let nanoString = nanos.toString();\n while (nanoString.length < 9) {\n nanoString = '0' + nanoString;\n }\n duration = `${seconds}.${nanoString}s`;\n } else {\n duration = `${seconds}s`;\n }\n return duration;\n}\n\nexport class AppCheckTokenGenerator {\n private readonly signer: CryptoSigner;\n\n constructor(signer: CryptoSigner) {\n this.signer = signer;\n }\n\n public async createCustomToken(\n appId: string,\n options?: AppCheckTokenOptions\n ): Promise<string> {\n if (!appId) {\n throw new Error(\n 'appId is invalid',\n );\n }\n let customOptions = {};\n if (typeof options !== 'undefined') {\n customOptions = this.validateTokenOptions(options);\n }\n\n const account = await this.signer.getAccountId();\n\n const iat = Math.floor(Date.now() / 1000);\n const body = {\n iss: account,\n sub: account,\n app_id: appId,\n aud: FIREBASE_APP_CHECK_AUDIENCE,\n exp: iat + ONE_MINUTE_IN_SECONDS * 5,\n iat,\n ...customOptions\n };\n\n return this.signer.sign(body);\n }\n\n private validateTokenOptions(options: AppCheckTokenOptions): {\n [key: string]: unknown;\n } {\n if (typeof options.ttlMillis !== 'undefined') {\n if (\n options.ttlMillis < ONE_MINUTE_IN_MILLIS * 30 ||\n options.ttlMillis > ONE_DAY_IN_MILLIS * 7\n ) {\n throw new Error(\n 'ttlMillis must be a duration in milliseconds between 30 minutes and 7 days (inclusive).'\n );\n }\n\n return { ttl: transformMillisecondsToSecondsString(options.ttlMillis) };\n }\n return {};\n }\n}\n","import { Redis } from \"@upstash/redis\";\n\nimport type { AppCheckOptions } from '../adapters/types';\nimport { initializeAdminConfig } from '../utils/config';\nimport { getAppCheck } from \"./index\";\n\ninterface CachedToken {\n token: string;\n expiresAt: number;\n}\n\n/**\n * Redis client interface for AppCheck token caching (Upstash Redis or compatible adapter)\n */\ninterface RedisClient {\n get(key: string): Promise<any>;\n set(key: string, value: any, opts?: { px?: number }): Promise<any>;\n del(key: string): Promise<number>;\n}\n\n\nexport class ServerAppCheckManager {\n private static instances: Map<string, ServerAppCheckManager> = new Map();\n private memoryCache: Map<string, CachedToken> = new Map();\n private redisClient: RedisClient | null = null;\n private readonly options: Required<Omit<AppCheckOptions, 'redis' | 'skipInMemoryFirst'>> & {\n redis?: AppCheckOptions['redis'];\n skipInMemoryFirst: boolean;\n };\n private pendingTokens: Map<string, Promise<string | null>> = new Map();\n\n private constructor(options?: AppCheckOptions) {\n const defaultOptions: Required<Omit<AppCheckOptions, 'redis' | 'skipInMemoryFirst'>> & { skipInMemoryFirst: boolean } = {\n strategy: 'memory',\n ttlMillis: 3600000, // 1 hour\n refreshBufferMillis: 300000, // 5 minutes\n keyPrefix: 'appcheck:token:',\n skipInMemoryFirst: false,\n };\n\n this.options = { ...defaultOptions, ...options };\n\n if (this.options.strategy === 'redis' && this.options.redis) {\n void this.initializeRedis(this.options.redis);\n }\n }\n\n private initializeRedis = (config: AppCheckOptions['redis']): void => {\n if (!config) {\n throw new Error('[AppCheck] Redis configuration is required when strategy is \"redis\"');\n }\n\n try {\n this.redisClient = new Redis({\n url: config.url,\n token: config.token,\n });\n\n } catch (error) {\n console.error('[AppCheck] Failed to initialize Redis client:', error);\n throw new Error('[AppCheck] Redis initialization failed.');\n }\n }\n\n public static getInstance(options?: AppCheckOptions): ServerAppCheckManager {\n const key = options?.strategy || 'memory';\n\n if (!ServerAppCheckManager.instances.has(key)) {\n ServerAppCheckManager.instances.set(key, new ServerAppCheckManager(options));\n }\n\n const instance = ServerAppCheckManager.instances.get(key);\n if (!instance) {\n throw new Error('[AppCheck] Failed to get instance');\n }\n\n return instance;\n }\n\n private buildCacheKey(appId: string): string {\n return `${this.options.keyPrefix}${appId}`;\n }\n\n\n private getCachedToken = async (appId: string): Promise<CachedToken | null> => {\n if (this.options.strategy === 'memory') {\n return this.memoryCache.get(appId) || null;\n }\n\n if (this.options.strategy === 'redis') {\n // Check in-memory cache first (unless skipInMemoryFirst is true)\n if (!this.options.skipInMemoryFirst) {\n const memCached = this.memoryCache.get(appId);\n if (memCached) {\n return memCached;\n }\n }\n\n // Fallback to Redis\n if (this.redisClient) {\n try {\n const key = this.buildCacheKey(appId);\n const cached = await this.redisClient.get(key);\n\n if (cached) {\n const parsed: CachedToken = typeof cached === 'string' ? JSON.parse(cached) : cached;\n\n if (!this.options.skipInMemoryFirst) {\n this.memoryCache.set(appId, parsed);\n }\n\n return parsed;\n }\n } catch (error) {\n console.error('[AppCheck] Redis get error:', error);\n }\n }\n }\n\n return null;\n }\n\n\n private setCachedToken = async (appId: string, token: string, expiresAt: number): Promise<void> => {\n const cachedToken: CachedToken = { token, expiresAt };\n\n // Always store in memory cache for both strategies\n this.memoryCache.set(appId, cachedToken);\n\n if (this.options.strategy === 'memory') {\n return;\n }\n\n // For Redis strategy, also persist to Redis\n if (this.options.strategy === 'redis' && this.redisClient) {\n try {\n const key = this.buildCacheKey(appId);\n const ttl = expiresAt - Date.now();\n\n await this.redisClient.set(key, JSON.stringify(cachedToken), {\n px: ttl, // Expiry in milliseconds (lowercase for Upstash)\n });\n } catch (error) {\n console.error('[AppCheck] Redis set error:', error);\n }\n }\n }\n\n getOrGenerateToken = async (appId: string): Promise<string | null> => {\n const cached = await this.getCachedToken(appId);\n const now = Date.now();\n\n if (cached && cached.expiresAt > now + this.options.refreshBufferMillis) {\n return cached.token;\n }\n\n const pending = this.pendingTokens.get(appId);\n if (pending) {\n return pending;\n }\n\n const tokenPromise = this.generateAndCacheToken(appId);\n this.pendingTokens.set(appId, tokenPromise);\n\n try {\n const token = await tokenPromise;\n return token;\n } finally {\n this.pendingTokens.delete(appId);\n }\n }\n\n /**\n * Generate and cache a new token\n */\n private generateAndCacheToken = async (appId: string): Promise<string | null> => {\n try {\n const now = Date.now();\n const config = initializeAdminConfig();\n const appCheck = getAppCheck(config);\n\n const appCheckToken = await appCheck.createToken(config.projectId, appId, {\n ttlMillis: this.options.ttlMillis,\n });\n\n const expiresAt = now + this.options.ttlMillis;\n await this.setCachedToken(appId, appCheckToken.token, expiresAt);\n\n return appCheckToken.token;\n } catch (error) {\n console.error('[AppCheck] Failed to generate token:', error);\n return null;\n }\n }\n\n clearCache = async (appId?: string): Promise<void> => {\n if (appId) {\n this.memoryCache.delete(appId);\n } else {\n this.memoryCache.clear();\n }\n\n if (this.options.strategy === 'redis' && this.redisClient) {\n try {\n if (appId) {\n const key = this.buildCacheKey(appId);\n await this.redisClient.del(key);\n }\n } catch (error) {\n console.error('[AppCheck] Redis delete error:', error);\n }\n }\n }\n\n getCacheStats(): {\n strategy: string;\n memorySize: number;\n entries: Array<{ appId: string; expiresIn: number }>\n } {\n const now = Date.now();\n const entries = Array.from(this.memoryCache.entries()).map(([appId, cached]) => ({\n appId,\n expiresIn: Math.max(0, cached.expiresAt - now),\n }));\n\n return {\n strategy: this.options.strategy,\n memorySize: this.memoryCache.size,\n entries,\n };\n }\n\n /**\n * Close Redis connection\n */\n disconnect(): void {\n if (this.redisClient) {\n this.redisClient = null;\n }\n }\n}","import type { DecodedAppCheckToken } from '@tern-secure/types';\nimport { createRemoteJWKSet, type KeyLike, type ProtectedHeaderParameters } from 'jose';\n\nimport type { Credential } from '../auth';\nimport type { JwtReturnType } from '../jwt';\nimport { ternDecodeJwt, verifyAppCheckJwt, type VerifyJwtOptions } from '../jwt/verifyJwt';\nimport type { LoadJWKFromRemoteOptions } from '../tokens/keys';\nimport { TokenVerificationError, TokenVerificationErrorReason } from '../utils/errors';\n\nexport type VerifyAppcheckOptions = Omit<VerifyJwtOptions, 'key'> & Omit<LoadJWKFromRemoteOptions, 'kid'> & {\n currentDate?: Date;\n checkRevoked?: boolean;\n referer?: string;\n experimental_enableTokenRefreshOnExpiredKidHeader?: boolean;\n};\n\nconst getPublicKey = async (header: ProtectedHeaderParameters, keyURL: string): Promise<KeyLike> => {\n const jswksUrl: URL = new URL(keyURL);\n const getKey = createRemoteJWKSet(jswksUrl);\n\n return getKey(header);\n\n}\n\n\nconst verifyAppCheckToken = async (\n token: string,\n options: VerifyAppcheckOptions,\n): Promise<JwtReturnType<DecodedAppCheckToken, TokenVerificationError>> => {\n const { data: decodedResult, errors } = ternDecodeJwt(token);\n\n if (errors) {\n throw errors[0];\n }\n\n const { header } = decodedResult;\n const { kid } = header;\n\n if (!kid) {\n return {\n errors: [\n new TokenVerificationError({\n reason: TokenVerificationErrorReason.TokenInvalid,\n message: 'JWT \"kid\" header is missing.',\n }),\n ],\n };\n }\n\n try {\n const getPublicKeyForToken = () => getPublicKey(header, options.keyURL || '');\n\n return await verifyAppCheckJwt(token, { ...options, key: getPublicKeyForToken });\n } catch (error) {\n if (error instanceof TokenVerificationError) {\n return { errors: [error] };\n }\n return {\n errors: [error as TokenVerificationError],\n };\n }\n};\n\nexport class AppcheckTokenVerifier {\n constructor(private readonly credential: Credential) { }\n\n public verifyToken = async (\n token: string,\n projectId: string,\n options: VerifyAppcheckOptions,\n ): Promise<DecodedAppCheckToken> => {\n const { data, errors } = await verifyAppCheckToken(token, options);\n if (errors) {\n throw errors[0];\n }\n\n return data;\n };\n}","import type { VerifyAppCheckTokenResponse } from \"@tern-secure/types\";\n\nimport type { Credential, ServiceAccount } from \"../auth\";\nimport { ServiceAccountManager } from \"../auth\";\nimport { cryptoSignerFromCredential } from '../utils/token-generator';\nimport { AppCheckApi } from \"./AppCheckApi\";\nimport { AppCheckTokenGenerator } from \"./generator\";\nimport { ServerAppCheckManager } from \"./serverAppCheck\";\nimport type { AppCheckToken, AppCheckTokenOptions } from \"./types\";\nimport { AppcheckTokenVerifier, type VerifyAppcheckOptions } from \"./verifier\";\n\n\nconst JWKS_URL = 'https://firebaseappcheck.googleapis.com/v1/jwks';\n\nclass AppCheck {\n private readonly client: AppCheckApi;\n private readonly tokenGenerator: AppCheckTokenGenerator;\n private readonly appCheckTokenVerifier: AppcheckTokenVerifier;\n private readonly limitedUse?: boolean;\n\n constructor(credential: Credential, tenantId?: string, limitedUse?: boolean) {\n this.client = new AppCheckApi(credential);\n this.tokenGenerator = new AppCheckTokenGenerator(\n cryptoSignerFromCredential(credential, tenantId)\n );\n this.appCheckTokenVerifier = new AppcheckTokenVerifier(credential);\n this.limitedUse = limitedUse;\n }\n\n public createToken = (projectId: string, appId: string, options?: AppCheckTokenOptions): Promise<AppCheckToken> => {\n return this.tokenGenerator\n .createCustomToken(appId, options)\n .then((customToken) => {\n return this.client.exchangeToken({ customToken, projectId, appId });\n });\n };\n\n public verifyToken = async (appCheckToken: string, projectId: string, options: VerifyAppcheckOptions): Promise<VerifyAppCheckTokenResponse> => {\n return this.appCheckTokenVerifier\n .verifyToken(appCheckToken, projectId, { keyURL: JWKS_URL, ...options })\n .then((decodedToken) => {\n return {\n appId: decodedToken.app_id,\n token: decodedToken,\n };\n });\n\n }\n\n}\n\n\nfunction getAppCheck(serviceAccount: ServiceAccount, tenantId?: string, limitedUse?: boolean): AppCheck {\n return new AppCheck(new ServiceAccountManager(serviceAccount), tenantId, limitedUse);\n}\n\nexport { AppCheck, getAppCheck };\nexport { ServerAppCheckManager };"],"mappings":"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","names":["cert","verifyAppCheckToken"]}
package/dist/index.js CHANGED
@@ -1,9 +1,7 @@
1
1
  "use strict";
2
- var __create = Object.create;
3
2
  var __defProp = Object.defineProperty;
4
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
5
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
6
  var __export = (target, all) => {
9
7
  for (var name in all)
@@ -17,14 +15,6 @@ var __copyProps = (to, from, except, desc) => {
17
15
  }
18
16
  return to;
19
17
  };
20
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
- // If the importer is in node compatibility mode or this is not an ESM
22
- // file that has been converted to a CommonJS file using a Babel-
23
- // compatible transform (i.e. "__esModule" has not been set), then set
24
- // "default" to the CommonJS "module.exports" for node compatibility.
25
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
- mod
27
- ));
28
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
19
 
30
20
  // src/index.ts
@@ -1845,96 +1835,12 @@ var AppCheckTokenGenerator = class {
1845
1835
  // src/app-check/serverAppCheck.ts
1846
1836
  var import_redis = require("@upstash/redis");
1847
1837
 
1848
- // src/admin/sessionTernSecure.ts
1849
- var import_errors6 = require("@tern-secure/shared/errors");
1850
-
1851
- // src/utils/admin-init.ts
1852
- var import_firebase_admin = __toESM(require("firebase-admin"));
1853
- var import_app_check = require("firebase-admin/app-check");
1854
-
1855
1838
  // src/utils/config.ts
1856
1839
  var loadAdminConfig = () => ({
1857
1840
  projectId: process.env.FIREBASE_PROJECT_ID || "",
1858
1841
  clientEmail: process.env.FIREBASE_CLIENT_EMAIL || "",
1859
1842
  privateKey: process.env.FIREBASE_PRIVATE_KEY || ""
1860
1843
  });
1861
- var validateAdminConfig = (config) => {
1862
- const requiredFields = [
1863
- "projectId",
1864
- "clientEmail",
1865
- "privateKey"
1866
- ];
1867
- const errors = [];
1868
- requiredFields.forEach((field) => {
1869
- if (!config[field]) {
1870
- errors.push(`Missing required field: FIREBASE_${String(field).toUpperCase()}`);
1871
- }
1872
- });
1873
- return {
1874
- isValid: errors.length === 0,
1875
- errors,
1876
- config
1877
- };
1878
- };
1879
- var initializeAdminConfig = () => {
1880
- const config = loadAdminConfig();
1881
- const validationResult = validateAdminConfig(config);
1882
- if (!validationResult.isValid) {
1883
- throw new Error(
1884
- `Firebase Admin configuration validation failed:
1885
- ${validationResult.errors.join("\n")}`
1886
- );
1887
- }
1888
- return config;
1889
- };
1890
-
1891
- // src/utils/admin-init.ts
1892
- if (!import_firebase_admin.default.apps.length) {
1893
- try {
1894
- const config = initializeAdminConfig();
1895
- import_firebase_admin.default.initializeApp({
1896
- credential: import_firebase_admin.default.credential.cert({
1897
- ...config,
1898
- privateKey: config.privateKey.replace(/\\n/g, "\n")
1899
- })
1900
- });
1901
- } catch (error) {
1902
- console.error("Firebase admin initialization error", error);
1903
- }
1904
- }
1905
- var adminTernSecureAuth = import_firebase_admin.default.auth();
1906
- var adminTernSecureDb = import_firebase_admin.default.firestore();
1907
- var TernSecureTenantManager = import_firebase_admin.default.auth().tenantManager();
1908
- var appCheckAdmin = (0, import_app_check.getAppCheck)();
1909
-
1910
- // src/admin/sessionTernSecure.ts
1911
- var DEFAULT_COOKIE_CONFIG = {
1912
- DEFAULT_EXPIRES_IN_MS: 5 * 60 * 1e3,
1913
- // 5 minutes
1914
- DEFAULT_EXPIRES_IN_SECONDS: 5 * 60,
1915
- REVOKE_REFRESH_TOKENS_ON_SIGNOUT: true
1916
- };
1917
- var DEFAULT_COOKIE_OPTIONS = {
1918
- httpOnly: true,
1919
- secure: process.env.NODE_ENV === "production",
1920
- sameSite: "strict",
1921
- path: "/"
1922
- };
1923
-
1924
- // src/admin/nextSessionTernSecure.ts
1925
- var import_cookie2 = require("@tern-secure/shared/cookie");
1926
- var import_errors7 = require("@tern-secure/shared/errors");
1927
- var import_headers = require("next/headers");
1928
- var SESSION_CONSTANTS = {
1929
- COOKIE_NAME: constants.Cookies.Session,
1930
- DEFAULT_EXPIRES_IN_MS: 60 * 60 * 24 * 5 * 1e3,
1931
- // 5 days
1932
- DEFAULT_EXPIRES_IN_SECONDS: 60 * 60 * 24 * 5,
1933
- REVOKE_REFRESH_TOKENS_ON_SIGNOUT: true
1934
- };
1935
-
1936
- // src/admin/user.ts
1937
- var import_errors8 = require("@tern-secure/shared/errors");
1938
1844
 
1939
1845
  // src/app-check/verifier.ts
1940
1846
  var import_jose6 = require("jose");
@@ -2014,7 +1920,7 @@ var AppCheck = class {
2014
1920
  });
2015
1921
  };
2016
1922
  };
2017
- function getAppCheck2(serviceAccount, tenantId, limitedUse) {
1923
+ function getAppCheck(serviceAccount, tenantId, limitedUse) {
2018
1924
  return new AppCheck(new ServiceAccountManager(serviceAccount), tenantId, limitedUse);
2019
1925
  }
2020
1926
 
@@ -2121,7 +2027,7 @@ function getAuth(options) {
2121
2027
  async function createAppCheckToken() {
2122
2028
  const adminConfig = loadAdminConfig();
2123
2029
  const appId = process.env.NEXT_PUBLIC_FIREBASE_APP_ID || "";
2124
- const appCheck = getAppCheck2(adminConfig, options.tenantId);
2030
+ const appCheck = getAppCheck(adminConfig, options.tenantId);
2125
2031
  try {
2126
2032
  const appCheckResponse = await appCheck.createToken(adminConfig.projectId, appId);
2127
2033
  return {
@@ -2137,7 +2043,7 @@ function getAuth(options) {
2137
2043
  }
2138
2044
  async function verifyAppCheckToken2(token) {
2139
2045
  const adminConfig = loadAdminConfig();
2140
- const appCheck = getAppCheck2(adminConfig, options.tenantId);
2046
+ const appCheck = getAppCheck(adminConfig, options.tenantId);
2141
2047
  try {
2142
2048
  const decodedToken = await appCheck.verifyToken(token, adminConfig.projectId, {});
2143
2049
  return {
@@ -2232,7 +2138,7 @@ var ServiceAccountManager = class {
2232
2138
  };
2233
2139
 
2234
2140
  // src/tokens/cookie.ts
2235
- var import_cookie3 = require("@tern-secure/shared/cookie");
2141
+ var import_cookie2 = require("@tern-secure/shared/cookie");
2236
2142
 
2237
2143
  // src/tokens/request.ts
2238
2144
  function hasAuthorizationHeader(request) {
@@ -2244,7 +2150,7 @@ function convertToSeconds(value) {
2244
2150
  function isRequestForRefresh(error, context, request) {
2245
2151
  return error.reason === TokenVerificationErrorReason.TokenExpired && !!context.refreshTokenInCookie && request.method === "GET";
2246
2152
  }
2247
- async function authenticateRequest2(request, options) {
2153
+ async function authenticateRequest(request, options) {
2248
2154
  const context = createRequestProcessor(createTernSecureRequest(request), options);
2249
2155
  const { refreshTokenInCookie } = context;
2250
2156
  const { refreshExpiredIdToken } = getAuth(options);
@@ -2280,8 +2186,8 @@ async function authenticateRequest2(request, options) {
2280
2186
  const headers = new Headers();
2281
2187
  const { idToken } = refreshedData;
2282
2188
  const maxAge = 365 * 24 * 60 * 60;
2283
- const cookiePrefix = (0, import_cookie3.getCookiePrefix)();
2284
- const idTokenCookieName = (0, import_cookie3.getCookieName)(constants.Cookies.IdToken, cookiePrefix);
2189
+ const cookiePrefix = (0, import_cookie2.getCookiePrefix)();
2190
+ const idTokenCookieName = (0, import_cookie2.getCookieName)(constants.Cookies.IdToken, cookiePrefix);
2285
2191
  const baseCookieAttributes = `HttpOnly; Secure; SameSite=Strict; Max-Age=${maxAge}; Path=/`;
2286
2192
  const idTokenCookie = `${idTokenCookieName}=${idToken}; ${baseCookieAttributes};`;
2287
2193
  headers.append("Set-Cookie", idTokenCookie);
@@ -2429,7 +2335,7 @@ function createAuthenticateRequest(params) {
2429
2335
  const apiClient = params.apiClient;
2430
2336
  const handleAuthenticateRequest = (request, options = {}) => {
2431
2337
  const { apiUrl } = buildTimeOptions;
2432
- return authenticateRequest2(request, { ...options, apiUrl, apiClient });
2338
+ return authenticateRequest(request, { ...options, apiUrl, apiClient });
2433
2339
  };
2434
2340
  return {
2435
2341
  authenticateRequest: handleAuthenticateRequest