@stackframe/js 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.
Files changed (32) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/dist/esm/lib/cookie.js +36 -7
  3. package/dist/esm/lib/cookie.js.map +1 -1
  4. package/dist/esm/lib/stack-app/apps/implementations/admin-app-impl.js +17 -0
  5. package/dist/esm/lib/stack-app/apps/implementations/admin-app-impl.js.map +1 -1
  6. package/dist/esm/lib/stack-app/apps/implementations/client-app-impl.js +255 -56
  7. package/dist/esm/lib/stack-app/apps/implementations/client-app-impl.js.map +1 -1
  8. package/dist/esm/lib/stack-app/apps/implementations/common.js +1 -1
  9. package/dist/esm/lib/stack-app/apps/implementations/common.js.map +1 -1
  10. package/dist/esm/lib/stack-app/apps/implementations/server-app-impl.js +40 -1
  11. package/dist/esm/lib/stack-app/apps/implementations/server-app-impl.js.map +1 -1
  12. package/dist/esm/lib/stack-app/apps/interfaces/admin-app.js.map +1 -1
  13. package/dist/esm/lib/stack-app/apps/interfaces/client-app.js.map +1 -1
  14. package/dist/esm/lib/stack-app/common.js.map +1 -1
  15. package/dist/esm/lib/stack-app/users/index.js.map +1 -1
  16. package/dist/index.d.mts +97 -79
  17. package/dist/index.d.ts +97 -79
  18. package/dist/lib/cookie.js +38 -7
  19. package/dist/lib/cookie.js.map +1 -1
  20. package/dist/lib/stack-app/apps/implementations/admin-app-impl.js +17 -0
  21. package/dist/lib/stack-app/apps/implementations/admin-app-impl.js.map +1 -1
  22. package/dist/lib/stack-app/apps/implementations/client-app-impl.js +254 -55
  23. package/dist/lib/stack-app/apps/implementations/client-app-impl.js.map +1 -1
  24. package/dist/lib/stack-app/apps/implementations/common.js +1 -1
  25. package/dist/lib/stack-app/apps/implementations/common.js.map +1 -1
  26. package/dist/lib/stack-app/apps/implementations/server-app-impl.js +49 -0
  27. package/dist/lib/stack-app/apps/implementations/server-app-impl.js.map +1 -1
  28. package/dist/lib/stack-app/apps/interfaces/admin-app.js.map +1 -1
  29. package/dist/lib/stack-app/apps/interfaces/client-app.js.map +1 -1
  30. package/dist/lib/stack-app/common.js.map +1 -1
  31. package/dist/lib/stack-app/users/index.js.map +1 -1
  32. package/package.json +2 -2
@@ -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");
@@ -57,6 +58,7 @@ var import_projects = require("../../projects/index.js");
57
58
  var import_teams = require("../../teams/index.js");
58
59
  var import_users = require("../../users/index.js");
59
60
  var import_common2 = require("./common.js");
61
+ var import_json = require("@stackframe/stack-shared/dist/utils/json");
60
62
  var isReactServer = false;
61
63
  var process = globalThis.process ?? { env: {} };
62
64
  var allClientApps = /* @__PURE__ */ new Map();
@@ -205,11 +207,15 @@ var __StackClientAppImplIncomplete = class __StackClientAppImplIncomplete {
205
207
  this._convexPartialUserCache = (0, import_common2.createCache)(
206
208
  async ([ctx]) => await this._getPartialUserFromConvex(ctx)
207
209
  );
210
+ this._trustedParentDomainCache = (0, import_common2.createCache)(
211
+ async ([domain]) => await this._getTrustedParentDomain(domain)
212
+ );
208
213
  this._anonymousSignUpInProgress = null;
209
214
  this._memoryTokenStore = (0, import_common2.createEmptyTokenStore)();
210
215
  this._nextServerCookiesTokenStores = /* @__PURE__ */ new WeakMap();
211
216
  this._requestTokenStores = /* @__PURE__ */ new WeakMap();
212
217
  this._storedBrowserCookieTokenStore = null;
218
+ this._mostRecentQueuedCookieRefreshIndex = 0;
213
219
  /**
214
220
  * A map from token stores and session keys to sessions.
215
221
  *
@@ -225,13 +231,17 @@ var __StackClientAppImplIncomplete = class __StackClientAppImplIncomplete {
225
231
  }
226
232
  this._options = resolvedOptions;
227
233
  this._extraOptions = extraOptions;
234
+ const projectId = resolvedOptions.projectId ?? (0, import_common2.getDefaultProjectId)();
235
+ 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)) {
236
+ throw new Error(`Invalid project ID: ${projectId}. Project IDs must be UUIDs. Please check your environment variables and/or your StackApp.`);
237
+ }
228
238
  if (extraOptions && extraOptions.interface) {
229
239
  this._interface = extraOptions.interface;
230
240
  } else {
231
241
  this._interface = new import_stack_shared.StackClientInterface({
232
242
  getBaseUrl: () => (0, import_common2.getBaseUrl)(resolvedOptions.baseUrl),
233
243
  extraRequestHeaders: resolvedOptions.extraRequestHeaders ?? (0, import_common2.getDefaultExtraRequestHeaders)(),
234
- projectId: resolvedOptions.projectId ?? (0, import_common2.getDefaultProjectId)(),
244
+ projectId,
235
245
  clientVersion: import_common2.clientVersion,
236
246
  publishableClientKey: resolvedOptions.publishableClientKey ?? (0, import_common2.getDefaultPublishableClientKey)(),
237
247
  prepareRequest: async () => {
@@ -325,13 +335,90 @@ var __StackClientAppImplIncomplete = class __StackClientAppImplIncomplete {
325
335
  (0, import_promises.runAsynchronously)(this._checkFeatureSupport(name, options));
326
336
  throw new import_errors.StackAssertionError(`${name} is not currently supported. Please reach out to Stack support for more information.`);
327
337
  }
338
+ get _legacyRefreshTokenCookieName() {
339
+ return `stack-refresh-${this.projectId}`;
340
+ }
328
341
  get _refreshTokenCookieName() {
329
342
  return `stack-refresh-${this.projectId}`;
330
343
  }
344
+ _getRefreshTokenDefaultCookieNameForSecure(secure) {
345
+ return `${secure ? "__Host-" : ""}${this._refreshTokenCookieName}--default`;
346
+ }
347
+ _getCustomRefreshCookieName(domain) {
348
+ const encoded = (0, import_bytes.encodeBase32)(new TextEncoder().encode(domain.toLowerCase()));
349
+ return `${this._refreshTokenCookieName}--custom-${encoded}`;
350
+ }
351
+ _formatRefreshCookieValue(refreshToken, updatedAt) {
352
+ return JSON.stringify({
353
+ refresh_token: refreshToken,
354
+ updated_at_millis: updatedAt
355
+ });
356
+ }
357
+ _formatAccessCookieValue(refreshToken, accessToken) {
358
+ return refreshToken && accessToken ? JSON.stringify([refreshToken, accessToken]) : null;
359
+ }
360
+ _parseStructuredRefreshCookie(value) {
361
+ if (!value) {
362
+ return null;
363
+ }
364
+ const parsed = (0, import_json.parseJson)(value);
365
+ if (parsed.status !== "ok" || typeof parsed.data !== "object" || parsed.data === null) {
366
+ console.warn("Failed to parse structured refresh cookie");
367
+ return null;
368
+ }
369
+ const data = parsed.data;
370
+ const refreshToken = "refresh_token" in data && typeof data.refresh_token === "string" ? data.refresh_token : null;
371
+ const updatedAt = "updated_at_millis" in data && typeof data.updated_at_millis === "number" ? data.updated_at_millis : null;
372
+ if (!refreshToken) {
373
+ console.warn("Refresh token not found in structured refresh cookie");
374
+ return null;
375
+ }
376
+ return {
377
+ refreshToken,
378
+ updatedAt
379
+ };
380
+ }
381
+ _extractRefreshTokenFromCookieMap(cookies) {
382
+ const { legacyNames, structuredPrefixes } = this._getRefreshTokenCookieNamePatterns();
383
+ for (const name of legacyNames) {
384
+ const value = cookies[name];
385
+ if (value) {
386
+ return { refreshToken: value, updatedAt: null };
387
+ }
388
+ }
389
+ let selected = null;
390
+ for (const [name, value] of Object.entries(cookies)) {
391
+ if (!structuredPrefixes.some((prefix) => name.startsWith(prefix))) continue;
392
+ const parsed = this._parseStructuredRefreshCookie(value);
393
+ if (!parsed) continue;
394
+ const candidateUpdatedAt = parsed.updatedAt ?? Number.NEGATIVE_INFINITY;
395
+ const selectedUpdatedAt = selected?.updatedAt ?? Number.NEGATIVE_INFINITY;
396
+ if (!selected || candidateUpdatedAt > selectedUpdatedAt) {
397
+ selected = parsed;
398
+ }
399
+ }
400
+ if (!selected) {
401
+ return { refreshToken: null, updatedAt: null };
402
+ }
403
+ return {
404
+ refreshToken: selected.refreshToken,
405
+ updatedAt: selected.updatedAt ?? null
406
+ };
407
+ }
331
408
  _getTokensFromCookies(cookies) {
332
- const refreshToken = cookies.refreshTokenCookie;
333
- const accessTokenObject = cookies.accessTokenCookie?.startsWith('["') ? JSON.parse(cookies.accessTokenCookie) : null;
334
- const accessToken = accessTokenObject && refreshToken === accessTokenObject[0] ? accessTokenObject[1] : null;
409
+ const { refreshToken } = this._extractRefreshTokenFromCookieMap(cookies);
410
+ const accessTokenCookie = cookies[this._accessTokenCookieName] ?? null;
411
+ let accessToken = null;
412
+ if (accessTokenCookie && accessTokenCookie.startsWith('["')) {
413
+ const parsed = (0, import_json.parseJson)(accessTokenCookie);
414
+ 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") {
415
+ if (parsed.data[0] === refreshToken) {
416
+ accessToken = parsed.data[1];
417
+ }
418
+ } else {
419
+ console.warn("Access token cookie has invalid format");
420
+ }
421
+ }
335
422
  return {
336
423
  refreshToken,
337
424
  accessToken
@@ -340,17 +427,98 @@ var __StackClientAppImplIncomplete = class __StackClientAppImplIncomplete {
340
427
  get _accessTokenCookieName() {
341
428
  return `stack-access`;
342
429
  }
430
+ _getAllBrowserCookies() {
431
+ if (!(0, import_env.isBrowserLike)()) {
432
+ throw new import_errors.StackAssertionError("Cannot get browser cookies on the server!");
433
+ }
434
+ return cookie.parse(document.cookie || "");
435
+ }
436
+ _getRefreshTokenCookieNamePatterns() {
437
+ return {
438
+ legacyNames: [this._legacyRefreshTokenCookieName, "stack-refresh"],
439
+ structuredPrefixes: [
440
+ `${this._refreshTokenCookieName}--`,
441
+ `__Host-${this._refreshTokenCookieName}--`
442
+ ]
443
+ };
444
+ }
445
+ _collectRefreshTokenCookieNames(cookies) {
446
+ const { legacyNames, structuredPrefixes } = this._getRefreshTokenCookieNamePatterns();
447
+ const names = /* @__PURE__ */ new Set();
448
+ for (const name of legacyNames) {
449
+ if (cookies[name]) {
450
+ names.add(name);
451
+ }
452
+ }
453
+ for (const name of Object.keys(cookies)) {
454
+ if (structuredPrefixes.some((prefix) => name.startsWith(prefix))) {
455
+ names.add(name);
456
+ }
457
+ }
458
+ return names;
459
+ }
460
+ _prepareRefreshCookieUpdate(existingCookies, refreshToken, accessToken, defaultCookieName) {
461
+ const cookieNames = this._collectRefreshTokenCookieNames(existingCookies);
462
+ cookieNames.delete(defaultCookieName);
463
+ const updatedAt = refreshToken ? Date.now() : null;
464
+ const refreshCookieValue = refreshToken && updatedAt !== null ? this._formatRefreshCookieValue(refreshToken, updatedAt) : null;
465
+ const accessTokenPayload = this._formatAccessCookieValue(refreshToken, accessToken);
466
+ return {
467
+ updatedAt,
468
+ refreshCookieValue,
469
+ accessTokenPayload,
470
+ cookieNamesToDelete: [...cookieNames]
471
+ };
472
+ }
473
+ _queueCustomRefreshCookieUpdate(refreshToken, updatedAt, context) {
474
+ (0, import_promises.runAsynchronously)(async () => {
475
+ this._mostRecentQueuedCookieRefreshIndex++;
476
+ const updateIndex = this._mostRecentQueuedCookieRefreshIndex;
477
+ let hostname;
478
+ if ((0, import_env.isBrowserLike)()) {
479
+ hostname = window.location.hostname;
480
+ }
481
+ if (!hostname) {
482
+ console.warn("No hostname found when queueing custom refresh cookie update");
483
+ return;
484
+ }
485
+ const domain = await this._trustedParentDomainCache.getOrWait([hostname], "read-write");
486
+ const setCookie = async (targetDomain, value2) => {
487
+ const name = this._getCustomRefreshCookieName(targetDomain);
488
+ const options = { maxAge: 60 * 60 * 24 * 365, domain: targetDomain, noOpIfServerComponent: true };
489
+ if (context === "browser") {
490
+ (0, import_cookie.setOrDeleteCookieClient)(name, value2, options);
491
+ } else {
492
+ await (0, import_cookie.setOrDeleteCookie)(name, value2, options);
493
+ }
494
+ };
495
+ if (domain.status === "error" || !domain.data || updateIndex !== this._mostRecentQueuedCookieRefreshIndex) {
496
+ return;
497
+ }
498
+ const value = refreshToken && updatedAt ? this._formatRefreshCookieValue(refreshToken, updatedAt) : null;
499
+ await setCookie(domain.data, value);
500
+ });
501
+ }
502
+ async _getTrustedParentDomain(currentDomain) {
503
+ const project = import_results.Result.orThrow(await this._interface.getClientProject());
504
+ const domains = project.config.domains.map((d) => d.domain.trim().replace(/^https?:\/\//, "").split("/")[0]?.toLowerCase());
505
+ const trustedWildcards = domains.filter((d) => d.startsWith("**."));
506
+ const parts = currentDomain.split(".");
507
+ for (let i = parts.length - 2; i >= 0; i--) {
508
+ const parentDomain = parts.slice(i).join(".");
509
+ if (domains.includes(parentDomain) && trustedWildcards.includes("**." + parentDomain)) {
510
+ return parentDomain;
511
+ }
512
+ }
513
+ return null;
514
+ }
343
515
  _getBrowserCookieTokenStore() {
344
516
  if (!(0, import_env.isBrowserLike)()) {
345
517
  throw new Error("Cannot use cookie token store on the server!");
346
518
  }
347
519
  if (this._storedBrowserCookieTokenStore === null) {
348
520
  const getCurrentValue = (old) => {
349
- const tokens = this._getTokensFromCookies({
350
- refreshTokenCookie: (0, import_cookie.getCookieClient)(this._refreshTokenCookieName) ?? (0, import_cookie.getCookieClient)("stack-refresh"),
351
- // keep old cookie name for backwards-compatibility
352
- accessTokenCookie: (0, import_cookie.getCookieClient)(this._accessTokenCookieName)
353
- });
521
+ const tokens = this._getTokensFromCookies(this._getAllBrowserCookies());
354
522
  return {
355
523
  refreshToken: tokens.refreshToken,
356
524
  accessToken: tokens.accessToken ?? (old?.refreshToken === tokens.refreshToken ? old.accessToken : null)
@@ -369,9 +537,19 @@ var __StackClientAppImplIncomplete = class __StackClientAppImplIncomplete {
369
537
  }, 100);
370
538
  this._storedBrowserCookieTokenStore.onChange((value) => {
371
539
  try {
372
- (0, import_cookie.setOrDeleteCookieClient)(this._refreshTokenCookieName, value.refreshToken, { maxAge: 60 * 60 * 24 * 365 });
373
- (0, import_cookie.setOrDeleteCookieClient)(this._accessTokenCookieName, value.accessToken ? JSON.stringify([value.refreshToken, value.accessToken]) : null, { maxAge: 60 * 60 * 24 });
374
- (0, import_cookie.deleteCookieClient)("stack-refresh");
540
+ const refreshToken = value.refreshToken;
541
+ const secure = window.location.protocol === "https:";
542
+ const defaultName = this._getRefreshTokenDefaultCookieNameForSecure(secure);
543
+ const { updatedAt, refreshCookieValue, accessTokenPayload, cookieNamesToDelete } = this._prepareRefreshCookieUpdate(
544
+ this._getAllBrowserCookies(),
545
+ refreshToken,
546
+ value.accessToken ?? null,
547
+ defaultName
548
+ );
549
+ (0, import_cookie.setOrDeleteCookieClient)(defaultName, refreshCookieValue, { maxAge: 60 * 60 * 24 * 365, secure });
550
+ (0, import_cookie.setOrDeleteCookieClient)(this._accessTokenCookieName, accessTokenPayload, { maxAge: 60 * 60 * 24 });
551
+ cookieNamesToDelete.forEach((name) => (0, import_cookie.deleteCookieClient)(name));
552
+ this._queueCustomRefreshCookieUpdate(refreshToken, updatedAt, "browser");
375
553
  hasSucceededInWriting = true;
376
554
  } catch (e) {
377
555
  if (!(0, import_env.isBrowserLike)()) {
@@ -394,18 +572,31 @@ var __StackClientAppImplIncomplete = class __StackClientAppImplIncomplete {
394
572
  if ((0, import_env.isBrowserLike)()) {
395
573
  return this._getBrowserCookieTokenStore();
396
574
  } else {
397
- const tokens = this._getTokensFromCookies({
398
- refreshTokenCookie: cookieHelper.get(this._refreshTokenCookieName) ?? cookieHelper.get("stack-refresh"),
399
- // keep old cookie name for backwards-compatibility
400
- accessTokenCookie: cookieHelper.get(this._accessTokenCookieName)
401
- });
575
+ const tokens = this._getTokensFromCookies(cookieHelper.getAll());
402
576
  const store = new import_stores.Store(tokens);
403
577
  store.onChange((value) => {
404
578
  (0, import_promises.runAsynchronously)(async () => {
579
+ const refreshToken = value.refreshToken;
580
+ const secure = await (0, import_cookie.isSecure)();
581
+ const defaultName = this._getRefreshTokenDefaultCookieNameForSecure(secure);
582
+ const { updatedAt, refreshCookieValue, accessTokenPayload, cookieNamesToDelete } = this._prepareRefreshCookieUpdate(
583
+ cookieHelper.getAll(),
584
+ refreshToken,
585
+ value.accessToken ?? null,
586
+ defaultName
587
+ );
405
588
  await Promise.all([
406
- (0, import_cookie.setOrDeleteCookie)(this._refreshTokenCookieName, value.refreshToken, { maxAge: 60 * 60 * 24 * 365, noOpIfServerComponent: true }),
407
- (0, import_cookie.setOrDeleteCookie)(this._accessTokenCookieName, value.accessToken ? JSON.stringify([value.refreshToken, value.accessToken]) : null, { maxAge: 60 * 60 * 24, noOpIfServerComponent: true })
589
+ (0, import_cookie.setOrDeleteCookie)(defaultName, refreshCookieValue, { maxAge: 60 * 60 * 24 * 365, noOpIfServerComponent: true }),
590
+ (0, import_cookie.setOrDeleteCookie)(this._accessTokenCookieName, accessTokenPayload, { maxAge: 60 * 60 * 24, noOpIfServerComponent: true })
408
591
  ]);
592
+ if (cookieNamesToDelete.length > 0) {
593
+ await Promise.all(
594
+ cookieNamesToDelete.map(
595
+ (name) => (0, import_cookie.setOrDeleteCookie)(name, null, { noOpIfServerComponent: true })
596
+ )
597
+ );
598
+ }
599
+ this._queueCustomRefreshCookieUpdate(refreshToken, updatedAt, "server");
409
600
  });
410
601
  });
411
602
  return store;
@@ -436,11 +627,7 @@ var __StackClientAppImplIncomplete = class __StackClientAppImplIncomplete {
436
627
  }
437
628
  const cookieHeader = tokenStoreInit.headers.get("cookie");
438
629
  const parsed = cookie.parse(cookieHeader || "");
439
- const res = new import_stores.Store({
440
- refreshToken: parsed[this._refreshTokenCookieName] || parsed["stack-refresh"] || null,
441
- // keep old cookie name for backwards-compatibility
442
- accessToken: parsed[this._accessTokenCookieName] || null
443
- });
630
+ const res = new import_stores.Store(this._getTokensFromCookies(parsed));
444
631
  this._requestTokenStores.set(tokenStoreInit, res);
445
632
  return res;
446
633
  } else if ("accessToken" in tokenStoreInit || "refreshToken" in tokenStoreInit) {
@@ -757,35 +944,6 @@ var __StackClientAppImplIncomplete = class __StackClientAppImplIncomplete {
757
944
  const tokens = await this.currentSession.getTokens();
758
945
  return tokens;
759
946
  },
760
- async registerPasskey(options) {
761
- const hostname = (await app._getCurrentUrl())?.hostname;
762
- if (!hostname) {
763
- throw new import_errors.StackAssertionError("hostname must be provided if the Stack App does not have a redirect method");
764
- }
765
- const initiationResult = await app._interface.initiatePasskeyRegistration({}, session);
766
- if (initiationResult.status !== "ok") {
767
- return import_results.Result.error(new import_stack_shared.KnownErrors.PasskeyRegistrationFailed("Failed to get initiation options for passkey registration"));
768
- }
769
- const { options_json, code } = initiationResult.data;
770
- if (options_json.rp.id !== "THIS_VALUE_WILL_BE_REPLACED.example.com") {
771
- throw new import_errors.StackAssertionError(`Expected returned RP ID from server to equal sentinel, but found ${options_json.rp.id}`);
772
- }
773
- options_json.rp.id = hostname;
774
- let attResp;
775
- try {
776
- attResp = await (0, import_browser.startRegistration)({ optionsJSON: options_json });
777
- } catch (error) {
778
- if (error instanceof import_browser.WebAuthnError) {
779
- return import_results.Result.error(new import_stack_shared.KnownErrors.PasskeyWebAuthnError(error.message, error.name));
780
- } else {
781
- (0, import_errors.captureError)("passkey-registration-failed", error);
782
- return import_results.Result.error(new import_stack_shared.KnownErrors.PasskeyRegistrationFailed("Failed to start passkey registration due to unknown error"));
783
- }
784
- }
785
- const registrationResult = await app._interface.registerPasskey({ credential: attResp, code }, session);
786
- await app._refreshUser(session);
787
- return registrationResult;
788
- },
789
947
  signOut(options) {
790
948
  return app._signOut(session, options);
791
949
  }
@@ -970,6 +1128,35 @@ var __StackClientAppImplIncomplete = class __StackClientAppImplIncomplete {
970
1128
  async getOAuthProvider(id) {
971
1129
  const providers = await this.listOAuthProviders();
972
1130
  return providers.find((p) => p.id === id) ?? null;
1131
+ },
1132
+ async registerPasskey(options) {
1133
+ const hostname = (await app._getCurrentUrl())?.hostname;
1134
+ if (!hostname) {
1135
+ throw new import_errors.StackAssertionError("hostname must be provided if the Stack App does not have a redirect method");
1136
+ }
1137
+ const initiationResult = await app._interface.initiatePasskeyRegistration({}, session);
1138
+ if (initiationResult.status !== "ok") {
1139
+ return import_results.Result.error(new import_stack_shared.KnownErrors.PasskeyRegistrationFailed("Failed to get initiation options for passkey registration"));
1140
+ }
1141
+ const { options_json, code } = initiationResult.data;
1142
+ if (options_json.rp.id !== "THIS_VALUE_WILL_BE_REPLACED.example.com") {
1143
+ throw new import_errors.StackAssertionError(`Expected returned RP ID from server to equal sentinel, but found ${options_json.rp.id}`);
1144
+ }
1145
+ options_json.rp.id = hostname;
1146
+ let attResp;
1147
+ try {
1148
+ attResp = await (0, import_browser.startRegistration)({ optionsJSON: options_json });
1149
+ } catch (error) {
1150
+ if (error instanceof import_browser.WebAuthnError) {
1151
+ return import_results.Result.error(new import_stack_shared.KnownErrors.PasskeyWebAuthnError(error.message, error.name));
1152
+ } else {
1153
+ (0, import_errors.captureError)("passkey-registration-failed", error);
1154
+ return import_results.Result.error(new import_stack_shared.KnownErrors.PasskeyRegistrationFailed("Failed to start passkey registration due to unknown error"));
1155
+ }
1156
+ }
1157
+ const registrationResult = await app._interface.registerPasskey({ credential: attResp, code }, session);
1158
+ await app._refreshUser(session);
1159
+ return registrationResult;
973
1160
  }
974
1161
  };
975
1162
  }
@@ -1652,10 +1839,22 @@ ${url}`);
1652
1839
  });
1653
1840
  }
1654
1841
  async signOut(options) {
1655
- const user = await this.getUser();
1842
+ const user = await this.getUser({ tokenStore: options?.tokenStore ?? void 0 });
1843
+ if (user) {
1844
+ await user.signOut({ redirectUrl: options?.redirectUrl });
1845
+ }
1846
+ }
1847
+ async getAuthHeaders(options) {
1848
+ return {
1849
+ "x-stack-auth": JSON.stringify(await this.getAuthJson(options))
1850
+ };
1851
+ }
1852
+ async getAuthJson(options) {
1853
+ const user = await this.getUser({ tokenStore: options?.tokenStore ?? void 0 });
1656
1854
  if (user) {
1657
- await user.signOut(options);
1855
+ return await user.getAuthJson();
1658
1856
  }
1857
+ return { accessToken: null, refreshToken: null };
1659
1858
  }
1660
1859
  async getProject() {
1661
1860
  const crud = import_results.Result.orThrow(await this._currentProjectCache.getOrWait([], "write-only"));