@stackframe/react 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 +274 -0
- package/dist/components-page/stack-handler-client.js.map +1 -0
- package/dist/components-page/stack-handler.js +4 -239
- 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 +250 -0
- package/dist/esm/components-page/stack-handler-client.js.map +1 -0
- package/dist/esm/components-page/stack-handler.js +4 -239
- 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 +36 -7
- 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 +255 -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 -89
- package/dist/index.d.ts +124 -89
- package/dist/lib/cookie.js +38 -7
- 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 +254 -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 +5 -5
|
@@ -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_env = require("@stackframe/stack-shared/dist/utils/env");
|
|
40
41
|
var import_errors = require("@stackframe/stack-shared/dist/utils/errors");
|
|
41
42
|
var import_maps = require("@stackframe/stack-shared/dist/utils/maps");
|
|
@@ -59,6 +60,7 @@ var import_projects = require("../../projects/index.js");
|
|
|
59
60
|
var import_teams = require("../../teams/index.js");
|
|
60
61
|
var import_users = require("../../users/index.js");
|
|
61
62
|
var import_common2 = require("./common.js");
|
|
63
|
+
var import_json = require("@stackframe/stack-shared/dist/utils/json");
|
|
62
64
|
var import_common3 = require("./common.js");
|
|
63
65
|
var isReactServer = false;
|
|
64
66
|
var process = globalThis.process ?? { env: {} };
|
|
@@ -209,11 +211,15 @@ var __StackClientAppImplIncomplete = class __StackClientAppImplIncomplete {
|
|
|
209
211
|
this._convexPartialUserCache = (0, import_common2.createCache)(
|
|
210
212
|
async ([ctx]) => await this._getPartialUserFromConvex(ctx)
|
|
211
213
|
);
|
|
214
|
+
this._trustedParentDomainCache = (0, import_common2.createCache)(
|
|
215
|
+
async ([domain]) => await this._getTrustedParentDomain(domain)
|
|
216
|
+
);
|
|
212
217
|
this._anonymousSignUpInProgress = null;
|
|
213
218
|
this._memoryTokenStore = (0, import_common2.createEmptyTokenStore)();
|
|
214
219
|
this._nextServerCookiesTokenStores = /* @__PURE__ */ new WeakMap();
|
|
215
220
|
this._requestTokenStores = /* @__PURE__ */ new WeakMap();
|
|
216
221
|
this._storedBrowserCookieTokenStore = null;
|
|
222
|
+
this._mostRecentQueuedCookieRefreshIndex = 0;
|
|
217
223
|
/**
|
|
218
224
|
* A map from token stores and session keys to sessions.
|
|
219
225
|
*
|
|
@@ -229,13 +235,17 @@ var __StackClientAppImplIncomplete = class __StackClientAppImplIncomplete {
|
|
|
229
235
|
}
|
|
230
236
|
this._options = resolvedOptions;
|
|
231
237
|
this._extraOptions = extraOptions;
|
|
238
|
+
const projectId = resolvedOptions.projectId ?? (0, import_common2.getDefaultProjectId)();
|
|
239
|
+
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)) {
|
|
240
|
+
throw new Error(`Invalid project ID: ${projectId}. Project IDs must be UUIDs. Please check your environment variables and/or your StackApp.`);
|
|
241
|
+
}
|
|
232
242
|
if (extraOptions && extraOptions.interface) {
|
|
233
243
|
this._interface = extraOptions.interface;
|
|
234
244
|
} else {
|
|
235
245
|
this._interface = new import_stack_shared.StackClientInterface({
|
|
236
246
|
getBaseUrl: () => (0, import_common2.getBaseUrl)(resolvedOptions.baseUrl),
|
|
237
247
|
extraRequestHeaders: resolvedOptions.extraRequestHeaders ?? (0, import_common2.getDefaultExtraRequestHeaders)(),
|
|
238
|
-
projectId
|
|
248
|
+
projectId,
|
|
239
249
|
clientVersion: import_common2.clientVersion,
|
|
240
250
|
publishableClientKey: resolvedOptions.publishableClientKey ?? (0, import_common2.getDefaultPublishableClientKey)(),
|
|
241
251
|
prepareRequest: async () => {
|
|
@@ -336,13 +346,90 @@ var __StackClientAppImplIncomplete = class __StackClientAppImplIncomplete {
|
|
|
336
346
|
(0, import_promises.runAsynchronously)(this._checkFeatureSupport(name, options));
|
|
337
347
|
throw new import_errors.StackAssertionError(`${name} is not currently supported. Please reach out to Stack support for more information.`);
|
|
338
348
|
}
|
|
349
|
+
get _legacyRefreshTokenCookieName() {
|
|
350
|
+
return `stack-refresh-${this.projectId}`;
|
|
351
|
+
}
|
|
339
352
|
get _refreshTokenCookieName() {
|
|
340
353
|
return `stack-refresh-${this.projectId}`;
|
|
341
354
|
}
|
|
355
|
+
_getRefreshTokenDefaultCookieNameForSecure(secure) {
|
|
356
|
+
return `${secure ? "__Host-" : ""}${this._refreshTokenCookieName}--default`;
|
|
357
|
+
}
|
|
358
|
+
_getCustomRefreshCookieName(domain) {
|
|
359
|
+
const encoded = (0, import_bytes.encodeBase32)(new TextEncoder().encode(domain.toLowerCase()));
|
|
360
|
+
return `${this._refreshTokenCookieName}--custom-${encoded}`;
|
|
361
|
+
}
|
|
362
|
+
_formatRefreshCookieValue(refreshToken, updatedAt) {
|
|
363
|
+
return JSON.stringify({
|
|
364
|
+
refresh_token: refreshToken,
|
|
365
|
+
updated_at_millis: updatedAt
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
_formatAccessCookieValue(refreshToken, accessToken) {
|
|
369
|
+
return refreshToken && accessToken ? JSON.stringify([refreshToken, accessToken]) : null;
|
|
370
|
+
}
|
|
371
|
+
_parseStructuredRefreshCookie(value) {
|
|
372
|
+
if (!value) {
|
|
373
|
+
return null;
|
|
374
|
+
}
|
|
375
|
+
const parsed = (0, import_json.parseJson)(value);
|
|
376
|
+
if (parsed.status !== "ok" || typeof parsed.data !== "object" || parsed.data === null) {
|
|
377
|
+
console.warn("Failed to parse structured refresh cookie");
|
|
378
|
+
return null;
|
|
379
|
+
}
|
|
380
|
+
const data = parsed.data;
|
|
381
|
+
const refreshToken = "refresh_token" in data && typeof data.refresh_token === "string" ? data.refresh_token : null;
|
|
382
|
+
const updatedAt = "updated_at_millis" in data && typeof data.updated_at_millis === "number" ? data.updated_at_millis : null;
|
|
383
|
+
if (!refreshToken) {
|
|
384
|
+
console.warn("Refresh token not found in structured refresh cookie");
|
|
385
|
+
return null;
|
|
386
|
+
}
|
|
387
|
+
return {
|
|
388
|
+
refreshToken,
|
|
389
|
+
updatedAt
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
_extractRefreshTokenFromCookieMap(cookies) {
|
|
393
|
+
const { legacyNames, structuredPrefixes } = this._getRefreshTokenCookieNamePatterns();
|
|
394
|
+
for (const name of legacyNames) {
|
|
395
|
+
const value = cookies[name];
|
|
396
|
+
if (value) {
|
|
397
|
+
return { refreshToken: value, updatedAt: null };
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
let selected = null;
|
|
401
|
+
for (const [name, value] of Object.entries(cookies)) {
|
|
402
|
+
if (!structuredPrefixes.some((prefix) => name.startsWith(prefix))) continue;
|
|
403
|
+
const parsed = this._parseStructuredRefreshCookie(value);
|
|
404
|
+
if (!parsed) continue;
|
|
405
|
+
const candidateUpdatedAt = parsed.updatedAt ?? Number.NEGATIVE_INFINITY;
|
|
406
|
+
const selectedUpdatedAt = selected?.updatedAt ?? Number.NEGATIVE_INFINITY;
|
|
407
|
+
if (!selected || candidateUpdatedAt > selectedUpdatedAt) {
|
|
408
|
+
selected = parsed;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
if (!selected) {
|
|
412
|
+
return { refreshToken: null, updatedAt: null };
|
|
413
|
+
}
|
|
414
|
+
return {
|
|
415
|
+
refreshToken: selected.refreshToken,
|
|
416
|
+
updatedAt: selected.updatedAt ?? null
|
|
417
|
+
};
|
|
418
|
+
}
|
|
342
419
|
_getTokensFromCookies(cookies) {
|
|
343
|
-
const refreshToken = cookies
|
|
344
|
-
const
|
|
345
|
-
|
|
420
|
+
const { refreshToken } = this._extractRefreshTokenFromCookieMap(cookies);
|
|
421
|
+
const accessTokenCookie = cookies[this._accessTokenCookieName] ?? null;
|
|
422
|
+
let accessToken = null;
|
|
423
|
+
if (accessTokenCookie && accessTokenCookie.startsWith('["')) {
|
|
424
|
+
const parsed = (0, import_json.parseJson)(accessTokenCookie);
|
|
425
|
+
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") {
|
|
426
|
+
if (parsed.data[0] === refreshToken) {
|
|
427
|
+
accessToken = parsed.data[1];
|
|
428
|
+
}
|
|
429
|
+
} else {
|
|
430
|
+
console.warn("Access token cookie has invalid format");
|
|
431
|
+
}
|
|
432
|
+
}
|
|
346
433
|
return {
|
|
347
434
|
refreshToken,
|
|
348
435
|
accessToken
|
|
@@ -351,17 +438,98 @@ var __StackClientAppImplIncomplete = class __StackClientAppImplIncomplete {
|
|
|
351
438
|
get _accessTokenCookieName() {
|
|
352
439
|
return `stack-access`;
|
|
353
440
|
}
|
|
441
|
+
_getAllBrowserCookies() {
|
|
442
|
+
if (!(0, import_env.isBrowserLike)()) {
|
|
443
|
+
throw new import_errors.StackAssertionError("Cannot get browser cookies on the server!");
|
|
444
|
+
}
|
|
445
|
+
return cookie.parse(document.cookie || "");
|
|
446
|
+
}
|
|
447
|
+
_getRefreshTokenCookieNamePatterns() {
|
|
448
|
+
return {
|
|
449
|
+
legacyNames: [this._legacyRefreshTokenCookieName, "stack-refresh"],
|
|
450
|
+
structuredPrefixes: [
|
|
451
|
+
`${this._refreshTokenCookieName}--`,
|
|
452
|
+
`__Host-${this._refreshTokenCookieName}--`
|
|
453
|
+
]
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
_collectRefreshTokenCookieNames(cookies) {
|
|
457
|
+
const { legacyNames, structuredPrefixes } = this._getRefreshTokenCookieNamePatterns();
|
|
458
|
+
const names = /* @__PURE__ */ new Set();
|
|
459
|
+
for (const name of legacyNames) {
|
|
460
|
+
if (cookies[name]) {
|
|
461
|
+
names.add(name);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
for (const name of Object.keys(cookies)) {
|
|
465
|
+
if (structuredPrefixes.some((prefix) => name.startsWith(prefix))) {
|
|
466
|
+
names.add(name);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
return names;
|
|
470
|
+
}
|
|
471
|
+
_prepareRefreshCookieUpdate(existingCookies, refreshToken, accessToken, defaultCookieName) {
|
|
472
|
+
const cookieNames = this._collectRefreshTokenCookieNames(existingCookies);
|
|
473
|
+
cookieNames.delete(defaultCookieName);
|
|
474
|
+
const updatedAt = refreshToken ? Date.now() : null;
|
|
475
|
+
const refreshCookieValue = refreshToken && updatedAt !== null ? this._formatRefreshCookieValue(refreshToken, updatedAt) : null;
|
|
476
|
+
const accessTokenPayload = this._formatAccessCookieValue(refreshToken, accessToken);
|
|
477
|
+
return {
|
|
478
|
+
updatedAt,
|
|
479
|
+
refreshCookieValue,
|
|
480
|
+
accessTokenPayload,
|
|
481
|
+
cookieNamesToDelete: [...cookieNames]
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
_queueCustomRefreshCookieUpdate(refreshToken, updatedAt, context) {
|
|
485
|
+
(0, import_promises.runAsynchronously)(async () => {
|
|
486
|
+
this._mostRecentQueuedCookieRefreshIndex++;
|
|
487
|
+
const updateIndex = this._mostRecentQueuedCookieRefreshIndex;
|
|
488
|
+
let hostname;
|
|
489
|
+
if ((0, import_env.isBrowserLike)()) {
|
|
490
|
+
hostname = window.location.hostname;
|
|
491
|
+
}
|
|
492
|
+
if (!hostname) {
|
|
493
|
+
console.warn("No hostname found when queueing custom refresh cookie update");
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
const domain = await this._trustedParentDomainCache.getOrWait([hostname], "read-write");
|
|
497
|
+
const setCookie = async (targetDomain, value2) => {
|
|
498
|
+
const name = this._getCustomRefreshCookieName(targetDomain);
|
|
499
|
+
const options = { maxAge: 60 * 60 * 24 * 365, domain: targetDomain, noOpIfServerComponent: true };
|
|
500
|
+
if (context === "browser") {
|
|
501
|
+
(0, import_cookie.setOrDeleteCookieClient)(name, value2, options);
|
|
502
|
+
} else {
|
|
503
|
+
await (0, import_cookie.setOrDeleteCookie)(name, value2, options);
|
|
504
|
+
}
|
|
505
|
+
};
|
|
506
|
+
if (domain.status === "error" || !domain.data || updateIndex !== this._mostRecentQueuedCookieRefreshIndex) {
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
const value = refreshToken && updatedAt ? this._formatRefreshCookieValue(refreshToken, updatedAt) : null;
|
|
510
|
+
await setCookie(domain.data, value);
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
async _getTrustedParentDomain(currentDomain) {
|
|
514
|
+
const project = import_results.Result.orThrow(await this._interface.getClientProject());
|
|
515
|
+
const domains = project.config.domains.map((d) => d.domain.trim().replace(/^https?:\/\//, "").split("/")[0]?.toLowerCase());
|
|
516
|
+
const trustedWildcards = domains.filter((d) => d.startsWith("**."));
|
|
517
|
+
const parts = currentDomain.split(".");
|
|
518
|
+
for (let i = parts.length - 2; i >= 0; i--) {
|
|
519
|
+
const parentDomain = parts.slice(i).join(".");
|
|
520
|
+
if (domains.includes(parentDomain) && trustedWildcards.includes("**." + parentDomain)) {
|
|
521
|
+
return parentDomain;
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
return null;
|
|
525
|
+
}
|
|
354
526
|
_getBrowserCookieTokenStore() {
|
|
355
527
|
if (!(0, import_env.isBrowserLike)()) {
|
|
356
528
|
throw new Error("Cannot use cookie token store on the server!");
|
|
357
529
|
}
|
|
358
530
|
if (this._storedBrowserCookieTokenStore === null) {
|
|
359
531
|
const getCurrentValue = (old) => {
|
|
360
|
-
const tokens = this._getTokensFromCookies(
|
|
361
|
-
refreshTokenCookie: (0, import_cookie.getCookieClient)(this._refreshTokenCookieName) ?? (0, import_cookie.getCookieClient)("stack-refresh"),
|
|
362
|
-
// keep old cookie name for backwards-compatibility
|
|
363
|
-
accessTokenCookie: (0, import_cookie.getCookieClient)(this._accessTokenCookieName)
|
|
364
|
-
});
|
|
532
|
+
const tokens = this._getTokensFromCookies(this._getAllBrowserCookies());
|
|
365
533
|
return {
|
|
366
534
|
refreshToken: tokens.refreshToken,
|
|
367
535
|
accessToken: tokens.accessToken ?? (old?.refreshToken === tokens.refreshToken ? old.accessToken : null)
|
|
@@ -380,9 +548,19 @@ var __StackClientAppImplIncomplete = class __StackClientAppImplIncomplete {
|
|
|
380
548
|
}, 100);
|
|
381
549
|
this._storedBrowserCookieTokenStore.onChange((value) => {
|
|
382
550
|
try {
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
551
|
+
const refreshToken = value.refreshToken;
|
|
552
|
+
const secure = window.location.protocol === "https:";
|
|
553
|
+
const defaultName = this._getRefreshTokenDefaultCookieNameForSecure(secure);
|
|
554
|
+
const { updatedAt, refreshCookieValue, accessTokenPayload, cookieNamesToDelete } = this._prepareRefreshCookieUpdate(
|
|
555
|
+
this._getAllBrowserCookies(),
|
|
556
|
+
refreshToken,
|
|
557
|
+
value.accessToken ?? null,
|
|
558
|
+
defaultName
|
|
559
|
+
);
|
|
560
|
+
(0, import_cookie.setOrDeleteCookieClient)(defaultName, refreshCookieValue, { maxAge: 60 * 60 * 24 * 365, secure });
|
|
561
|
+
(0, import_cookie.setOrDeleteCookieClient)(this._accessTokenCookieName, accessTokenPayload, { maxAge: 60 * 60 * 24 });
|
|
562
|
+
cookieNamesToDelete.forEach((name) => (0, import_cookie.deleteCookieClient)(name));
|
|
563
|
+
this._queueCustomRefreshCookieUpdate(refreshToken, updatedAt, "browser");
|
|
386
564
|
hasSucceededInWriting = true;
|
|
387
565
|
} catch (e) {
|
|
388
566
|
if (!(0, import_env.isBrowserLike)()) {
|
|
@@ -405,18 +583,31 @@ var __StackClientAppImplIncomplete = class __StackClientAppImplIncomplete {
|
|
|
405
583
|
if ((0, import_env.isBrowserLike)()) {
|
|
406
584
|
return this._getBrowserCookieTokenStore();
|
|
407
585
|
} else {
|
|
408
|
-
const tokens = this._getTokensFromCookies(
|
|
409
|
-
refreshTokenCookie: cookieHelper.get(this._refreshTokenCookieName) ?? cookieHelper.get("stack-refresh"),
|
|
410
|
-
// keep old cookie name for backwards-compatibility
|
|
411
|
-
accessTokenCookie: cookieHelper.get(this._accessTokenCookieName)
|
|
412
|
-
});
|
|
586
|
+
const tokens = this._getTokensFromCookies(cookieHelper.getAll());
|
|
413
587
|
const store = new import_stores.Store(tokens);
|
|
414
588
|
store.onChange((value) => {
|
|
415
589
|
(0, import_promises.runAsynchronously)(async () => {
|
|
590
|
+
const refreshToken = value.refreshToken;
|
|
591
|
+
const secure = await (0, import_cookie.isSecure)();
|
|
592
|
+
const defaultName = this._getRefreshTokenDefaultCookieNameForSecure(secure);
|
|
593
|
+
const { updatedAt, refreshCookieValue, accessTokenPayload, cookieNamesToDelete } = this._prepareRefreshCookieUpdate(
|
|
594
|
+
cookieHelper.getAll(),
|
|
595
|
+
refreshToken,
|
|
596
|
+
value.accessToken ?? null,
|
|
597
|
+
defaultName
|
|
598
|
+
);
|
|
416
599
|
await Promise.all([
|
|
417
|
-
(0, import_cookie.setOrDeleteCookie)(
|
|
418
|
-
(0, import_cookie.setOrDeleteCookie)(this._accessTokenCookieName,
|
|
600
|
+
(0, import_cookie.setOrDeleteCookie)(defaultName, refreshCookieValue, { maxAge: 60 * 60 * 24 * 365, noOpIfServerComponent: true }),
|
|
601
|
+
(0, import_cookie.setOrDeleteCookie)(this._accessTokenCookieName, accessTokenPayload, { maxAge: 60 * 60 * 24, noOpIfServerComponent: true })
|
|
419
602
|
]);
|
|
603
|
+
if (cookieNamesToDelete.length > 0) {
|
|
604
|
+
await Promise.all(
|
|
605
|
+
cookieNamesToDelete.map(
|
|
606
|
+
(name) => (0, import_cookie.setOrDeleteCookie)(name, null, { noOpIfServerComponent: true })
|
|
607
|
+
)
|
|
608
|
+
);
|
|
609
|
+
}
|
|
610
|
+
this._queueCustomRefreshCookieUpdate(refreshToken, updatedAt, "server");
|
|
420
611
|
});
|
|
421
612
|
});
|
|
422
613
|
return store;
|
|
@@ -447,11 +638,7 @@ var __StackClientAppImplIncomplete = class __StackClientAppImplIncomplete {
|
|
|
447
638
|
}
|
|
448
639
|
const cookieHeader = tokenStoreInit.headers.get("cookie");
|
|
449
640
|
const parsed = cookie.parse(cookieHeader || "");
|
|
450
|
-
const res = new import_stores.Store(
|
|
451
|
-
refreshToken: parsed[this._refreshTokenCookieName] || parsed["stack-refresh"] || null,
|
|
452
|
-
// keep old cookie name for backwards-compatibility
|
|
453
|
-
accessToken: parsed[this._accessTokenCookieName] || null
|
|
454
|
-
});
|
|
641
|
+
const res = new import_stores.Store(this._getTokensFromCookies(parsed));
|
|
455
642
|
this._requestTokenStores.set(tokenStoreInit, res);
|
|
456
643
|
return res;
|
|
457
644
|
} else if ("accessToken" in tokenStoreInit || "refreshToken" in tokenStoreInit) {
|
|
@@ -797,35 +984,6 @@ var __StackClientAppImplIncomplete = class __StackClientAppImplIncomplete {
|
|
|
797
984
|
const tokens = await this.currentSession.getTokens();
|
|
798
985
|
return tokens;
|
|
799
986
|
},
|
|
800
|
-
async registerPasskey(options) {
|
|
801
|
-
const hostname = (await app._getCurrentUrl())?.hostname;
|
|
802
|
-
if (!hostname) {
|
|
803
|
-
throw new import_errors.StackAssertionError("hostname must be provided if the Stack App does not have a redirect method");
|
|
804
|
-
}
|
|
805
|
-
const initiationResult = await app._interface.initiatePasskeyRegistration({}, session);
|
|
806
|
-
if (initiationResult.status !== "ok") {
|
|
807
|
-
return import_results.Result.error(new import_stack_shared.KnownErrors.PasskeyRegistrationFailed("Failed to get initiation options for passkey registration"));
|
|
808
|
-
}
|
|
809
|
-
const { options_json, code } = initiationResult.data;
|
|
810
|
-
if (options_json.rp.id !== "THIS_VALUE_WILL_BE_REPLACED.example.com") {
|
|
811
|
-
throw new import_errors.StackAssertionError(`Expected returned RP ID from server to equal sentinel, but found ${options_json.rp.id}`);
|
|
812
|
-
}
|
|
813
|
-
options_json.rp.id = hostname;
|
|
814
|
-
let attResp;
|
|
815
|
-
try {
|
|
816
|
-
attResp = await (0, import_browser.startRegistration)({ optionsJSON: options_json });
|
|
817
|
-
} catch (error) {
|
|
818
|
-
if (error instanceof import_browser.WebAuthnError) {
|
|
819
|
-
return import_results.Result.error(new import_stack_shared.KnownErrors.PasskeyWebAuthnError(error.message, error.name));
|
|
820
|
-
} else {
|
|
821
|
-
(0, import_errors.captureError)("passkey-registration-failed", error);
|
|
822
|
-
return import_results.Result.error(new import_stack_shared.KnownErrors.PasskeyRegistrationFailed("Failed to start passkey registration due to unknown error"));
|
|
823
|
-
}
|
|
824
|
-
}
|
|
825
|
-
const registrationResult = await app._interface.registerPasskey({ credential: attResp, code }, session);
|
|
826
|
-
await app._refreshUser(session);
|
|
827
|
-
return registrationResult;
|
|
828
|
-
},
|
|
829
987
|
signOut(options) {
|
|
830
988
|
return app._signOut(session, options);
|
|
831
989
|
}
|
|
@@ -1074,6 +1232,35 @@ var __StackClientAppImplIncomplete = class __StackClientAppImplIncomplete {
|
|
|
1074
1232
|
async getOAuthProvider(id) {
|
|
1075
1233
|
const providers = await this.listOAuthProviders();
|
|
1076
1234
|
return providers.find((p) => p.id === id) ?? null;
|
|
1235
|
+
},
|
|
1236
|
+
async registerPasskey(options) {
|
|
1237
|
+
const hostname = (await app._getCurrentUrl())?.hostname;
|
|
1238
|
+
if (!hostname) {
|
|
1239
|
+
throw new import_errors.StackAssertionError("hostname must be provided if the Stack App does not have a redirect method");
|
|
1240
|
+
}
|
|
1241
|
+
const initiationResult = await app._interface.initiatePasskeyRegistration({}, session);
|
|
1242
|
+
if (initiationResult.status !== "ok") {
|
|
1243
|
+
return import_results.Result.error(new import_stack_shared.KnownErrors.PasskeyRegistrationFailed("Failed to get initiation options for passkey registration"));
|
|
1244
|
+
}
|
|
1245
|
+
const { options_json, code } = initiationResult.data;
|
|
1246
|
+
if (options_json.rp.id !== "THIS_VALUE_WILL_BE_REPLACED.example.com") {
|
|
1247
|
+
throw new import_errors.StackAssertionError(`Expected returned RP ID from server to equal sentinel, but found ${options_json.rp.id}`);
|
|
1248
|
+
}
|
|
1249
|
+
options_json.rp.id = hostname;
|
|
1250
|
+
let attResp;
|
|
1251
|
+
try {
|
|
1252
|
+
attResp = await (0, import_browser.startRegistration)({ optionsJSON: options_json });
|
|
1253
|
+
} catch (error) {
|
|
1254
|
+
if (error instanceof import_browser.WebAuthnError) {
|
|
1255
|
+
return import_results.Result.error(new import_stack_shared.KnownErrors.PasskeyWebAuthnError(error.message, error.name));
|
|
1256
|
+
} else {
|
|
1257
|
+
(0, import_errors.captureError)("passkey-registration-failed", error);
|
|
1258
|
+
return import_results.Result.error(new import_stack_shared.KnownErrors.PasskeyRegistrationFailed("Failed to start passkey registration due to unknown error"));
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
const registrationResult = await app._interface.registerPasskey({ credential: attResp, code }, session);
|
|
1262
|
+
await app._refreshUser(session);
|
|
1263
|
+
return registrationResult;
|
|
1077
1264
|
}
|
|
1078
1265
|
};
|
|
1079
1266
|
}
|
|
@@ -1842,10 +2029,22 @@ ${url}`);
|
|
|
1842
2029
|
});
|
|
1843
2030
|
}
|
|
1844
2031
|
async signOut(options) {
|
|
1845
|
-
const user = await this.getUser();
|
|
2032
|
+
const user = await this.getUser({ tokenStore: options?.tokenStore ?? void 0 });
|
|
2033
|
+
if (user) {
|
|
2034
|
+
await user.signOut({ redirectUrl: options?.redirectUrl });
|
|
2035
|
+
}
|
|
2036
|
+
}
|
|
2037
|
+
async getAuthHeaders(options) {
|
|
2038
|
+
return {
|
|
2039
|
+
"x-stack-auth": JSON.stringify(await this.getAuthJson(options))
|
|
2040
|
+
};
|
|
2041
|
+
}
|
|
2042
|
+
async getAuthJson(options) {
|
|
2043
|
+
const user = await this.getUser({ tokenStore: options?.tokenStore ?? void 0 });
|
|
1846
2044
|
if (user) {
|
|
1847
|
-
await user.
|
|
2045
|
+
return await user.getAuthJson();
|
|
1848
2046
|
}
|
|
2047
|
+
return { accessToken: null, refreshToken: null };
|
|
1849
2048
|
}
|
|
1850
2049
|
async getProject() {
|
|
1851
2050
|
const crud = import_results.Result.orThrow(await this._currentProjectCache.getOrWait([], "write-only"));
|