@tern-secure/backend 1.2.0-canary.v20251202164451 → 1.2.0-canary.v20251202175855
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/app-check/package.json +5 -0
- package/dist/admin/index.mjs +561 -23
- package/dist/admin/index.mjs.map +1 -1
- package/dist/app-check/index.js +4 -119
- package/dist/app-check/index.js.map +1 -1
- package/dist/app-check/index.mjs +2 -2
- package/dist/app-check/serverAppCheck.d.ts.map +1 -1
- package/dist/auth/index.js +3 -148
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/index.mjs +2 -2
- package/dist/chunk-4NYVEI6S.mjs +142 -0
- package/dist/chunk-4NYVEI6S.mjs.map +1 -0
- package/dist/chunk-PYNFU7M3.mjs +71 -0
- package/dist/chunk-PYNFU7M3.mjs.map +1 -0
- package/dist/{chunk-UCSJDX6Y.mjs → chunk-ZGZR5TER.mjs} +7 -6
- package/dist/chunk-ZGZR5TER.mjs.map +1 -0
- package/dist/fireRestApi/endpoints/SignInApi.d.ts +4 -1
- package/dist/fireRestApi/endpoints/SignInApi.d.ts.map +1 -1
- package/dist/index.js +15 -104
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +13 -6
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -3
- package/dist/chunk-34QENCWP.mjs +0 -784
- package/dist/chunk-34QENCWP.mjs.map +0 -1
- package/dist/chunk-UCSJDX6Y.mjs.map +0 -1
package/dist/chunk-34QENCWP.mjs
DELETED
|
@@ -1,784 +0,0 @@
|
|
|
1
|
-
// src/admin/sessionTernSecure.ts
|
|
2
|
-
import { handleFirebaseAuthError } from "@tern-secure/shared/errors";
|
|
3
|
-
|
|
4
|
-
// src/constants.ts
|
|
5
|
-
var GOOGLE_PUBLIC_KEYS_URL = "https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com";
|
|
6
|
-
var FIREBASE_APP_CHECK_AUDIENCE = "https://firebaseappcheck.googleapis.com/google.firebase.appcheck.v1.TokenExchangeService";
|
|
7
|
-
var MAX_CACHE_LAST_UPDATED_AT_SECONDS = 5 * 60;
|
|
8
|
-
var DEFAULT_CACHE_DURATION = 3600 * 1e3;
|
|
9
|
-
var CACHE_CONTROL_REGEX = /max-age=(\d+)/;
|
|
10
|
-
var TOKEN_EXPIRY_THRESHOLD_MILLIS = 5 * 60 * 1e3;
|
|
11
|
-
var GOOGLE_TOKEN_AUDIENCE = "https://accounts.google.com/o/oauth2/token";
|
|
12
|
-
var GOOGLE_AUTH_TOKEN_HOST = "accounts.google.com";
|
|
13
|
-
var GOOGLE_AUTH_TOKEN_PATH = "/o/oauth2/token";
|
|
14
|
-
var ONE_HOUR_IN_SECONDS = 60 * 60;
|
|
15
|
-
var ONE_MINUTE_IN_SECONDS = 60;
|
|
16
|
-
var ONE_MINUTE_IN_MILLIS = ONE_MINUTE_IN_SECONDS * 1e3;
|
|
17
|
-
var ONE_DAY_IN_MILLIS = 24 * 60 * 60 * 1e3;
|
|
18
|
-
var Attributes = {
|
|
19
|
-
AuthToken: "__ternsecureAuthToken",
|
|
20
|
-
AuthSignature: "__ternsecureAuthSignature",
|
|
21
|
-
AuthStatus: "__ternsecureAuthStatus",
|
|
22
|
-
AuthReason: "__ternsecureAuthReason",
|
|
23
|
-
AuthMessage: "__ternsecureAuthMessage",
|
|
24
|
-
TernSecureUrl: "__ternsecureUrl"
|
|
25
|
-
};
|
|
26
|
-
var Cookies = {
|
|
27
|
-
Session: "__session",
|
|
28
|
-
CsrfToken: "__terncf",
|
|
29
|
-
IdToken: "TernSecure_[DEFAULT]",
|
|
30
|
-
Refresh: "TernSecureID_[DEFAULT]",
|
|
31
|
-
Custom: "__custom",
|
|
32
|
-
TernAut: "tern_aut",
|
|
33
|
-
Handshake: "__ternsecure_handshake",
|
|
34
|
-
DevBrowser: "__ternsecure_db_jwt",
|
|
35
|
-
RedirectCount: "__ternsecure_redirect_count",
|
|
36
|
-
HandshakeNonce: "__ternsecure_handshake_nonce"
|
|
37
|
-
};
|
|
38
|
-
var QueryParameters = {
|
|
39
|
-
TernSynced: "__tern_synced",
|
|
40
|
-
SuffixedCookies: "suffixed_cookies",
|
|
41
|
-
TernRedirectUrl: "__tern_redirect_url",
|
|
42
|
-
// use the reference to Cookies to indicate that it's the same value
|
|
43
|
-
DevBrowser: Cookies.DevBrowser,
|
|
44
|
-
Handshake: Cookies.Handshake,
|
|
45
|
-
HandshakeHelp: "__tern_help",
|
|
46
|
-
LegacyDevBrowser: "__dev_session",
|
|
47
|
-
HandshakeReason: "__tern_hs_reason",
|
|
48
|
-
HandshakeNonce: Cookies.HandshakeNonce
|
|
49
|
-
};
|
|
50
|
-
var Headers2 = {
|
|
51
|
-
Accept: "accept",
|
|
52
|
-
AppCheckToken: "x-ternsecure-appcheck",
|
|
53
|
-
AuthMessage: "x-ternsecure-auth-message",
|
|
54
|
-
Authorization: "authorization",
|
|
55
|
-
AuthReason: "x-ternsecure-auth-reason",
|
|
56
|
-
AuthSignature: "x-ternsecure-auth-signature",
|
|
57
|
-
AuthStatus: "x-ternsecure-auth-status",
|
|
58
|
-
AuthToken: "x-ternsecure-auth-token",
|
|
59
|
-
CacheControl: "cache-control",
|
|
60
|
-
TernSecureRedirectTo: "x-ternsecure-redirect-to",
|
|
61
|
-
TernSecureRequestData: "x-ternsecure-request-data",
|
|
62
|
-
TernSecureUrl: "x-ternsecure-url",
|
|
63
|
-
CloudFrontForwardedProto: "cloudfront-forwarded-proto",
|
|
64
|
-
ContentType: "content-type",
|
|
65
|
-
ContentSecurityPolicy: "content-security-policy",
|
|
66
|
-
ContentSecurityPolicyReportOnly: "content-security-policy-report-only",
|
|
67
|
-
EnableDebug: "x-ternsecure-debug",
|
|
68
|
-
ForwardedHost: "x-forwarded-host",
|
|
69
|
-
ForwardedPort: "x-forwarded-port",
|
|
70
|
-
ForwardedProto: "x-forwarded-proto",
|
|
71
|
-
Host: "host",
|
|
72
|
-
Location: "location",
|
|
73
|
-
Nonce: "x-nonce",
|
|
74
|
-
Origin: "origin",
|
|
75
|
-
Referrer: "referer",
|
|
76
|
-
SecFetchDest: "sec-fetch-dest",
|
|
77
|
-
UserAgent: "user-agent",
|
|
78
|
-
ReportingEndpoints: "reporting-endpoints"
|
|
79
|
-
};
|
|
80
|
-
var ContentTypes = {
|
|
81
|
-
Json: "application/json"
|
|
82
|
-
};
|
|
83
|
-
var constants = {
|
|
84
|
-
Attributes,
|
|
85
|
-
Cookies,
|
|
86
|
-
Headers: Headers2,
|
|
87
|
-
ContentTypes,
|
|
88
|
-
QueryParameters
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
// src/utils/admin-init.ts
|
|
92
|
-
import admin from "firebase-admin";
|
|
93
|
-
import { getAppCheck } from "firebase-admin/app-check";
|
|
94
|
-
|
|
95
|
-
// src/utils/config.ts
|
|
96
|
-
var loadAdminConfig = () => ({
|
|
97
|
-
projectId: process.env.FIREBASE_PROJECT_ID || "",
|
|
98
|
-
clientEmail: process.env.FIREBASE_CLIENT_EMAIL || "",
|
|
99
|
-
privateKey: process.env.FIREBASE_PRIVATE_KEY || ""
|
|
100
|
-
});
|
|
101
|
-
var validateAdminConfig = (config) => {
|
|
102
|
-
const requiredFields = [
|
|
103
|
-
"projectId",
|
|
104
|
-
"clientEmail",
|
|
105
|
-
"privateKey"
|
|
106
|
-
];
|
|
107
|
-
const errors = [];
|
|
108
|
-
requiredFields.forEach((field) => {
|
|
109
|
-
if (!config[field]) {
|
|
110
|
-
errors.push(`Missing required field: FIREBASE_${String(field).toUpperCase()}`);
|
|
111
|
-
}
|
|
112
|
-
});
|
|
113
|
-
return {
|
|
114
|
-
isValid: errors.length === 0,
|
|
115
|
-
errors,
|
|
116
|
-
config
|
|
117
|
-
};
|
|
118
|
-
};
|
|
119
|
-
var initializeAdminConfig = () => {
|
|
120
|
-
const config = loadAdminConfig();
|
|
121
|
-
const validationResult = validateAdminConfig(config);
|
|
122
|
-
if (!validationResult.isValid) {
|
|
123
|
-
throw new Error(
|
|
124
|
-
`Firebase Admin configuration validation failed:
|
|
125
|
-
${validationResult.errors.join("\n")}`
|
|
126
|
-
);
|
|
127
|
-
}
|
|
128
|
-
return config;
|
|
129
|
-
};
|
|
130
|
-
|
|
131
|
-
// src/utils/admin-init.ts
|
|
132
|
-
if (!admin.apps.length) {
|
|
133
|
-
try {
|
|
134
|
-
const config = initializeAdminConfig();
|
|
135
|
-
admin.initializeApp({
|
|
136
|
-
credential: admin.credential.cert({
|
|
137
|
-
...config,
|
|
138
|
-
privateKey: config.privateKey.replace(/\\n/g, "\n")
|
|
139
|
-
})
|
|
140
|
-
});
|
|
141
|
-
} catch (error) {
|
|
142
|
-
console.error("Firebase admin initialization error", error);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
var adminTernSecureAuth = admin.auth();
|
|
146
|
-
var adminTernSecureDb = admin.firestore();
|
|
147
|
-
var TernSecureTenantManager = admin.auth().tenantManager();
|
|
148
|
-
var appCheckAdmin = getAppCheck();
|
|
149
|
-
function getAuthForTenant(tenantId) {
|
|
150
|
-
if (tenantId) {
|
|
151
|
-
return TernSecureTenantManager.authForTenant(tenantId);
|
|
152
|
-
}
|
|
153
|
-
return admin.auth();
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// src/admin/sessionTernSecure.ts
|
|
157
|
-
var DEFAULT_COOKIE_CONFIG = {
|
|
158
|
-
DEFAULT_EXPIRES_IN_MS: 5 * 60 * 1e3,
|
|
159
|
-
// 5 minutes
|
|
160
|
-
DEFAULT_EXPIRES_IN_SECONDS: 5 * 60,
|
|
161
|
-
REVOKE_REFRESH_TOKENS_ON_SIGNOUT: true
|
|
162
|
-
};
|
|
163
|
-
var DEFAULT_COOKIE_OPTIONS = {
|
|
164
|
-
httpOnly: true,
|
|
165
|
-
secure: process.env.NODE_ENV === "production",
|
|
166
|
-
sameSite: "strict",
|
|
167
|
-
path: "/"
|
|
168
|
-
};
|
|
169
|
-
var getCookieName = (baseName, prefix) => {
|
|
170
|
-
return prefix ? `${prefix}${baseName}` : baseName;
|
|
171
|
-
};
|
|
172
|
-
var createCookieOptions = (maxAge, overrides) => {
|
|
173
|
-
return {
|
|
174
|
-
maxAge,
|
|
175
|
-
httpOnly: overrides?.httpOnly ?? DEFAULT_COOKIE_OPTIONS.httpOnly,
|
|
176
|
-
secure: overrides?.secure ?? DEFAULT_COOKIE_OPTIONS.secure,
|
|
177
|
-
sameSite: overrides?.sameSite ?? DEFAULT_COOKIE_OPTIONS.sameSite,
|
|
178
|
-
path: overrides?.path ?? DEFAULT_COOKIE_OPTIONS.path
|
|
179
|
-
};
|
|
180
|
-
};
|
|
181
|
-
var getCookiePrefix = () => {
|
|
182
|
-
const isProduction = process.env.NODE_ENV === "production";
|
|
183
|
-
return isProduction ? "__HOST-" : "__dev_";
|
|
184
|
-
};
|
|
185
|
-
async function createSessionCookie(params, cookieStore, options) {
|
|
186
|
-
try {
|
|
187
|
-
const tenantAuth = getAuthForTenant(options?.tenantId || "");
|
|
188
|
-
const idToken = typeof params === "string" ? params : params.idToken;
|
|
189
|
-
const refreshToken = typeof params === "string" ? void 0 : params.refreshToken;
|
|
190
|
-
if (!idToken) {
|
|
191
|
-
return {
|
|
192
|
-
success: false,
|
|
193
|
-
message: "ID token is required",
|
|
194
|
-
error: "INVALID_TOKEN"
|
|
195
|
-
};
|
|
196
|
-
}
|
|
197
|
-
let decodedToken;
|
|
198
|
-
try {
|
|
199
|
-
decodedToken = await tenantAuth.verifyIdToken(idToken);
|
|
200
|
-
} catch (verifyError) {
|
|
201
|
-
const authError = handleFirebaseAuthError(verifyError);
|
|
202
|
-
return {
|
|
203
|
-
success: false,
|
|
204
|
-
message: authError.message,
|
|
205
|
-
error: authError.code
|
|
206
|
-
};
|
|
207
|
-
}
|
|
208
|
-
const cookiePromises = [];
|
|
209
|
-
const cookiePrefix = getCookiePrefix();
|
|
210
|
-
const idTokenCookieName = getCookieName(constants.Cookies.IdToken, cookiePrefix);
|
|
211
|
-
cookiePromises.push(
|
|
212
|
-
cookieStore.set(
|
|
213
|
-
idTokenCookieName,
|
|
214
|
-
idToken,
|
|
215
|
-
createCookieOptions(DEFAULT_COOKIE_CONFIG.DEFAULT_EXPIRES_IN_SECONDS)
|
|
216
|
-
)
|
|
217
|
-
);
|
|
218
|
-
if (refreshToken) {
|
|
219
|
-
const refreshTokenCookieName = getCookieName(constants.Cookies.Refresh, cookiePrefix);
|
|
220
|
-
cookiePromises.push(
|
|
221
|
-
cookieStore.set(
|
|
222
|
-
refreshTokenCookieName,
|
|
223
|
-
refreshToken,
|
|
224
|
-
createCookieOptions(DEFAULT_COOKIE_CONFIG.DEFAULT_EXPIRES_IN_SECONDS)
|
|
225
|
-
)
|
|
226
|
-
);
|
|
227
|
-
}
|
|
228
|
-
if (options?.cookies) {
|
|
229
|
-
const sessionOptions = options.cookies;
|
|
230
|
-
const sessionCookieName = getCookieName(constants.Cookies.Session);
|
|
231
|
-
const expiresIn = sessionOptions.maxAge ? sessionOptions.maxAge * 1e3 : DEFAULT_COOKIE_CONFIG.DEFAULT_EXPIRES_IN_MS;
|
|
232
|
-
try {
|
|
233
|
-
const sessionCookie = await tenantAuth.createSessionCookie(idToken, { expiresIn });
|
|
234
|
-
cookiePromises.push(
|
|
235
|
-
cookieStore.set(
|
|
236
|
-
sessionCookieName,
|
|
237
|
-
sessionCookie,
|
|
238
|
-
createCookieOptions(
|
|
239
|
-
sessionOptions.maxAge || DEFAULT_COOKIE_CONFIG.DEFAULT_EXPIRES_IN_SECONDS,
|
|
240
|
-
{
|
|
241
|
-
httpOnly: sessionOptions.httpOnly,
|
|
242
|
-
sameSite: sessionOptions.sameSite,
|
|
243
|
-
path: sessionOptions.path
|
|
244
|
-
}
|
|
245
|
-
)
|
|
246
|
-
)
|
|
247
|
-
);
|
|
248
|
-
} catch (sessionError) {
|
|
249
|
-
console.error(
|
|
250
|
-
"[createSessionCookie] Firebase session cookie creation failed:",
|
|
251
|
-
sessionError
|
|
252
|
-
);
|
|
253
|
-
const authError = handleFirebaseAuthError(sessionError);
|
|
254
|
-
return {
|
|
255
|
-
success: false,
|
|
256
|
-
message: authError.message,
|
|
257
|
-
error: authError.code
|
|
258
|
-
};
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
if (options?.enableCustomToken && decodedToken?.uid) {
|
|
262
|
-
const customTokenCookieName = getCookieName(constants.Cookies.Custom, cookiePrefix);
|
|
263
|
-
const customToken = await createCustomToken(decodedToken.uid, options);
|
|
264
|
-
if (customToken) {
|
|
265
|
-
cookiePromises.push(
|
|
266
|
-
cookieStore.set(
|
|
267
|
-
customTokenCookieName,
|
|
268
|
-
customToken,
|
|
269
|
-
createCookieOptions(DEFAULT_COOKIE_CONFIG.DEFAULT_EXPIRES_IN_SECONDS)
|
|
270
|
-
)
|
|
271
|
-
);
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
await Promise.all(cookiePromises);
|
|
275
|
-
return {
|
|
276
|
-
success: true,
|
|
277
|
-
message: "Session created successfully",
|
|
278
|
-
expiresIn: DEFAULT_COOKIE_CONFIG.DEFAULT_EXPIRES_IN_SECONDS
|
|
279
|
-
};
|
|
280
|
-
} catch (error) {
|
|
281
|
-
console.error("[createSessionCookie] Unexpected error:", error);
|
|
282
|
-
const authError = handleFirebaseAuthError(error);
|
|
283
|
-
return {
|
|
284
|
-
success: false,
|
|
285
|
-
message: authError.message || "Failed to create session",
|
|
286
|
-
error: authError.code || "INTERNAL_ERROR"
|
|
287
|
-
};
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
async function clearSessionCookie(cookieStore, options) {
|
|
291
|
-
try {
|
|
292
|
-
const adminAuth = getAuthForTenant(options?.tenantId || "");
|
|
293
|
-
const cookiePrefix = getCookiePrefix();
|
|
294
|
-
const sessionCookieName = getCookieName(constants.Cookies.Session, cookiePrefix);
|
|
295
|
-
const sessionCookie = await cookieStore.get(sessionCookieName);
|
|
296
|
-
const deletionPromises = [];
|
|
297
|
-
if (options?.cookies) {
|
|
298
|
-
deletionPromises.push(cookieStore.delete(sessionCookieName));
|
|
299
|
-
}
|
|
300
|
-
const idTokenCookieName = getCookieName(constants.Cookies.IdToken, cookiePrefix);
|
|
301
|
-
deletionPromises.push(cookieStore.delete(idTokenCookieName));
|
|
302
|
-
const refreshTokenCookieName = getCookieName(constants.Cookies.Refresh, cookiePrefix);
|
|
303
|
-
deletionPromises.push(cookieStore.delete(refreshTokenCookieName));
|
|
304
|
-
const customTokenCookieName = getCookieName(constants.Cookies.Custom, cookiePrefix);
|
|
305
|
-
deletionPromises.push(cookieStore.delete(customTokenCookieName));
|
|
306
|
-
const authTimeCookieName = constants.Cookies.TernAut;
|
|
307
|
-
deletionPromises.push(cookieStore.delete(authTimeCookieName));
|
|
308
|
-
deletionPromises.push(cookieStore.delete(constants.Cookies.Session));
|
|
309
|
-
await Promise.all(deletionPromises);
|
|
310
|
-
if (DEFAULT_COOKIE_CONFIG.REVOKE_REFRESH_TOKENS_ON_SIGNOUT && sessionCookie?.value) {
|
|
311
|
-
try {
|
|
312
|
-
const decodedClaims = await adminAuth.verifySessionCookie(sessionCookie.value);
|
|
313
|
-
await adminAuth.revokeRefreshTokens(decodedClaims.sub);
|
|
314
|
-
} catch (revokeError) {
|
|
315
|
-
console.error("[clearSessionCookie] Failed to revoke refresh tokens:", revokeError);
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
return {
|
|
319
|
-
success: true,
|
|
320
|
-
message: "Session cleared successfully"
|
|
321
|
-
};
|
|
322
|
-
} catch (error) {
|
|
323
|
-
const authError = handleFirebaseAuthError(error);
|
|
324
|
-
return {
|
|
325
|
-
success: false,
|
|
326
|
-
message: authError.message || "Failed to clear session",
|
|
327
|
-
error: authError.code || "INTERNAL_ERROR"
|
|
328
|
-
};
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
async function createCustomToken(uid, options) {
|
|
332
|
-
const adminAuth = getAuthForTenant(options?.tenantId || "");
|
|
333
|
-
try {
|
|
334
|
-
const customToken = await adminAuth.createCustomToken(uid);
|
|
335
|
-
return customToken;
|
|
336
|
-
} catch (error) {
|
|
337
|
-
console.error("[createCustomToken] Error creating custom token:", error);
|
|
338
|
-
return null;
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
async function createCustomTokenClaims(uid, developerClaims) {
|
|
342
|
-
const adminAuth = getAuthForTenant();
|
|
343
|
-
try {
|
|
344
|
-
const customToken = await adminAuth.createCustomToken(uid, developerClaims);
|
|
345
|
-
return customToken;
|
|
346
|
-
} catch (error) {
|
|
347
|
-
console.error("[createCustomToken] Error creating custom token:", error);
|
|
348
|
-
return "";
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
// src/admin/tenant.ts
|
|
353
|
-
async function createTenant(displayName, emailSignInConfig, multiFactorConfig) {
|
|
354
|
-
try {
|
|
355
|
-
const tenantConfig = {
|
|
356
|
-
displayName,
|
|
357
|
-
emailSignInConfig,
|
|
358
|
-
...multiFactorConfig && { multiFactorConfig }
|
|
359
|
-
};
|
|
360
|
-
const tenant = await TernSecureTenantManager.createTenant(tenantConfig);
|
|
361
|
-
return {
|
|
362
|
-
success: true,
|
|
363
|
-
tenantId: tenant.tenantId,
|
|
364
|
-
displayName: tenant.displayName
|
|
365
|
-
};
|
|
366
|
-
} catch (error) {
|
|
367
|
-
console.error("Error creating tenant:", error);
|
|
368
|
-
throw new Error("Failed to create tenant");
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
async function createTenantUser(email, password, tenantId) {
|
|
372
|
-
try {
|
|
373
|
-
const tenantAuth = TernSecureTenantManager.authForTenant(tenantId);
|
|
374
|
-
const userRecord = await tenantAuth.createUser({
|
|
375
|
-
email,
|
|
376
|
-
password,
|
|
377
|
-
emailVerified: false,
|
|
378
|
-
disabled: false
|
|
379
|
-
});
|
|
380
|
-
return {
|
|
381
|
-
status: "success",
|
|
382
|
-
user: userRecord,
|
|
383
|
-
message: "Tenant user created successfully"
|
|
384
|
-
};
|
|
385
|
-
} catch (error) {
|
|
386
|
-
console.error("Error creating tenant user:", error);
|
|
387
|
-
throw new Error("Failed to create tenant user");
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
// src/admin/nextSessionTernSecure.ts
|
|
392
|
-
import { getCookieName as getCookieName2, getCookiePrefix as getCookiePrefix2 } from "@tern-secure/shared/cookie";
|
|
393
|
-
import { handleFirebaseAuthError as handleFirebaseAuthError2 } from "@tern-secure/shared/errors";
|
|
394
|
-
import { cookies } from "next/headers";
|
|
395
|
-
var SESSION_CONSTANTS = {
|
|
396
|
-
COOKIE_NAME: constants.Cookies.Session,
|
|
397
|
-
DEFAULT_EXPIRES_IN_MS: 60 * 60 * 24 * 5 * 1e3,
|
|
398
|
-
// 5 days
|
|
399
|
-
DEFAULT_EXPIRES_IN_SECONDS: 60 * 60 * 24 * 5,
|
|
400
|
-
REVOKE_REFRESH_TOKENS_ON_SIGNOUT: true
|
|
401
|
-
};
|
|
402
|
-
var debugLog = {
|
|
403
|
-
log: (...args) => {
|
|
404
|
-
if (process.env.NODE_ENV === "development") {
|
|
405
|
-
console.log(...args);
|
|
406
|
-
}
|
|
407
|
-
},
|
|
408
|
-
warn: (...args) => {
|
|
409
|
-
if (process.env.NODE_ENV === "development") {
|
|
410
|
-
console.warn(...args);
|
|
411
|
-
}
|
|
412
|
-
},
|
|
413
|
-
error: (...args) => {
|
|
414
|
-
console.error(...args);
|
|
415
|
-
}
|
|
416
|
-
};
|
|
417
|
-
async function CreateNextSessionCookie(idToken) {
|
|
418
|
-
try {
|
|
419
|
-
const expiresIn = 60 * 60 * 24 * 5 * 1e3;
|
|
420
|
-
const sessionCookie = await adminTernSecureAuth.createSessionCookie(idToken, {
|
|
421
|
-
expiresIn
|
|
422
|
-
});
|
|
423
|
-
const cookieStore = await cookies();
|
|
424
|
-
cookieStore.set(constants.Cookies.Session, sessionCookie, {
|
|
425
|
-
maxAge: expiresIn,
|
|
426
|
-
httpOnly: true,
|
|
427
|
-
secure: process.env.NODE_ENV === "production",
|
|
428
|
-
path: "/"
|
|
429
|
-
});
|
|
430
|
-
return { success: true, message: "Session created" };
|
|
431
|
-
} catch (error) {
|
|
432
|
-
return { success: false, message: "Failed to create session" };
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
async function GetNextServerSessionCookie() {
|
|
436
|
-
const cookieStore = await cookies();
|
|
437
|
-
const sessionCookie = cookieStore.get("_session_cookie")?.value;
|
|
438
|
-
if (!sessionCookie) {
|
|
439
|
-
throw new Error("No session cookie found");
|
|
440
|
-
}
|
|
441
|
-
try {
|
|
442
|
-
const decondeClaims = await adminTernSecureAuth.verifySessionCookie(sessionCookie, true);
|
|
443
|
-
return {
|
|
444
|
-
token: sessionCookie,
|
|
445
|
-
userId: decondeClaims.uid
|
|
446
|
-
};
|
|
447
|
-
} catch (error) {
|
|
448
|
-
console.error("Error verifying session:", error);
|
|
449
|
-
throw new Error("Invalid Session");
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
async function GetNextIdToken() {
|
|
453
|
-
const cookieStore = await cookies();
|
|
454
|
-
const token = cookieStore.get("_session_token")?.value;
|
|
455
|
-
if (!token) {
|
|
456
|
-
throw new Error("No session cookie found");
|
|
457
|
-
}
|
|
458
|
-
try {
|
|
459
|
-
const decodedClaims = await adminTernSecureAuth.verifyIdToken(token);
|
|
460
|
-
return {
|
|
461
|
-
token,
|
|
462
|
-
userId: decodedClaims.uid
|
|
463
|
-
};
|
|
464
|
-
} catch (error) {
|
|
465
|
-
console.error("Error verifying session:", error);
|
|
466
|
-
throw new Error("Invalid Session");
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
async function SetNextServerSession(token) {
|
|
470
|
-
try {
|
|
471
|
-
const cookieStore = await cookies();
|
|
472
|
-
cookieStore.set("_session_token", token, {
|
|
473
|
-
httpOnly: true,
|
|
474
|
-
secure: process.env.NODE_ENV === "production",
|
|
475
|
-
sameSite: "strict",
|
|
476
|
-
maxAge: 60 * 60,
|
|
477
|
-
// 1 hour
|
|
478
|
-
path: "/"
|
|
479
|
-
});
|
|
480
|
-
return { success: true, message: "Session created" };
|
|
481
|
-
} catch {
|
|
482
|
-
return { success: false, message: "Failed to create session" };
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
async function SetNextServerToken(token) {
|
|
486
|
-
try {
|
|
487
|
-
const cookieStore = await cookies();
|
|
488
|
-
cookieStore.set("_tern", token, {
|
|
489
|
-
httpOnly: true,
|
|
490
|
-
secure: process.env.NODE_ENV === "production",
|
|
491
|
-
sameSite: "strict",
|
|
492
|
-
maxAge: 60 * 60,
|
|
493
|
-
// 1 hour
|
|
494
|
-
path: "/"
|
|
495
|
-
});
|
|
496
|
-
return { success: true, message: "Session created" };
|
|
497
|
-
} catch {
|
|
498
|
-
return { success: false, message: "Failed to create session" };
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
async function VerifyNextTernIdToken(token) {
|
|
502
|
-
try {
|
|
503
|
-
const decodedToken = await adminTernSecureAuth.verifyIdToken(token);
|
|
504
|
-
return {
|
|
505
|
-
...decodedToken,
|
|
506
|
-
valid: true
|
|
507
|
-
};
|
|
508
|
-
} catch (error) {
|
|
509
|
-
console.error("[VerifyNextTernIdToken] Error verifying session:", error);
|
|
510
|
-
const authError = handleFirebaseAuthError2(error);
|
|
511
|
-
return {
|
|
512
|
-
valid: false,
|
|
513
|
-
error: authError
|
|
514
|
-
};
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
async function VerifyNextTernSessionCookie(session) {
|
|
518
|
-
try {
|
|
519
|
-
const res = await adminTernSecureAuth.verifySessionCookie(session);
|
|
520
|
-
console.warn("[VerifyNextTernSessionCookie] uid in Decoded Token:", res.uid);
|
|
521
|
-
return {
|
|
522
|
-
valid: true,
|
|
523
|
-
...res
|
|
524
|
-
};
|
|
525
|
-
} catch (error) {
|
|
526
|
-
console.error("[VerifyNextTernSessionCookie] Error verifying session:", error);
|
|
527
|
-
const authError = handleFirebaseAuthError2(error);
|
|
528
|
-
return {
|
|
529
|
-
valid: false,
|
|
530
|
-
error: authError
|
|
531
|
-
};
|
|
532
|
-
}
|
|
533
|
-
}
|
|
534
|
-
async function ClearNextSessionCookie(tenantId, deleteOptions) {
|
|
535
|
-
try {
|
|
536
|
-
const tenantAuth = getAuthForTenant(tenantId || "");
|
|
537
|
-
const cookieStore = await cookies();
|
|
538
|
-
const sessionCookie = cookieStore.get(SESSION_CONSTANTS.COOKIE_NAME);
|
|
539
|
-
const cookiePrefix = getCookiePrefix2();
|
|
540
|
-
const idTokenCookieName = getCookieName2(constants.Cookies.IdToken, cookiePrefix);
|
|
541
|
-
const idTokenCookie = cookieStore.get(idTokenCookieName);
|
|
542
|
-
const finalDeleteOptions = {
|
|
543
|
-
path: deleteOptions?.path,
|
|
544
|
-
domain: deleteOptions?.domain,
|
|
545
|
-
httpOnly: deleteOptions?.httpOnly,
|
|
546
|
-
secure: deleteOptions?.secure,
|
|
547
|
-
sameSite: deleteOptions?.sameSite
|
|
548
|
-
};
|
|
549
|
-
const idRefreshCustomTokenDeleteOptions = {
|
|
550
|
-
path: "/",
|
|
551
|
-
httpOnly: true,
|
|
552
|
-
secure: process.env.NODE_ENV === "production",
|
|
553
|
-
sameSite: "strict"
|
|
554
|
-
};
|
|
555
|
-
cookieStore.delete({ name: SESSION_CONSTANTS.COOKIE_NAME, ...finalDeleteOptions });
|
|
556
|
-
cookieStore.delete({ name: constants.Cookies.TernAut });
|
|
557
|
-
cookieStore.delete({ name: idTokenCookieName, ...idRefreshCustomTokenDeleteOptions });
|
|
558
|
-
cookieStore.delete({
|
|
559
|
-
name: getCookieName2(constants.Cookies.Refresh, cookiePrefix),
|
|
560
|
-
...idRefreshCustomTokenDeleteOptions
|
|
561
|
-
});
|
|
562
|
-
cookieStore.delete({ name: constants.Cookies.Custom, ...idRefreshCustomTokenDeleteOptions });
|
|
563
|
-
const shouldRevokeTokens = deleteOptions?.revokeRefreshTokensOnSignOut ?? SESSION_CONSTANTS.REVOKE_REFRESH_TOKENS_ON_SIGNOUT;
|
|
564
|
-
if (shouldRevokeTokens) {
|
|
565
|
-
try {
|
|
566
|
-
let userSub;
|
|
567
|
-
if (sessionCookie?.value) {
|
|
568
|
-
try {
|
|
569
|
-
const decodedClaims = await tenantAuth.verifySessionCookie(sessionCookie.value);
|
|
570
|
-
userSub = decodedClaims.sub;
|
|
571
|
-
} catch (sessionError) {
|
|
572
|
-
debugLog.warn(
|
|
573
|
-
"[ClearNextSessionCookie] Session cookie verification failed:",
|
|
574
|
-
sessionError
|
|
575
|
-
);
|
|
576
|
-
}
|
|
577
|
-
}
|
|
578
|
-
if (!userSub) {
|
|
579
|
-
if (idTokenCookie?.value) {
|
|
580
|
-
try {
|
|
581
|
-
const decodedIdToken = await tenantAuth.verifyIdToken(idTokenCookie.value);
|
|
582
|
-
userSub = decodedIdToken.sub;
|
|
583
|
-
} catch (idTokenError) {
|
|
584
|
-
debugLog.warn("[ClearNextSessionCookie] ID token verification failed:", idTokenError);
|
|
585
|
-
}
|
|
586
|
-
}
|
|
587
|
-
}
|
|
588
|
-
if (userSub) {
|
|
589
|
-
await tenantAuth.revokeRefreshTokens(userSub);
|
|
590
|
-
debugLog.log(`[ClearNextSessionCookie] Successfully revoked tokens for user: ${userSub}`);
|
|
591
|
-
} else {
|
|
592
|
-
debugLog.warn("[ClearNextSessionCookie] No valid token found for revocation");
|
|
593
|
-
}
|
|
594
|
-
} catch (revokeError) {
|
|
595
|
-
debugLog.error("[ClearNextSessionCookie] Failed to revoke refresh tokens:", revokeError);
|
|
596
|
-
}
|
|
597
|
-
}
|
|
598
|
-
return { success: true, message: "Session cleared successfully" };
|
|
599
|
-
} catch (error) {
|
|
600
|
-
debugLog.error("Error clearing session:", error);
|
|
601
|
-
return { success: false, message: "Failed to clear session cookies" };
|
|
602
|
-
}
|
|
603
|
-
}
|
|
604
|
-
|
|
605
|
-
// src/tokens/ternSecureRequest.ts
|
|
606
|
-
import { parse } from "cookie";
|
|
607
|
-
|
|
608
|
-
// src/tokens/ternUrl.ts
|
|
609
|
-
var TernUrl = class extends URL {
|
|
610
|
-
isCrossOrigin(other) {
|
|
611
|
-
return this.origin !== new URL(other.toString()).origin;
|
|
612
|
-
}
|
|
613
|
-
};
|
|
614
|
-
var createTernUrl = (...args) => {
|
|
615
|
-
return new TernUrl(...args);
|
|
616
|
-
};
|
|
617
|
-
|
|
618
|
-
// src/tokens/ternSecureRequest.ts
|
|
619
|
-
var TernSecureRequest = class extends Request {
|
|
620
|
-
ternUrl;
|
|
621
|
-
cookies;
|
|
622
|
-
constructor(input, init) {
|
|
623
|
-
const url = typeof input !== "string" && "url" in input ? input.url : String(input);
|
|
624
|
-
super(url, init || typeof input === "string" ? void 0 : input);
|
|
625
|
-
this.ternUrl = this.deriveUrlFromHeaders(this);
|
|
626
|
-
this.cookies = this.parseCookies(this);
|
|
627
|
-
}
|
|
628
|
-
toJSON() {
|
|
629
|
-
return {
|
|
630
|
-
url: this.ternUrl.href,
|
|
631
|
-
method: this.method,
|
|
632
|
-
headers: JSON.stringify(Object.fromEntries(this.headers)),
|
|
633
|
-
ternUrl: this.ternUrl.toString(),
|
|
634
|
-
cookies: JSON.stringify(Object.fromEntries(this.cookies))
|
|
635
|
-
};
|
|
636
|
-
}
|
|
637
|
-
deriveUrlFromHeaders(req) {
|
|
638
|
-
const initialUrl = new URL(req.url);
|
|
639
|
-
const forwardedProto = req.headers.get(constants.Headers.ForwardedProto);
|
|
640
|
-
const forwardedHost = req.headers.get(constants.Headers.ForwardedHost);
|
|
641
|
-
const host = req.headers.get(constants.Headers.Host);
|
|
642
|
-
const protocol = initialUrl.protocol;
|
|
643
|
-
const resolvedHost = this.getFirstValueFromHeader(forwardedHost) ?? host;
|
|
644
|
-
const resolvedProtocol = this.getFirstValueFromHeader(forwardedProto) ?? protocol?.replace(/[:/]/, "");
|
|
645
|
-
const origin = resolvedHost && resolvedProtocol ? `${resolvedProtocol}://${resolvedHost}` : initialUrl.origin;
|
|
646
|
-
if (origin === initialUrl.origin) {
|
|
647
|
-
return createTernUrl(initialUrl);
|
|
648
|
-
}
|
|
649
|
-
return createTernUrl(initialUrl.pathname + initialUrl.search, origin);
|
|
650
|
-
}
|
|
651
|
-
getFirstValueFromHeader(value) {
|
|
652
|
-
return value?.split(",")[0];
|
|
653
|
-
}
|
|
654
|
-
parseCookies(req) {
|
|
655
|
-
const cookiesRecord = parse(
|
|
656
|
-
this.decodeCookieValue(req.headers.get("cookie") || "")
|
|
657
|
-
);
|
|
658
|
-
return new Map(Object.entries(cookiesRecord));
|
|
659
|
-
}
|
|
660
|
-
decodeCookieValue(str) {
|
|
661
|
-
return str ? str.replace(/(%[0-9A-Z]{2})+/g, decodeURIComponent) : str;
|
|
662
|
-
}
|
|
663
|
-
};
|
|
664
|
-
var createTernSecureRequest = (...args) => {
|
|
665
|
-
return args[0] instanceof TernSecureRequest ? args[0] : new TernSecureRequest(...args);
|
|
666
|
-
};
|
|
667
|
-
|
|
668
|
-
// src/instance/backendInstance.ts
|
|
669
|
-
var createBackendInstance = async (request) => {
|
|
670
|
-
const ternSecureRequest = createTernSecureRequest(request);
|
|
671
|
-
const requestState = await authenticateRequest(request);
|
|
672
|
-
return {
|
|
673
|
-
ternSecureRequest,
|
|
674
|
-
requestState
|
|
675
|
-
};
|
|
676
|
-
};
|
|
677
|
-
async function authenticateRequest(request) {
|
|
678
|
-
const sessionCookie = request.headers.get("cookie");
|
|
679
|
-
const sessionToken = sessionCookie?.split(";").find((c) => c.trim().startsWith("_session_cookie="))?.split("=")[1];
|
|
680
|
-
if (!sessionToken) {
|
|
681
|
-
throw new Error("No session token found");
|
|
682
|
-
}
|
|
683
|
-
const verificationResult = await VerifyNextTernSessionCookie(sessionToken);
|
|
684
|
-
if (!verificationResult.valid) {
|
|
685
|
-
throw new Error("Invalid session token");
|
|
686
|
-
}
|
|
687
|
-
return signedIn(
|
|
688
|
-
verificationResult,
|
|
689
|
-
new Headers(request.headers),
|
|
690
|
-
sessionToken
|
|
691
|
-
);
|
|
692
|
-
}
|
|
693
|
-
function signInAuthObject(session) {
|
|
694
|
-
return {
|
|
695
|
-
session,
|
|
696
|
-
userId: session.uid,
|
|
697
|
-
has: {}
|
|
698
|
-
};
|
|
699
|
-
}
|
|
700
|
-
function signedIn(session, headers = new Headers(), token) {
|
|
701
|
-
const authObject = signInAuthObject(session);
|
|
702
|
-
return {
|
|
703
|
-
auth: () => authObject,
|
|
704
|
-
token,
|
|
705
|
-
headers
|
|
706
|
-
};
|
|
707
|
-
}
|
|
708
|
-
|
|
709
|
-
// src/admin/user.ts
|
|
710
|
-
import { handleFirebaseAuthError as handleFirebaseAuthError3 } from "@tern-secure/shared/errors";
|
|
711
|
-
function RetrieveUser(tenantId) {
|
|
712
|
-
const auth = getAuthForTenant(tenantId);
|
|
713
|
-
async function getUserUid(uid) {
|
|
714
|
-
try {
|
|
715
|
-
const user = await auth.getUser(uid);
|
|
716
|
-
return { data: user, error: null };
|
|
717
|
-
} catch (error) {
|
|
718
|
-
return { data: null, error: handleFirebaseAuthError3(error) };
|
|
719
|
-
}
|
|
720
|
-
}
|
|
721
|
-
async function getUserByEmail(email) {
|
|
722
|
-
try {
|
|
723
|
-
const user = await auth.getUserByEmail(email);
|
|
724
|
-
return { data: user, error: null };
|
|
725
|
-
} catch (error) {
|
|
726
|
-
return { data: null, error: handleFirebaseAuthError3(error) };
|
|
727
|
-
}
|
|
728
|
-
}
|
|
729
|
-
async function getUserByPhoneNumber(phoneNumber) {
|
|
730
|
-
try {
|
|
731
|
-
const user = await auth.getUserByPhoneNumber(phoneNumber);
|
|
732
|
-
return { data: user, error: null };
|
|
733
|
-
} catch (error) {
|
|
734
|
-
return { data: null, error: handleFirebaseAuthError3(error) };
|
|
735
|
-
}
|
|
736
|
-
}
|
|
737
|
-
return {
|
|
738
|
-
getUserUid,
|
|
739
|
-
getUserByEmail,
|
|
740
|
-
getUserByPhoneNumber
|
|
741
|
-
};
|
|
742
|
-
}
|
|
743
|
-
|
|
744
|
-
export {
|
|
745
|
-
GOOGLE_PUBLIC_KEYS_URL,
|
|
746
|
-
FIREBASE_APP_CHECK_AUDIENCE,
|
|
747
|
-
MAX_CACHE_LAST_UPDATED_AT_SECONDS,
|
|
748
|
-
DEFAULT_CACHE_DURATION,
|
|
749
|
-
CACHE_CONTROL_REGEX,
|
|
750
|
-
TOKEN_EXPIRY_THRESHOLD_MILLIS,
|
|
751
|
-
GOOGLE_TOKEN_AUDIENCE,
|
|
752
|
-
GOOGLE_AUTH_TOKEN_HOST,
|
|
753
|
-
GOOGLE_AUTH_TOKEN_PATH,
|
|
754
|
-
ONE_HOUR_IN_SECONDS,
|
|
755
|
-
ONE_MINUTE_IN_SECONDS,
|
|
756
|
-
ONE_MINUTE_IN_MILLIS,
|
|
757
|
-
ONE_DAY_IN_MILLIS,
|
|
758
|
-
constants,
|
|
759
|
-
createTernSecureRequest,
|
|
760
|
-
loadAdminConfig,
|
|
761
|
-
initializeAdminConfig,
|
|
762
|
-
adminTernSecureAuth,
|
|
763
|
-
adminTernSecureDb,
|
|
764
|
-
TernSecureTenantManager,
|
|
765
|
-
appCheckAdmin,
|
|
766
|
-
createSessionCookie,
|
|
767
|
-
clearSessionCookie,
|
|
768
|
-
createCustomTokenClaims,
|
|
769
|
-
createTenant,
|
|
770
|
-
createTenantUser,
|
|
771
|
-
CreateNextSessionCookie,
|
|
772
|
-
GetNextServerSessionCookie,
|
|
773
|
-
GetNextIdToken,
|
|
774
|
-
SetNextServerSession,
|
|
775
|
-
SetNextServerToken,
|
|
776
|
-
VerifyNextTernIdToken,
|
|
777
|
-
VerifyNextTernSessionCookie,
|
|
778
|
-
ClearNextSessionCookie,
|
|
779
|
-
createBackendInstance,
|
|
780
|
-
authenticateRequest,
|
|
781
|
-
signedIn,
|
|
782
|
-
RetrieveUser
|
|
783
|
-
};
|
|
784
|
-
//# sourceMappingURL=chunk-34QENCWP.mjs.map
|