@poly-x/react 0.1.0-alpha.6 → 0.1.0-alpha.8
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 +493 -2
- package/dist/index.d.cts +102 -1
- package/dist/index.d.mts +102 -1
- package/dist/index.mjs +495 -6
- package/dist/styles.css +78 -0
- package/package.json +2 -2
package/dist/index.d.mts
CHANGED
|
@@ -203,6 +203,7 @@ interface SignInLabels {
|
|
|
203
203
|
passwordPlaceholder: string;
|
|
204
204
|
submit: string;
|
|
205
205
|
submitting: string;
|
|
206
|
+
forgotPassword: string;
|
|
206
207
|
chooseWorkspace: string;
|
|
207
208
|
chooseWorkspaceSubheading: string;
|
|
208
209
|
securedBy: string;
|
|
@@ -235,6 +236,11 @@ interface SignInProps {
|
|
|
235
236
|
panel?: ReactNode;
|
|
236
237
|
/** BFF endpoint backed by `createAuthHandlers().authenticate`. Default `/api/polyx/authenticate`. */
|
|
237
238
|
action?: string;
|
|
239
|
+
/**
|
|
240
|
+
* URL of your reset flow (where `<ForgotPassword/>` lives). When set, a
|
|
241
|
+
* "Forgot password?" link appears under the password field. Omit to hide it.
|
|
242
|
+
*/
|
|
243
|
+
forgotPasswordUrl?: string;
|
|
238
244
|
/** Where to send the browser after success. Overrides the server's default redirect. */
|
|
239
245
|
afterSignInPath?: string;
|
|
240
246
|
/** Pre-scope the login to a single organization (skips org discovery). */
|
|
@@ -261,6 +267,101 @@ interface SignInProps {
|
|
|
261
267
|
*/
|
|
262
268
|
declare function SignIn(props: SignInProps): ReactNode;
|
|
263
269
|
//#endregion
|
|
270
|
+
//#region src/components/forgot-password.d.ts
|
|
271
|
+
/** Every user-facing string, overridable for i18n / white-labelling. */
|
|
272
|
+
interface ForgotPasswordLabels {
|
|
273
|
+
heading: string;
|
|
274
|
+
subheading: string;
|
|
275
|
+
emailLabel: string;
|
|
276
|
+
emailPlaceholder: string;
|
|
277
|
+
submit: string;
|
|
278
|
+
submitting: string;
|
|
279
|
+
/** Shown after submit — always, regardless of whether the account exists. */
|
|
280
|
+
sentHeading: string;
|
|
281
|
+
/** Confirmation body. `{email}` is replaced with the entered address, emphasized. */
|
|
282
|
+
sentSubheading: string;
|
|
283
|
+
differentEmail: string;
|
|
284
|
+
backToSignIn: string;
|
|
285
|
+
securedBy: string;
|
|
286
|
+
genericError: string;
|
|
287
|
+
}
|
|
288
|
+
interface ForgotPasswordProps {
|
|
289
|
+
/** BFF endpoint backed by `createAuthHandlers().forgotPassword`. Default `/api/polyx/forgot-password`. */
|
|
290
|
+
action?: string;
|
|
291
|
+
/** Where the "back to sign in" link points. Default `/sign-in`. */
|
|
292
|
+
signInPath?: string;
|
|
293
|
+
/** Rendered above the heading — your wordmark or logo. */
|
|
294
|
+
logo?: ReactNode;
|
|
295
|
+
/** Show the "Secured by PolyX" attribution under the form. Default true. */
|
|
296
|
+
showSecuredBy?: boolean;
|
|
297
|
+
/** Appended to the root element's class list, for escape-hatch styling. */
|
|
298
|
+
className?: string;
|
|
299
|
+
labels?: Partial<ForgotPasswordLabels>;
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* The self-service "forgot password" form (poly-x v5 / F040). Renders an email field
|
|
303
|
+
* inside the consuming app and POSTs to the SDK's BFF (`@poly-x/next` `forgotPassword`),
|
|
304
|
+
* which asks poly-auth to email a single-use reset link SERVER-SIDE. The response is
|
|
305
|
+
* always the same — no token, no account-existence signal — so after submit the form
|
|
306
|
+
* shows an identical "check your email" state whether or not the account exists.
|
|
307
|
+
*
|
|
308
|
+
* Reuses the `<SignIn/>` styling: `import "@poly-x/react/styles.css"` once, then theme
|
|
309
|
+
* via the `--polyx-*` custom properties. Target `.polyx-forgot` for form-specific tweaks.
|
|
310
|
+
*/
|
|
311
|
+
declare function ForgotPassword(props: ForgotPasswordProps): ReactNode;
|
|
312
|
+
//#endregion
|
|
313
|
+
//#region src/components/reset-password.d.ts
|
|
314
|
+
/** Every user-facing string, overridable for i18n / white-labelling. */
|
|
315
|
+
interface ResetPasswordLabels {
|
|
316
|
+
heading: string;
|
|
317
|
+
subheading: string;
|
|
318
|
+
newPasswordLabel: string;
|
|
319
|
+
confirmPasswordLabel: string;
|
|
320
|
+
passwordPlaceholder: string;
|
|
321
|
+
submit: string;
|
|
322
|
+
submitting: string;
|
|
323
|
+
successHeading: string;
|
|
324
|
+
successSubheading: string;
|
|
325
|
+
invalidHeading: string;
|
|
326
|
+
invalidSubheading: string;
|
|
327
|
+
requestNewLink: string;
|
|
328
|
+
backToSignIn: string;
|
|
329
|
+
securedBy: string;
|
|
330
|
+
passwordMismatch: string;
|
|
331
|
+
weakPassword: string;
|
|
332
|
+
genericError: string;
|
|
333
|
+
}
|
|
334
|
+
interface ResetPasswordProps {
|
|
335
|
+
/** BFF endpoint backed by `createAuthHandlers().resetPassword`. Default `/api/polyx/reset-password`. */
|
|
336
|
+
action?: string;
|
|
337
|
+
/** The opaque reset token. Defaults to `?token=` from the URL (client-side). */
|
|
338
|
+
token?: string;
|
|
339
|
+
/** Where to send the browser after a successful reset. Default `/sign-in`. */
|
|
340
|
+
afterResetPath?: string;
|
|
341
|
+
/** Where "request a new link" / "back to sign in" point. Default `/forgot-password` and `/sign-in`. */
|
|
342
|
+
forgotPasswordPath?: string;
|
|
343
|
+
signInPath?: string;
|
|
344
|
+
/** Rendered above the heading — your wordmark or logo. */
|
|
345
|
+
logo?: ReactNode;
|
|
346
|
+
/** Show the "Secured by PolyX" attribution under the form. Default true. */
|
|
347
|
+
showSecuredBy?: boolean;
|
|
348
|
+
/** Client-side minimum password length (the backend enforces the real floor). Default 8. */
|
|
349
|
+
minLength?: number;
|
|
350
|
+
/** Appended to the root element's class list, for escape-hatch styling. */
|
|
351
|
+
className?: string;
|
|
352
|
+
labels?: Partial<ResetPasswordLabels>;
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* The reset-completion form (poly-x v5 / F040). Reads the opaque token from the URL,
|
|
356
|
+
* collects a new password, and POSTs to the SDK's BFF (`@poly-x/next` `resetPassword`),
|
|
357
|
+
* which redeems the token single-use server-side. The token is never decoded in the
|
|
358
|
+
* browser — identity, validity, and expiry are all enforced by the backend.
|
|
359
|
+
*
|
|
360
|
+
* Reuses the `<SignIn/>` styling: `import "@poly-x/react/styles.css"` once, then theme
|
|
361
|
+
* via the `--polyx-*` custom properties. Target `.polyx-reset` for form-specific tweaks.
|
|
362
|
+
*/
|
|
363
|
+
declare function ResetPassword(props: ResetPasswordProps): ReactNode;
|
|
364
|
+
//#endregion
|
|
264
365
|
//#region src/components/auth-callback.d.ts
|
|
265
366
|
interface AuthCallbackProps {
|
|
266
367
|
/** Rendered while the exchange completes (e.g. a spinner). */
|
|
@@ -279,4 +380,4 @@ declare function AuthCallback({
|
|
|
279
380
|
declare const PACKAGE_NAME = "@poly-x/react";
|
|
280
381
|
declare const version = "0.0.0";
|
|
281
382
|
//#endregion
|
|
282
|
-
export { AuthCallback, type AuthCallbackMessage, type AuthCallbackProps, type AuthChannel, BroadcastAuthChannel, BroadcastChannelSessionChannel, type BrowserBridge, type CallbackParams, type LoginController, type LoginControllerDeps, PACKAGE_NAME, PolyXContext, type PolyXContextValue, PolyXProvider, type PolyXProviderProps, PopupCancelledError, type PopupHandle, Protected, type ProtectedProps, SessionStoragePkceStore, SignIn, type SignInLabels, type SignInOptions, type SignInProps, type SignInVariant, type UseAuth, WebLocksLock, WindowBrowserBridge, createLoginController, useAuth, useAuthCallback, useSession, useUser, version };
|
|
383
|
+
export { AuthCallback, type AuthCallbackMessage, type AuthCallbackProps, type AuthChannel, BroadcastAuthChannel, BroadcastChannelSessionChannel, type BrowserBridge, type CallbackParams, ForgotPassword, type ForgotPasswordLabels, type ForgotPasswordProps, type LoginController, type LoginControllerDeps, PACKAGE_NAME, PolyXContext, type PolyXContextValue, PolyXProvider, type PolyXProviderProps, PopupCancelledError, type PopupHandle, Protected, type ProtectedProps, ResetPassword, type ResetPasswordLabels, type ResetPasswordProps, SessionStoragePkceStore, SignIn, type SignInLabels, type SignInOptions, type SignInProps, type SignInVariant, type UseAuth, WebLocksLock, WindowBrowserBridge, createLoginController, useAuth, useAuthCallback, useSession, useUser, version };
|
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { AuthClient, FetchTransport, InMemoryLock, InMemoryPkceStore, InMemorySessionChannel, InMemorySessionStore, createSessionEngine, generatePkce, randomState, resolveConfig } from "@poly-x/core";
|
|
3
|
-
import { createContext, useCallback, useContext, useEffect, useMemo, useState, useSyncExternalStore } from "react";
|
|
4
|
-
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { Fragment, createContext, useCallback, useContext, useEffect, useMemo, useState, useSyncExternalStore } from "react";
|
|
4
|
+
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
5
5
|
//#region src/auth-channel.ts
|
|
6
6
|
var BroadcastAuthChannel = class {
|
|
7
7
|
supported;
|
|
@@ -351,7 +351,7 @@ function Protected({ children, fallback = null, loading = null }) {
|
|
|
351
351
|
}
|
|
352
352
|
//#endregion
|
|
353
353
|
//#region src/components/sign-in.tsx
|
|
354
|
-
const DEFAULT_LABELS = {
|
|
354
|
+
const DEFAULT_LABELS$2 = {
|
|
355
355
|
heading: "Sign in",
|
|
356
356
|
subheading: "Enter your credentials to continue.",
|
|
357
357
|
tagline: "",
|
|
@@ -361,6 +361,7 @@ const DEFAULT_LABELS = {
|
|
|
361
361
|
passwordPlaceholder: "••••••••",
|
|
362
362
|
submit: "Sign in",
|
|
363
363
|
submitting: "Signing in…",
|
|
364
|
+
forgotPassword: "Forgot password?",
|
|
364
365
|
chooseWorkspace: "Choose a workspace",
|
|
365
366
|
chooseWorkspaceSubheading: "You have access to more than one workspace.",
|
|
366
367
|
securedBy: "Secured by PolyX",
|
|
@@ -392,7 +393,7 @@ function SignIn(props) {
|
|
|
392
393
|
const action = props.action ?? "/api/polyx/authenticate";
|
|
393
394
|
const variant = props.variant ?? "card";
|
|
394
395
|
const labels = {
|
|
395
|
-
...DEFAULT_LABELS,
|
|
396
|
+
...DEFAULT_LABELS$2,
|
|
396
397
|
...props.labels
|
|
397
398
|
};
|
|
398
399
|
const [email, setEmail] = useState("");
|
|
@@ -631,6 +632,14 @@ function SignIn(props) {
|
|
|
631
632
|
onChange: (event) => setPassword(event.target.value)
|
|
632
633
|
})]
|
|
633
634
|
}),
|
|
635
|
+
props.forgotPasswordUrl ? /* @__PURE__ */ jsx("div", {
|
|
636
|
+
className: "polyx-signin__forgot",
|
|
637
|
+
children: /* @__PURE__ */ jsx("a", {
|
|
638
|
+
className: "polyx-signin__link",
|
|
639
|
+
href: props.forgotPasswordUrl,
|
|
640
|
+
children: labels.forgotPassword
|
|
641
|
+
})
|
|
642
|
+
}) : null,
|
|
634
643
|
errorNode,
|
|
635
644
|
/* @__PURE__ */ jsx("button", {
|
|
636
645
|
type: "submit",
|
|
@@ -648,7 +657,7 @@ function SignIn(props) {
|
|
|
648
657
|
"data-polyx-signin": step,
|
|
649
658
|
children: [/* @__PURE__ */ jsx("aside", {
|
|
650
659
|
className: "polyx-signin__panel",
|
|
651
|
-
children: props.panel ?? /* @__PURE__ */ jsxs(Fragment, { children: [props.logo ? /* @__PURE__ */ jsx("div", {
|
|
660
|
+
children: props.panel ?? /* @__PURE__ */ jsxs(Fragment$1, { children: [props.logo ? /* @__PURE__ */ jsx("div", {
|
|
652
661
|
className: "polyx-signin__panel-logo",
|
|
653
662
|
children: props.logo
|
|
654
663
|
}) : null, labels.tagline ? /* @__PURE__ */ jsx("p", {
|
|
@@ -667,6 +676,486 @@ function SignIn(props) {
|
|
|
667
676
|
});
|
|
668
677
|
}
|
|
669
678
|
//#endregion
|
|
679
|
+
//#region src/components/_recovery-status.tsx
|
|
680
|
+
const ICONS = {
|
|
681
|
+
mail: /* @__PURE__ */ jsxs("svg", {
|
|
682
|
+
viewBox: "0 0 24 24",
|
|
683
|
+
fill: "none",
|
|
684
|
+
stroke: "currentColor",
|
|
685
|
+
strokeWidth: "1.75",
|
|
686
|
+
"aria-hidden": "true",
|
|
687
|
+
focusable: "false",
|
|
688
|
+
children: [/* @__PURE__ */ jsx("rect", {
|
|
689
|
+
x: "3",
|
|
690
|
+
y: "5",
|
|
691
|
+
width: "18",
|
|
692
|
+
height: "14",
|
|
693
|
+
rx: "2.5"
|
|
694
|
+
}), /* @__PURE__ */ jsx("path", {
|
|
695
|
+
d: "m4 7 8 6 8-6",
|
|
696
|
+
strokeLinecap: "round",
|
|
697
|
+
strokeLinejoin: "round"
|
|
698
|
+
})]
|
|
699
|
+
}),
|
|
700
|
+
check: /* @__PURE__ */ jsx("svg", {
|
|
701
|
+
viewBox: "0 0 24 24",
|
|
702
|
+
fill: "none",
|
|
703
|
+
stroke: "currentColor",
|
|
704
|
+
strokeWidth: "2",
|
|
705
|
+
"aria-hidden": "true",
|
|
706
|
+
focusable: "false",
|
|
707
|
+
children: /* @__PURE__ */ jsx("path", {
|
|
708
|
+
d: "m5 12.5 4.5 4.5L19 7",
|
|
709
|
+
strokeLinecap: "round",
|
|
710
|
+
strokeLinejoin: "round"
|
|
711
|
+
})
|
|
712
|
+
}),
|
|
713
|
+
warning: /* @__PURE__ */ jsxs("svg", {
|
|
714
|
+
viewBox: "0 0 24 24",
|
|
715
|
+
fill: "none",
|
|
716
|
+
stroke: "currentColor",
|
|
717
|
+
strokeWidth: "1.75",
|
|
718
|
+
"aria-hidden": "true",
|
|
719
|
+
focusable: "false",
|
|
720
|
+
children: [
|
|
721
|
+
/* @__PURE__ */ jsx("circle", {
|
|
722
|
+
cx: "12",
|
|
723
|
+
cy: "12",
|
|
724
|
+
r: "9"
|
|
725
|
+
}),
|
|
726
|
+
/* @__PURE__ */ jsx("path", {
|
|
727
|
+
d: "M12 7.5v5",
|
|
728
|
+
strokeLinecap: "round"
|
|
729
|
+
}),
|
|
730
|
+
/* @__PURE__ */ jsx("circle", {
|
|
731
|
+
cx: "12",
|
|
732
|
+
cy: "16",
|
|
733
|
+
r: "0.6",
|
|
734
|
+
fill: "currentColor",
|
|
735
|
+
stroke: "none"
|
|
736
|
+
})
|
|
737
|
+
]
|
|
738
|
+
})
|
|
739
|
+
};
|
|
740
|
+
/**
|
|
741
|
+
* A centered, icon-topped confirmation card — the shape every recovery terminal
|
|
742
|
+
* state shares (link sent, password updated, link expired). Reuses the `<SignIn/>`
|
|
743
|
+
* class anatomy so it themes from the same `--polyx-*` custom properties.
|
|
744
|
+
*/
|
|
745
|
+
function RecoveryStatus(props) {
|
|
746
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
747
|
+
className: "polyx-signin__card",
|
|
748
|
+
children: [
|
|
749
|
+
/* @__PURE__ */ jsxs("div", {
|
|
750
|
+
className: "polyx-signin__header polyx-signin__header--center",
|
|
751
|
+
children: [
|
|
752
|
+
/* @__PURE__ */ jsx("span", {
|
|
753
|
+
className: `polyx-signin__status-icon polyx-signin__status-icon--${props.tone ?? "brand"}`,
|
|
754
|
+
children: ICONS[props.icon]
|
|
755
|
+
}),
|
|
756
|
+
/* @__PURE__ */ jsx("h1", {
|
|
757
|
+
className: "polyx-signin__heading",
|
|
758
|
+
children: props.heading
|
|
759
|
+
}),
|
|
760
|
+
/* @__PURE__ */ jsx("p", {
|
|
761
|
+
className: "polyx-signin__subheading",
|
|
762
|
+
children: props.children
|
|
763
|
+
})
|
|
764
|
+
]
|
|
765
|
+
}),
|
|
766
|
+
props.actions ? /* @__PURE__ */ jsx("div", {
|
|
767
|
+
className: "polyx-signin__actions",
|
|
768
|
+
children: props.actions
|
|
769
|
+
}) : null,
|
|
770
|
+
props.footer
|
|
771
|
+
]
|
|
772
|
+
});
|
|
773
|
+
}
|
|
774
|
+
//#endregion
|
|
775
|
+
//#region src/components/forgot-password.tsx
|
|
776
|
+
const DEFAULT_LABELS$1 = {
|
|
777
|
+
heading: "Reset your password",
|
|
778
|
+
subheading: "Enter your email and we'll send you a reset link.",
|
|
779
|
+
emailLabel: "Email",
|
|
780
|
+
emailPlaceholder: "you@company.com",
|
|
781
|
+
submit: "Send reset link",
|
|
782
|
+
submitting: "Sending…",
|
|
783
|
+
sentHeading: "Check your email",
|
|
784
|
+
sentSubheading: "If an account exists for {email}, a password reset link is on its way. It expires in 30 minutes.",
|
|
785
|
+
differentEmail: "Use a different email",
|
|
786
|
+
backToSignIn: "Back to sign in",
|
|
787
|
+
securedBy: "Secured by PolyX",
|
|
788
|
+
genericError: "Something went wrong. Please try again."
|
|
789
|
+
};
|
|
790
|
+
/** Splits a label on `{email}` and drops the entered address in, emphasized. */
|
|
791
|
+
function withEmail(template, email) {
|
|
792
|
+
return template.split("{email}").map((chunk, index, all) => /* @__PURE__ */ jsxs(Fragment, { children: [chunk, index < all.length - 1 ? /* @__PURE__ */ jsx("span", {
|
|
793
|
+
className: "polyx-signin__email",
|
|
794
|
+
children: email || "that address"
|
|
795
|
+
}) : null] }, index));
|
|
796
|
+
}
|
|
797
|
+
/**
|
|
798
|
+
* The self-service "forgot password" form (poly-x v5 / F040). Renders an email field
|
|
799
|
+
* inside the consuming app and POSTs to the SDK's BFF (`@poly-x/next` `forgotPassword`),
|
|
800
|
+
* which asks poly-auth to email a single-use reset link SERVER-SIDE. The response is
|
|
801
|
+
* always the same — no token, no account-existence signal — so after submit the form
|
|
802
|
+
* shows an identical "check your email" state whether or not the account exists.
|
|
803
|
+
*
|
|
804
|
+
* Reuses the `<SignIn/>` styling: `import "@poly-x/react/styles.css"` once, then theme
|
|
805
|
+
* via the `--polyx-*` custom properties. Target `.polyx-forgot` for form-specific tweaks.
|
|
806
|
+
*/
|
|
807
|
+
function ForgotPassword(props) {
|
|
808
|
+
const action = props.action ?? "/api/polyx/forgot-password";
|
|
809
|
+
const signInPath = props.signInPath ?? "/sign-in";
|
|
810
|
+
const labels = {
|
|
811
|
+
...DEFAULT_LABELS$1,
|
|
812
|
+
...props.labels
|
|
813
|
+
};
|
|
814
|
+
const [email, setEmail] = useState("");
|
|
815
|
+
const [submitting, setSubmitting] = useState(false);
|
|
816
|
+
const [sent, setSent] = useState(false);
|
|
817
|
+
const [error, setError] = useState(null);
|
|
818
|
+
async function onSubmit(event) {
|
|
819
|
+
event.preventDefault();
|
|
820
|
+
setSubmitting(true);
|
|
821
|
+
setError(null);
|
|
822
|
+
try {
|
|
823
|
+
if ((await (await fetch(action, {
|
|
824
|
+
method: "POST",
|
|
825
|
+
headers: { "content-type": "application/json" },
|
|
826
|
+
body: JSON.stringify({ email })
|
|
827
|
+
})).json().catch(() => ({}))).status === "accepted") {
|
|
828
|
+
setSent(true);
|
|
829
|
+
return;
|
|
830
|
+
}
|
|
831
|
+
setError(labels.genericError);
|
|
832
|
+
} catch {
|
|
833
|
+
setError(labels.genericError);
|
|
834
|
+
} finally {
|
|
835
|
+
setSubmitting(false);
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
const rootClass = [
|
|
839
|
+
"polyx-signin",
|
|
840
|
+
"polyx-forgot",
|
|
841
|
+
props.className
|
|
842
|
+
].filter(Boolean).join(" ");
|
|
843
|
+
const footer = props.showSecuredBy === false ? null : /* @__PURE__ */ jsxs("p", {
|
|
844
|
+
className: "polyx-signin__footer",
|
|
845
|
+
children: [/* @__PURE__ */ jsx("svg", {
|
|
846
|
+
className: "polyx-signin__shield",
|
|
847
|
+
viewBox: "0 0 16 16",
|
|
848
|
+
fill: "currentColor",
|
|
849
|
+
"aria-hidden": "true",
|
|
850
|
+
focusable: "false",
|
|
851
|
+
children: /* @__PURE__ */ jsx("path", { d: "M8 0.5 2 3v4.2c0 3.5 2.5 6.7 6 7.8 3.5-1.1 6-4.3 6-7.8V3L8 .5Zm2.9 5.6-3.4 3.4a.7.7 0 0 1-1 0L5.1 8.1a.7.7 0 1 1 1-1l.9.9 2.9-2.9a.7.7 0 0 1 1 1Z" })
|
|
852
|
+
}), labels.securedBy]
|
|
853
|
+
});
|
|
854
|
+
const header = (heading, subheading) => /* @__PURE__ */ jsxs("div", {
|
|
855
|
+
className: "polyx-signin__header",
|
|
856
|
+
children: [
|
|
857
|
+
props.logo ? /* @__PURE__ */ jsx("div", {
|
|
858
|
+
className: "polyx-signin__logo",
|
|
859
|
+
children: props.logo
|
|
860
|
+
}) : null,
|
|
861
|
+
/* @__PURE__ */ jsx("h1", {
|
|
862
|
+
className: "polyx-signin__heading",
|
|
863
|
+
children: heading
|
|
864
|
+
}),
|
|
865
|
+
/* @__PURE__ */ jsx("p", {
|
|
866
|
+
className: "polyx-signin__subheading",
|
|
867
|
+
children: subheading
|
|
868
|
+
})
|
|
869
|
+
]
|
|
870
|
+
});
|
|
871
|
+
if (sent) return /* @__PURE__ */ jsx("div", {
|
|
872
|
+
className: rootClass,
|
|
873
|
+
"data-polyx-forgot": "sent",
|
|
874
|
+
children: /* @__PURE__ */ jsx(RecoveryStatus, {
|
|
875
|
+
icon: "mail",
|
|
876
|
+
heading: labels.sentHeading,
|
|
877
|
+
footer,
|
|
878
|
+
actions: /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("button", {
|
|
879
|
+
type: "button",
|
|
880
|
+
className: "polyx-signin__link",
|
|
881
|
+
onClick: () => setSent(false),
|
|
882
|
+
children: labels.differentEmail
|
|
883
|
+
}), /* @__PURE__ */ jsx("a", {
|
|
884
|
+
className: "polyx-signin__link",
|
|
885
|
+
href: signInPath,
|
|
886
|
+
children: labels.backToSignIn
|
|
887
|
+
})] }),
|
|
888
|
+
children: withEmail(labels.sentSubheading, email)
|
|
889
|
+
})
|
|
890
|
+
});
|
|
891
|
+
return /* @__PURE__ */ jsx("div", {
|
|
892
|
+
className: rootClass,
|
|
893
|
+
"data-polyx-forgot": "request",
|
|
894
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
895
|
+
className: "polyx-signin__card",
|
|
896
|
+
children: [
|
|
897
|
+
header(labels.heading, labels.subheading),
|
|
898
|
+
/* @__PURE__ */ jsxs("form", {
|
|
899
|
+
className: "polyx-signin__form",
|
|
900
|
+
onSubmit,
|
|
901
|
+
children: [
|
|
902
|
+
/* @__PURE__ */ jsxs("label", {
|
|
903
|
+
className: "polyx-signin__field",
|
|
904
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
905
|
+
className: "polyx-signin__label",
|
|
906
|
+
children: labels.emailLabel
|
|
907
|
+
}), /* @__PURE__ */ jsx("input", {
|
|
908
|
+
className: "polyx-signin__input",
|
|
909
|
+
type: "email",
|
|
910
|
+
name: "email",
|
|
911
|
+
autoComplete: "username",
|
|
912
|
+
placeholder: labels.emailPlaceholder,
|
|
913
|
+
required: true,
|
|
914
|
+
value: email,
|
|
915
|
+
disabled: submitting,
|
|
916
|
+
onChange: (event) => setEmail(event.target.value)
|
|
917
|
+
})]
|
|
918
|
+
}),
|
|
919
|
+
error ? /* @__PURE__ */ jsx("p", {
|
|
920
|
+
role: "alert",
|
|
921
|
+
className: "polyx-signin__error",
|
|
922
|
+
children: error
|
|
923
|
+
}) : null,
|
|
924
|
+
/* @__PURE__ */ jsx("button", {
|
|
925
|
+
type: "submit",
|
|
926
|
+
className: "polyx-signin__submit",
|
|
927
|
+
disabled: submitting,
|
|
928
|
+
children: submitting ? labels.submitting : labels.submit
|
|
929
|
+
})
|
|
930
|
+
]
|
|
931
|
+
}),
|
|
932
|
+
footer
|
|
933
|
+
]
|
|
934
|
+
})
|
|
935
|
+
});
|
|
936
|
+
}
|
|
937
|
+
//#endregion
|
|
938
|
+
//#region src/components/reset-password.tsx
|
|
939
|
+
const DEFAULT_LABELS = {
|
|
940
|
+
heading: "Choose a new password",
|
|
941
|
+
subheading: "Enter and confirm your new password.",
|
|
942
|
+
newPasswordLabel: "New password",
|
|
943
|
+
confirmPasswordLabel: "Confirm new password",
|
|
944
|
+
passwordPlaceholder: "••••••••",
|
|
945
|
+
submit: "Reset password",
|
|
946
|
+
submitting: "Resetting…",
|
|
947
|
+
successHeading: "Password updated",
|
|
948
|
+
successSubheading: "You can now sign in with your new password.",
|
|
949
|
+
invalidHeading: "This link is invalid or expired",
|
|
950
|
+
invalidSubheading: "Password reset links can be used once and expire quickly. Request a new one.",
|
|
951
|
+
requestNewLink: "Request a new link",
|
|
952
|
+
backToSignIn: "Back to sign in",
|
|
953
|
+
securedBy: "Secured by PolyX",
|
|
954
|
+
passwordMismatch: "Those passwords don't match.",
|
|
955
|
+
weakPassword: "Password must be at least 8 characters.",
|
|
956
|
+
genericError: "Something went wrong. Please try again."
|
|
957
|
+
};
|
|
958
|
+
/** Matches poly-auth's v5 reset floor (F040). */
|
|
959
|
+
const DEFAULT_MIN_LENGTH = 8;
|
|
960
|
+
/**
|
|
961
|
+
* The reset-completion form (poly-x v5 / F040). Reads the opaque token from the URL,
|
|
962
|
+
* collects a new password, and POSTs to the SDK's BFF (`@poly-x/next` `resetPassword`),
|
|
963
|
+
* which redeems the token single-use server-side. The token is never decoded in the
|
|
964
|
+
* browser — identity, validity, and expiry are all enforced by the backend.
|
|
965
|
+
*
|
|
966
|
+
* Reuses the `<SignIn/>` styling: `import "@poly-x/react/styles.css"` once, then theme
|
|
967
|
+
* via the `--polyx-*` custom properties. Target `.polyx-reset` for form-specific tweaks.
|
|
968
|
+
*/
|
|
969
|
+
function ResetPassword(props) {
|
|
970
|
+
const action = props.action ?? "/api/polyx/reset-password";
|
|
971
|
+
const minLength = props.minLength ?? DEFAULT_MIN_LENGTH;
|
|
972
|
+
const signInPath = props.signInPath ?? "/sign-in";
|
|
973
|
+
const forgotPasswordPath = props.forgotPasswordPath ?? "/forgot-password";
|
|
974
|
+
const labels = {
|
|
975
|
+
...DEFAULT_LABELS,
|
|
976
|
+
...props.labels
|
|
977
|
+
};
|
|
978
|
+
const [token] = useState(() => props.token ?? (typeof window !== "undefined" ? new URLSearchParams(window.location.search).get("token") ?? "" : ""));
|
|
979
|
+
const [newPassword, setNewPassword] = useState("");
|
|
980
|
+
const [confirmPassword, setConfirmPassword] = useState("");
|
|
981
|
+
const [submitting, setSubmitting] = useState(false);
|
|
982
|
+
const [error, setError] = useState(null);
|
|
983
|
+
const [step, setStep] = useState(token ? "form" : "invalid");
|
|
984
|
+
async function onSubmit(event) {
|
|
985
|
+
event.preventDefault();
|
|
986
|
+
if (newPassword !== confirmPassword) {
|
|
987
|
+
setError(labels.passwordMismatch);
|
|
988
|
+
return;
|
|
989
|
+
}
|
|
990
|
+
setSubmitting(true);
|
|
991
|
+
setError(null);
|
|
992
|
+
try {
|
|
993
|
+
switch ((await (await fetch(action, {
|
|
994
|
+
method: "POST",
|
|
995
|
+
headers: { "content-type": "application/json" },
|
|
996
|
+
body: JSON.stringify({
|
|
997
|
+
token,
|
|
998
|
+
newPassword,
|
|
999
|
+
confirmPassword
|
|
1000
|
+
})
|
|
1001
|
+
})).json().catch(() => ({}))).status) {
|
|
1002
|
+
case "success":
|
|
1003
|
+
setStep("success");
|
|
1004
|
+
if (props.afterResetPath) window.location.assign(props.afterResetPath);
|
|
1005
|
+
return;
|
|
1006
|
+
case "weak_password":
|
|
1007
|
+
setError(labels.weakPassword);
|
|
1008
|
+
return;
|
|
1009
|
+
case "password_mismatch":
|
|
1010
|
+
setError(labels.passwordMismatch);
|
|
1011
|
+
return;
|
|
1012
|
+
case "invalid_or_expired":
|
|
1013
|
+
setStep("invalid");
|
|
1014
|
+
return;
|
|
1015
|
+
default:
|
|
1016
|
+
setError(labels.genericError);
|
|
1017
|
+
return;
|
|
1018
|
+
}
|
|
1019
|
+
} catch {
|
|
1020
|
+
setError(labels.genericError);
|
|
1021
|
+
} finally {
|
|
1022
|
+
setSubmitting(false);
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
const rootClass = [
|
|
1026
|
+
"polyx-signin",
|
|
1027
|
+
"polyx-reset",
|
|
1028
|
+
props.className
|
|
1029
|
+
].filter(Boolean).join(" ");
|
|
1030
|
+
const footer = props.showSecuredBy === false ? null : /* @__PURE__ */ jsxs("p", {
|
|
1031
|
+
className: "polyx-signin__footer",
|
|
1032
|
+
children: [/* @__PURE__ */ jsx("svg", {
|
|
1033
|
+
className: "polyx-signin__shield",
|
|
1034
|
+
viewBox: "0 0 16 16",
|
|
1035
|
+
fill: "currentColor",
|
|
1036
|
+
"aria-hidden": "true",
|
|
1037
|
+
focusable: "false",
|
|
1038
|
+
children: /* @__PURE__ */ jsx("path", { d: "M8 0.5 2 3v4.2c0 3.5 2.5 6.7 6 7.8 3.5-1.1 6-4.3 6-7.8V3L8 .5Zm2.9 5.6-3.4 3.4a.7.7 0 0 1-1 0L5.1 8.1a.7.7 0 1 1 1-1l.9.9 2.9-2.9a.7.7 0 0 1 1 1Z" })
|
|
1039
|
+
}), labels.securedBy]
|
|
1040
|
+
});
|
|
1041
|
+
const header = (heading, subheading) => /* @__PURE__ */ jsxs("div", {
|
|
1042
|
+
className: "polyx-signin__header",
|
|
1043
|
+
children: [
|
|
1044
|
+
props.logo ? /* @__PURE__ */ jsx("div", {
|
|
1045
|
+
className: "polyx-signin__logo",
|
|
1046
|
+
children: props.logo
|
|
1047
|
+
}) : null,
|
|
1048
|
+
/* @__PURE__ */ jsx("h1", {
|
|
1049
|
+
className: "polyx-signin__heading",
|
|
1050
|
+
children: heading
|
|
1051
|
+
}),
|
|
1052
|
+
/* @__PURE__ */ jsx("p", {
|
|
1053
|
+
className: "polyx-signin__subheading",
|
|
1054
|
+
children: subheading
|
|
1055
|
+
})
|
|
1056
|
+
]
|
|
1057
|
+
});
|
|
1058
|
+
if (step === "success") return /* @__PURE__ */ jsx("div", {
|
|
1059
|
+
className: rootClass,
|
|
1060
|
+
"data-polyx-reset": "success",
|
|
1061
|
+
children: /* @__PURE__ */ jsx(RecoveryStatus, {
|
|
1062
|
+
icon: "check",
|
|
1063
|
+
heading: labels.successHeading,
|
|
1064
|
+
footer,
|
|
1065
|
+
actions: /* @__PURE__ */ jsx("a", {
|
|
1066
|
+
className: "polyx-signin__link",
|
|
1067
|
+
href: signInPath,
|
|
1068
|
+
children: labels.backToSignIn
|
|
1069
|
+
}),
|
|
1070
|
+
children: labels.successSubheading
|
|
1071
|
+
})
|
|
1072
|
+
});
|
|
1073
|
+
if (step === "invalid") return /* @__PURE__ */ jsx("div", {
|
|
1074
|
+
className: rootClass,
|
|
1075
|
+
"data-polyx-reset": "invalid",
|
|
1076
|
+
children: /* @__PURE__ */ jsx(RecoveryStatus, {
|
|
1077
|
+
icon: "warning",
|
|
1078
|
+
tone: "danger",
|
|
1079
|
+
heading: labels.invalidHeading,
|
|
1080
|
+
footer,
|
|
1081
|
+
actions: /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("a", {
|
|
1082
|
+
className: "polyx-signin__link",
|
|
1083
|
+
href: forgotPasswordPath,
|
|
1084
|
+
children: labels.requestNewLink
|
|
1085
|
+
}), /* @__PURE__ */ jsx("a", {
|
|
1086
|
+
className: "polyx-signin__link",
|
|
1087
|
+
href: signInPath,
|
|
1088
|
+
children: labels.backToSignIn
|
|
1089
|
+
})] }),
|
|
1090
|
+
children: labels.invalidSubheading
|
|
1091
|
+
})
|
|
1092
|
+
});
|
|
1093
|
+
return /* @__PURE__ */ jsx("div", {
|
|
1094
|
+
className: rootClass,
|
|
1095
|
+
"data-polyx-reset": step,
|
|
1096
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
1097
|
+
className: "polyx-signin__card",
|
|
1098
|
+
children: [
|
|
1099
|
+
header(labels.heading, labels.subheading),
|
|
1100
|
+
/* @__PURE__ */ jsxs("form", {
|
|
1101
|
+
className: "polyx-signin__form",
|
|
1102
|
+
onSubmit,
|
|
1103
|
+
children: [
|
|
1104
|
+
/* @__PURE__ */ jsxs("label", {
|
|
1105
|
+
className: "polyx-signin__field",
|
|
1106
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
1107
|
+
className: "polyx-signin__label",
|
|
1108
|
+
children: labels.newPasswordLabel
|
|
1109
|
+
}), /* @__PURE__ */ jsx("input", {
|
|
1110
|
+
className: "polyx-signin__input",
|
|
1111
|
+
type: "password",
|
|
1112
|
+
name: "newPassword",
|
|
1113
|
+
autoComplete: "new-password",
|
|
1114
|
+
placeholder: labels.passwordPlaceholder,
|
|
1115
|
+
required: true,
|
|
1116
|
+
minLength,
|
|
1117
|
+
value: newPassword,
|
|
1118
|
+
disabled: submitting,
|
|
1119
|
+
onChange: (event) => setNewPassword(event.target.value)
|
|
1120
|
+
})]
|
|
1121
|
+
}),
|
|
1122
|
+
/* @__PURE__ */ jsxs("label", {
|
|
1123
|
+
className: "polyx-signin__field",
|
|
1124
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
1125
|
+
className: "polyx-signin__label",
|
|
1126
|
+
children: labels.confirmPasswordLabel
|
|
1127
|
+
}), /* @__PURE__ */ jsx("input", {
|
|
1128
|
+
className: "polyx-signin__input",
|
|
1129
|
+
type: "password",
|
|
1130
|
+
name: "confirmPassword",
|
|
1131
|
+
autoComplete: "new-password",
|
|
1132
|
+
placeholder: labels.passwordPlaceholder,
|
|
1133
|
+
required: true,
|
|
1134
|
+
minLength,
|
|
1135
|
+
value: confirmPassword,
|
|
1136
|
+
disabled: submitting,
|
|
1137
|
+
onChange: (event) => setConfirmPassword(event.target.value)
|
|
1138
|
+
})]
|
|
1139
|
+
}),
|
|
1140
|
+
error ? /* @__PURE__ */ jsx("p", {
|
|
1141
|
+
role: "alert",
|
|
1142
|
+
className: "polyx-signin__error",
|
|
1143
|
+
children: error
|
|
1144
|
+
}) : null,
|
|
1145
|
+
/* @__PURE__ */ jsx("button", {
|
|
1146
|
+
type: "submit",
|
|
1147
|
+
className: "polyx-signin__submit",
|
|
1148
|
+
disabled: submitting,
|
|
1149
|
+
children: submitting ? labels.submitting : labels.submit
|
|
1150
|
+
})
|
|
1151
|
+
]
|
|
1152
|
+
}),
|
|
1153
|
+
footer
|
|
1154
|
+
]
|
|
1155
|
+
})
|
|
1156
|
+
});
|
|
1157
|
+
}
|
|
1158
|
+
//#endregion
|
|
670
1159
|
//#region src/components/auth-callback.tsx
|
|
671
1160
|
/** Drop on your `/auth/callback` route: completes the sign-in exchange on mount. */
|
|
672
1161
|
function AuthCallback({ children = null }) {
|
|
@@ -685,4 +1174,4 @@ function AuthCallback({ children = null }) {
|
|
|
685
1174
|
const PACKAGE_NAME = "@poly-x/react";
|
|
686
1175
|
const version = "0.0.0";
|
|
687
1176
|
//#endregion
|
|
688
|
-
export { AuthCallback, BroadcastAuthChannel, BroadcastChannelSessionChannel, PACKAGE_NAME, PolyXContext, PolyXProvider, PopupCancelledError, Protected, SessionStoragePkceStore, SignIn, WebLocksLock, WindowBrowserBridge, createLoginController, useAuth, useAuthCallback, useSession, useUser, version };
|
|
1177
|
+
export { AuthCallback, BroadcastAuthChannel, BroadcastChannelSessionChannel, ForgotPassword, PACKAGE_NAME, PolyXContext, PolyXProvider, PopupCancelledError, Protected, ResetPassword, SessionStoragePkceStore, SignIn, WebLocksLock, WindowBrowserBridge, createLoginController, useAuth, useAuthCallback, useSession, useUser, version };
|