cauthy-auth 0.22.0 → 0.23.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
@@ -606,13 +606,34 @@ interface AuthClient {
606
606
  }): Promise<AuthResult<'signed_in', {
607
607
  session: PublicSession;
608
608
  }>>;
609
- /** While the second step is asked for: whether the account has an authenticator app, and the phone (masked) a code can be sent to. */
609
+ /** While the second step is asked for: whether the account has an authenticator app, the phone (masked) a code can be sent to, and phones that approve sign-ins. */
610
610
  mfaMethods(): Promise<AuthResult<'ok', {
611
611
  totp: boolean;
612
612
  phone: {
613
613
  number: string;
614
614
  channel: string;
615
615
  } | null;
616
+ push: {
617
+ devices: number;
618
+ } | null;
619
+ }>>;
620
+ /**
621
+ * Sends a push to the account's phones (the application's app, with
622
+ * cauthy-expo): show `number`, which the person picks on the phone, then
623
+ * call `mfaPushWait`.
624
+ */
625
+ mfaPush(): Promise<AuthResult<'sent', {
626
+ number: number;
627
+ }>>;
628
+ /**
629
+ * Waits for the phone's answer, listening (server-sent events) rather than
630
+ * asking again and again, and signs in when it approves. `denied`: turned
631
+ * down on the phone; `expired`: nobody answered in time.
632
+ */
633
+ mfaPushWait(o?: {
634
+ signal?: AbortSignal;
635
+ }): Promise<AuthResult<'signed_in', {
636
+ session: PublicSession;
616
637
  }>>;
617
638
  /** Sends a code to the account's phone, by SMS or WhatsApp; `mfa` takes it. A few per sign-in. */
618
639
  mfaSendCode(): Promise<AuthResult<'sent', {
@@ -815,6 +836,10 @@ interface AuthTexts {
815
836
  sendCodeSms: string;
816
837
  sendCodeWhatsapp: string;
817
838
  codeSent: string;
839
+ /** The second step approved on the phone; "{n}" is the number to pick. */
840
+ pushSend: string;
841
+ pushPick: string;
842
+ pushWaiting: string;
818
843
  phone: string;
819
844
  phoneHelp: string;
820
845
  phoneOn: string;
package/dist/react.d.ts CHANGED
@@ -606,13 +606,34 @@ interface AuthClient {
606
606
  }): Promise<AuthResult<'signed_in', {
607
607
  session: PublicSession;
608
608
  }>>;
609
- /** While the second step is asked for: whether the account has an authenticator app, and the phone (masked) a code can be sent to. */
609
+ /** While the second step is asked for: whether the account has an authenticator app, the phone (masked) a code can be sent to, and phones that approve sign-ins. */
610
610
  mfaMethods(): Promise<AuthResult<'ok', {
611
611
  totp: boolean;
612
612
  phone: {
613
613
  number: string;
614
614
  channel: string;
615
615
  } | null;
616
+ push: {
617
+ devices: number;
618
+ } | null;
619
+ }>>;
620
+ /**
621
+ * Sends a push to the account's phones (the application's app, with
622
+ * cauthy-expo): show `number`, which the person picks on the phone, then
623
+ * call `mfaPushWait`.
624
+ */
625
+ mfaPush(): Promise<AuthResult<'sent', {
626
+ number: number;
627
+ }>>;
628
+ /**
629
+ * Waits for the phone's answer, listening (server-sent events) rather than
630
+ * asking again and again, and signs in when it approves. `denied`: turned
631
+ * down on the phone; `expired`: nobody answered in time.
632
+ */
633
+ mfaPushWait(o?: {
634
+ signal?: AbortSignal;
635
+ }): Promise<AuthResult<'signed_in', {
636
+ session: PublicSession;
616
637
  }>>;
617
638
  /** Sends a code to the account's phone, by SMS or WhatsApp; `mfa` takes it. A few per sign-in. */
618
639
  mfaSendCode(): Promise<AuthResult<'sent', {
@@ -815,6 +836,10 @@ interface AuthTexts {
815
836
  sendCodeSms: string;
816
837
  sendCodeWhatsapp: string;
817
838
  codeSent: string;
839
+ /** The second step approved on the phone; "{n}" is the number to pick. */
840
+ pushSend: string;
841
+ pushPick: string;
842
+ pushWaiting: string;
818
843
  phone: string;
819
844
  phoneHelp: string;
820
845
  phoneOn: string;
package/dist/react.js CHANGED
@@ -310,6 +310,35 @@ function createAuthClient(options = {}) {
310
310
  mfa: (f) => post("/sign-in/mfa", { code: f.code }, checkCode(f.code)),
311
311
  mfaMethods: () => post("/sign-in/mfa/methods", {}, null),
312
312
  mfaSendCode: () => post("/sign-in/mfa/send", {}, null),
313
+ mfaPush: () => post("/sign-in/mfa/push", {}, null),
314
+ async mfaPushWait(o = {}) {
315
+ const status = await new Promise((resolve) => {
316
+ if (typeof EventSource !== "function") return resolve("approved");
317
+ const source = new EventSource(`${base}/sign-in/mfa/push/events`, { withCredentials: true });
318
+ const stop = (st) => {
319
+ source.close();
320
+ resolve(st);
321
+ };
322
+ o.signal?.addEventListener("abort", () => stop("aborted"));
323
+ source.addEventListener("status", (e) => {
324
+ const st = (() => {
325
+ try {
326
+ return String(JSON.parse(e.data).status);
327
+ } catch {
328
+ return "";
329
+ }
330
+ })();
331
+ if (st && st !== "pending") stop(st);
332
+ });
333
+ source.onerror = () => {
334
+ if (source.readyState === EventSource.CLOSED) stop("expired");
335
+ };
336
+ });
337
+ if (status === "aborted") return { ok: false, error: { code: "aborted", message: "Stopped waiting." } };
338
+ if (status === "denied") return { ok: false, error: { code: "push_denied", message: "The sign-in was turned down on the phone." } };
339
+ if (status === "expired") return { ok: false, error: { code: "push_expired", message: "Nobody answered on the phone in time." } };
340
+ return post("/sign-in/mfa/push/finish", {}, null);
341
+ },
313
342
  magicLink: (f) => post("/sign-in/magic-link", { email: f.email, ...f.create ? { create: true } : {}, ...f.returnTo ? { returnTo: f.returnTo } : {} }, checkEmail(f.email), f.captchaToken),
314
343
  magicLinkVerify: (f) => post("/sign-in/magic-link/verify", { token: f.token }, null),
315
344
  emailCode: (f) => post("/sign-in/email-code", { code: f.code }, checkCode(f.code)),
@@ -574,6 +603,9 @@ var TEXTS = {
574
603
  sendCodeSms: "Send a code by SMS to {to}",
575
604
  sendCodeWhatsapp: "Send a code by WhatsApp to {to}",
576
605
  codeSent: "We sent a code to {to}. Type it here.",
606
+ pushSend: "Approve on my phone",
607
+ pushPick: "On your phone, pick {n}",
608
+ pushWaiting: "We sent a notification to your phone. Open it and pick this number.",
577
609
  phone: "Phone, with its country code",
578
610
  phoneHelp: "Get your codes by SMS or WhatsApp instead of an app.",
579
611
  phoneOn: "Codes go to {to}.",
@@ -720,6 +752,9 @@ var TEXTS = {
720
752
  sendCodeSms: "Enviar un c\xF3digo por SMS a {to}",
721
753
  sendCodeWhatsapp: "Enviar un c\xF3digo por WhatsApp a {to}",
722
754
  codeSent: "Te enviamos un c\xF3digo a {to}. Escr\xEDbelo aqu\xED.",
755
+ pushSend: "Aprobar en mi tel\xE9fono",
756
+ pushPick: "En tu tel\xE9fono, elige el {n}",
757
+ pushWaiting: "Enviamos una notificaci\xF3n a tu tel\xE9fono. \xC1brela y elige este n\xFAmero.",
723
758
  phone: "Tel\xE9fono, con su c\xF3digo de pa\xEDs",
724
759
  phoneHelp: "Recibe tus c\xF3digos por SMS o WhatsApp en vez de una app.",
725
760
  phoneOn: "Los c\xF3digos van a {to}.",
@@ -1022,9 +1057,28 @@ function SignIn(props) {
1022
1057
  (0, import_react.useEffect)(() => {
1023
1058
  if (step !== "code") return;
1024
1059
  void auth.signIn.mfaMethods().then((r) => {
1025
- if (r.ok) setPhoneWay({ totp: r.totp, phone: r.phone });
1060
+ if (r.ok) setPhoneWay({ totp: r.totp, phone: r.phone, push: r.push });
1026
1061
  });
1027
1062
  }, [step]);
1063
+ const [pushNumber, setPushNumber] = (0, import_react.useState)(0);
1064
+ const pushWait = (0, import_react.useRef)(null);
1065
+ (0, import_react.useEffect)(() => () => pushWait.current?.abort(), []);
1066
+ const sendPush = async () => {
1067
+ f.setBusy(true);
1068
+ f.setError(null);
1069
+ const r = await auth.signIn.mfaPush();
1070
+ f.setBusy(false);
1071
+ if (!r.ok) return f.setError(r.error);
1072
+ setPushNumber(r.number);
1073
+ pushWait.current?.abort();
1074
+ const ctl = new AbortController();
1075
+ pushWait.current = ctl;
1076
+ const done = await auth.signIn.mfaPushWait({ signal: ctl.signal });
1077
+ if (ctl.signal.aborted) return;
1078
+ setPushNumber(0);
1079
+ if (!done.ok) return f.setError(done.error);
1080
+ f.done();
1081
+ };
1028
1082
  const sendCode = async () => {
1029
1083
  f.setBusy(true);
1030
1084
  f.setError(null);
@@ -1136,6 +1190,11 @@ function SignIn(props) {
1136
1190
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { className: f.cx("form"), ...f.root, onSubmit: submit, noValidate: true, children: [
1137
1191
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: f.cx("notice"), children: phoneSent ? f.t.codeSent.replace("{to}", phoneSent) : phoneWay?.phone && !phoneWay.totp ? f.t.codeHelpPhone : f.t.codeHelp }),
1138
1192
  general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
1193
+ pushNumber > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: f.cx("notice"), role: "status", "aria-live": "polite", children: [
1194
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: f.t.pushPick.replace("{n}", String(pushNumber)) }),
1195
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { fontSize: 48, fontWeight: 700, margin: "4px 0", textAlign: "center" }, children: pushNumber }),
1196
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: f.t.pushWaiting })
1197
+ ] }) : phoneWay?.push && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("button"), onClick: () => void sendPush(), children: f.t.pushSend }),
1139
1198
  phoneWay?.phone && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => void sendCode(), children: (phoneWay.phone.channel === "whatsapp" ? f.t.sendCodeWhatsapp : f.t.sendCodeSms).replace("{to}", to) }),
1140
1199
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "code", type: "text", label: f.t.code, value: code, autoComplete: "one-time-code", required: true, onChange: setCode }),
1141
1200
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.verify })
@@ -1928,6 +1987,7 @@ function useAuth() {
1928
1987
  anonymously: (f) => then(client.signIn.anonymously(f)),
1929
1988
  email: (f) => then(client.signIn.email(f)),
1930
1989
  mfa: (f) => then(client.signIn.mfa(f)),
1990
+ mfaPushWait: (o) => then(client.signIn.mfaPushWait(o)),
1931
1991
  magicLinkVerify: (f) => then(client.signIn.magicLinkVerify(f)),
1932
1992
  emailCode: (f) => then(client.signIn.emailCode(f)),
1933
1993
  passkey: (o) => then(client.signIn.passkey(o)),