@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.
- package/CHANGELOG.md +30 -0
- 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 +1 -1
- 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 +40 -1
- 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 +97 -79
- package/dist/index.d.ts +97 -79
- 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 +1 -1
- 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 +2 -2
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { WebAuthnError, startAuthentication, startRegistration } from "@simplewebauthn/browser";
|
|
3
3
|
import { KnownErrors, StackClientInterface } from "@stackframe/stack-shared";
|
|
4
4
|
import { InternalSession } from "@stackframe/stack-shared/dist/sessions";
|
|
5
|
+
import { encodeBase32 } from "@stackframe/stack-shared/dist/utils/bytes";
|
|
5
6
|
import { isBrowserLike } from "@stackframe/stack-shared/dist/utils/env";
|
|
6
7
|
import { StackAssertionError, captureError, throwErr } from "@stackframe/stack-shared/dist/utils/errors";
|
|
7
8
|
import { DependenciesMap } from "@stackframe/stack-shared/dist/utils/maps";
|
|
@@ -15,7 +16,7 @@ import { generateUuid } from "@stackframe/stack-shared/dist/utils/uuids";
|
|
|
15
16
|
import * as cookie from "cookie";
|
|
16
17
|
import { constructRedirectUrl } from "../../../../utils/url.js";
|
|
17
18
|
import { addNewOAuthProviderOrScope, callOAuthCallback, signInWithOAuth } from "../../../auth.js";
|
|
18
|
-
import { createCookieHelper, createPlaceholderCookieHelper, deleteCookieClient,
|
|
19
|
+
import { createCookieHelper, createPlaceholderCookieHelper, deleteCookieClient, isSecure as isSecureCookieContext, setOrDeleteCookie, setOrDeleteCookieClient } from "../../../cookie.js";
|
|
19
20
|
import { apiKeyCreationOptionsToCrud } from "../../api-keys/index.js";
|
|
20
21
|
import { stackAppInternalsSymbol } from "../../common.js";
|
|
21
22
|
import { contactChannelCreateOptionsToCrud, contactChannelUpdateOptionsToCrud } from "../../contact-channels/index.js";
|
|
@@ -23,6 +24,7 @@ import { adminProjectCreateOptionsToCrud } from "../../projects/index.js";
|
|
|
23
24
|
import { teamCreateOptionsToCrud, teamUpdateOptionsToCrud } from "../../teams/index.js";
|
|
24
25
|
import { attachUserDestructureGuard, userUpdateOptionsToCrud } from "../../users/index.js";
|
|
25
26
|
import { clientVersion, createCache, createCacheBySession, createEmptyTokenStore, getBaseUrl, getDefaultExtraRequestHeaders, getDefaultProjectId, getDefaultPublishableClientKey, getUrls, resolveConstructorOptions } from "./common.js";
|
|
27
|
+
import { parseJson } from "@stackframe/stack-shared/dist/utils/json";
|
|
26
28
|
var isReactServer = false;
|
|
27
29
|
var process = globalThis.process ?? { env: {} };
|
|
28
30
|
var allClientApps = /* @__PURE__ */ new Map();
|
|
@@ -171,11 +173,15 @@ var __StackClientAppImplIncomplete = class __StackClientAppImplIncomplete {
|
|
|
171
173
|
this._convexPartialUserCache = createCache(
|
|
172
174
|
async ([ctx]) => await this._getPartialUserFromConvex(ctx)
|
|
173
175
|
);
|
|
176
|
+
this._trustedParentDomainCache = createCache(
|
|
177
|
+
async ([domain]) => await this._getTrustedParentDomain(domain)
|
|
178
|
+
);
|
|
174
179
|
this._anonymousSignUpInProgress = null;
|
|
175
180
|
this._memoryTokenStore = createEmptyTokenStore();
|
|
176
181
|
this._nextServerCookiesTokenStores = /* @__PURE__ */ new WeakMap();
|
|
177
182
|
this._requestTokenStores = /* @__PURE__ */ new WeakMap();
|
|
178
183
|
this._storedBrowserCookieTokenStore = null;
|
|
184
|
+
this._mostRecentQueuedCookieRefreshIndex = 0;
|
|
179
185
|
/**
|
|
180
186
|
* A map from token stores and session keys to sessions.
|
|
181
187
|
*
|
|
@@ -191,13 +197,17 @@ var __StackClientAppImplIncomplete = class __StackClientAppImplIncomplete {
|
|
|
191
197
|
}
|
|
192
198
|
this._options = resolvedOptions;
|
|
193
199
|
this._extraOptions = extraOptions;
|
|
200
|
+
const projectId = resolvedOptions.projectId ?? getDefaultProjectId();
|
|
201
|
+
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)) {
|
|
202
|
+
throw new Error(`Invalid project ID: ${projectId}. Project IDs must be UUIDs. Please check your environment variables and/or your StackApp.`);
|
|
203
|
+
}
|
|
194
204
|
if (extraOptions && extraOptions.interface) {
|
|
195
205
|
this._interface = extraOptions.interface;
|
|
196
206
|
} else {
|
|
197
207
|
this._interface = new StackClientInterface({
|
|
198
208
|
getBaseUrl: () => getBaseUrl(resolvedOptions.baseUrl),
|
|
199
209
|
extraRequestHeaders: resolvedOptions.extraRequestHeaders ?? getDefaultExtraRequestHeaders(),
|
|
200
|
-
projectId
|
|
210
|
+
projectId,
|
|
201
211
|
clientVersion,
|
|
202
212
|
publishableClientKey: resolvedOptions.publishableClientKey ?? getDefaultPublishableClientKey(),
|
|
203
213
|
prepareRequest: async () => {
|
|
@@ -291,13 +301,90 @@ var __StackClientAppImplIncomplete = class __StackClientAppImplIncomplete {
|
|
|
291
301
|
runAsynchronously(this._checkFeatureSupport(name, options));
|
|
292
302
|
throw new StackAssertionError(`${name} is not currently supported. Please reach out to Stack support for more information.`);
|
|
293
303
|
}
|
|
304
|
+
get _legacyRefreshTokenCookieName() {
|
|
305
|
+
return `stack-refresh-${this.projectId}`;
|
|
306
|
+
}
|
|
294
307
|
get _refreshTokenCookieName() {
|
|
295
308
|
return `stack-refresh-${this.projectId}`;
|
|
296
309
|
}
|
|
310
|
+
_getRefreshTokenDefaultCookieNameForSecure(secure) {
|
|
311
|
+
return `${secure ? "__Host-" : ""}${this._refreshTokenCookieName}--default`;
|
|
312
|
+
}
|
|
313
|
+
_getCustomRefreshCookieName(domain) {
|
|
314
|
+
const encoded = encodeBase32(new TextEncoder().encode(domain.toLowerCase()));
|
|
315
|
+
return `${this._refreshTokenCookieName}--custom-${encoded}`;
|
|
316
|
+
}
|
|
317
|
+
_formatRefreshCookieValue(refreshToken, updatedAt) {
|
|
318
|
+
return JSON.stringify({
|
|
319
|
+
refresh_token: refreshToken,
|
|
320
|
+
updated_at_millis: updatedAt
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
_formatAccessCookieValue(refreshToken, accessToken) {
|
|
324
|
+
return refreshToken && accessToken ? JSON.stringify([refreshToken, accessToken]) : null;
|
|
325
|
+
}
|
|
326
|
+
_parseStructuredRefreshCookie(value) {
|
|
327
|
+
if (!value) {
|
|
328
|
+
return null;
|
|
329
|
+
}
|
|
330
|
+
const parsed = parseJson(value);
|
|
331
|
+
if (parsed.status !== "ok" || typeof parsed.data !== "object" || parsed.data === null) {
|
|
332
|
+
console.warn("Failed to parse structured refresh cookie");
|
|
333
|
+
return null;
|
|
334
|
+
}
|
|
335
|
+
const data = parsed.data;
|
|
336
|
+
const refreshToken = "refresh_token" in data && typeof data.refresh_token === "string" ? data.refresh_token : null;
|
|
337
|
+
const updatedAt = "updated_at_millis" in data && typeof data.updated_at_millis === "number" ? data.updated_at_millis : null;
|
|
338
|
+
if (!refreshToken) {
|
|
339
|
+
console.warn("Refresh token not found in structured refresh cookie");
|
|
340
|
+
return null;
|
|
341
|
+
}
|
|
342
|
+
return {
|
|
343
|
+
refreshToken,
|
|
344
|
+
updatedAt
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
_extractRefreshTokenFromCookieMap(cookies) {
|
|
348
|
+
const { legacyNames, structuredPrefixes } = this._getRefreshTokenCookieNamePatterns();
|
|
349
|
+
for (const name of legacyNames) {
|
|
350
|
+
const value = cookies[name];
|
|
351
|
+
if (value) {
|
|
352
|
+
return { refreshToken: value, updatedAt: null };
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
let selected = null;
|
|
356
|
+
for (const [name, value] of Object.entries(cookies)) {
|
|
357
|
+
if (!structuredPrefixes.some((prefix) => name.startsWith(prefix))) continue;
|
|
358
|
+
const parsed = this._parseStructuredRefreshCookie(value);
|
|
359
|
+
if (!parsed) continue;
|
|
360
|
+
const candidateUpdatedAt = parsed.updatedAt ?? Number.NEGATIVE_INFINITY;
|
|
361
|
+
const selectedUpdatedAt = selected?.updatedAt ?? Number.NEGATIVE_INFINITY;
|
|
362
|
+
if (!selected || candidateUpdatedAt > selectedUpdatedAt) {
|
|
363
|
+
selected = parsed;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
if (!selected) {
|
|
367
|
+
return { refreshToken: null, updatedAt: null };
|
|
368
|
+
}
|
|
369
|
+
return {
|
|
370
|
+
refreshToken: selected.refreshToken,
|
|
371
|
+
updatedAt: selected.updatedAt ?? null
|
|
372
|
+
};
|
|
373
|
+
}
|
|
297
374
|
_getTokensFromCookies(cookies) {
|
|
298
|
-
const refreshToken = cookies
|
|
299
|
-
const
|
|
300
|
-
|
|
375
|
+
const { refreshToken } = this._extractRefreshTokenFromCookieMap(cookies);
|
|
376
|
+
const accessTokenCookie = cookies[this._accessTokenCookieName] ?? null;
|
|
377
|
+
let accessToken = null;
|
|
378
|
+
if (accessTokenCookie && accessTokenCookie.startsWith('["')) {
|
|
379
|
+
const parsed = parseJson(accessTokenCookie);
|
|
380
|
+
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") {
|
|
381
|
+
if (parsed.data[0] === refreshToken) {
|
|
382
|
+
accessToken = parsed.data[1];
|
|
383
|
+
}
|
|
384
|
+
} else {
|
|
385
|
+
console.warn("Access token cookie has invalid format");
|
|
386
|
+
}
|
|
387
|
+
}
|
|
301
388
|
return {
|
|
302
389
|
refreshToken,
|
|
303
390
|
accessToken
|
|
@@ -306,17 +393,98 @@ var __StackClientAppImplIncomplete = class __StackClientAppImplIncomplete {
|
|
|
306
393
|
get _accessTokenCookieName() {
|
|
307
394
|
return `stack-access`;
|
|
308
395
|
}
|
|
396
|
+
_getAllBrowserCookies() {
|
|
397
|
+
if (!isBrowserLike()) {
|
|
398
|
+
throw new StackAssertionError("Cannot get browser cookies on the server!");
|
|
399
|
+
}
|
|
400
|
+
return cookie.parse(document.cookie || "");
|
|
401
|
+
}
|
|
402
|
+
_getRefreshTokenCookieNamePatterns() {
|
|
403
|
+
return {
|
|
404
|
+
legacyNames: [this._legacyRefreshTokenCookieName, "stack-refresh"],
|
|
405
|
+
structuredPrefixes: [
|
|
406
|
+
`${this._refreshTokenCookieName}--`,
|
|
407
|
+
`__Host-${this._refreshTokenCookieName}--`
|
|
408
|
+
]
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
_collectRefreshTokenCookieNames(cookies) {
|
|
412
|
+
const { legacyNames, structuredPrefixes } = this._getRefreshTokenCookieNamePatterns();
|
|
413
|
+
const names = /* @__PURE__ */ new Set();
|
|
414
|
+
for (const name of legacyNames) {
|
|
415
|
+
if (cookies[name]) {
|
|
416
|
+
names.add(name);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
for (const name of Object.keys(cookies)) {
|
|
420
|
+
if (structuredPrefixes.some((prefix) => name.startsWith(prefix))) {
|
|
421
|
+
names.add(name);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
return names;
|
|
425
|
+
}
|
|
426
|
+
_prepareRefreshCookieUpdate(existingCookies, refreshToken, accessToken, defaultCookieName) {
|
|
427
|
+
const cookieNames = this._collectRefreshTokenCookieNames(existingCookies);
|
|
428
|
+
cookieNames.delete(defaultCookieName);
|
|
429
|
+
const updatedAt = refreshToken ? Date.now() : null;
|
|
430
|
+
const refreshCookieValue = refreshToken && updatedAt !== null ? this._formatRefreshCookieValue(refreshToken, updatedAt) : null;
|
|
431
|
+
const accessTokenPayload = this._formatAccessCookieValue(refreshToken, accessToken);
|
|
432
|
+
return {
|
|
433
|
+
updatedAt,
|
|
434
|
+
refreshCookieValue,
|
|
435
|
+
accessTokenPayload,
|
|
436
|
+
cookieNamesToDelete: [...cookieNames]
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
_queueCustomRefreshCookieUpdate(refreshToken, updatedAt, context) {
|
|
440
|
+
runAsynchronously(async () => {
|
|
441
|
+
this._mostRecentQueuedCookieRefreshIndex++;
|
|
442
|
+
const updateIndex = this._mostRecentQueuedCookieRefreshIndex;
|
|
443
|
+
let hostname;
|
|
444
|
+
if (isBrowserLike()) {
|
|
445
|
+
hostname = window.location.hostname;
|
|
446
|
+
}
|
|
447
|
+
if (!hostname) {
|
|
448
|
+
console.warn("No hostname found when queueing custom refresh cookie update");
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
const domain = await this._trustedParentDomainCache.getOrWait([hostname], "read-write");
|
|
452
|
+
const setCookie = async (targetDomain, value2) => {
|
|
453
|
+
const name = this._getCustomRefreshCookieName(targetDomain);
|
|
454
|
+
const options = { maxAge: 60 * 60 * 24 * 365, domain: targetDomain, noOpIfServerComponent: true };
|
|
455
|
+
if (context === "browser") {
|
|
456
|
+
setOrDeleteCookieClient(name, value2, options);
|
|
457
|
+
} else {
|
|
458
|
+
await setOrDeleteCookie(name, value2, options);
|
|
459
|
+
}
|
|
460
|
+
};
|
|
461
|
+
if (domain.status === "error" || !domain.data || updateIndex !== this._mostRecentQueuedCookieRefreshIndex) {
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
464
|
+
const value = refreshToken && updatedAt ? this._formatRefreshCookieValue(refreshToken, updatedAt) : null;
|
|
465
|
+
await setCookie(domain.data, value);
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
async _getTrustedParentDomain(currentDomain) {
|
|
469
|
+
const project = Result.orThrow(await this._interface.getClientProject());
|
|
470
|
+
const domains = project.config.domains.map((d) => d.domain.trim().replace(/^https?:\/\//, "").split("/")[0]?.toLowerCase());
|
|
471
|
+
const trustedWildcards = domains.filter((d) => d.startsWith("**."));
|
|
472
|
+
const parts = currentDomain.split(".");
|
|
473
|
+
for (let i = parts.length - 2; i >= 0; i--) {
|
|
474
|
+
const parentDomain = parts.slice(i).join(".");
|
|
475
|
+
if (domains.includes(parentDomain) && trustedWildcards.includes("**." + parentDomain)) {
|
|
476
|
+
return parentDomain;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
return null;
|
|
480
|
+
}
|
|
309
481
|
_getBrowserCookieTokenStore() {
|
|
310
482
|
if (!isBrowserLike()) {
|
|
311
483
|
throw new Error("Cannot use cookie token store on the server!");
|
|
312
484
|
}
|
|
313
485
|
if (this._storedBrowserCookieTokenStore === null) {
|
|
314
486
|
const getCurrentValue = (old) => {
|
|
315
|
-
const tokens = this._getTokensFromCookies(
|
|
316
|
-
refreshTokenCookie: getCookieClient(this._refreshTokenCookieName) ?? getCookieClient("stack-refresh"),
|
|
317
|
-
// keep old cookie name for backwards-compatibility
|
|
318
|
-
accessTokenCookie: getCookieClient(this._accessTokenCookieName)
|
|
319
|
-
});
|
|
487
|
+
const tokens = this._getTokensFromCookies(this._getAllBrowserCookies());
|
|
320
488
|
return {
|
|
321
489
|
refreshToken: tokens.refreshToken,
|
|
322
490
|
accessToken: tokens.accessToken ?? (old?.refreshToken === tokens.refreshToken ? old.accessToken : null)
|
|
@@ -335,9 +503,19 @@ var __StackClientAppImplIncomplete = class __StackClientAppImplIncomplete {
|
|
|
335
503
|
}, 100);
|
|
336
504
|
this._storedBrowserCookieTokenStore.onChange((value) => {
|
|
337
505
|
try {
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
506
|
+
const refreshToken = value.refreshToken;
|
|
507
|
+
const secure = window.location.protocol === "https:";
|
|
508
|
+
const defaultName = this._getRefreshTokenDefaultCookieNameForSecure(secure);
|
|
509
|
+
const { updatedAt, refreshCookieValue, accessTokenPayload, cookieNamesToDelete } = this._prepareRefreshCookieUpdate(
|
|
510
|
+
this._getAllBrowserCookies(),
|
|
511
|
+
refreshToken,
|
|
512
|
+
value.accessToken ?? null,
|
|
513
|
+
defaultName
|
|
514
|
+
);
|
|
515
|
+
setOrDeleteCookieClient(defaultName, refreshCookieValue, { maxAge: 60 * 60 * 24 * 365, secure });
|
|
516
|
+
setOrDeleteCookieClient(this._accessTokenCookieName, accessTokenPayload, { maxAge: 60 * 60 * 24 });
|
|
517
|
+
cookieNamesToDelete.forEach((name) => deleteCookieClient(name));
|
|
518
|
+
this._queueCustomRefreshCookieUpdate(refreshToken, updatedAt, "browser");
|
|
341
519
|
hasSucceededInWriting = true;
|
|
342
520
|
} catch (e) {
|
|
343
521
|
if (!isBrowserLike()) {
|
|
@@ -360,18 +538,31 @@ var __StackClientAppImplIncomplete = class __StackClientAppImplIncomplete {
|
|
|
360
538
|
if (isBrowserLike()) {
|
|
361
539
|
return this._getBrowserCookieTokenStore();
|
|
362
540
|
} else {
|
|
363
|
-
const tokens = this._getTokensFromCookies(
|
|
364
|
-
refreshTokenCookie: cookieHelper.get(this._refreshTokenCookieName) ?? cookieHelper.get("stack-refresh"),
|
|
365
|
-
// keep old cookie name for backwards-compatibility
|
|
366
|
-
accessTokenCookie: cookieHelper.get(this._accessTokenCookieName)
|
|
367
|
-
});
|
|
541
|
+
const tokens = this._getTokensFromCookies(cookieHelper.getAll());
|
|
368
542
|
const store = new Store(tokens);
|
|
369
543
|
store.onChange((value) => {
|
|
370
544
|
runAsynchronously(async () => {
|
|
545
|
+
const refreshToken = value.refreshToken;
|
|
546
|
+
const secure = await isSecureCookieContext();
|
|
547
|
+
const defaultName = this._getRefreshTokenDefaultCookieNameForSecure(secure);
|
|
548
|
+
const { updatedAt, refreshCookieValue, accessTokenPayload, cookieNamesToDelete } = this._prepareRefreshCookieUpdate(
|
|
549
|
+
cookieHelper.getAll(),
|
|
550
|
+
refreshToken,
|
|
551
|
+
value.accessToken ?? null,
|
|
552
|
+
defaultName
|
|
553
|
+
);
|
|
371
554
|
await Promise.all([
|
|
372
|
-
setOrDeleteCookie(
|
|
373
|
-
setOrDeleteCookie(this._accessTokenCookieName,
|
|
555
|
+
setOrDeleteCookie(defaultName, refreshCookieValue, { maxAge: 60 * 60 * 24 * 365, noOpIfServerComponent: true }),
|
|
556
|
+
setOrDeleteCookie(this._accessTokenCookieName, accessTokenPayload, { maxAge: 60 * 60 * 24, noOpIfServerComponent: true })
|
|
374
557
|
]);
|
|
558
|
+
if (cookieNamesToDelete.length > 0) {
|
|
559
|
+
await Promise.all(
|
|
560
|
+
cookieNamesToDelete.map(
|
|
561
|
+
(name) => setOrDeleteCookie(name, null, { noOpIfServerComponent: true })
|
|
562
|
+
)
|
|
563
|
+
);
|
|
564
|
+
}
|
|
565
|
+
this._queueCustomRefreshCookieUpdate(refreshToken, updatedAt, "server");
|
|
375
566
|
});
|
|
376
567
|
});
|
|
377
568
|
return store;
|
|
@@ -402,11 +593,7 @@ var __StackClientAppImplIncomplete = class __StackClientAppImplIncomplete {
|
|
|
402
593
|
}
|
|
403
594
|
const cookieHeader = tokenStoreInit.headers.get("cookie");
|
|
404
595
|
const parsed = cookie.parse(cookieHeader || "");
|
|
405
|
-
const res = new Store(
|
|
406
|
-
refreshToken: parsed[this._refreshTokenCookieName] || parsed["stack-refresh"] || null,
|
|
407
|
-
// keep old cookie name for backwards-compatibility
|
|
408
|
-
accessToken: parsed[this._accessTokenCookieName] || null
|
|
409
|
-
});
|
|
596
|
+
const res = new Store(this._getTokensFromCookies(parsed));
|
|
410
597
|
this._requestTokenStores.set(tokenStoreInit, res);
|
|
411
598
|
return res;
|
|
412
599
|
} else if ("accessToken" in tokenStoreInit || "refreshToken" in tokenStoreInit) {
|
|
@@ -723,35 +910,6 @@ var __StackClientAppImplIncomplete = class __StackClientAppImplIncomplete {
|
|
|
723
910
|
const tokens = await this.currentSession.getTokens();
|
|
724
911
|
return tokens;
|
|
725
912
|
},
|
|
726
|
-
async registerPasskey(options) {
|
|
727
|
-
const hostname = (await app._getCurrentUrl())?.hostname;
|
|
728
|
-
if (!hostname) {
|
|
729
|
-
throw new StackAssertionError("hostname must be provided if the Stack App does not have a redirect method");
|
|
730
|
-
}
|
|
731
|
-
const initiationResult = await app._interface.initiatePasskeyRegistration({}, session);
|
|
732
|
-
if (initiationResult.status !== "ok") {
|
|
733
|
-
return Result.error(new KnownErrors.PasskeyRegistrationFailed("Failed to get initiation options for passkey registration"));
|
|
734
|
-
}
|
|
735
|
-
const { options_json, code } = initiationResult.data;
|
|
736
|
-
if (options_json.rp.id !== "THIS_VALUE_WILL_BE_REPLACED.example.com") {
|
|
737
|
-
throw new StackAssertionError(`Expected returned RP ID from server to equal sentinel, but found ${options_json.rp.id}`);
|
|
738
|
-
}
|
|
739
|
-
options_json.rp.id = hostname;
|
|
740
|
-
let attResp;
|
|
741
|
-
try {
|
|
742
|
-
attResp = await startRegistration({ optionsJSON: options_json });
|
|
743
|
-
} catch (error) {
|
|
744
|
-
if (error instanceof WebAuthnError) {
|
|
745
|
-
return Result.error(new KnownErrors.PasskeyWebAuthnError(error.message, error.name));
|
|
746
|
-
} else {
|
|
747
|
-
captureError("passkey-registration-failed", error);
|
|
748
|
-
return Result.error(new KnownErrors.PasskeyRegistrationFailed("Failed to start passkey registration due to unknown error"));
|
|
749
|
-
}
|
|
750
|
-
}
|
|
751
|
-
const registrationResult = await app._interface.registerPasskey({ credential: attResp, code }, session);
|
|
752
|
-
await app._refreshUser(session);
|
|
753
|
-
return registrationResult;
|
|
754
|
-
},
|
|
755
913
|
signOut(options) {
|
|
756
914
|
return app._signOut(session, options);
|
|
757
915
|
}
|
|
@@ -936,6 +1094,35 @@ var __StackClientAppImplIncomplete = class __StackClientAppImplIncomplete {
|
|
|
936
1094
|
async getOAuthProvider(id) {
|
|
937
1095
|
const providers = await this.listOAuthProviders();
|
|
938
1096
|
return providers.find((p) => p.id === id) ?? null;
|
|
1097
|
+
},
|
|
1098
|
+
async registerPasskey(options) {
|
|
1099
|
+
const hostname = (await app._getCurrentUrl())?.hostname;
|
|
1100
|
+
if (!hostname) {
|
|
1101
|
+
throw new StackAssertionError("hostname must be provided if the Stack App does not have a redirect method");
|
|
1102
|
+
}
|
|
1103
|
+
const initiationResult = await app._interface.initiatePasskeyRegistration({}, session);
|
|
1104
|
+
if (initiationResult.status !== "ok") {
|
|
1105
|
+
return Result.error(new KnownErrors.PasskeyRegistrationFailed("Failed to get initiation options for passkey registration"));
|
|
1106
|
+
}
|
|
1107
|
+
const { options_json, code } = initiationResult.data;
|
|
1108
|
+
if (options_json.rp.id !== "THIS_VALUE_WILL_BE_REPLACED.example.com") {
|
|
1109
|
+
throw new StackAssertionError(`Expected returned RP ID from server to equal sentinel, but found ${options_json.rp.id}`);
|
|
1110
|
+
}
|
|
1111
|
+
options_json.rp.id = hostname;
|
|
1112
|
+
let attResp;
|
|
1113
|
+
try {
|
|
1114
|
+
attResp = await startRegistration({ optionsJSON: options_json });
|
|
1115
|
+
} catch (error) {
|
|
1116
|
+
if (error instanceof WebAuthnError) {
|
|
1117
|
+
return Result.error(new KnownErrors.PasskeyWebAuthnError(error.message, error.name));
|
|
1118
|
+
} else {
|
|
1119
|
+
captureError("passkey-registration-failed", error);
|
|
1120
|
+
return Result.error(new KnownErrors.PasskeyRegistrationFailed("Failed to start passkey registration due to unknown error"));
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
const registrationResult = await app._interface.registerPasskey({ credential: attResp, code }, session);
|
|
1124
|
+
await app._refreshUser(session);
|
|
1125
|
+
return registrationResult;
|
|
939
1126
|
}
|
|
940
1127
|
};
|
|
941
1128
|
}
|
|
@@ -1618,10 +1805,22 @@ ${url}`);
|
|
|
1618
1805
|
});
|
|
1619
1806
|
}
|
|
1620
1807
|
async signOut(options) {
|
|
1621
|
-
const user = await this.getUser();
|
|
1808
|
+
const user = await this.getUser({ tokenStore: options?.tokenStore ?? void 0 });
|
|
1809
|
+
if (user) {
|
|
1810
|
+
await user.signOut({ redirectUrl: options?.redirectUrl });
|
|
1811
|
+
}
|
|
1812
|
+
}
|
|
1813
|
+
async getAuthHeaders(options) {
|
|
1814
|
+
return {
|
|
1815
|
+
"x-stack-auth": JSON.stringify(await this.getAuthJson(options))
|
|
1816
|
+
};
|
|
1817
|
+
}
|
|
1818
|
+
async getAuthJson(options) {
|
|
1819
|
+
const user = await this.getUser({ tokenStore: options?.tokenStore ?? void 0 });
|
|
1622
1820
|
if (user) {
|
|
1623
|
-
await user.
|
|
1821
|
+
return await user.getAuthJson();
|
|
1624
1822
|
}
|
|
1823
|
+
return { accessToken: null, refreshToken: null };
|
|
1625
1824
|
}
|
|
1626
1825
|
async getProject() {
|
|
1627
1826
|
const crud = Result.orThrow(await this._currentProjectCache.getOrWait([], "write-only"));
|