@tern-secure/backend 1.2.0-canary.v20250919134427 → 1.2.0-canary.v20251002175916
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/auth/package.json +5 -0
- package/dist/admin/index.d.ts +1 -1
- package/dist/admin/index.d.ts.map +1 -1
- package/dist/admin/index.js +200 -182
- package/dist/admin/index.js.map +1 -1
- package/dist/admin/index.mjs +23 -489
- package/dist/admin/index.mjs.map +1 -1
- package/dist/admin/nextSessionTernSecure.d.ts.map +1 -1
- package/dist/admin/sessionTernSecure.d.ts +27 -5
- package/dist/admin/sessionTernSecure.d.ts.map +1 -1
- package/dist/auth/getauth.d.ts +15 -0
- package/dist/auth/getauth.d.ts.map +1 -0
- package/dist/auth/index.d.ts +2 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +694 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/index.mjs +53 -0
- package/dist/auth/index.mjs.map +1 -0
- package/dist/{chunk-ZIO4EKS5.mjs → chunk-4SGWLAJG.mjs} +8 -31
- package/dist/chunk-4SGWLAJG.mjs.map +1 -0
- package/dist/chunk-NEPV6OWI.mjs +550 -0
- package/dist/chunk-NEPV6OWI.mjs.map +1 -0
- package/dist/chunk-YKIA5EBF.mjs +142 -0
- package/dist/chunk-YKIA5EBF.mjs.map +1 -0
- package/dist/constants.d.ts +4 -5
- package/dist/constants.d.ts.map +1 -1
- package/dist/fireRestApi/createFireApi.d.ts +12 -0
- package/dist/fireRestApi/createFireApi.d.ts.map +1 -0
- package/dist/fireRestApi/emulator.d.ts +4 -0
- package/dist/fireRestApi/emulator.d.ts.map +1 -0
- package/dist/fireRestApi/endpointUrl.d.ts +6 -0
- package/dist/fireRestApi/endpointUrl.d.ts.map +1 -0
- package/dist/fireRestApi/endpoints/AbstractApi.d.ts +7 -0
- package/dist/fireRestApi/endpoints/AbstractApi.d.ts.map +1 -0
- package/dist/fireRestApi/endpoints/EmailApi.d.ts +14 -0
- package/dist/fireRestApi/endpoints/EmailApi.d.ts.map +1 -0
- package/dist/fireRestApi/endpoints/PasswordApi.d.ts +20 -0
- package/dist/fireRestApi/endpoints/PasswordApi.d.ts.map +1 -0
- package/dist/fireRestApi/endpoints/SignInTokenApi.d.ts +11 -0
- package/dist/fireRestApi/endpoints/SignInTokenApi.d.ts.map +1 -0
- package/dist/fireRestApi/endpoints/SignUpApi.d.ts +11 -0
- package/dist/fireRestApi/endpoints/SignUpApi.d.ts.map +1 -0
- package/dist/fireRestApi/endpoints/TokenApi.d.ts +21 -0
- package/dist/fireRestApi/endpoints/TokenApi.d.ts.map +1 -0
- package/dist/fireRestApi/endpoints/index.d.ts +6 -0
- package/dist/fireRestApi/endpoints/index.d.ts.map +1 -0
- package/dist/fireRestApi/index.d.ts +2 -0
- package/dist/fireRestApi/index.d.ts.map +1 -0
- package/dist/fireRestApi/request.d.ts +34 -0
- package/dist/fireRestApi/request.d.ts.map +1 -0
- package/dist/fireRestApi/resources/JSON.d.ts +50 -0
- package/dist/fireRestApi/resources/JSON.d.ts.map +1 -0
- package/dist/fireRestApi/resources/Token.d.ts +13 -0
- package/dist/fireRestApi/resources/Token.d.ts.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +271 -75
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +257 -179
- package/dist/index.mjs.map +1 -1
- package/dist/instance/backendFireInstance.d.ts +4 -4
- package/dist/instance/backendFireInstance.d.ts.map +1 -1
- package/dist/instance/backendInstanceEdge.d.ts +2 -2
- package/dist/instance/backendInstanceEdge.d.ts.map +1 -1
- package/dist/tokens/authstate.d.ts +1 -1
- package/dist/tokens/authstate.d.ts.map +1 -1
- package/dist/tokens/keys.d.ts.map +1 -1
- package/dist/tokens/request.d.ts +3 -3
- package/dist/tokens/request.d.ts.map +1 -1
- package/dist/tokens/requestFire.d.ts.map +1 -1
- package/dist/tokens/types.d.ts +5 -2
- package/dist/tokens/types.d.ts.map +1 -1
- package/dist/utils/options.d.ts +1 -1
- package/dist/utils/options.d.ts.map +1 -1
- package/package.json +14 -3
- package/dist/admin/gemini.sessionTernSecure.d.ts +0 -8
- package/dist/admin/gemini.sessionTernSecure.d.ts.map +0 -1
- package/dist/chunk-ZIO4EKS5.mjs.map +0 -1
|
@@ -0,0 +1,694 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
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
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/auth/index.ts
|
|
31
|
+
var auth_exports = {};
|
|
32
|
+
__export(auth_exports, {
|
|
33
|
+
getAuth: () => getAuth
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(auth_exports);
|
|
36
|
+
|
|
37
|
+
// src/admin/sessionTernSecure.ts
|
|
38
|
+
var import_errors = require("@tern-secure/shared/errors");
|
|
39
|
+
|
|
40
|
+
// src/constants.ts
|
|
41
|
+
var GOOGLE_PUBLIC_KEYS_URL = "https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com";
|
|
42
|
+
var MAX_CACHE_LAST_UPDATED_AT_SECONDS = 5 * 60;
|
|
43
|
+
var DEFAULT_CACHE_DURATION = 3600 * 1e3;
|
|
44
|
+
var CACHE_CONTROL_REGEX = /max-age=(\d+)/;
|
|
45
|
+
var Attributes = {
|
|
46
|
+
AuthToken: "__ternsecureAuthToken",
|
|
47
|
+
AuthSignature: "__ternsecureAuthSignature",
|
|
48
|
+
AuthStatus: "__ternsecureAuthStatus",
|
|
49
|
+
AuthReason: "__ternsecureAuthReason",
|
|
50
|
+
AuthMessage: "__ternsecureAuthMessage",
|
|
51
|
+
TernSecureUrl: "__ternsecureUrl"
|
|
52
|
+
};
|
|
53
|
+
var Cookies = {
|
|
54
|
+
Session: "__session",
|
|
55
|
+
CsrfToken: "__session_terncf",
|
|
56
|
+
IdToken: "FIREBASE_[DEFAULT]",
|
|
57
|
+
Refresh: "FIREBASEID_[DEFAULT]",
|
|
58
|
+
Custom: "__custom",
|
|
59
|
+
Handshake: "__ternsecure_handshake",
|
|
60
|
+
DevBrowser: "__ternsecure_db_jwt",
|
|
61
|
+
RedirectCount: "__ternsecure_redirect_count",
|
|
62
|
+
HandshakeNonce: "__ternsecure_handshake_nonce"
|
|
63
|
+
};
|
|
64
|
+
var Headers2 = {
|
|
65
|
+
Accept: "accept",
|
|
66
|
+
AuthMessage: "x-ternsecure-auth-message",
|
|
67
|
+
Authorization: "authorization",
|
|
68
|
+
AuthReason: "x-ternsecure-auth-reason",
|
|
69
|
+
AuthSignature: "x-ternsecure-auth-signature",
|
|
70
|
+
AuthStatus: "x-ternsecure-auth-status",
|
|
71
|
+
AuthToken: "x-ternsecure-auth-token",
|
|
72
|
+
CacheControl: "cache-control",
|
|
73
|
+
TernSecureRedirectTo: "x-ternsecure-redirect-to",
|
|
74
|
+
TernSecureRequestData: "x-ternsecure-request-data",
|
|
75
|
+
TernSecureUrl: "x-ternsecure-url",
|
|
76
|
+
CloudFrontForwardedProto: "cloudfront-forwarded-proto",
|
|
77
|
+
ContentType: "content-type",
|
|
78
|
+
ContentSecurityPolicy: "content-security-policy",
|
|
79
|
+
ContentSecurityPolicyReportOnly: "content-security-policy-report-only",
|
|
80
|
+
EnableDebug: "x-ternsecure-debug",
|
|
81
|
+
ForwardedHost: "x-forwarded-host",
|
|
82
|
+
ForwardedPort: "x-forwarded-port",
|
|
83
|
+
ForwardedProto: "x-forwarded-proto",
|
|
84
|
+
Host: "host",
|
|
85
|
+
Location: "location",
|
|
86
|
+
Nonce: "x-nonce",
|
|
87
|
+
Origin: "origin",
|
|
88
|
+
Referrer: "referer",
|
|
89
|
+
SecFetchDest: "sec-fetch-dest",
|
|
90
|
+
UserAgent: "user-agent",
|
|
91
|
+
ReportingEndpoints: "reporting-endpoints"
|
|
92
|
+
};
|
|
93
|
+
var ContentTypes = {
|
|
94
|
+
Json: "application/json"
|
|
95
|
+
};
|
|
96
|
+
var constants = {
|
|
97
|
+
Attributes,
|
|
98
|
+
Cookies,
|
|
99
|
+
Headers: Headers2,
|
|
100
|
+
ContentTypes
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
// src/utils/admin-init.ts
|
|
104
|
+
var import_firebase_admin = __toESM(require("firebase-admin"));
|
|
105
|
+
|
|
106
|
+
// src/utils/config.ts
|
|
107
|
+
var loadAdminConfig = () => ({
|
|
108
|
+
projectId: process.env.FIREBASE_PROJECT_ID || "",
|
|
109
|
+
clientEmail: process.env.FIREBASE_CLIENT_EMAIL || "",
|
|
110
|
+
privateKey: process.env.FIREBASE_PRIVATE_KEY || ""
|
|
111
|
+
});
|
|
112
|
+
var validateAdminConfig = (config) => {
|
|
113
|
+
const requiredFields = [
|
|
114
|
+
"projectId",
|
|
115
|
+
"clientEmail",
|
|
116
|
+
"privateKey"
|
|
117
|
+
];
|
|
118
|
+
const errors = [];
|
|
119
|
+
requiredFields.forEach((field) => {
|
|
120
|
+
if (!config[field]) {
|
|
121
|
+
errors.push(`Missing required field: FIREBASE_${String(field).toUpperCase()}`);
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
return {
|
|
125
|
+
isValid: errors.length === 0,
|
|
126
|
+
errors,
|
|
127
|
+
config
|
|
128
|
+
};
|
|
129
|
+
};
|
|
130
|
+
var initializeAdminConfig = () => {
|
|
131
|
+
const config = loadAdminConfig();
|
|
132
|
+
const validationResult = validateAdminConfig(config);
|
|
133
|
+
if (!validationResult.isValid) {
|
|
134
|
+
throw new Error(
|
|
135
|
+
`Firebase Admin configuration validation failed:
|
|
136
|
+
${validationResult.errors.join("\n")}`
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
return config;
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
// src/utils/admin-init.ts
|
|
143
|
+
if (!import_firebase_admin.default.apps.length) {
|
|
144
|
+
try {
|
|
145
|
+
const config = initializeAdminConfig();
|
|
146
|
+
import_firebase_admin.default.initializeApp({
|
|
147
|
+
credential: import_firebase_admin.default.credential.cert({
|
|
148
|
+
...config,
|
|
149
|
+
privateKey: config.privateKey.replace(/\\n/g, "\n")
|
|
150
|
+
})
|
|
151
|
+
});
|
|
152
|
+
} catch (error) {
|
|
153
|
+
console.error("Firebase admin initialization error", error);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
var adminTernSecureAuth = import_firebase_admin.default.auth();
|
|
157
|
+
var adminTernSecureDb = import_firebase_admin.default.firestore();
|
|
158
|
+
var TernSecureTenantManager = import_firebase_admin.default.auth().tenantManager();
|
|
159
|
+
function getAuthForTenant(tenantId) {
|
|
160
|
+
if (tenantId) {
|
|
161
|
+
return TernSecureTenantManager.authForTenant(tenantId);
|
|
162
|
+
}
|
|
163
|
+
return import_firebase_admin.default.auth();
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// src/admin/sessionTernSecure.ts
|
|
167
|
+
var SESSION_CONSTANTS = {
|
|
168
|
+
COOKIE_NAME: "_session_cookie",
|
|
169
|
+
//DEFAULT_EXPIRES_IN_MS: 60 * 60 * 24 * 5 * 1000, // 5 days
|
|
170
|
+
//DEFAULT_EXPIRES_IN_SECONDS: 60 * 60 * 24 * 5, // 5days
|
|
171
|
+
DEFAULT_EXPIRES_IN_MS: 5 * 60 * 1e3,
|
|
172
|
+
// 5 minutes
|
|
173
|
+
DEFAULT_EXPIRES_IN_SECONDS: 5 * 60,
|
|
174
|
+
REVOKE_REFRESH_TOKENS_ON_SIGNOUT: true
|
|
175
|
+
};
|
|
176
|
+
var COOKIE_OPTIONS = {
|
|
177
|
+
httpOnly: true,
|
|
178
|
+
secure: process.env.NODE_ENV === "production",
|
|
179
|
+
sameSite: "strict",
|
|
180
|
+
path: "/"
|
|
181
|
+
};
|
|
182
|
+
var DEFAULT_COOKIE_CONFIG = {
|
|
183
|
+
DEFAULT_EXPIRES_IN_MS: 5 * 60 * 1e3,
|
|
184
|
+
// 5 minutes
|
|
185
|
+
DEFAULT_EXPIRES_IN_SECONDS: 5 * 60,
|
|
186
|
+
REVOKE_REFRESH_TOKENS_ON_SIGNOUT: true
|
|
187
|
+
};
|
|
188
|
+
var DEFAULT_COOKIE_OPTIONS = {
|
|
189
|
+
httpOnly: true,
|
|
190
|
+
secure: process.env.NODE_ENV === "production",
|
|
191
|
+
sameSite: "strict",
|
|
192
|
+
path: "/"
|
|
193
|
+
};
|
|
194
|
+
async function createCustomTokenClaims(uid, developerClaims) {
|
|
195
|
+
const adminAuth = getAuthForTenant();
|
|
196
|
+
try {
|
|
197
|
+
const customToken = await adminAuth.createCustomToken(uid, developerClaims);
|
|
198
|
+
return customToken;
|
|
199
|
+
} catch (error) {
|
|
200
|
+
console.error("[createCustomToken] Error creating custom token:", error);
|
|
201
|
+
return "";
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// src/admin/nextSessionTernSecure.ts
|
|
206
|
+
var import_errors2 = require("@tern-secure/shared/errors");
|
|
207
|
+
var import_headers = require("next/headers");
|
|
208
|
+
var SESSION_CONSTANTS2 = {
|
|
209
|
+
COOKIE_NAME: constants.Cookies.Session,
|
|
210
|
+
DEFAULT_EXPIRES_IN_MS: 60 * 60 * 24 * 5 * 1e3,
|
|
211
|
+
// 5 days
|
|
212
|
+
DEFAULT_EXPIRES_IN_SECONDS: 60 * 60 * 24 * 5,
|
|
213
|
+
REVOKE_REFRESH_TOKENS_ON_SIGNOUT: true
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
// src/tokens/ternSecureRequest.ts
|
|
217
|
+
var import_cookie = require("cookie");
|
|
218
|
+
|
|
219
|
+
// src/jwt/verifyJwt.ts
|
|
220
|
+
var import_jose2 = require("jose");
|
|
221
|
+
|
|
222
|
+
// src/utils/errors.ts
|
|
223
|
+
var TokenVerificationErrorReason = {
|
|
224
|
+
TokenExpired: "token-expired",
|
|
225
|
+
TokenInvalid: "token-invalid",
|
|
226
|
+
TokenInvalidAlgorithm: "token-invalid-algorithm",
|
|
227
|
+
TokenInvalidAuthorizedParties: "token-invalid-authorized-parties",
|
|
228
|
+
TokenInvalidSignature: "token-invalid-signature",
|
|
229
|
+
TokenNotActiveYet: "token-not-active-yet",
|
|
230
|
+
TokenIatInTheFuture: "token-iat-in-the-future",
|
|
231
|
+
TokenVerificationFailed: "token-verification-failed",
|
|
232
|
+
InvalidSecretKey: "secret-key-invalid",
|
|
233
|
+
LocalJWKMissing: "jwk-local-missing",
|
|
234
|
+
RemoteJWKFailedToLoad: "jwk-remote-failed-to-load",
|
|
235
|
+
RemoteJWKInvalid: "jwk-remote-invalid",
|
|
236
|
+
RemoteJWKMissing: "jwk-remote-missing",
|
|
237
|
+
JWKFailedToResolve: "jwk-failed-to-resolve",
|
|
238
|
+
JWKKidMismatch: "jwk-kid-mismatch"
|
|
239
|
+
};
|
|
240
|
+
var TokenVerificationError = class _TokenVerificationError extends Error {
|
|
241
|
+
reason;
|
|
242
|
+
tokenCarrier;
|
|
243
|
+
constructor({
|
|
244
|
+
message,
|
|
245
|
+
reason
|
|
246
|
+
}) {
|
|
247
|
+
super(message);
|
|
248
|
+
Object.setPrototypeOf(this, _TokenVerificationError.prototype);
|
|
249
|
+
this.reason = reason;
|
|
250
|
+
this.message = message;
|
|
251
|
+
}
|
|
252
|
+
getFullMessage() {
|
|
253
|
+
return `${[this.message].filter((m) => m).join(" ")} (reason=${this.reason}, token-carrier=${this.tokenCarrier})`;
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
// src/utils/mapDecode.ts
|
|
258
|
+
function mapJwtPayloadToDecodedIdToken(payload) {
|
|
259
|
+
const decodedIdToken = payload;
|
|
260
|
+
decodedIdToken.uid = decodedIdToken.sub;
|
|
261
|
+
return decodedIdToken;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// src/utils/rfc4648.ts
|
|
265
|
+
var base64url = {
|
|
266
|
+
parse(string, opts) {
|
|
267
|
+
return parse2(string, base64UrlEncoding, opts);
|
|
268
|
+
},
|
|
269
|
+
stringify(data, opts) {
|
|
270
|
+
return stringify(data, base64UrlEncoding, opts);
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
var base64UrlEncoding = {
|
|
274
|
+
chars: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_",
|
|
275
|
+
bits: 6
|
|
276
|
+
};
|
|
277
|
+
function parse2(string, encoding, opts = {}) {
|
|
278
|
+
if (!encoding.codes) {
|
|
279
|
+
encoding.codes = {};
|
|
280
|
+
for (let i = 0; i < encoding.chars.length; ++i) {
|
|
281
|
+
encoding.codes[encoding.chars[i]] = i;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
if (!opts.loose && string.length * encoding.bits & 7) {
|
|
285
|
+
throw new SyntaxError("Invalid padding");
|
|
286
|
+
}
|
|
287
|
+
let end = string.length;
|
|
288
|
+
while (string[end - 1] === "=") {
|
|
289
|
+
--end;
|
|
290
|
+
if (!opts.loose && !((string.length - end) * encoding.bits & 7)) {
|
|
291
|
+
throw new SyntaxError("Invalid padding");
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
const out = new (opts.out ?? Uint8Array)(end * encoding.bits / 8 | 0);
|
|
295
|
+
let bits = 0;
|
|
296
|
+
let buffer = 0;
|
|
297
|
+
let written = 0;
|
|
298
|
+
for (let i = 0; i < end; ++i) {
|
|
299
|
+
const value = encoding.codes[string[i]];
|
|
300
|
+
if (value === void 0) {
|
|
301
|
+
throw new SyntaxError("Invalid character " + string[i]);
|
|
302
|
+
}
|
|
303
|
+
buffer = buffer << encoding.bits | value;
|
|
304
|
+
bits += encoding.bits;
|
|
305
|
+
if (bits >= 8) {
|
|
306
|
+
bits -= 8;
|
|
307
|
+
out[written++] = 255 & buffer >> bits;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
if (bits >= encoding.bits || 255 & buffer << 8 - bits) {
|
|
311
|
+
throw new SyntaxError("Unexpected end of data");
|
|
312
|
+
}
|
|
313
|
+
return out;
|
|
314
|
+
}
|
|
315
|
+
function stringify(data, encoding, opts = {}) {
|
|
316
|
+
const { pad = true } = opts;
|
|
317
|
+
const mask = (1 << encoding.bits) - 1;
|
|
318
|
+
let out = "";
|
|
319
|
+
let bits = 0;
|
|
320
|
+
let buffer = 0;
|
|
321
|
+
for (let i = 0; i < data.length; ++i) {
|
|
322
|
+
buffer = buffer << 8 | 255 & data[i];
|
|
323
|
+
bits += 8;
|
|
324
|
+
while (bits > encoding.bits) {
|
|
325
|
+
bits -= encoding.bits;
|
|
326
|
+
out += encoding.chars[mask & buffer >> bits];
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
if (bits) {
|
|
330
|
+
out += encoding.chars[mask & buffer << encoding.bits - bits];
|
|
331
|
+
}
|
|
332
|
+
if (pad) {
|
|
333
|
+
while (out.length * encoding.bits & 7) {
|
|
334
|
+
out += "=";
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
return out;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// src/jwt/cryptoKeys.ts
|
|
341
|
+
var import_jose = require("jose");
|
|
342
|
+
async function importKey(key, algorithm) {
|
|
343
|
+
if (typeof key === "object") {
|
|
344
|
+
const result = await (0, import_jose.importJWK)(key, algorithm);
|
|
345
|
+
if (result instanceof Uint8Array) {
|
|
346
|
+
throw new Error("Unexpected Uint8Array result from JWK import");
|
|
347
|
+
}
|
|
348
|
+
return result;
|
|
349
|
+
}
|
|
350
|
+
const keyString = key.trim();
|
|
351
|
+
if (keyString.includes("-----BEGIN CERTIFICATE-----")) {
|
|
352
|
+
return await (0, import_jose.importX509)(keyString, algorithm);
|
|
353
|
+
}
|
|
354
|
+
if (keyString.includes("-----BEGIN PUBLIC KEY-----")) {
|
|
355
|
+
return await (0, import_jose.importSPKI)(keyString, algorithm);
|
|
356
|
+
}
|
|
357
|
+
try {
|
|
358
|
+
return await (0, import_jose.importSPKI)(keyString, algorithm);
|
|
359
|
+
} catch (error) {
|
|
360
|
+
throw new Error(
|
|
361
|
+
`Unsupported key format. Supported formats: X.509 certificate (PEM), SPKI (PEM), JWK (JSON object or string). Error: ${error}`
|
|
362
|
+
);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// src/jwt/algorithms.ts
|
|
367
|
+
var algToHash = {
|
|
368
|
+
RS256: "SHA-256",
|
|
369
|
+
RS384: "SHA-384",
|
|
370
|
+
RS512: "SHA-512"
|
|
371
|
+
};
|
|
372
|
+
var algs = Object.keys(algToHash);
|
|
373
|
+
|
|
374
|
+
// src/jwt/verifyContent.ts
|
|
375
|
+
var verifyHeaderKid = (kid) => {
|
|
376
|
+
if (typeof kid === "undefined") {
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
if (typeof kid !== "string") {
|
|
380
|
+
throw new TokenVerificationError({
|
|
381
|
+
reason: TokenVerificationErrorReason.TokenInvalid,
|
|
382
|
+
message: `Invalid JWT kid ${JSON.stringify(kid)}. Expected a string.`
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
};
|
|
386
|
+
var verifySubClaim = (sub) => {
|
|
387
|
+
if (typeof sub !== "string") {
|
|
388
|
+
throw new TokenVerificationError({
|
|
389
|
+
reason: TokenVerificationErrorReason.TokenVerificationFailed,
|
|
390
|
+
message: `Subject claim (sub) is required and must be a string. Received ${JSON.stringify(sub)}.`
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
};
|
|
394
|
+
var verifyExpirationClaim = (exp, clockSkewInMs) => {
|
|
395
|
+
if (typeof exp !== "number") {
|
|
396
|
+
throw new TokenVerificationError({
|
|
397
|
+
reason: TokenVerificationErrorReason.TokenVerificationFailed,
|
|
398
|
+
message: `Invalid JWT expiry date (exp) claim ${JSON.stringify(exp)}. Expected a number.`
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
const currentDate = new Date(Date.now());
|
|
402
|
+
const expiryDate = /* @__PURE__ */ new Date(0);
|
|
403
|
+
expiryDate.setUTCSeconds(exp);
|
|
404
|
+
const expired = expiryDate.getTime() <= currentDate.getTime() - clockSkewInMs;
|
|
405
|
+
if (expired) {
|
|
406
|
+
throw new TokenVerificationError({
|
|
407
|
+
reason: TokenVerificationErrorReason.TokenExpired,
|
|
408
|
+
message: `JWT is expired. Expiry date: ${expiryDate.toUTCString()}, Current date: ${currentDate.toUTCString()}.`
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
};
|
|
412
|
+
var verifyIssuedAtClaim = (iat, clockSkewInMs) => {
|
|
413
|
+
if (typeof iat === "undefined") {
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
if (typeof iat !== "number") {
|
|
417
|
+
throw new TokenVerificationError({
|
|
418
|
+
reason: TokenVerificationErrorReason.TokenVerificationFailed,
|
|
419
|
+
message: `Invalid JWT issued at date claim (iat) ${JSON.stringify(iat)}. Expected a number.`
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
const currentDate = new Date(Date.now());
|
|
423
|
+
const issuedAtDate = /* @__PURE__ */ new Date(0);
|
|
424
|
+
issuedAtDate.setUTCSeconds(iat);
|
|
425
|
+
const postIssued = issuedAtDate.getTime() > currentDate.getTime() + clockSkewInMs;
|
|
426
|
+
if (postIssued) {
|
|
427
|
+
throw new TokenVerificationError({
|
|
428
|
+
reason: TokenVerificationErrorReason.TokenIatInTheFuture,
|
|
429
|
+
message: `JWT issued at date claim (iat) is in the future. Issued at date: ${issuedAtDate.toUTCString()}; Current date: ${currentDate.toUTCString()};`
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
};
|
|
433
|
+
|
|
434
|
+
// src/jwt/verifyJwt.ts
|
|
435
|
+
var DEFAULT_CLOCK_SKEW_IN_MS = 5 * 1e3;
|
|
436
|
+
async function verifySignature(jwt, key) {
|
|
437
|
+
const { header, raw } = jwt;
|
|
438
|
+
const joseAlgorithm = header.alg || "RS256";
|
|
439
|
+
try {
|
|
440
|
+
const publicKey = await importKey(key, joseAlgorithm);
|
|
441
|
+
const { payload } = await (0, import_jose2.jwtVerify)(raw.text, publicKey);
|
|
442
|
+
return { data: payload };
|
|
443
|
+
} catch (error) {
|
|
444
|
+
return {
|
|
445
|
+
errors: [
|
|
446
|
+
new TokenVerificationError({
|
|
447
|
+
reason: TokenVerificationErrorReason.TokenInvalidSignature,
|
|
448
|
+
message: error.message
|
|
449
|
+
})
|
|
450
|
+
]
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
function ternDecodeJwt(token) {
|
|
455
|
+
try {
|
|
456
|
+
const header = (0, import_jose2.decodeProtectedHeader)(token);
|
|
457
|
+
const payload = (0, import_jose2.decodeJwt)(token);
|
|
458
|
+
const tokenParts = (token || "").toString().split(".");
|
|
459
|
+
if (tokenParts.length !== 3) {
|
|
460
|
+
return {
|
|
461
|
+
errors: [
|
|
462
|
+
new TokenVerificationError({
|
|
463
|
+
reason: TokenVerificationErrorReason.TokenInvalid,
|
|
464
|
+
message: "Invalid JWT format"
|
|
465
|
+
})
|
|
466
|
+
]
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
const [rawHeader, rawPayload, rawSignature] = tokenParts;
|
|
470
|
+
const signature = base64url.parse(rawSignature, { loose: true });
|
|
471
|
+
const data = {
|
|
472
|
+
header,
|
|
473
|
+
payload,
|
|
474
|
+
signature,
|
|
475
|
+
raw: {
|
|
476
|
+
header: rawHeader,
|
|
477
|
+
payload: rawPayload,
|
|
478
|
+
signature: rawSignature,
|
|
479
|
+
text: token
|
|
480
|
+
}
|
|
481
|
+
};
|
|
482
|
+
return { data };
|
|
483
|
+
} catch (error) {
|
|
484
|
+
return {
|
|
485
|
+
errors: [
|
|
486
|
+
new TokenVerificationError({
|
|
487
|
+
reason: TokenVerificationErrorReason.TokenInvalid,
|
|
488
|
+
message: error.message
|
|
489
|
+
})
|
|
490
|
+
]
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
async function verifyJwt(token, options) {
|
|
495
|
+
const { key } = options;
|
|
496
|
+
const clockSkew = options.clockSkewInMs || DEFAULT_CLOCK_SKEW_IN_MS;
|
|
497
|
+
const { data: decoded, errors } = ternDecodeJwt(token);
|
|
498
|
+
if (errors) {
|
|
499
|
+
return { errors };
|
|
500
|
+
}
|
|
501
|
+
const { header, payload } = decoded;
|
|
502
|
+
try {
|
|
503
|
+
verifyHeaderKid(header.kid);
|
|
504
|
+
verifySubClaim(payload.sub);
|
|
505
|
+
verifyExpirationClaim(payload.exp, clockSkew);
|
|
506
|
+
verifyIssuedAtClaim(payload.iat, clockSkew);
|
|
507
|
+
} catch (error) {
|
|
508
|
+
return { errors: [error] };
|
|
509
|
+
}
|
|
510
|
+
const { data: verifiedPayload, errors: signatureErrors } = await verifySignature(decoded, key);
|
|
511
|
+
if (signatureErrors) {
|
|
512
|
+
return {
|
|
513
|
+
errors: [
|
|
514
|
+
new TokenVerificationError({
|
|
515
|
+
reason: TokenVerificationErrorReason.TokenInvalidSignature,
|
|
516
|
+
message: "Token signature verification failed."
|
|
517
|
+
})
|
|
518
|
+
]
|
|
519
|
+
};
|
|
520
|
+
}
|
|
521
|
+
const decodedIdToken = mapJwtPayloadToDecodedIdToken(verifiedPayload);
|
|
522
|
+
return { data: decodedIdToken };
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
// src/tokens/keys.ts
|
|
526
|
+
var cache = {};
|
|
527
|
+
var lastUpdatedAt = 0;
|
|
528
|
+
var googleExpiresAt = 0;
|
|
529
|
+
function getFromCache(kid) {
|
|
530
|
+
return cache[kid];
|
|
531
|
+
}
|
|
532
|
+
function getCacheValues() {
|
|
533
|
+
return Object.values(cache);
|
|
534
|
+
}
|
|
535
|
+
function setInCache(kid, certificate, shouldExpire = true) {
|
|
536
|
+
cache[kid] = certificate;
|
|
537
|
+
lastUpdatedAt = shouldExpire ? Date.now() : -1;
|
|
538
|
+
}
|
|
539
|
+
async function fetchPublicKeys(keyUrl) {
|
|
540
|
+
const url = new URL(keyUrl);
|
|
541
|
+
const response = await fetch(url);
|
|
542
|
+
if (!response.ok) {
|
|
543
|
+
throw new TokenVerificationError({
|
|
544
|
+
message: `Error loading public keys from ${url.href} with code=${response.status} `,
|
|
545
|
+
reason: TokenVerificationErrorReason.TokenInvalid
|
|
546
|
+
});
|
|
547
|
+
}
|
|
548
|
+
const data = await response.json();
|
|
549
|
+
const expiresAt = getExpiresAt(response);
|
|
550
|
+
return {
|
|
551
|
+
keys: data,
|
|
552
|
+
expiresAt
|
|
553
|
+
};
|
|
554
|
+
}
|
|
555
|
+
async function loadJWKFromRemote({
|
|
556
|
+
keyURL = GOOGLE_PUBLIC_KEYS_URL,
|
|
557
|
+
skipJwksCache,
|
|
558
|
+
kid
|
|
559
|
+
}) {
|
|
560
|
+
if (skipJwksCache || isCacheExpired() || !getFromCache(kid)) {
|
|
561
|
+
const { keys, expiresAt } = await fetchPublicKeys(keyURL);
|
|
562
|
+
if (!keys || Object.keys(keys).length === 0) {
|
|
563
|
+
throw new TokenVerificationError({
|
|
564
|
+
message: `The JWKS endpoint ${keyURL} returned no keys`,
|
|
565
|
+
reason: TokenVerificationErrorReason.RemoteJWKFailedToLoad
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
googleExpiresAt = expiresAt;
|
|
569
|
+
Object.entries(keys).forEach(([keyId, cert2]) => {
|
|
570
|
+
setInCache(keyId, cert2);
|
|
571
|
+
});
|
|
572
|
+
}
|
|
573
|
+
const cert = getFromCache(kid);
|
|
574
|
+
if (!cert) {
|
|
575
|
+
getCacheValues();
|
|
576
|
+
const availableKids = Object.keys(cache).sort().join(", ");
|
|
577
|
+
throw new TokenVerificationError({
|
|
578
|
+
message: `No public key found for kid "${kid}". Available kids: [${availableKids}]`,
|
|
579
|
+
reason: TokenVerificationErrorReason.TokenInvalid
|
|
580
|
+
});
|
|
581
|
+
}
|
|
582
|
+
return cert;
|
|
583
|
+
}
|
|
584
|
+
function isCacheExpired() {
|
|
585
|
+
const now = Date.now();
|
|
586
|
+
if (lastUpdatedAt === -1) {
|
|
587
|
+
return false;
|
|
588
|
+
}
|
|
589
|
+
const cacheAge = now - lastUpdatedAt;
|
|
590
|
+
const maxCacheAge = MAX_CACHE_LAST_UPDATED_AT_SECONDS * 1e3;
|
|
591
|
+
const localCacheExpired = cacheAge >= maxCacheAge;
|
|
592
|
+
const googleCacheExpired = now >= googleExpiresAt;
|
|
593
|
+
const isExpired = localCacheExpired || googleCacheExpired;
|
|
594
|
+
if (isExpired) {
|
|
595
|
+
cache = {};
|
|
596
|
+
}
|
|
597
|
+
return isExpired;
|
|
598
|
+
}
|
|
599
|
+
function getExpiresAt(res) {
|
|
600
|
+
const cacheControlHeader = res.headers.get("cache-control");
|
|
601
|
+
if (!cacheControlHeader) {
|
|
602
|
+
return Date.now() + DEFAULT_CACHE_DURATION;
|
|
603
|
+
}
|
|
604
|
+
const maxAgeMatch = cacheControlHeader.match(CACHE_CONTROL_REGEX);
|
|
605
|
+
const maxAge = maxAgeMatch ? parseInt(maxAgeMatch[1], 10) : DEFAULT_CACHE_DURATION / 1e3;
|
|
606
|
+
return Date.now() + maxAge * 1e3;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
// src/tokens/verify.ts
|
|
610
|
+
async function verifyToken(token, options) {
|
|
611
|
+
const { data: decodedResult, errors } = ternDecodeJwt(token);
|
|
612
|
+
if (errors) {
|
|
613
|
+
return { errors };
|
|
614
|
+
}
|
|
615
|
+
const { header } = decodedResult;
|
|
616
|
+
const { kid } = header;
|
|
617
|
+
if (!kid) {
|
|
618
|
+
return {
|
|
619
|
+
errors: [
|
|
620
|
+
new TokenVerificationError({
|
|
621
|
+
reason: TokenVerificationErrorReason.TokenInvalid,
|
|
622
|
+
message: 'JWT "kid" header is missing.'
|
|
623
|
+
})
|
|
624
|
+
]
|
|
625
|
+
};
|
|
626
|
+
}
|
|
627
|
+
try {
|
|
628
|
+
const key = options.jwtKey || await loadJWKFromRemote({ ...options, kid });
|
|
629
|
+
if (!key) {
|
|
630
|
+
return {
|
|
631
|
+
errors: [
|
|
632
|
+
new TokenVerificationError({
|
|
633
|
+
reason: TokenVerificationErrorReason.TokenInvalid,
|
|
634
|
+
message: `No public key found for kid "${kid}".`
|
|
635
|
+
})
|
|
636
|
+
]
|
|
637
|
+
};
|
|
638
|
+
}
|
|
639
|
+
return await verifyJwt(token, { ...options, key });
|
|
640
|
+
} catch (error) {
|
|
641
|
+
if (error instanceof TokenVerificationError) {
|
|
642
|
+
return { errors: [error] };
|
|
643
|
+
}
|
|
644
|
+
return {
|
|
645
|
+
errors: [error]
|
|
646
|
+
};
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
// src/auth/getauth.ts
|
|
651
|
+
function getAuth(options) {
|
|
652
|
+
const { apiKey, tenantId } = options.firebaseConfig || {};
|
|
653
|
+
async function customForIdAndRefreshToken(customToken) {
|
|
654
|
+
if (!apiKey) {
|
|
655
|
+
throw new Error("API Key is required to create custom token");
|
|
656
|
+
}
|
|
657
|
+
const response = await options.apiClient?.tokens.exchangeCustomForIdAndRefreshTokens(apiKey, {
|
|
658
|
+
token: customToken,
|
|
659
|
+
returnSecureToken: true
|
|
660
|
+
});
|
|
661
|
+
return {
|
|
662
|
+
idToken: response?.idToken || "",
|
|
663
|
+
refreshToken: response?.refreshToken || ""
|
|
664
|
+
};
|
|
665
|
+
}
|
|
666
|
+
async function createCustomIdAndRefreshToken(idToken) {
|
|
667
|
+
const decoded = await verifyToken(idToken, options);
|
|
668
|
+
const { data, errors } = decoded;
|
|
669
|
+
if (errors) {
|
|
670
|
+
console.error("Token Verification failed:", errors);
|
|
671
|
+
throw errors[0];
|
|
672
|
+
}
|
|
673
|
+
const customToken = await createCustomTokenClaims(data.uid, {
|
|
674
|
+
emailVerified: data.email_verified,
|
|
675
|
+
source_sign_in_provider: data.firebase.sign_in_provider
|
|
676
|
+
});
|
|
677
|
+
const idAndRefreshTokens = await customForIdAndRefreshToken(
|
|
678
|
+
customToken
|
|
679
|
+
);
|
|
680
|
+
return {
|
|
681
|
+
...idAndRefreshTokens,
|
|
682
|
+
customToken
|
|
683
|
+
};
|
|
684
|
+
}
|
|
685
|
+
return {
|
|
686
|
+
customForIdAndRefreshToken,
|
|
687
|
+
createCustomIdAndRefreshToken
|
|
688
|
+
};
|
|
689
|
+
}
|
|
690
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
691
|
+
0 && (module.exports = {
|
|
692
|
+
getAuth
|
|
693
|
+
});
|
|
694
|
+
//# sourceMappingURL=index.js.map
|