@authowl/react 0.25.2 → 0.25.3
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/index.cjs +42 -22
- package/dist/index.d.cts +6 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.js +42 -22
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -4649,26 +4649,32 @@ var KNOWN_METHODS = [
|
|
|
4649
4649
|
function emailAutocomplete(isPasskeyHost) {
|
|
4650
4650
|
return isPasskeyHost ? "email webauthn" : "email";
|
|
4651
4651
|
}
|
|
4652
|
-
function
|
|
4653
|
-
|
|
4654
|
-
|
|
4655
|
-
if (!
|
|
4656
|
-
|
|
4657
|
-
|
|
4658
|
-
|
|
4659
|
-
|
|
4660
|
-
|
|
4661
|
-
|
|
4652
|
+
function hostCoveredByRelyingParty(pageHost, rpId) {
|
|
4653
|
+
const host = pageHost.toLowerCase();
|
|
4654
|
+
const id = rpId.toLowerCase();
|
|
4655
|
+
if (id !== "localhost" && !id.includes(".")) return false;
|
|
4656
|
+
return host === id || host.endsWith(`.${id}`);
|
|
4657
|
+
}
|
|
4658
|
+
function resolveRelyingPartyId(authBaseUrl, relyingPartyId) {
|
|
4659
|
+
if (relyingPartyId) return relyingPartyId;
|
|
4660
|
+
if (!authBaseUrl) return void 0;
|
|
4661
|
+
try {
|
|
4662
|
+
return new URL(authBaseUrl).hostname || void 0;
|
|
4663
|
+
} catch {
|
|
4664
|
+
return void 0;
|
|
4662
4665
|
}
|
|
4663
|
-
if (rpId !== "localhost" && !rpId.includes(".")) return false;
|
|
4664
|
-
return pageHost === rpId || pageHost.endsWith(`.${rpId}`);
|
|
4665
4666
|
}
|
|
4666
|
-
function
|
|
4667
|
-
return
|
|
4667
|
+
function passkeyBlockingDomain(config, pageHost) {
|
|
4668
|
+
if (pageHost === void 0) return void 0;
|
|
4669
|
+
const rpId = resolveRelyingPartyId(
|
|
4668
4670
|
config?.authBaseUrl,
|
|
4669
|
-
pageHost,
|
|
4670
4671
|
config?.authentication?.passkey?.relyingPartyId
|
|
4671
4672
|
);
|
|
4673
|
+
if (rpId === void 0) return void 0;
|
|
4674
|
+
return hostCoveredByRelyingParty(pageHost, rpId) ? void 0 : rpId;
|
|
4675
|
+
}
|
|
4676
|
+
function passkeyReachableForConfig(config, pageHost) {
|
|
4677
|
+
return passkeyBlockingDomain(config, pageHost) === void 0;
|
|
4672
4678
|
}
|
|
4673
4679
|
function resolveSignInMethods(config, pageHost) {
|
|
4674
4680
|
const methods = config?.enabledMethods ?? ["password"];
|
|
@@ -4713,6 +4719,11 @@ var React20 = __toESM(require("react"), 1);
|
|
|
4713
4719
|
init_hooks();
|
|
4714
4720
|
init_i18n();
|
|
4715
4721
|
|
|
4722
|
+
// src/components/page-host.ts
|
|
4723
|
+
function currentPageHost() {
|
|
4724
|
+
return typeof window === "undefined" ? void 0 : window.location.hostname;
|
|
4725
|
+
}
|
|
4726
|
+
|
|
4716
4727
|
// src/components/redirect.ts
|
|
4717
4728
|
function isSafeRedirect(redirectTo, origin) {
|
|
4718
4729
|
try {
|
|
@@ -4950,6 +4961,10 @@ function PasskeyButton({ redirectTo, onSignedIn }) {
|
|
|
4950
4961
|
const { sessionStore } = useAuthClient();
|
|
4951
4962
|
const { signInPasskey } = useSignIn();
|
|
4952
4963
|
const { pending, error, run } = useSubmitAction();
|
|
4964
|
+
const { config, isLoading: configLoading, isError: configError } = usePublicConfig();
|
|
4965
|
+
const configPending = configLoading || config === null && !configError;
|
|
4966
|
+
const blocked = passkeyBlockingDomain(config, currentPageHost()) !== void 0;
|
|
4967
|
+
if (configPending || blocked) return null;
|
|
4953
4968
|
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "ba-fields", children: [
|
|
4954
4969
|
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
4955
4970
|
"button",
|
|
@@ -6103,10 +6118,7 @@ function SignIn({
|
|
|
6103
6118
|
recordSignInMethod("phone-otp");
|
|
6104
6119
|
onSignedIn?.();
|
|
6105
6120
|
}, [onSignedIn, recordSignInMethod]);
|
|
6106
|
-
const plan = resolveSignInMethods(
|
|
6107
|
-
config,
|
|
6108
|
-
typeof window === "undefined" ? void 0 : window.location.hostname
|
|
6109
|
-
);
|
|
6121
|
+
const plan = resolveSignInMethods(config, currentPageHost());
|
|
6110
6122
|
usePasskeyAutofill({
|
|
6111
6123
|
enabled: credentialMode === "email" && plan.autofillHost !== null,
|
|
6112
6124
|
redirectTo,
|
|
@@ -7625,6 +7637,9 @@ function PasskeyManagerBody({
|
|
|
7625
7637
|
const api = usePasskeys();
|
|
7626
7638
|
const apiRef = React31.useRef(api);
|
|
7627
7639
|
apiRef.current = api;
|
|
7640
|
+
const { config, isLoading: configLoading, isError: configError } = usePublicConfig();
|
|
7641
|
+
const blockedBy = passkeyBlockingDomain(config, currentPageHost());
|
|
7642
|
+
const configPending = configLoading || config === null && !configError;
|
|
7628
7643
|
const { pending, error, setError, run } = useSubmitAction();
|
|
7629
7644
|
const [passkeys, setPasskeys] = React31.useState(null);
|
|
7630
7645
|
const loadTokenRef = React31.useRef(0);
|
|
@@ -7697,7 +7712,12 @@ function PasskeyManagerBody({
|
|
|
7697
7712
|
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(FormError, { children: error }),
|
|
7698
7713
|
passkeys === null && /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("button", { className: "ba-link-button", type: "button", onClick: () => void load(), children: t("userProfile.retry") })
|
|
7699
7714
|
] }),
|
|
7700
|
-
passkeys !== null && allowAdd &&
|
|
7715
|
+
passkeys !== null && allowAdd && !configPending && (blockedBy !== void 0 ? (
|
|
7716
|
+
// The add button is HIDDEN, not disabled, and the list stays: a user
|
|
7717
|
+
// may hold passkeys enrolled on the relying-party origin and must
|
|
7718
|
+
// still be able to rename or remove them from here.
|
|
7719
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)("p", { className: "ba-muted", "data-testid": "passkey-add-elsewhere", children: t("passkeys.addElsewhere", { domain: blockedBy }) })
|
|
7720
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
7701
7721
|
"button",
|
|
7702
7722
|
{
|
|
7703
7723
|
className: "ba-button",
|
|
@@ -7710,7 +7730,7 @@ function PasskeyManagerBody({
|
|
|
7710
7730
|
}),
|
|
7711
7731
|
children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(Busy, { busy: pending, label: t("common.working"), children: t("passkeys.add") })
|
|
7712
7732
|
}
|
|
7713
|
-
)
|
|
7733
|
+
))
|
|
7714
7734
|
] });
|
|
7715
7735
|
}
|
|
7716
7736
|
|
|
@@ -7729,7 +7749,7 @@ function usePasskeyOffer() {
|
|
|
7729
7749
|
const api = React32.useRef(listPasskeys);
|
|
7730
7750
|
api.current = listPasskeys;
|
|
7731
7751
|
const projectId = config?.environmentId ?? null;
|
|
7732
|
-
const eligible = projectId !== null && isSignedIn && (0, import_core4.resolveProjectCapabilities)(config).passkeyAdd && typeof window !== "undefined" && "PublicKeyCredential" in window && passkeyReachableForConfig(config,
|
|
7752
|
+
const eligible = projectId !== null && isSignedIn && (0, import_core4.resolveProjectCapabilities)(config).passkeyAdd && typeof window !== "undefined" && "PublicKeyCredential" in window && passkeyReachableForConfig(config, currentPageHost());
|
|
7733
7753
|
const userId = user?.id ?? null;
|
|
7734
7754
|
const sessionId = sessionData?.session?.id ?? null;
|
|
7735
7755
|
const subject = React32.useMemo(
|
package/dist/index.d.cts
CHANGED
|
@@ -372,6 +372,11 @@ type SignInPlan = {
|
|
|
372
372
|
* passkeys inline. One place owns the token string.
|
|
373
373
|
*/
|
|
374
374
|
declare function emailAutocomplete(isPasskeyHost: boolean): string;
|
|
375
|
+
/**
|
|
376
|
+
* Resolve the sign-in surfaces from a project's public config. A null config
|
|
377
|
+
* gets a structural password default for callers that resolve before the fetch
|
|
378
|
+
* settles; the drop-in components fail closed when the fetch itself errors.
|
|
379
|
+
*/
|
|
375
380
|
declare function resolveSignInMethods(config: PublicConfig | null,
|
|
376
381
|
/** The hostname the form is rendering on; omit when it is not knowable. */
|
|
377
382
|
pageHost?: string): SignInPlan;
|
|
@@ -581,7 +586,7 @@ type PasskeyButtonProps = {
|
|
|
581
586
|
* click. Complements the inline conditional-mediation autofill armed by
|
|
582
587
|
* <SignIn/> - the button always works even where autofill is unavailable.
|
|
583
588
|
*/
|
|
584
|
-
declare function PasskeyButton({ redirectTo, onSignedIn }: PasskeyButtonProps): React.JSX.Element;
|
|
589
|
+
declare function PasskeyButton({ redirectTo, onSignedIn }: PasskeyButtonProps): React.JSX.Element | null;
|
|
585
590
|
|
|
586
591
|
type ForgotPasswordProps = {
|
|
587
592
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -372,6 +372,11 @@ type SignInPlan = {
|
|
|
372
372
|
* passkeys inline. One place owns the token string.
|
|
373
373
|
*/
|
|
374
374
|
declare function emailAutocomplete(isPasskeyHost: boolean): string;
|
|
375
|
+
/**
|
|
376
|
+
* Resolve the sign-in surfaces from a project's public config. A null config
|
|
377
|
+
* gets a structural password default for callers that resolve before the fetch
|
|
378
|
+
* settles; the drop-in components fail closed when the fetch itself errors.
|
|
379
|
+
*/
|
|
375
380
|
declare function resolveSignInMethods(config: PublicConfig | null,
|
|
376
381
|
/** The hostname the form is rendering on; omit when it is not knowable. */
|
|
377
382
|
pageHost?: string): SignInPlan;
|
|
@@ -581,7 +586,7 @@ type PasskeyButtonProps = {
|
|
|
581
586
|
* click. Complements the inline conditional-mediation autofill armed by
|
|
582
587
|
* <SignIn/> - the button always works even where autofill is unavailable.
|
|
583
588
|
*/
|
|
584
|
-
declare function PasskeyButton({ redirectTo, onSignedIn }: PasskeyButtonProps): React.JSX.Element;
|
|
589
|
+
declare function PasskeyButton({ redirectTo, onSignedIn }: PasskeyButtonProps): React.JSX.Element | null;
|
|
585
590
|
|
|
586
591
|
type ForgotPasswordProps = {
|
|
587
592
|
/**
|
package/dist/index.js
CHANGED
|
@@ -60,26 +60,32 @@ var KNOWN_METHODS = [
|
|
|
60
60
|
function emailAutocomplete(isPasskeyHost) {
|
|
61
61
|
return isPasskeyHost ? "email webauthn" : "email";
|
|
62
62
|
}
|
|
63
|
-
function
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
if (!
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
63
|
+
function hostCoveredByRelyingParty(pageHost, rpId) {
|
|
64
|
+
const host = pageHost.toLowerCase();
|
|
65
|
+
const id = rpId.toLowerCase();
|
|
66
|
+
if (id !== "localhost" && !id.includes(".")) return false;
|
|
67
|
+
return host === id || host.endsWith(`.${id}`);
|
|
68
|
+
}
|
|
69
|
+
function resolveRelyingPartyId(authBaseUrl, relyingPartyId) {
|
|
70
|
+
if (relyingPartyId) return relyingPartyId;
|
|
71
|
+
if (!authBaseUrl) return void 0;
|
|
72
|
+
try {
|
|
73
|
+
return new URL(authBaseUrl).hostname || void 0;
|
|
74
|
+
} catch {
|
|
75
|
+
return void 0;
|
|
73
76
|
}
|
|
74
|
-
if (rpId !== "localhost" && !rpId.includes(".")) return false;
|
|
75
|
-
return pageHost === rpId || pageHost.endsWith(`.${rpId}`);
|
|
76
77
|
}
|
|
77
|
-
function
|
|
78
|
-
return
|
|
78
|
+
function passkeyBlockingDomain(config, pageHost) {
|
|
79
|
+
if (pageHost === void 0) return void 0;
|
|
80
|
+
const rpId = resolveRelyingPartyId(
|
|
79
81
|
config?.authBaseUrl,
|
|
80
|
-
pageHost,
|
|
81
82
|
config?.authentication?.passkey?.relyingPartyId
|
|
82
83
|
);
|
|
84
|
+
if (rpId === void 0) return void 0;
|
|
85
|
+
return hostCoveredByRelyingParty(pageHost, rpId) ? void 0 : rpId;
|
|
86
|
+
}
|
|
87
|
+
function passkeyReachableForConfig(config, pageHost) {
|
|
88
|
+
return passkeyBlockingDomain(config, pageHost) === void 0;
|
|
83
89
|
}
|
|
84
90
|
function resolveSignInMethods(config, pageHost) {
|
|
85
91
|
const methods = config?.enabledMethods ?? ["password"];
|
|
@@ -122,6 +128,11 @@ function resolveSignInMethods(config, pageHost) {
|
|
|
122
128
|
// src/components/SignIn.tsx
|
|
123
129
|
import * as React13 from "react";
|
|
124
130
|
|
|
131
|
+
// src/components/page-host.ts
|
|
132
|
+
function currentPageHost() {
|
|
133
|
+
return typeof window === "undefined" ? void 0 : window.location.hostname;
|
|
134
|
+
}
|
|
135
|
+
|
|
125
136
|
// src/components/redirect.ts
|
|
126
137
|
function isSafeRedirect(redirectTo, origin) {
|
|
127
138
|
try {
|
|
@@ -342,6 +353,10 @@ function PasskeyButton({ redirectTo, onSignedIn }) {
|
|
|
342
353
|
const { sessionStore } = useAuthClient();
|
|
343
354
|
const { signInPasskey } = useSignIn();
|
|
344
355
|
const { pending, error, run } = useSubmitAction();
|
|
356
|
+
const { config, isLoading: configLoading, isError: configError } = usePublicConfig();
|
|
357
|
+
const configPending = configLoading || config === null && !configError;
|
|
358
|
+
const blocked = passkeyBlockingDomain(config, currentPageHost()) !== void 0;
|
|
359
|
+
if (configPending || blocked) return null;
|
|
345
360
|
return /* @__PURE__ */ jsxs3("div", { className: "ba-fields", children: [
|
|
346
361
|
/* @__PURE__ */ jsx3(
|
|
347
362
|
"button",
|
|
@@ -1452,10 +1467,7 @@ function SignIn({
|
|
|
1452
1467
|
recordSignInMethod("phone-otp");
|
|
1453
1468
|
onSignedIn?.();
|
|
1454
1469
|
}, [onSignedIn, recordSignInMethod]);
|
|
1455
|
-
const plan = resolveSignInMethods(
|
|
1456
|
-
config,
|
|
1457
|
-
typeof window === "undefined" ? void 0 : window.location.hostname
|
|
1458
|
-
);
|
|
1470
|
+
const plan = resolveSignInMethods(config, currentPageHost());
|
|
1459
1471
|
usePasskeyAutofill({
|
|
1460
1472
|
enabled: credentialMode === "email" && plan.autofillHost !== null,
|
|
1461
1473
|
redirectTo,
|
|
@@ -2912,6 +2924,9 @@ function PasskeyManagerBody({
|
|
|
2912
2924
|
const api = usePasskeys();
|
|
2913
2925
|
const apiRef = React24.useRef(api);
|
|
2914
2926
|
apiRef.current = api;
|
|
2927
|
+
const { config, isLoading: configLoading, isError: configError } = usePublicConfig();
|
|
2928
|
+
const blockedBy = passkeyBlockingDomain(config, currentPageHost());
|
|
2929
|
+
const configPending = configLoading || config === null && !configError;
|
|
2915
2930
|
const { pending, error, setError, run } = useSubmitAction();
|
|
2916
2931
|
const [passkeys, setPasskeys] = React24.useState(null);
|
|
2917
2932
|
const loadTokenRef = React24.useRef(0);
|
|
@@ -2984,7 +2999,12 @@ function PasskeyManagerBody({
|
|
|
2984
2999
|
/* @__PURE__ */ jsx33(FormError, { children: error }),
|
|
2985
3000
|
passkeys === null && /* @__PURE__ */ jsx33("button", { className: "ba-link-button", type: "button", onClick: () => void load(), children: t("userProfile.retry") })
|
|
2986
3001
|
] }),
|
|
2987
|
-
passkeys !== null && allowAdd &&
|
|
3002
|
+
passkeys !== null && allowAdd && !configPending && (blockedBy !== void 0 ? (
|
|
3003
|
+
// The add button is HIDDEN, not disabled, and the list stays: a user
|
|
3004
|
+
// may hold passkeys enrolled on the relying-party origin and must
|
|
3005
|
+
// still be able to rename or remove them from here.
|
|
3006
|
+
/* @__PURE__ */ jsx33("p", { className: "ba-muted", "data-testid": "passkey-add-elsewhere", children: t("passkeys.addElsewhere", { domain: blockedBy }) })
|
|
3007
|
+
) : /* @__PURE__ */ jsx33(
|
|
2988
3008
|
"button",
|
|
2989
3009
|
{
|
|
2990
3010
|
className: "ba-button",
|
|
@@ -2997,7 +3017,7 @@ function PasskeyManagerBody({
|
|
|
2997
3017
|
}),
|
|
2998
3018
|
children: /* @__PURE__ */ jsx33(Busy, { busy: pending, label: t("common.working"), children: t("passkeys.add") })
|
|
2999
3019
|
}
|
|
3000
|
-
)
|
|
3020
|
+
))
|
|
3001
3021
|
] });
|
|
3002
3022
|
}
|
|
3003
3023
|
|
|
@@ -3019,7 +3039,7 @@ function usePasskeyOffer() {
|
|
|
3019
3039
|
const api = React25.useRef(listPasskeys);
|
|
3020
3040
|
api.current = listPasskeys;
|
|
3021
3041
|
const projectId = config?.environmentId ?? null;
|
|
3022
|
-
const eligible = projectId !== null && isSignedIn && resolveProjectCapabilities(config).passkeyAdd && typeof window !== "undefined" && "PublicKeyCredential" in window && passkeyReachableForConfig(config,
|
|
3042
|
+
const eligible = projectId !== null && isSignedIn && resolveProjectCapabilities(config).passkeyAdd && typeof window !== "undefined" && "PublicKeyCredential" in window && passkeyReachableForConfig(config, currentPageHost());
|
|
3023
3043
|
const userId = user?.id ?? null;
|
|
3024
3044
|
const sessionId = sessionData?.session?.id ?? null;
|
|
3025
3045
|
const subject = React25.useMemo(
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@authowl/react",
|
|
3
|
-
"version": "0.25.
|
|
3
|
+
"version": "0.25.3",
|
|
4
4
|
"description": "React provider, hooks, and drop-in components for AuthOwl, the multi-tenant auth SaaS.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"publishConfig": {
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"LICENSE"
|
|
48
48
|
],
|
|
49
49
|
"dependencies": {
|
|
50
|
-
"@authowl/core": "0.24.
|
|
50
|
+
"@authowl/core": "0.24.2"
|
|
51
51
|
},
|
|
52
52
|
"peerDependencies": {
|
|
53
53
|
"react": ">=18",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"devDependencies": {
|
|
57
57
|
"@testing-library/react": "16.3.2",
|
|
58
58
|
"@types/react": "^19.2.18",
|
|
59
|
-
"@types/react-dom": "^19.2.
|
|
59
|
+
"@types/react-dom": "^19.2.5",
|
|
60
60
|
"jsdom": "26.1.0",
|
|
61
61
|
"qrcode-generator": "^2.0.4",
|
|
62
62
|
"react": "^19.2.8",
|