@stackframe/stack 2.8.48 → 2.8.51
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/CHANGELOG.md +30 -0
- package/dist/components-page/password-reset.js +6 -5
- package/dist/components-page/password-reset.js.map +1 -1
- package/dist/components-page/sign-out.js +2 -12
- package/dist/components-page/sign-out.js.map +1 -1
- package/dist/components-page/stack-handler-client.js +266 -0
- package/dist/components-page/stack-handler-client.js.map +1 -0
- package/dist/components-page/stack-handler.js +4 -243
- package/dist/components-page/stack-handler.js.map +1 -1
- package/dist/components-page/team-invitation.js +6 -5
- package/dist/components-page/team-invitation.js.map +1 -1
- package/dist/esm/components-page/password-reset.js +3 -2
- package/dist/esm/components-page/password-reset.js.map +1 -1
- package/dist/esm/components-page/sign-out.js +2 -2
- package/dist/esm/components-page/sign-out.js.map +1 -1
- package/dist/esm/components-page/stack-handler-client.js +242 -0
- package/dist/esm/components-page/stack-handler-client.js.map +1 -0
- package/dist/esm/components-page/stack-handler.js +5 -244
- package/dist/esm/components-page/stack-handler.js.map +1 -1
- package/dist/esm/components-page/team-invitation.js +3 -2
- package/dist/esm/components-page/team-invitation.js.map +1 -1
- package/dist/esm/lib/cookie.js +57 -14
- package/dist/esm/lib/cookie.js.map +1 -1
- package/dist/esm/lib/stack-app/apps/implementations/admin-app-impl.js +17 -0
- package/dist/esm/lib/stack-app/apps/implementations/admin-app-impl.js.map +1 -1
- package/dist/esm/lib/stack-app/apps/implementations/client-app-impl.js +257 -56
- package/dist/esm/lib/stack-app/apps/implementations/client-app-impl.js.map +1 -1
- package/dist/esm/lib/stack-app/apps/implementations/common.js +3 -3
- package/dist/esm/lib/stack-app/apps/implementations/common.js.map +1 -1
- package/dist/esm/lib/stack-app/apps/implementations/server-app-impl.js +39 -0
- package/dist/esm/lib/stack-app/apps/implementations/server-app-impl.js.map +1 -1
- package/dist/esm/lib/stack-app/apps/interfaces/admin-app.js.map +1 -1
- package/dist/esm/lib/stack-app/apps/interfaces/client-app.js.map +1 -1
- package/dist/esm/lib/stack-app/common.js.map +1 -1
- package/dist/esm/lib/stack-app/users/index.js.map +1 -1
- package/dist/index.d.mts +124 -98
- package/dist/index.d.ts +124 -98
- package/dist/lib/cookie.js +59 -14
- package/dist/lib/cookie.js.map +1 -1
- package/dist/lib/stack-app/apps/implementations/admin-app-impl.js +17 -0
- package/dist/lib/stack-app/apps/implementations/admin-app-impl.js.map +1 -1
- package/dist/lib/stack-app/apps/implementations/client-app-impl.js +256 -55
- package/dist/lib/stack-app/apps/implementations/client-app-impl.js.map +1 -1
- package/dist/lib/stack-app/apps/implementations/common.js +2 -2
- package/dist/lib/stack-app/apps/implementations/common.js.map +1 -1
- package/dist/lib/stack-app/apps/implementations/server-app-impl.js +49 -0
- package/dist/lib/stack-app/apps/implementations/server-app-impl.js.map +1 -1
- package/dist/lib/stack-app/apps/interfaces/admin-app.js.map +1 -1
- package/dist/lib/stack-app/apps/interfaces/client-app.js.map +1 -1
- package/dist/lib/stack-app/common.js.map +1 -1
- package/dist/lib/stack-app/users/index.js.map +1 -1
- package/package.json +8 -8
|
@@ -36,6 +36,7 @@ module.exports = __toCommonJS(client_app_impl_exports);
|
|
|
36
36
|
var import_browser = require("@simplewebauthn/browser");
|
|
37
37
|
var import_stack_shared = require("@stackframe/stack-shared");
|
|
38
38
|
var import_sessions = require("@stackframe/stack-shared/dist/sessions");
|
|
39
|
+
var import_bytes = require("@stackframe/stack-shared/dist/utils/bytes");
|
|
39
40
|
var import_compile_time = require("@stackframe/stack-shared/dist/utils/compile-time");
|
|
40
41
|
var import_env = require("@stackframe/stack-shared/dist/utils/env");
|
|
41
42
|
var import_errors = require("@stackframe/stack-shared/dist/utils/errors");
|
|
@@ -61,6 +62,7 @@ var import_projects = require("../../projects/index.js");
|
|
|
61
62
|
var import_teams = require("../../teams/index.js");
|
|
62
63
|
var import_users = require("../../users/index.js");
|
|
63
64
|
var import_common2 = require("./common.js");
|
|
65
|
+
var import_json = require("@stackframe/stack-shared/dist/utils/json");
|
|
64
66
|
var import_common3 = require("./common.js");
|
|
65
67
|
var sc = __toESM(require("@stackframe/stack-sc"));
|
|
66
68
|
var import_stack_sc = require("@stackframe/stack-sc");
|
|
@@ -214,11 +216,15 @@ var __StackClientAppImplIncomplete = class __StackClientAppImplIncomplete {
|
|
|
214
216
|
this._convexPartialUserCache = (0, import_common2.createCache)(
|
|
215
217
|
async ([ctx]) => await this._getPartialUserFromConvex(ctx)
|
|
216
218
|
);
|
|
219
|
+
this._trustedParentDomainCache = (0, import_common2.createCache)(
|
|
220
|
+
async ([domain]) => await this._getTrustedParentDomain(domain)
|
|
221
|
+
);
|
|
217
222
|
this._anonymousSignUpInProgress = null;
|
|
218
223
|
this._memoryTokenStore = (0, import_common2.createEmptyTokenStore)();
|
|
219
224
|
this._nextServerCookiesTokenStores = /* @__PURE__ */ new WeakMap();
|
|
220
225
|
this._requestTokenStores = /* @__PURE__ */ new WeakMap();
|
|
221
226
|
this._storedBrowserCookieTokenStore = null;
|
|
227
|
+
this._mostRecentQueuedCookieRefreshIndex = 0;
|
|
222
228
|
/**
|
|
223
229
|
* A map from token stores and session keys to sessions.
|
|
224
230
|
*
|
|
@@ -234,13 +240,17 @@ var __StackClientAppImplIncomplete = class __StackClientAppImplIncomplete {
|
|
|
234
240
|
}
|
|
235
241
|
this._options = resolvedOptions;
|
|
236
242
|
this._extraOptions = extraOptions;
|
|
243
|
+
const projectId = resolvedOptions.projectId ?? (0, import_common2.getDefaultProjectId)();
|
|
244
|
+
if (projectId !== "internal" && !projectId.match(/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$/i)) {
|
|
245
|
+
throw new Error(`Invalid project ID: ${projectId}. Project IDs must be UUIDs. Please check your environment variables and/or your StackApp.`);
|
|
246
|
+
}
|
|
237
247
|
if (extraOptions && extraOptions.interface) {
|
|
238
248
|
this._interface = extraOptions.interface;
|
|
239
249
|
} else {
|
|
240
250
|
this._interface = new import_stack_shared.StackClientInterface({
|
|
241
251
|
getBaseUrl: () => (0, import_common2.getBaseUrl)(resolvedOptions.baseUrl),
|
|
242
252
|
extraRequestHeaders: resolvedOptions.extraRequestHeaders ?? (0, import_common2.getDefaultExtraRequestHeaders)(),
|
|
243
|
-
projectId
|
|
253
|
+
projectId,
|
|
244
254
|
clientVersion: import_common2.clientVersion,
|
|
245
255
|
publishableClientKey: resolvedOptions.publishableClientKey ?? (0, import_common2.getDefaultPublishableClientKey)(),
|
|
246
256
|
prepareRequest: async () => {
|
|
@@ -343,13 +353,90 @@ var __StackClientAppImplIncomplete = class __StackClientAppImplIncomplete {
|
|
|
343
353
|
(0, import_promises.runAsynchronously)(this._checkFeatureSupport(name, options));
|
|
344
354
|
throw new import_errors.StackAssertionError(`${name} is not currently supported. Please reach out to Stack support for more information.`);
|
|
345
355
|
}
|
|
356
|
+
get _legacyRefreshTokenCookieName() {
|
|
357
|
+
return `stack-refresh-${this.projectId}`;
|
|
358
|
+
}
|
|
346
359
|
get _refreshTokenCookieName() {
|
|
347
360
|
return `stack-refresh-${this.projectId}`;
|
|
348
361
|
}
|
|
362
|
+
_getRefreshTokenDefaultCookieNameForSecure(secure) {
|
|
363
|
+
return `${secure ? "__Host-" : ""}${this._refreshTokenCookieName}--default`;
|
|
364
|
+
}
|
|
365
|
+
_getCustomRefreshCookieName(domain) {
|
|
366
|
+
const encoded = (0, import_bytes.encodeBase32)(new TextEncoder().encode(domain.toLowerCase()));
|
|
367
|
+
return `${this._refreshTokenCookieName}--custom-${encoded}`;
|
|
368
|
+
}
|
|
369
|
+
_formatRefreshCookieValue(refreshToken, updatedAt) {
|
|
370
|
+
return JSON.stringify({
|
|
371
|
+
refresh_token: refreshToken,
|
|
372
|
+
updated_at_millis: updatedAt
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
_formatAccessCookieValue(refreshToken, accessToken) {
|
|
376
|
+
return refreshToken && accessToken ? JSON.stringify([refreshToken, accessToken]) : null;
|
|
377
|
+
}
|
|
378
|
+
_parseStructuredRefreshCookie(value) {
|
|
379
|
+
if (!value) {
|
|
380
|
+
return null;
|
|
381
|
+
}
|
|
382
|
+
const parsed = (0, import_json.parseJson)(value);
|
|
383
|
+
if (parsed.status !== "ok" || typeof parsed.data !== "object" || parsed.data === null) {
|
|
384
|
+
console.warn("Failed to parse structured refresh cookie");
|
|
385
|
+
return null;
|
|
386
|
+
}
|
|
387
|
+
const data = parsed.data;
|
|
388
|
+
const refreshToken = "refresh_token" in data && typeof data.refresh_token === "string" ? data.refresh_token : null;
|
|
389
|
+
const updatedAt = "updated_at_millis" in data && typeof data.updated_at_millis === "number" ? data.updated_at_millis : null;
|
|
390
|
+
if (!refreshToken) {
|
|
391
|
+
console.warn("Refresh token not found in structured refresh cookie");
|
|
392
|
+
return null;
|
|
393
|
+
}
|
|
394
|
+
return {
|
|
395
|
+
refreshToken,
|
|
396
|
+
updatedAt
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
_extractRefreshTokenFromCookieMap(cookies2) {
|
|
400
|
+
const { legacyNames, structuredPrefixes } = this._getRefreshTokenCookieNamePatterns();
|
|
401
|
+
for (const name of legacyNames) {
|
|
402
|
+
const value = cookies2[name];
|
|
403
|
+
if (value) {
|
|
404
|
+
return { refreshToken: value, updatedAt: null };
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
let selected = null;
|
|
408
|
+
for (const [name, value] of Object.entries(cookies2)) {
|
|
409
|
+
if (!structuredPrefixes.some((prefix) => name.startsWith(prefix))) continue;
|
|
410
|
+
const parsed = this._parseStructuredRefreshCookie(value);
|
|
411
|
+
if (!parsed) continue;
|
|
412
|
+
const candidateUpdatedAt = parsed.updatedAt ?? Number.NEGATIVE_INFINITY;
|
|
413
|
+
const selectedUpdatedAt = selected?.updatedAt ?? Number.NEGATIVE_INFINITY;
|
|
414
|
+
if (!selected || candidateUpdatedAt > selectedUpdatedAt) {
|
|
415
|
+
selected = parsed;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
if (!selected) {
|
|
419
|
+
return { refreshToken: null, updatedAt: null };
|
|
420
|
+
}
|
|
421
|
+
return {
|
|
422
|
+
refreshToken: selected.refreshToken,
|
|
423
|
+
updatedAt: selected.updatedAt ?? null
|
|
424
|
+
};
|
|
425
|
+
}
|
|
349
426
|
_getTokensFromCookies(cookies2) {
|
|
350
|
-
const refreshToken = cookies2
|
|
351
|
-
const
|
|
352
|
-
|
|
427
|
+
const { refreshToken } = this._extractRefreshTokenFromCookieMap(cookies2);
|
|
428
|
+
const accessTokenCookie = cookies2[this._accessTokenCookieName] ?? null;
|
|
429
|
+
let accessToken = null;
|
|
430
|
+
if (accessTokenCookie && accessTokenCookie.startsWith('["')) {
|
|
431
|
+
const parsed = (0, import_json.parseJson)(accessTokenCookie);
|
|
432
|
+
if (parsed.status === "ok" && typeof parsed.data === "object" && parsed.data !== null && Array.isArray(parsed.data) && parsed.data.length === 2 && typeof parsed.data[0] === "string" && typeof parsed.data[1] === "string") {
|
|
433
|
+
if (parsed.data[0] === refreshToken) {
|
|
434
|
+
accessToken = parsed.data[1];
|
|
435
|
+
}
|
|
436
|
+
} else {
|
|
437
|
+
console.warn("Access token cookie has invalid format");
|
|
438
|
+
}
|
|
439
|
+
}
|
|
353
440
|
return {
|
|
354
441
|
refreshToken,
|
|
355
442
|
accessToken
|
|
@@ -358,17 +445,100 @@ var __StackClientAppImplIncomplete = class __StackClientAppImplIncomplete {
|
|
|
358
445
|
get _accessTokenCookieName() {
|
|
359
446
|
return `stack-access`;
|
|
360
447
|
}
|
|
448
|
+
_getAllBrowserCookies() {
|
|
449
|
+
if (!(0, import_env.isBrowserLike)()) {
|
|
450
|
+
throw new import_errors.StackAssertionError("Cannot get browser cookies on the server!");
|
|
451
|
+
}
|
|
452
|
+
return cookie.parse(document.cookie || "");
|
|
453
|
+
}
|
|
454
|
+
_getRefreshTokenCookieNamePatterns() {
|
|
455
|
+
return {
|
|
456
|
+
legacyNames: [this._legacyRefreshTokenCookieName, "stack-refresh"],
|
|
457
|
+
structuredPrefixes: [
|
|
458
|
+
`${this._refreshTokenCookieName}--`,
|
|
459
|
+
`__Host-${this._refreshTokenCookieName}--`
|
|
460
|
+
]
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
_collectRefreshTokenCookieNames(cookies2) {
|
|
464
|
+
const { legacyNames, structuredPrefixes } = this._getRefreshTokenCookieNamePatterns();
|
|
465
|
+
const names = /* @__PURE__ */ new Set();
|
|
466
|
+
for (const name of legacyNames) {
|
|
467
|
+
if (cookies2[name]) {
|
|
468
|
+
names.add(name);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
for (const name of Object.keys(cookies2)) {
|
|
472
|
+
if (structuredPrefixes.some((prefix) => name.startsWith(prefix))) {
|
|
473
|
+
names.add(name);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
return names;
|
|
477
|
+
}
|
|
478
|
+
_prepareRefreshCookieUpdate(existingCookies, refreshToken, accessToken, defaultCookieName) {
|
|
479
|
+
const cookieNames = this._collectRefreshTokenCookieNames(existingCookies);
|
|
480
|
+
cookieNames.delete(defaultCookieName);
|
|
481
|
+
const updatedAt = refreshToken ? Date.now() : null;
|
|
482
|
+
const refreshCookieValue = refreshToken && updatedAt !== null ? this._formatRefreshCookieValue(refreshToken, updatedAt) : null;
|
|
483
|
+
const accessTokenPayload = this._formatAccessCookieValue(refreshToken, accessToken);
|
|
484
|
+
return {
|
|
485
|
+
updatedAt,
|
|
486
|
+
refreshCookieValue,
|
|
487
|
+
accessTokenPayload,
|
|
488
|
+
cookieNamesToDelete: [...cookieNames]
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
_queueCustomRefreshCookieUpdate(refreshToken, updatedAt, context) {
|
|
492
|
+
(0, import_promises.runAsynchronously)(async () => {
|
|
493
|
+
this._mostRecentQueuedCookieRefreshIndex++;
|
|
494
|
+
const updateIndex = this._mostRecentQueuedCookieRefreshIndex;
|
|
495
|
+
let hostname;
|
|
496
|
+
if ((0, import_env.isBrowserLike)()) {
|
|
497
|
+
hostname = window.location.hostname;
|
|
498
|
+
} else {
|
|
499
|
+
hostname = (await sc.headers?.())?.get("host");
|
|
500
|
+
}
|
|
501
|
+
if (!hostname) {
|
|
502
|
+
console.warn("No hostname found when queueing custom refresh cookie update");
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
505
|
+
const domain = await this._trustedParentDomainCache.getOrWait([hostname], "read-write");
|
|
506
|
+
const setCookie = async (targetDomain, value2) => {
|
|
507
|
+
const name = this._getCustomRefreshCookieName(targetDomain);
|
|
508
|
+
const options = { maxAge: 60 * 60 * 24 * 365, domain: targetDomain, noOpIfServerComponent: true };
|
|
509
|
+
if (context === "browser") {
|
|
510
|
+
(0, import_cookie.setOrDeleteCookieClient)(name, value2, options);
|
|
511
|
+
} else {
|
|
512
|
+
await (0, import_cookie.setOrDeleteCookie)(name, value2, options);
|
|
513
|
+
}
|
|
514
|
+
};
|
|
515
|
+
if (domain.status === "error" || !domain.data || updateIndex !== this._mostRecentQueuedCookieRefreshIndex) {
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
518
|
+
const value = refreshToken && updatedAt ? this._formatRefreshCookieValue(refreshToken, updatedAt) : null;
|
|
519
|
+
await setCookie(domain.data, value);
|
|
520
|
+
});
|
|
521
|
+
}
|
|
522
|
+
async _getTrustedParentDomain(currentDomain) {
|
|
523
|
+
const project = import_results.Result.orThrow(await this._interface.getClientProject());
|
|
524
|
+
const domains = project.config.domains.map((d) => d.domain.trim().replace(/^https?:\/\//, "").split("/")[0]?.toLowerCase());
|
|
525
|
+
const trustedWildcards = domains.filter((d) => d.startsWith("**."));
|
|
526
|
+
const parts = currentDomain.split(".");
|
|
527
|
+
for (let i = parts.length - 2; i >= 0; i--) {
|
|
528
|
+
const parentDomain = parts.slice(i).join(".");
|
|
529
|
+
if (domains.includes(parentDomain) && trustedWildcards.includes("**." + parentDomain)) {
|
|
530
|
+
return parentDomain;
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
return null;
|
|
534
|
+
}
|
|
361
535
|
_getBrowserCookieTokenStore() {
|
|
362
536
|
if (!(0, import_env.isBrowserLike)()) {
|
|
363
537
|
throw new Error("Cannot use cookie token store on the server!");
|
|
364
538
|
}
|
|
365
539
|
if (this._storedBrowserCookieTokenStore === null) {
|
|
366
540
|
const getCurrentValue = (old) => {
|
|
367
|
-
const tokens = this._getTokensFromCookies(
|
|
368
|
-
refreshTokenCookie: (0, import_cookie.getCookieClient)(this._refreshTokenCookieName) ?? (0, import_cookie.getCookieClient)("stack-refresh"),
|
|
369
|
-
// keep old cookie name for backwards-compatibility
|
|
370
|
-
accessTokenCookie: (0, import_cookie.getCookieClient)(this._accessTokenCookieName)
|
|
371
|
-
});
|
|
541
|
+
const tokens = this._getTokensFromCookies(this._getAllBrowserCookies());
|
|
372
542
|
return {
|
|
373
543
|
refreshToken: tokens.refreshToken,
|
|
374
544
|
accessToken: tokens.accessToken ?? (old?.refreshToken === tokens.refreshToken ? old.accessToken : null)
|
|
@@ -387,9 +557,19 @@ var __StackClientAppImplIncomplete = class __StackClientAppImplIncomplete {
|
|
|
387
557
|
}, 100);
|
|
388
558
|
this._storedBrowserCookieTokenStore.onChange((value) => {
|
|
389
559
|
try {
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
560
|
+
const refreshToken = value.refreshToken;
|
|
561
|
+
const secure = window.location.protocol === "https:";
|
|
562
|
+
const defaultName = this._getRefreshTokenDefaultCookieNameForSecure(secure);
|
|
563
|
+
const { updatedAt, refreshCookieValue, accessTokenPayload, cookieNamesToDelete } = this._prepareRefreshCookieUpdate(
|
|
564
|
+
this._getAllBrowserCookies(),
|
|
565
|
+
refreshToken,
|
|
566
|
+
value.accessToken ?? null,
|
|
567
|
+
defaultName
|
|
568
|
+
);
|
|
569
|
+
(0, import_cookie.setOrDeleteCookieClient)(defaultName, refreshCookieValue, { maxAge: 60 * 60 * 24 * 365, secure });
|
|
570
|
+
(0, import_cookie.setOrDeleteCookieClient)(this._accessTokenCookieName, accessTokenPayload, { maxAge: 60 * 60 * 24 });
|
|
571
|
+
cookieNamesToDelete.forEach((name) => (0, import_cookie.deleteCookieClient)(name));
|
|
572
|
+
this._queueCustomRefreshCookieUpdate(refreshToken, updatedAt, "browser");
|
|
393
573
|
hasSucceededInWriting = true;
|
|
394
574
|
} catch (e) {
|
|
395
575
|
if (!(0, import_env.isBrowserLike)()) {
|
|
@@ -412,18 +592,31 @@ var __StackClientAppImplIncomplete = class __StackClientAppImplIncomplete {
|
|
|
412
592
|
if ((0, import_env.isBrowserLike)()) {
|
|
413
593
|
return this._getBrowserCookieTokenStore();
|
|
414
594
|
} else {
|
|
415
|
-
const tokens = this._getTokensFromCookies(
|
|
416
|
-
refreshTokenCookie: cookieHelper.get(this._refreshTokenCookieName) ?? cookieHelper.get("stack-refresh"),
|
|
417
|
-
// keep old cookie name for backwards-compatibility
|
|
418
|
-
accessTokenCookie: cookieHelper.get(this._accessTokenCookieName)
|
|
419
|
-
});
|
|
595
|
+
const tokens = this._getTokensFromCookies(cookieHelper.getAll());
|
|
420
596
|
const store = new import_stores.Store(tokens);
|
|
421
597
|
store.onChange((value) => {
|
|
422
598
|
(0, import_promises.runAsynchronously)(async () => {
|
|
599
|
+
const refreshToken = value.refreshToken;
|
|
600
|
+
const secure = await (0, import_cookie.isSecure)();
|
|
601
|
+
const defaultName = this._getRefreshTokenDefaultCookieNameForSecure(secure);
|
|
602
|
+
const { updatedAt, refreshCookieValue, accessTokenPayload, cookieNamesToDelete } = this._prepareRefreshCookieUpdate(
|
|
603
|
+
cookieHelper.getAll(),
|
|
604
|
+
refreshToken,
|
|
605
|
+
value.accessToken ?? null,
|
|
606
|
+
defaultName
|
|
607
|
+
);
|
|
423
608
|
await Promise.all([
|
|
424
|
-
(0, import_cookie.setOrDeleteCookie)(
|
|
425
|
-
(0, import_cookie.setOrDeleteCookie)(this._accessTokenCookieName,
|
|
609
|
+
(0, import_cookie.setOrDeleteCookie)(defaultName, refreshCookieValue, { maxAge: 60 * 60 * 24 * 365, noOpIfServerComponent: true }),
|
|
610
|
+
(0, import_cookie.setOrDeleteCookie)(this._accessTokenCookieName, accessTokenPayload, { maxAge: 60 * 60 * 24, noOpIfServerComponent: true })
|
|
426
611
|
]);
|
|
612
|
+
if (cookieNamesToDelete.length > 0) {
|
|
613
|
+
await Promise.all(
|
|
614
|
+
cookieNamesToDelete.map(
|
|
615
|
+
(name) => (0, import_cookie.setOrDeleteCookie)(name, null, { noOpIfServerComponent: true })
|
|
616
|
+
)
|
|
617
|
+
);
|
|
618
|
+
}
|
|
619
|
+
this._queueCustomRefreshCookieUpdate(refreshToken, updatedAt, "server");
|
|
427
620
|
});
|
|
428
621
|
});
|
|
429
622
|
return store;
|
|
@@ -454,11 +647,7 @@ var __StackClientAppImplIncomplete = class __StackClientAppImplIncomplete {
|
|
|
454
647
|
}
|
|
455
648
|
const cookieHeader = tokenStoreInit.headers.get("cookie");
|
|
456
649
|
const parsed = cookie.parse(cookieHeader || "");
|
|
457
|
-
const res = new import_stores.Store(
|
|
458
|
-
refreshToken: parsed[this._refreshTokenCookieName] || parsed["stack-refresh"] || null,
|
|
459
|
-
// keep old cookie name for backwards-compatibility
|
|
460
|
-
accessToken: parsed[this._accessTokenCookieName] || null
|
|
461
|
-
});
|
|
650
|
+
const res = new import_stores.Store(this._getTokensFromCookies(parsed));
|
|
462
651
|
this._requestTokenStores.set(tokenStoreInit, res);
|
|
463
652
|
return res;
|
|
464
653
|
} else if ("accessToken" in tokenStoreInit || "refreshToken" in tokenStoreInit) {
|
|
@@ -804,35 +993,6 @@ var __StackClientAppImplIncomplete = class __StackClientAppImplIncomplete {
|
|
|
804
993
|
const tokens = await this.currentSession.getTokens();
|
|
805
994
|
return tokens;
|
|
806
995
|
},
|
|
807
|
-
async registerPasskey(options) {
|
|
808
|
-
const hostname = (await app._getCurrentUrl())?.hostname;
|
|
809
|
-
if (!hostname) {
|
|
810
|
-
throw new import_errors.StackAssertionError("hostname must be provided if the Stack App does not have a redirect method");
|
|
811
|
-
}
|
|
812
|
-
const initiationResult = await app._interface.initiatePasskeyRegistration({}, session);
|
|
813
|
-
if (initiationResult.status !== "ok") {
|
|
814
|
-
return import_results.Result.error(new import_stack_shared.KnownErrors.PasskeyRegistrationFailed("Failed to get initiation options for passkey registration"));
|
|
815
|
-
}
|
|
816
|
-
const { options_json, code } = initiationResult.data;
|
|
817
|
-
if (options_json.rp.id !== "THIS_VALUE_WILL_BE_REPLACED.example.com") {
|
|
818
|
-
throw new import_errors.StackAssertionError(`Expected returned RP ID from server to equal sentinel, but found ${options_json.rp.id}`);
|
|
819
|
-
}
|
|
820
|
-
options_json.rp.id = hostname;
|
|
821
|
-
let attResp;
|
|
822
|
-
try {
|
|
823
|
-
attResp = await (0, import_browser.startRegistration)({ optionsJSON: options_json });
|
|
824
|
-
} catch (error) {
|
|
825
|
-
if (error instanceof import_browser.WebAuthnError) {
|
|
826
|
-
return import_results.Result.error(new import_stack_shared.KnownErrors.PasskeyWebAuthnError(error.message, error.name));
|
|
827
|
-
} else {
|
|
828
|
-
(0, import_errors.captureError)("passkey-registration-failed", error);
|
|
829
|
-
return import_results.Result.error(new import_stack_shared.KnownErrors.PasskeyRegistrationFailed("Failed to start passkey registration due to unknown error"));
|
|
830
|
-
}
|
|
831
|
-
}
|
|
832
|
-
const registrationResult = await app._interface.registerPasskey({ credential: attResp, code }, session);
|
|
833
|
-
await app._refreshUser(session);
|
|
834
|
-
return registrationResult;
|
|
835
|
-
},
|
|
836
996
|
signOut(options) {
|
|
837
997
|
return app._signOut(session, options);
|
|
838
998
|
}
|
|
@@ -1081,6 +1241,35 @@ var __StackClientAppImplIncomplete = class __StackClientAppImplIncomplete {
|
|
|
1081
1241
|
async getOAuthProvider(id) {
|
|
1082
1242
|
const providers = await this.listOAuthProviders();
|
|
1083
1243
|
return providers.find((p) => p.id === id) ?? null;
|
|
1244
|
+
},
|
|
1245
|
+
async registerPasskey(options) {
|
|
1246
|
+
const hostname = (await app._getCurrentUrl())?.hostname;
|
|
1247
|
+
if (!hostname) {
|
|
1248
|
+
throw new import_errors.StackAssertionError("hostname must be provided if the Stack App does not have a redirect method");
|
|
1249
|
+
}
|
|
1250
|
+
const initiationResult = await app._interface.initiatePasskeyRegistration({}, session);
|
|
1251
|
+
if (initiationResult.status !== "ok") {
|
|
1252
|
+
return import_results.Result.error(new import_stack_shared.KnownErrors.PasskeyRegistrationFailed("Failed to get initiation options for passkey registration"));
|
|
1253
|
+
}
|
|
1254
|
+
const { options_json, code } = initiationResult.data;
|
|
1255
|
+
if (options_json.rp.id !== "THIS_VALUE_WILL_BE_REPLACED.example.com") {
|
|
1256
|
+
throw new import_errors.StackAssertionError(`Expected returned RP ID from server to equal sentinel, but found ${options_json.rp.id}`);
|
|
1257
|
+
}
|
|
1258
|
+
options_json.rp.id = hostname;
|
|
1259
|
+
let attResp;
|
|
1260
|
+
try {
|
|
1261
|
+
attResp = await (0, import_browser.startRegistration)({ optionsJSON: options_json });
|
|
1262
|
+
} catch (error) {
|
|
1263
|
+
if (error instanceof import_browser.WebAuthnError) {
|
|
1264
|
+
return import_results.Result.error(new import_stack_shared.KnownErrors.PasskeyWebAuthnError(error.message, error.name));
|
|
1265
|
+
} else {
|
|
1266
|
+
(0, import_errors.captureError)("passkey-registration-failed", error);
|
|
1267
|
+
return import_results.Result.error(new import_stack_shared.KnownErrors.PasskeyRegistrationFailed("Failed to start passkey registration due to unknown error"));
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
const registrationResult = await app._interface.registerPasskey({ credential: attResp, code }, session);
|
|
1271
|
+
await app._refreshUser(session);
|
|
1272
|
+
return registrationResult;
|
|
1084
1273
|
}
|
|
1085
1274
|
};
|
|
1086
1275
|
}
|
|
@@ -1854,10 +2043,22 @@ ${url}`);
|
|
|
1854
2043
|
});
|
|
1855
2044
|
}
|
|
1856
2045
|
async signOut(options) {
|
|
1857
|
-
const user = await this.getUser();
|
|
2046
|
+
const user = await this.getUser({ tokenStore: options?.tokenStore ?? void 0 });
|
|
2047
|
+
if (user) {
|
|
2048
|
+
await user.signOut({ redirectUrl: options?.redirectUrl });
|
|
2049
|
+
}
|
|
2050
|
+
}
|
|
2051
|
+
async getAuthHeaders(options) {
|
|
2052
|
+
return {
|
|
2053
|
+
"x-stack-auth": JSON.stringify(await this.getAuthJson(options))
|
|
2054
|
+
};
|
|
2055
|
+
}
|
|
2056
|
+
async getAuthJson(options) {
|
|
2057
|
+
const user = await this.getUser({ tokenStore: options?.tokenStore ?? void 0 });
|
|
1858
2058
|
if (user) {
|
|
1859
|
-
await user.
|
|
2059
|
+
return await user.getAuthJson();
|
|
1860
2060
|
}
|
|
2061
|
+
return { accessToken: null, refreshToken: null };
|
|
1861
2062
|
}
|
|
1862
2063
|
async getProject() {
|
|
1863
2064
|
const crud = import_results.Result.orThrow(await this._currentProjectCache.getOrWait([], "write-only"));
|