cauthy-auth 0.16.0 → 0.18.0

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/react.d.mts CHANGED
@@ -484,10 +484,19 @@ interface AuthClient {
484
484
  sentTo: string;
485
485
  channel: string;
486
486
  }>>;
487
- /** Mails a link that signs the person in. The answer is the same whether or not the address has an account. */
487
+ /**
488
+ * Mails a link (and a code) that signs the person in. The answer is the
489
+ * same whether or not the address has an account. `create`: an address
490
+ * with no account gets one when the link or code is used, if the
491
+ * workspace accepts new accounts — one screen signs people in and up.
492
+ * `returnTo` (a path of this site) travels in the link: the page it opens
493
+ * gets it as `?return_to=`, and `emailCode` answers it.
494
+ */
488
495
  magicLink(form: {
489
496
  email: string;
490
497
  captchaToken?: string;
498
+ create?: boolean;
499
+ returnTo?: string;
491
500
  }): Promise<AuthResult<'sent'>>;
492
501
  /** Signs in with the token the mailed link opened the page with (`ml_token`). */
493
502
  magicLinkVerify(form: {
@@ -500,6 +509,7 @@ interface AuthClient {
500
509
  code: string;
501
510
  }): Promise<AuthResult<'signed_in', {
502
511
  session: PublicSession;
512
+ returnTo?: string;
503
513
  }>>;
504
514
  /**
505
515
  * Signs in with a passkey, on this page: the browser asks the person for
@@ -507,11 +517,14 @@ interface AuthClient {
507
517
  * workspace's passkeys (`methods().passkeyHere` is false), the browser goes
508
518
  * to Cauthy's page for that step alone and comes back signed in, at
509
519
  * `returnTo`: then the answer is `redirected`. `cancelled`: the person
510
- * closed the browser's prompt.
520
+ * closed the browser's prompt. `email`, when the person typed it: the
521
+ * browser is told which passkeys are theirs, so a security key that did
522
+ * not keep its passkey (one made before cauthy-auth 0.18.0) is found too.
511
523
  */
512
524
  passkey(options?: {
513
525
  returnTo?: string;
514
526
  locale?: string;
527
+ email?: string;
515
528
  }): Promise<AuthResult<'signed_in', {
516
529
  session: PublicSession;
517
530
  }> | AuthResult<'redirected'>>;
@@ -709,6 +722,12 @@ interface AuthFormProps {
709
722
  onSuccess?: () => void;
710
723
  /** Where to go after signing in, and where a provider returns to. Default: `/`. */
711
724
  returnTo?: string;
725
+ /**
726
+ * `<SignIn />`: a sign-in link sent to an address with no account makes
727
+ * one when it is used (if the workspace accepts new accounts), so this one
728
+ * screen signs people in and up alike. Default: off.
729
+ */
730
+ createAccounts?: boolean;
712
731
  /** Paths of the application's other screens, for the links between them. */
713
732
  links?: {
714
733
  signIn?: string;
package/dist/react.d.ts CHANGED
@@ -484,10 +484,19 @@ interface AuthClient {
484
484
  sentTo: string;
485
485
  channel: string;
486
486
  }>>;
487
- /** Mails a link that signs the person in. The answer is the same whether or not the address has an account. */
487
+ /**
488
+ * Mails a link (and a code) that signs the person in. The answer is the
489
+ * same whether or not the address has an account. `create`: an address
490
+ * with no account gets one when the link or code is used, if the
491
+ * workspace accepts new accounts — one screen signs people in and up.
492
+ * `returnTo` (a path of this site) travels in the link: the page it opens
493
+ * gets it as `?return_to=`, and `emailCode` answers it.
494
+ */
488
495
  magicLink(form: {
489
496
  email: string;
490
497
  captchaToken?: string;
498
+ create?: boolean;
499
+ returnTo?: string;
491
500
  }): Promise<AuthResult<'sent'>>;
492
501
  /** Signs in with the token the mailed link opened the page with (`ml_token`). */
493
502
  magicLinkVerify(form: {
@@ -500,6 +509,7 @@ interface AuthClient {
500
509
  code: string;
501
510
  }): Promise<AuthResult<'signed_in', {
502
511
  session: PublicSession;
512
+ returnTo?: string;
503
513
  }>>;
504
514
  /**
505
515
  * Signs in with a passkey, on this page: the browser asks the person for
@@ -507,11 +517,14 @@ interface AuthClient {
507
517
  * workspace's passkeys (`methods().passkeyHere` is false), the browser goes
508
518
  * to Cauthy's page for that step alone and comes back signed in, at
509
519
  * `returnTo`: then the answer is `redirected`. `cancelled`: the person
510
- * closed the browser's prompt.
520
+ * closed the browser's prompt. `email`, when the person typed it: the
521
+ * browser is told which passkeys are theirs, so a security key that did
522
+ * not keep its passkey (one made before cauthy-auth 0.18.0) is found too.
511
523
  */
512
524
  passkey(options?: {
513
525
  returnTo?: string;
514
526
  locale?: string;
527
+ email?: string;
515
528
  }): Promise<AuthResult<'signed_in', {
516
529
  session: PublicSession;
517
530
  }> | AuthResult<'redirected'>>;
@@ -709,6 +722,12 @@ interface AuthFormProps {
709
722
  onSuccess?: () => void;
710
723
  /** Where to go after signing in, and where a provider returns to. Default: `/`. */
711
724
  returnTo?: string;
725
+ /**
726
+ * `<SignIn />`: a sign-in link sent to an address with no account makes
727
+ * one when it is used (if the workspace accepts new accounts), so this one
728
+ * screen signs people in and up alike. Default: off.
729
+ */
730
+ createAccounts?: boolean;
712
731
  /** Paths of the application's other screens, for the links between them. */
713
732
  links?: {
714
733
  signIn?: string;
package/dist/react.js CHANGED
@@ -302,14 +302,15 @@ function createAuthClient(options = {}) {
302
302
  mfa: (f) => post("/sign-in/mfa", { code: f.code }, checkCode(f.code)),
303
303
  mfaMethods: () => post("/sign-in/mfa/methods", {}, null),
304
304
  mfaSendCode: () => post("/sign-in/mfa/send", {}, null),
305
- magicLink: (f) => post("/sign-in/magic-link", { email: f.email }, checkEmail(f.email), f.captchaToken),
305
+ magicLink: (f) => post("/sign-in/magic-link", { email: f.email, ...f.create ? { create: true } : {}, ...f.returnTo ? { returnTo: f.returnTo } : {} }, checkEmail(f.email), f.captchaToken),
306
306
  magicLinkVerify: (f) => post("/sign-in/magic-link/verify", { token: f.token }, null),
307
307
  emailCode: (f) => post("/sign-in/email-code", { code: f.code }, checkCode(f.code)),
308
308
  passkeyUrl,
309
309
  async passkey(o) {
310
310
  const m = await methods();
311
311
  if (m.passkeyHere && canUsePasskeys()) {
312
- const r = await ceremony("/sign-in/passkey/begin", "/sign-in/passkey/finish", {}, getPasskey);
312
+ const email = o?.email?.trim() ?? "";
313
+ const r = await ceremony("/sign-in/passkey/begin", "/sign-in/passkey/finish", email !== "" && checkEmail(email) === null ? { email } : {}, getPasskey);
313
314
  if (r.ok || r.error.code !== "passkey_unavailable" || !m.passkeyOnCauthy) return r;
314
315
  } else if (!m.passkeyOnCauthy) {
315
316
  return { ok: false, error: { code: "passkey_unavailable", message: "Passkeys cannot be used here." } };
@@ -836,6 +837,9 @@ function useMethods(auth) {
836
837
  }, []);
837
838
  return methods;
838
839
  }
840
+ function sitePath(to) {
841
+ return to && to.startsWith("/") && !to.startsWith("//") && !to.includes("\\") ? to : void 0;
842
+ }
839
843
  function useForm(props) {
840
844
  const t = { ...TEXTS[props.locale ?? "en"], ...props.texts, errors: { ...TEXTS[props.locale ?? "en"].errors, ...props.texts?.errors } };
841
845
  const cx = (slot) => props.classNames?.[slot] ?? `cauthy-${slot}`;
@@ -844,9 +848,9 @@ function useForm(props) {
844
848
  const [error, setError] = (0, import_react.useState)(null);
845
849
  const [busy, setBusy] = (0, import_react.useState)(false);
846
850
  const say = (e) => t.errors[e.code] ?? e.message;
847
- const done = () => {
851
+ const done = (to) => {
848
852
  if (props.onSuccess) props.onSuccess();
849
- else window.location.assign(props.returnTo ?? "/");
853
+ else window.location.assign(sitePath(to) ?? props.returnTo ?? "/");
850
854
  };
851
855
  const root = useRoot(props);
852
856
  return { t, cx, Button, Input, error, setError, busy, setBusy, say, done, root };
@@ -936,7 +940,7 @@ function SignIn(props) {
936
940
  if (!token) return;
937
941
  setStep("opening");
938
942
  void auth.signIn.magicLinkVerify({ token }).then((r) => {
939
- if (r.ok) return f.done();
943
+ if (r.ok) return f.done(new URLSearchParams(window.location.search).get("return_to") ?? void 0);
940
944
  f.setError(r.error);
941
945
  setStep("password");
942
946
  });
@@ -967,13 +971,13 @@ function SignIn(props) {
967
971
  return f.setError(r.error);
968
972
  }
969
973
  if (r.status === "mfa_required") return setStep("code");
970
- f.done();
974
+ f.done("returnTo" in r && typeof r.returnTo === "string" ? r.returnTo : void 0);
971
975
  };
972
976
  const usePasskey = async () => {
973
977
  autofill.current?.abort();
974
978
  f.setBusy(true);
975
979
  f.setError(null);
976
- const r = await auth.signIn.passkey({ returnTo: props.returnTo, locale: props.locale });
980
+ const r = await auth.signIn.passkey({ returnTo: props.returnTo, locale: props.locale, email });
977
981
  if (r.ok && r.status === "redirected") return;
978
982
  f.setBusy(false);
979
983
  if (!r.ok) setOffer((n) => n + 1);
@@ -984,7 +988,7 @@ function SignIn(props) {
984
988
  autofill.current?.abort();
985
989
  f.setBusy(true);
986
990
  f.setError(null);
987
- const r = await auth.signIn.magicLink({ email, captchaToken: challenge.token });
991
+ const r = await auth.signIn.magicLink({ email, captchaToken: challenge.token, create: props.createAccounts, returnTo: props.returnTo });
988
992
  f.setBusy(false);
989
993
  challenge.renew();
990
994
  if (!r.ok) return f.setError(r.error);