@hexclave/next 1.0.71 → 1.0.72
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/dist/{admin-app-impl-CBilzmOK.d.ts → admin-app-impl-DLqm4Zqy.d.ts} +2 -2
- package/dist/{admin-app-impl-CBilzmOK.d.ts.map → admin-app-impl-DLqm4Zqy.d.ts.map} +1 -1
- package/dist/esm/generated/quetzal-translations.d.ts +2 -2
- package/dist/esm/lib/hexclave-app/apps/implementations/admin-app-impl.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.cross-domain.test.js +3 -3
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.cross-domain.test.js.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.js +3 -4
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.js.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/common.js +2 -2
- package/dist/esm/lib/hexclave-app/apps/implementations/server-app-impl.d.ts +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/server-app-impl.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/server-app-impl.js +2 -2
- package/dist/esm/lib/hexclave-app/apps/implementations/server-app-impl.js.map +1 -1
- package/dist/esm/lib/hexclave-app/passkey-rp-id.d.ts +12 -0
- package/dist/esm/lib/hexclave-app/passkey-rp-id.d.ts.map +1 -0
- package/dist/esm/lib/hexclave-app/passkey-rp-id.js +15 -0
- package/dist/esm/lib/hexclave-app/passkey-rp-id.js.map +1 -0
- package/dist/esm/lib/hexclave-app/passkey-rp-id.test.d.ts +1 -0
- package/dist/esm/lib/hexclave-app/passkey-rp-id.test.js +26 -0
- package/dist/esm/lib/hexclave-app/passkey-rp-id.test.js.map +1 -0
- package/dist/generated/quetzal-translations.d.ts +1 -1
- package/dist/{hexclave-provider-DzhHCfqq.d.ts → hexclave-provider-B_KtSfuf.d.ts} +2 -2
- package/dist/{hexclave-provider-DzhHCfqq.d.ts.map → hexclave-provider-B_KtSfuf.d.ts.map} +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/lib/hexclave-app/apps/implementations/admin-app-impl.d.ts +1 -1
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.cross-domain.test.js +3 -3
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.cross-domain.test.js.map +1 -1
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.d.ts +1 -1
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.js +3 -4
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.js.map +1 -1
- package/dist/lib/hexclave-app/apps/implementations/common.js +2 -2
- package/dist/lib/hexclave-app/apps/implementations/index.d.ts +1 -1
- package/dist/lib/hexclave-app/apps/implementations/server-app-impl.d.ts +1 -1
- package/dist/lib/hexclave-app/apps/implementations/server-app-impl.js +2 -2
- package/dist/lib/hexclave-app/apps/implementations/server-app-impl.js.map +1 -1
- package/dist/lib/hexclave-app/passkey-rp-id.d.ts +12 -0
- package/dist/lib/hexclave-app/passkey-rp-id.d.ts.map +1 -0
- package/dist/lib/hexclave-app/passkey-rp-id.js +17 -0
- package/dist/lib/hexclave-app/passkey-rp-id.js.map +1 -0
- package/dist/lib/hexclave-app/passkey-rp-id.test.d.ts +1 -0
- package/dist/lib/hexclave-app/passkey-rp-id.test.js +25 -0
- package/dist/lib/hexclave-app/passkey-rp-id.test.js.map +1 -0
- package/dist/providers/hexclave-provider.d.ts +1 -1
- package/dist/providers/translation-provider.d.ts +1 -1
- package/dist/{quetzal-translations-BT1HrUsA.d.ts → quetzal-translations-XeH3zQ9l.d.ts} +3 -3
- package/dist/{quetzal-translations-BT1HrUsA.d.ts.map → quetzal-translations-XeH3zQ9l.d.ts.map} +1 -1
- package/dist/{translation-provider-C1wy92m_.d.ts → translation-provider-WOaZXqAz.d.ts} +2 -2
- package/dist/{translation-provider-C1wy92m_.d.ts.map → translation-provider-WOaZXqAz.d.ts.map} +1 -1
- package/package.json +4 -4
- package/src/lib/hexclave-app/apps/implementations/client-app-impl.cross-domain.test.ts +3 -3
- package/src/lib/hexclave-app/apps/implementations/client-app-impl.ts +10 -11
- package/src/lib/hexclave-app/apps/implementations/server-app-impl.ts +2 -7
- package/src/lib/hexclave-app/passkey-rp-id.test.ts +43 -0
- package/src/lib/hexclave-app/passkey-rp-id.ts +27 -0
package/dist/{quetzal-translations-BT1HrUsA.d.ts.map → quetzal-translations-XeH3zQ9l.d.ts.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"quetzal-translations-
|
|
1
|
+
{"version":3,"file":"quetzal-translations-XeH3zQ9l.d.ts","names":[],"sources":["../src/generated/quetzal-translations.ts"],"mappings":";cAUa,WAAA,EAAW,GAAA;AAAA,cAqTX,cAAA,EAAc,GAAA,mIAAA,GAAA,66qBAAA,GAAA,usnBAAA,GAAA,iyrBAAA,GAAA,g+qBAAA,GAAA,8srBAAA,GAAA,2trBAAA,GAAA,41pBAAA,GAAA,m3eAAA,GAAA,oreAAA,GAAA,swpBAAA,GAAA,+0qBAAA,GAAA,8wbAAA,GAAA"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as quetzalLocales } from "./quetzal-translations-
|
|
1
|
+
import { n as quetzalLocales } from "./quetzal-translations-XeH3zQ9l.js";
|
|
2
2
|
|
|
3
3
|
//#region src/providers/translation-provider.d.ts
|
|
4
4
|
declare function TranslationProvider({
|
|
@@ -12,4 +12,4 @@ declare function TranslationProvider({
|
|
|
12
12
|
}): import("react/jsx-runtime").JSX.Element;
|
|
13
13
|
//#endregion
|
|
14
14
|
export { TranslationProvider as t };
|
|
15
|
-
//# sourceMappingURL=translation-provider-
|
|
15
|
+
//# sourceMappingURL=translation-provider-WOaZXqAz.d.ts.map
|
package/dist/{translation-provider-C1wy92m_.d.ts.map → translation-provider-WOaZXqAz.d.ts.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"translation-provider-
|
|
1
|
+
{"version":3,"file":"translation-provider-WOaZXqAz.d.ts","names":[],"sources":["../src/providers/translation-provider.tsx"],"mappings":";;;iBAQgB,mBAAA;EAAsB,IAAA;EAAM,oBAAA;EAAsB;AAAA;EAChE,IAAA,EAAM,UAAA,QAAkB,cAAA,CAAe,GAAA;EACvC,oBAAA,GAAuB,MAAA;EACvB,QAAA,EAAU,KAAA,CAAM,SAAA;AAAA,gCACjB,GAAA,CAAA,OAAA"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"//": "THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY UNLESS YOU ALSO EDIT THE CORRESPONDING FILE IN packages/template (FOR package.json FILES, PLEASE EDIT package-template.json)",
|
|
3
3
|
"name": "@hexclave/next",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.72",
|
|
5
5
|
"repository": "https://github.com/hexclave/hexclave",
|
|
6
6
|
"sideEffects": false,
|
|
7
7
|
"main": "./dist/index.js",
|
|
@@ -74,9 +74,9 @@
|
|
|
74
74
|
"tailwindcss-animate": "^1.0.7",
|
|
75
75
|
"rrweb": "^1.1.3",
|
|
76
76
|
"yup": "^1.7.1",
|
|
77
|
-
"@hexclave/sc": "1.0.
|
|
78
|
-
"@hexclave/shared": "1.0.
|
|
79
|
-
"@hexclave/ui": "1.0.
|
|
77
|
+
"@hexclave/sc": "1.0.72",
|
|
78
|
+
"@hexclave/shared": "1.0.72",
|
|
79
|
+
"@hexclave/ui": "1.0.72"
|
|
80
80
|
},
|
|
81
81
|
"peerDependencies": {
|
|
82
82
|
"@types/react": ">=18.0.0",
|
|
@@ -535,7 +535,7 @@ describe("StackClientApp cross-domain auth", () => {
|
|
|
535
535
|
}
|
|
536
536
|
|
|
537
537
|
const errorUrl = new URL(redirectedUrl);
|
|
538
|
-
expect(errorUrl.origin).toBe(`https://${projectId}.built-with-
|
|
538
|
+
expect(errorUrl.origin).toBe(`https://${projectId}.built-with-hexclave.com`);
|
|
539
539
|
expect(errorUrl.pathname).toBe("/handler/error");
|
|
540
540
|
expect(errorUrl.searchParams.get("errorCode")).toBe("SIGN_UP_REJECTED");
|
|
541
541
|
expect(errorUrl.searchParams.get("message")).toBe("Your sign up was rejected by an administrator's sign-up rule.");
|
|
@@ -715,7 +715,7 @@ describe("StackClientApp cross-domain auth", () => {
|
|
|
715
715
|
redirectBackUrl.searchParams.set("hexclave_cross_domain_code_challenge", handoffCodeChallenge);
|
|
716
716
|
redirectBackUrl.searchParams.set("hexclave_cross_domain_after_callback_redirect_url", handoffAfterCallbackRedirect);
|
|
717
717
|
|
|
718
|
-
const arrivalUrl = new URL(`https://${projectId}.built-with-
|
|
718
|
+
const arrivalUrl = new URL(`https://${projectId}.built-with-hexclave.com/handler/sign-in`);
|
|
719
719
|
arrivalUrl.searchParams.set("after_auth_return_to", redirectBackUrl.toString());
|
|
720
720
|
arrivalUrl.searchParams.set("hexclave_cross_domain_state", handoffState);
|
|
721
721
|
arrivalUrl.searchParams.set("hexclave_cross_domain_code_challenge", handoffCodeChallenge);
|
|
@@ -754,7 +754,7 @@ describe("StackClientApp cross-domain auth", () => {
|
|
|
754
754
|
.mockResolvedValue(crossDomainAuthorizeRedirect);
|
|
755
755
|
|
|
756
756
|
// All redirect-back query params were dropped before the after-sign-in redirect.
|
|
757
|
-
windowMock.location.href = `https://${projectId}.built-with-
|
|
757
|
+
windowMock.location.href = `https://${projectId}.built-with-hexclave.com/handler/sign-in`;
|
|
758
758
|
|
|
759
759
|
const redirectUrl = await clientApp[hexclaveAppInternalsSymbol].getRedirectToHandlerUrl("afterSignIn");
|
|
760
760
|
|
|
@@ -61,6 +61,7 @@ import { EditableTeamMemberProfile, ReceivedTeamInvitation, SentTeamInvitation,
|
|
|
61
61
|
import { buildCliAuthConfirmUrl, getHostedHandlerUrl, isHostedHandlerUrlForProject, resolveHandlerUrls } from "../../url-targets";
|
|
62
62
|
import { augmentUrlWithPersistedRedirectBackState, saveRedirectBackStateFromUrl } from "./redirect-back-state";
|
|
63
63
|
import { recordRedirectAndThrowIfLoopDetected } from "./redirect-loop-breaker";
|
|
64
|
+
import { scopePasskeyAuthenticationToHostname, scopePasskeyRegistrationToHostname } from "../../passkey-rp-id";
|
|
64
65
|
import { ActiveSession, Auth, BaseUser, CurrentUser, InternalUserExtra, OAuthProvider, ProjectCurrentUser, SyncedPartialUser, TokenPartialUser, UserExtra, UserUpdateOptions, userUpdateOptionsToCrud, withUserDestructureGuard } from "../../users";
|
|
65
66
|
import { StackClientApp, StackClientAppConstructorOptions, StackClientAppJson } from "../interfaces/client-app";
|
|
66
67
|
import { _HexclaveAdminAppImplIncomplete } from "./admin-app-impl";
|
|
@@ -2446,12 +2447,11 @@ export class _HexclaveClientAppImplIncomplete<HasTokenStore extends boolean, Pro
|
|
|
2446
2447
|
|
|
2447
2448
|
const { options_json, code } = initiationResult.data;
|
|
2448
2449
|
|
|
2449
|
-
//
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
options_json.rp.id = hostname;
|
|
2450
|
+
// Passkeys intentionally stay scoped to the hostname where they were
|
|
2451
|
+
// registered. In particular, passkeys created on the legacy hosted
|
|
2452
|
+
// components domain continue to work there but are not carried over to
|
|
2453
|
+
// a replacement hosted domain.
|
|
2454
|
+
scopePasskeyRegistrationToHostname(options_json, hostname);
|
|
2455
2455
|
|
|
2456
2456
|
let attResp;
|
|
2457
2457
|
try {
|
|
@@ -3771,11 +3771,10 @@ export class _HexclaveClientAppImplIncomplete<HasTokenStore extends boolean, Pro
|
|
|
3771
3771
|
|
|
3772
3772
|
const { options_json, code } = initiationResult.data;
|
|
3773
3773
|
|
|
3774
|
-
//
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
|
|
3778
|
-
options_json.rpId = window.location.hostname;
|
|
3774
|
+
// Keep authentication scoped to the current hostname. This deliberately
|
|
3775
|
+
// does not add cross-domain compatibility for passkeys created on a
|
|
3776
|
+
// previous hosted components domain.
|
|
3777
|
+
scopePasskeyAuthenticationToHostname(options_json, window.location.hostname);
|
|
3779
3778
|
|
|
3780
3779
|
const authentication_response = await startAuthentication({ optionsJSON: options_json });
|
|
3781
3780
|
return await this._interface.signInWithPasskey({ authentication_response, code }, session);
|
|
@@ -36,6 +36,7 @@ import { Customer, CustomerProductsList, CustomerProductsRequestOptions, InlineP
|
|
|
36
36
|
import { DataVaultStore } from "../../data-vault";
|
|
37
37
|
import { EmailDeliveryInfo, SendEmailOptions } from "../../email";
|
|
38
38
|
import { NotificationCategory } from "../../notification-categories";
|
|
39
|
+
import { scopePasskeyRegistrationToHostname } from "../../passkey-rp-id";
|
|
39
40
|
import { AdminProjectPermissionDefinition, AdminTeamPermission, AdminTeamPermissionDefinition } from "../../permissions";
|
|
40
41
|
import { EditableTeamMemberProfile, ReceivedTeamInvitation, SentTeamInvitation, ServerListTeamsOptions, ServerListUsersOptions, ServerTeam, ServerTeamCreateOptions, ServerTeamUpdateOptions, ServerTeamUser, Team, serverTeamCreateOptionsToCrud, serverTeamUpdateOptionsToCrud } from "../../teams";
|
|
41
42
|
import { ProjectCurrentServerUser, ServerOAuthProvider, ServerUser, ServerUserCreateOptions, ServerUserUpdateOptions, serverUserCreateOptionsToCrud, serverUserUpdateOptionsToCrud, withUserDestructureGuard } from "../../users";
|
|
@@ -896,7 +897,6 @@ export class _HexclaveServerAppImplIncomplete<HasTokenStore extends boolean, Pro
|
|
|
896
897
|
return providers.find((p) => p.id === id) ?? null;
|
|
897
898
|
},
|
|
898
899
|
async registerPasskey(options?: { hostname?: string }): Promise<Result<undefined, KnownErrors["PasskeyRegistrationFailed"] | KnownErrors["PasskeyWebAuthnError"]>> {
|
|
899
|
-
// TODO remove duplicated code between this and the function in client-app-impl.ts
|
|
900
900
|
const hostname = options?.hostname || (await app._getCurrentUrl())?.hostname;
|
|
901
901
|
if (!hostname) {
|
|
902
902
|
throw new HexclaveAssertionError("hostname must be provided if the Stack App does not have a redirect method");
|
|
@@ -911,12 +911,7 @@ export class _HexclaveServerAppImplIncomplete<HasTokenStore extends boolean, Pro
|
|
|
911
911
|
|
|
912
912
|
const { options_json, code } = initiationResult.data;
|
|
913
913
|
|
|
914
|
-
|
|
915
|
-
if (options_json.rp.id !== "THIS_VALUE_WILL_BE_REPLACED.example.com") {
|
|
916
|
-
throw new HexclaveAssertionError(`Expected returned RP ID from server to equal sentinel, but found ${options_json.rp.id}`);
|
|
917
|
-
}
|
|
918
|
-
|
|
919
|
-
options_json.rp.id = hostname;
|
|
914
|
+
scopePasskeyRegistrationToHostname(options_json, hostname);
|
|
920
915
|
|
|
921
916
|
let attResp;
|
|
922
917
|
try {
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
|
|
2
|
+
//===========================================
|
|
3
|
+
// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY UNLESS YOU ALSO EDIT THE CORRESPONDING FILE IN packages/template
|
|
4
|
+
//===========================================
|
|
5
|
+
import { describe, expect, it } from "vitest";
|
|
6
|
+
import { scopePasskeyAuthenticationToHostname, scopePasskeyRegistrationToHostname } from "./passkey-rp-id";
|
|
7
|
+
|
|
8
|
+
const passkeyRpIdSentinel = "THIS_VALUE_WILL_BE_REPLACED.example.com";
|
|
9
|
+
|
|
10
|
+
describe.each([
|
|
11
|
+
"project-id.built-with-hexclave.com",
|
|
12
|
+
"project-id.built-with-stack-auth.com",
|
|
13
|
+
])("passkeys on %s", (hostname) => {
|
|
14
|
+
it("scopes registration to the current hostname", () => {
|
|
15
|
+
const options = { rp: { id: passkeyRpIdSentinel } };
|
|
16
|
+
|
|
17
|
+
scopePasskeyRegistrationToHostname(options, hostname);
|
|
18
|
+
|
|
19
|
+
expect(options.rp.id).toBe(hostname);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("scopes authentication to the current hostname", () => {
|
|
23
|
+
const options = { rpId: passkeyRpIdSentinel };
|
|
24
|
+
|
|
25
|
+
scopePasskeyAuthenticationToHostname(options, hostname);
|
|
26
|
+
|
|
27
|
+
expect(options.rpId).toBe(hostname);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("rejects registration options without the server sentinel", () => {
|
|
32
|
+
expect(() => scopePasskeyRegistrationToHostname(
|
|
33
|
+
{ rp: { id: "unexpected.example.com" } },
|
|
34
|
+
"project-id.built-with-hexclave.com",
|
|
35
|
+
)).toThrow("Expected returned RP ID from server to equal sentinel");
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("rejects authentication options without the server sentinel", () => {
|
|
39
|
+
expect(() => scopePasskeyAuthenticationToHostname(
|
|
40
|
+
{ rpId: "unexpected.example.com" },
|
|
41
|
+
"project-id.built-with-hexclave.com",
|
|
42
|
+
)).toThrow("Expected returned RP ID from server to equal sentinel");
|
|
43
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
|
|
2
|
+
//===========================================
|
|
3
|
+
// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY UNLESS YOU ALSO EDIT THE CORRESPONDING FILE IN packages/template
|
|
4
|
+
//===========================================
|
|
5
|
+
import { HexclaveAssertionError } from "@hexclave/shared/dist/utils/errors";
|
|
6
|
+
|
|
7
|
+
const passkeyRpIdSentinel = "THIS_VALUE_WILL_BE_REPLACED.example.com";
|
|
8
|
+
|
|
9
|
+
export function scopePasskeyRegistrationToHostname(
|
|
10
|
+
options: { rp: { id?: string } },
|
|
11
|
+
hostname: string,
|
|
12
|
+
): void {
|
|
13
|
+
if (options.rp.id !== passkeyRpIdSentinel) {
|
|
14
|
+
throw new HexclaveAssertionError(`Expected returned RP ID from server to equal sentinel, but found ${options.rp.id}`);
|
|
15
|
+
}
|
|
16
|
+
options.rp.id = hostname;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function scopePasskeyAuthenticationToHostname(
|
|
20
|
+
options: { rpId?: string },
|
|
21
|
+
hostname: string,
|
|
22
|
+
): void {
|
|
23
|
+
if (options.rpId !== passkeyRpIdSentinel) {
|
|
24
|
+
throw new HexclaveAssertionError(`Expected returned RP ID from server to equal sentinel, but found ${options.rpId}`);
|
|
25
|
+
}
|
|
26
|
+
options.rpId = hostname;
|
|
27
|
+
}
|