cauthy-auth 0.30.0 → 0.31.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.
Files changed (46) hide show
  1. package/README.md +10 -3
  2. package/dist/{cauthy-BXgsZ-Um.d.mts → cauthy-2jtP2LVQ.d.mts} +1 -1
  3. package/dist/{cauthy-CHENs452.d.ts → cauthy-CKfvvv_i.d.ts} +1 -1
  4. package/dist/cli.js +8 -5
  5. package/dist/client.d.mts +47 -6
  6. package/dist/client.d.ts +47 -6
  7. package/dist/client.js +16 -2
  8. package/dist/client.js.map +1 -1
  9. package/dist/client.mjs +16 -2
  10. package/dist/client.mjs.map +1 -1
  11. package/dist/express.d.mts +2 -2
  12. package/dist/express.d.ts +2 -2
  13. package/dist/fastify.d.mts +2 -2
  14. package/dist/fastify.d.ts +2 -2
  15. package/dist/hono.d.mts +2 -2
  16. package/dist/hono.d.ts +2 -2
  17. package/dist/index.d.mts +2 -2
  18. package/dist/index.d.ts +2 -2
  19. package/dist/index.js +64 -21
  20. package/dist/index.js.map +1 -1
  21. package/dist/index.mjs +64 -21
  22. package/dist/index.mjs.map +1 -1
  23. package/dist/next.d.mts +2 -2
  24. package/dist/next.d.ts +2 -2
  25. package/dist/react.d.mts +61 -5
  26. package/dist/react.d.ts +61 -5
  27. package/dist/react.js +183 -46
  28. package/dist/react.js.map +1 -1
  29. package/dist/react.mjs +182 -46
  30. package/dist/react.mjs.map +1 -1
  31. package/dist/styles.css +8 -0
  32. package/dist/svelte.d.mts +2 -2
  33. package/dist/svelte.d.ts +2 -2
  34. package/dist/svelte.js +16 -2
  35. package/dist/svelte.js.map +1 -1
  36. package/dist/svelte.mjs +16 -2
  37. package/dist/svelte.mjs.map +1 -1
  38. package/dist/{system-0mHrdAgO.d.mts → system-BrrJsrFW.d.mts} +14 -0
  39. package/dist/{system-BIYbS7h2.d.ts → system-BvvoNs2y.d.ts} +14 -0
  40. package/dist/vue.d.mts +2 -2
  41. package/dist/vue.d.ts +2 -2
  42. package/dist/vue.js +16 -2
  43. package/dist/vue.js.map +1 -1
  44. package/dist/vue.mjs +16 -2
  45. package/dist/vue.mjs.map +1 -1
  46. package/package.json +1 -1
package/dist/react.mjs CHANGED
@@ -373,11 +373,11 @@ function createAuthClient(options = {}) {
373
373
  if (res.ok) {
374
374
  const m = await res.json();
375
375
  if (m.password) policy = passwordPolicyOf(m.password);
376
- return { providers: [], signUp: true, magicLink: false, passkey: false, ...m, ...policy ? { password: policy } : {}, passkeyHere: m.passkeyHere === true, passkeyOnCauthy: m.passkeyOnCauthy ?? m.passkey === true, sso: m.sso === true };
376
+ return { providers: [], signUp: true, magicLink: false, passkey: false, ...m, ...policy ? { password: policy } : {}, passwordSignIn: m.passwordSignIn !== false, passkeyHere: m.passkeyHere === true, passkeyOnCauthy: m.passkeyOnCauthy ?? m.passkey === true, sso: m.sso === true };
377
377
  }
378
378
  } catch {
379
379
  }
380
- return { providers: [], signUp: true, magicLink: false, passkey: false, passkeyHere: false, passkeyOnCauthy: false, sso: false };
380
+ return { providers: [], signUp: true, passwordSignIn: true, magicLink: false, passkey: false, passkeyHere: false, passkeyOnCauthy: false, sso: false };
381
381
  }
382
382
  async function ceremony(begin, finish, form, run, more = {}) {
383
383
  const begun = await post(begin, form, null);
@@ -524,6 +524,20 @@ function createAuthClient(options = {}) {
524
524
  forgotPassword: (f) => post("/forgot-password", { email: f.email }, checkEmail(f.email), f.captchaToken),
525
525
  resetPassword: (f) => post("/reset-password", { token: f.token, password: f.password }, checkNewPassword(f.password, policy)),
526
526
  verifyEmail: (f) => post("/verify-email", { token: f.token }, null),
527
+ async invitation(token) {
528
+ const bad = { ok: false, error: { code: "invalid_token", message: "This invitation is not valid, or it has expired." } };
529
+ if (typeof token !== "string" || token.trim() === "") return bad;
530
+ try {
531
+ const res = await doFetch(`${base}/invitation?token=${encodeURIComponent(token.trim())}`, { credentials: "same-origin", headers: { accept: "application/json" } });
532
+ const body = await res.json().catch(() => null);
533
+ if (res.ok && body?.invitation) return { ok: true, status: "ok", invitation: body.invitation };
534
+ const e = body?.error;
535
+ return { ok: false, error: e && typeof e.code === "string" ? { code: e.code, message: String(e.message ?? "") } : UNAVAILABLE };
536
+ } catch {
537
+ return { ok: false, error: UNAVAILABLE };
538
+ }
539
+ },
540
+ acceptInvitation: (token, password) => post("/invitation/accept", { token, password }, checkNewPassword(password, policy)),
527
541
  signOut: () => post("/sign-out", {}, null),
528
542
  organizations: {
529
543
  async list(system) {
@@ -760,6 +774,13 @@ var TEXTS = {
760
774
  passwordChanged: "Your password was changed. You can sign in with it now.",
761
775
  confirming: "Confirming your address\u2026",
762
776
  emailConfirmed: "Your address is confirmed. You can sign in now.",
777
+ invitedTo: "You've been invited to {workspace}",
778
+ invitedToOrganization: "You've been invited to {organization} on {workspace}",
779
+ invitedAs: "You will sign in as {email}.",
780
+ choosePassword: "Choose a password",
781
+ acceptInvite: "Accept and sign in",
782
+ openingInvitation: "Opening your invitation\u2026",
783
+ invitationInvalid: "This invitation is not valid: it was already used, or it has expired. Ask whoever invited you to send it again.",
763
784
  profile: "Profile",
764
785
  save: "Save",
765
786
  saved: "Saved.",
@@ -874,7 +895,9 @@ var TEXTS = {
874
895
  invalid_assertion: "Your company's answer could not be checked. Try again.",
875
896
  provisioning_disabled: "Your company's sign-in worked, but this workspace has no account for you. Ask its admin to add you.",
876
897
  connection_inactive: "Your company's sign-in is turned off for this workspace.",
877
- invalid_grant: "That sign-in expired. Try again."
898
+ invalid_grant: "That sign-in expired. Try again.",
899
+ password_disabled: "This workspace does not sign in with a password. Use one of the other ways.",
900
+ passkeys_disabled: "Passkeys are turned off for this workspace."
878
901
  }
879
902
  },
880
903
  es: {
@@ -927,6 +950,13 @@ var TEXTS = {
927
950
  passwordChanged: "Tu contrase\xF1a se cambi\xF3. Ya puedes entrar con ella.",
928
951
  confirming: "Confirmando tu correo\u2026",
929
952
  emailConfirmed: "Tu correo qued\xF3 confirmado. Ya puedes entrar.",
953
+ invitedTo: "Te invitaron a {workspace}",
954
+ invitedToOrganization: "Te invitaron a {organization} en {workspace}",
955
+ invitedAs: "Vas a entrar como {email}.",
956
+ choosePassword: "Elige una contrase\xF1a",
957
+ acceptInvite: "Aceptar y entrar",
958
+ openingInvitation: "Abriendo tu invitaci\xF3n\u2026",
959
+ invitationInvalid: "Esta invitaci\xF3n no es v\xE1lida: ya se us\xF3 o venci\xF3. P\xEDdele a quien te invit\xF3 que te la env\xEDe de nuevo.",
930
960
  profile: "Perfil",
931
961
  save: "Guardar",
932
962
  saved: "Guardado.",
@@ -1041,7 +1071,9 @@ var TEXTS = {
1041
1071
  invalid_assertion: "No se pudo verificar la respuesta de tu empresa. Intenta de nuevo.",
1042
1072
  provisioning_disabled: "El ingreso con tu empresa funcion\xF3, pero este workspace no tiene una cuenta para ti. P\xEDdele a su admin que te agregue.",
1043
1073
  connection_inactive: "El inicio de sesi\xF3n de tu empresa est\xE1 apagado en este workspace.",
1044
- invalid_grant: "Ese ingreso venci\xF3. Intenta de nuevo."
1074
+ invalid_grant: "Ese ingreso venci\xF3. Intenta de nuevo.",
1075
+ password_disabled: "En este workspace no se entra con contrase\xF1a. Usa una de las otras formas.",
1076
+ passkeys_disabled: "Las llaves de acceso est\xE1n desactivadas en este workspace."
1045
1077
  }
1046
1078
  }
1047
1079
  };
@@ -1236,23 +1268,16 @@ function useMailedToken(name, open) {
1236
1268
  open(takeToken(name));
1237
1269
  }, []);
1238
1270
  }
1239
- function SignIn(props) {
1240
- const auth = useAuth();
1241
- const f = useForm(props);
1242
- const [methods, setMethods] = useState(null);
1243
- const [email, setEmail] = useState("");
1244
- const [password, setPassword] = useState("");
1271
+ function SecondStepForm(p) {
1272
+ const { f, auth } = p;
1245
1273
  const [code, setCode] = useState("");
1246
- const [step, setStep] = useState("password");
1247
- const challenge = useChallenge(methods?.captchaSiteKey, props.locale);
1248
1274
  const [phoneWay, setPhoneWay] = useState(null);
1249
1275
  const [phoneSent, setPhoneSent] = useState("");
1250
1276
  useEffect(() => {
1251
- if (step !== "code") return;
1252
1277
  void auth.signIn.mfaMethods().then((r) => {
1253
1278
  if (r.ok) setPhoneWay({ totp: r.totp, phone: r.phone, push: r.push, email: r.email });
1254
1279
  });
1255
- }, [step]);
1280
+ }, []);
1256
1281
  const sendEmail = async () => {
1257
1282
  f.setBusy(true);
1258
1283
  f.setError(null);
@@ -1288,6 +1313,55 @@ function SignIn(props) {
1288
1313
  if (!r.ok) return f.setError(r.error);
1289
1314
  setPhoneSent(r.sentTo);
1290
1315
  };
1316
+ const submit = async (e) => {
1317
+ e.preventDefault();
1318
+ f.setBusy(true);
1319
+ f.setError(null);
1320
+ const r = await auth.signIn.mfa({ code });
1321
+ f.setBusy(false);
1322
+ if (!r.ok) return f.setError(r.error);
1323
+ f.done();
1324
+ };
1325
+ const general = f.error && !f.error.field ? f.error : null;
1326
+ const to = phoneWay?.phone?.number ?? "";
1327
+ return /* @__PURE__ */ jsxs("form", { className: f.cx("form"), ...f.root, onSubmit: submit, noValidate: true, children: [
1328
+ /* @__PURE__ */ jsx("p", { className: f.cx("notice"), children: phoneSent ? f.t.codeSent.replace("{to}", phoneSent) : phoneWay?.email ? f.t.codeHelpEmail.replace("{to}", phoneWay.email.to) : phoneWay?.phone && !phoneWay.totp ? f.t.codeHelpPhone : f.t.codeHelp }),
1329
+ general && /* @__PURE__ */ jsx("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
1330
+ phoneWay?.email && /* @__PURE__ */ jsx(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => void sendEmail(), children: f.t.sendEmailAgain }),
1331
+ pushNumber > 0 ? /* @__PURE__ */ jsxs("div", { className: f.cx("notice"), role: "status", "aria-live": "polite", children: [
1332
+ /* @__PURE__ */ jsx("p", { children: f.t.pushPick.replace("{n}", String(pushNumber)) }),
1333
+ /* @__PURE__ */ jsx("p", { style: { fontSize: 48, fontWeight: 700, margin: "4px 0", textAlign: "center" }, children: pushNumber }),
1334
+ /* @__PURE__ */ jsx("p", { children: f.t.pushWaiting })
1335
+ ] }) : phoneWay?.push && /* @__PURE__ */ jsx(f.Button, { type: "button", disabled: f.busy, className: f.cx("button"), onClick: () => void sendPush(), children: f.t.pushSend }),
1336
+ phoneWay?.phone && /* @__PURE__ */ 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) }),
1337
+ /* @__PURE__ */ jsx(Field, { f, name: "code", type: "text", label: f.t.code, value: code, autoComplete: "one-time-code", required: true, onChange: setCode }),
1338
+ /* @__PURE__ */ jsx(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.verify })
1339
+ ] });
1340
+ }
1341
+ function ProviderButtons(p) {
1342
+ const { f } = p;
1343
+ return /* @__PURE__ */ jsx(Fragment, { children: p.providers.map((provider) => /* @__PURE__ */ jsxs(f.Button, { type: "button", className: f.cx("providerButton"), onClick: () => p.onPick(provider.id), children: [
1344
+ provider.iconUrl && /* @__PURE__ */ jsx("img", { src: provider.iconUrl, alt: "", width: 18, height: 18, className: f.cx("providerIcon") }),
1345
+ f.t.continueWith,
1346
+ " ",
1347
+ provider.name
1348
+ ] }, provider.id)) });
1349
+ }
1350
+ function signInOffers(methods) {
1351
+ const password = methods?.passwordSignIn !== false;
1352
+ return { password, email: password || methods?.magicLink === true || methods?.sso === true, passkey: methods?.passkey === true };
1353
+ }
1354
+ function SignIn(props) {
1355
+ const auth = useAuth();
1356
+ const f = useForm(props);
1357
+ const [methods, setMethods] = useState(null);
1358
+ const [email, setEmail] = useState("");
1359
+ const [password, setPassword] = useState("");
1360
+ const [code, setCode] = useState("");
1361
+ const [step, setStep] = useState("password");
1362
+ const challenge = useChallenge(methods?.captchaSiteKey, props.locale);
1363
+ const offers = signInOffers(methods);
1364
+ const passwordOn = offers.password;
1291
1365
  const autofill = useRef(null);
1292
1366
  const [offer, setOffer] = useState(0);
1293
1367
  const [company, setCompany] = useState(null);
@@ -1339,10 +1413,18 @@ function SignIn(props) {
1339
1413
  const submit = async (e) => {
1340
1414
  e.preventDefault();
1341
1415
  autofill.current?.abort();
1416
+ if (step === "password" && company?.enforced) {
1417
+ f.setError(null);
1418
+ return toCompany();
1419
+ }
1420
+ if (step === "password" && !passwordOn) {
1421
+ if (company) return toCompany();
1422
+ if (methods?.magicLink) return sendLink();
1423
+ return;
1424
+ }
1342
1425
  f.setBusy(true);
1343
1426
  f.setError(null);
1344
- if (step === "password" && company?.enforced) return toCompany();
1345
- const r = step === "code" ? await auth.signIn.mfa({ code }) : step === "sent" ? await auth.signIn.emailCode({ code }) : await auth.signIn.email({ email, password, captchaToken: challenge.token });
1427
+ const r = step === "sent" ? await auth.signIn.emailCode({ code }) : await auth.signIn.email({ email, password, captchaToken: challenge.token });
1346
1428
  f.setBusy(false);
1347
1429
  if (step === "password") challenge.renew();
1348
1430
  if (!r.ok && r.error.code === "sso_required") return toCompany();
@@ -1376,6 +1458,7 @@ function SignIn(props) {
1376
1458
  setStep("sent");
1377
1459
  };
1378
1460
  const general = f.error && !f.error.field ? f.error : null;
1461
+ const asksEmail = offers.email;
1379
1462
  if (step === "opening") return /* @__PURE__ */ jsx("div", { className: f.cx("form"), ...f.root, "aria-busy": "true" });
1380
1463
  if (step === "sent") {
1381
1464
  return /* @__PURE__ */ jsxs("form", { className: f.cx("form"), ...f.root, onSubmit: submit, noValidate: true, children: [
@@ -1386,39 +1469,20 @@ function SignIn(props) {
1386
1469
  /* @__PURE__ */ jsx(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.verify })
1387
1470
  ] });
1388
1471
  }
1389
- if (step === "code") {
1390
- const to = phoneWay?.phone?.number ?? "";
1391
- return /* @__PURE__ */ jsxs("form", { className: f.cx("form"), ...f.root, onSubmit: submit, noValidate: true, children: [
1392
- /* @__PURE__ */ jsx("p", { className: f.cx("notice"), children: phoneSent ? f.t.codeSent.replace("{to}", phoneSent) : phoneWay?.email ? f.t.codeHelpEmail.replace("{to}", phoneWay.email.to) : phoneWay?.phone && !phoneWay.totp ? f.t.codeHelpPhone : f.t.codeHelp }),
1393
- general && /* @__PURE__ */ jsx("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
1394
- phoneWay?.email && /* @__PURE__ */ jsx(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => void sendEmail(), children: f.t.sendEmailAgain }),
1395
- pushNumber > 0 ? /* @__PURE__ */ jsxs("div", { className: f.cx("notice"), role: "status", "aria-live": "polite", children: [
1396
- /* @__PURE__ */ jsx("p", { children: f.t.pushPick.replace("{n}", String(pushNumber)) }),
1397
- /* @__PURE__ */ jsx("p", { style: { fontSize: 48, fontWeight: 700, margin: "4px 0", textAlign: "center" }, children: pushNumber }),
1398
- /* @__PURE__ */ jsx("p", { children: f.t.pushWaiting })
1399
- ] }) : phoneWay?.push && /* @__PURE__ */ jsx(f.Button, { type: "button", disabled: f.busy, className: f.cx("button"), onClick: () => void sendPush(), children: f.t.pushSend }),
1400
- phoneWay?.phone && /* @__PURE__ */ 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) }),
1401
- /* @__PURE__ */ jsx(Field, { f, name: "code", type: "text", label: f.t.code, value: code, autoComplete: "one-time-code", required: true, onChange: setCode }),
1402
- /* @__PURE__ */ jsx(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.verify })
1403
- ] });
1404
- }
1472
+ if (step === "code") return /* @__PURE__ */ jsx(SecondStepForm, { f, auth });
1405
1473
  return /* @__PURE__ */ jsxs("form", { className: f.cx("form"), ...f.root, onSubmit: submit, noValidate: true, children: [
1406
1474
  general && /* @__PURE__ */ jsx("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
1407
- (methods?.providers ?? []).map((p) => /* @__PURE__ */ jsxs(f.Button, { type: "button", className: f.cx("providerButton"), onClick: () => auth.signIn.social(p.id, { returnTo: props.returnTo }), children: [
1408
- f.t.continueWith,
1409
- " ",
1410
- p.name
1411
- ] }, p.id)),
1412
- methods?.passkey && /* @__PURE__ */ jsx(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => void usePasskey(), children: f.t.usePasskey }),
1413
- ((methods?.providers.length ?? 0) > 0 || methods?.passkey) && /* @__PURE__ */ jsx("p", { className: f.cx("separator"), children: f.t.orWithEmail }),
1414
- /* @__PURE__ */ jsx(Field, { f, name: "email", type: "email", label: f.t.email, value: email, autoComplete: methods?.passkeyHere ? "username webauthn" : "username", required: true, onChange: setEmail }),
1475
+ /* @__PURE__ */ jsx(ProviderButtons, { f, providers: methods?.providers ?? [], onPick: (id) => auth.signIn.social(id, { returnTo: props.returnTo }) }),
1476
+ offers.passkey && /* @__PURE__ */ jsx(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => void usePasskey(), children: f.t.usePasskey }),
1477
+ asksEmail && ((methods?.providers.length ?? 0) > 0 || offers.passkey) && /* @__PURE__ */ jsx("p", { className: f.cx("separator"), children: f.t.orWithEmail }),
1478
+ asksEmail && /* @__PURE__ */ jsx(Field, { f, name: "email", type: "email", label: f.t.email, value: email, autoComplete: methods?.passkeyHere ? "username webauthn" : "username", required: true, onChange: setEmail }),
1415
1479
  company && /* @__PURE__ */ jsxs(f.Button, { type: "button", disabled: f.busy, className: f.cx(company.enforced ? "button" : "providerButton"), onClick: toCompany, children: [
1416
1480
  f.t.continueWith,
1417
1481
  " ",
1418
1482
  company.name
1419
1483
  ] }),
1420
1484
  company?.enforced && /* @__PURE__ */ jsx("p", { className: f.cx("notice"), children: f.t.ssoOnly }),
1421
- !company?.enforced && /* @__PURE__ */ jsxs(Fragment, { children: [
1485
+ !company?.enforced && passwordOn && /* @__PURE__ */ jsxs(Fragment, { children: [
1422
1486
  /* @__PURE__ */ jsx(
1423
1487
  Field,
1424
1488
  {
@@ -1436,6 +1500,7 @@ function SignIn(props) {
1436
1500
  challenge.node,
1437
1501
  /* @__PURE__ */ jsx(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.signIn })
1438
1502
  ] }),
1503
+ !company?.enforced && !passwordOn && methods?.magicLink && challenge.node,
1439
1504
  methods?.magicLink && !company?.enforced && /* @__PURE__ */ jsx(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => void sendLink(), children: f.t.emailMeALink }),
1440
1505
  methods?.signUp !== false && props.links?.signUp && /* @__PURE__ */ jsxs("p", { className: f.cx("footer"), children: [
1441
1506
  f.t.noAccount,
@@ -1495,14 +1560,30 @@ function SignUp(props) {
1495
1560
  f.done();
1496
1561
  };
1497
1562
  const general = f.error && !f.error.field ? f.error : null;
1563
+ const passwordOn = methods?.passwordSignIn !== false;
1564
+ const providers = auth.session?.user.isAnonymous === true || methods?.organization?.required ? [] : methods?.providers ?? [];
1498
1565
  if (sent) {
1499
1566
  return /* @__PURE__ */ jsxs("div", { className: f.cx("form"), ...f.root, children: [
1500
1567
  /* @__PURE__ */ jsx("p", { className: f.cx("notice"), children: f.t.checkEmail }),
1501
1568
  props.links?.signIn && /* @__PURE__ */ jsx("a", { className: f.cx("link"), href: props.links.signIn, children: f.t.backToSignIn })
1502
1569
  ] });
1503
1570
  }
1571
+ const footer = props.links?.signIn && /* @__PURE__ */ jsxs("p", { className: f.cx("footer"), children: [
1572
+ f.t.haveAccount,
1573
+ " ",
1574
+ /* @__PURE__ */ jsx("a", { className: f.cx("link"), href: props.links.signIn, children: f.t.signIn })
1575
+ ] });
1576
+ if (!passwordOn) {
1577
+ return /* @__PURE__ */ jsxs("div", { className: f.cx("form"), ...f.root, children: [
1578
+ general && /* @__PURE__ */ jsx("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
1579
+ /* @__PURE__ */ jsx(ProviderButtons, { f, providers, onPick: (id) => auth.signIn.social(id, { returnTo: props.returnTo }) }),
1580
+ footer
1581
+ ] });
1582
+ }
1504
1583
  return /* @__PURE__ */ jsxs("form", { className: f.cx("form"), ...f.root, onSubmit: submit, noValidate: true, children: [
1505
1584
  general && /* @__PURE__ */ jsx("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
1585
+ /* @__PURE__ */ jsx(ProviderButtons, { f, providers, onPick: (id) => auth.signIn.social(id, { returnTo: props.returnTo }) }),
1586
+ providers.length > 0 && /* @__PURE__ */ jsx("p", { className: f.cx("separator"), children: f.t.orWithEmail }),
1506
1587
  methods?.organization?.ask && /* @__PURE__ */ jsx(Field, { f, name: "organization", type: "text", label: f.t.company, value: organization, autoComplete: "organization", required: methods.organization.required, onChange: setOrganization }),
1507
1588
  /* @__PURE__ */ jsx(Field, { f, name: "name", type: "text", label: f.t.name, value: name, autoComplete: "name", onChange: setName }),
1508
1589
  /* @__PURE__ */ jsx(Field, { f, name: "email", type: "email", label: f.t.email, value: email, autoComplete: "username", required: true, onChange: setEmail }),
@@ -1528,11 +1609,7 @@ function SignUp(props) {
1528
1609
  ] }, p.key)),
1529
1610
  challenge.node,
1530
1611
  /* @__PURE__ */ jsx(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.signUp }),
1531
- props.links?.signIn && /* @__PURE__ */ jsxs("p", { className: f.cx("footer"), children: [
1532
- f.t.haveAccount,
1533
- " ",
1534
- /* @__PURE__ */ jsx("a", { className: f.cx("link"), href: props.links.signIn, children: f.t.signIn })
1535
- ] })
1612
+ footer
1536
1613
  ] });
1537
1614
  }
1538
1615
  function ForgotPassword(props) {
@@ -1629,6 +1706,62 @@ function VerifyEmail(props) {
1629
1706
  state !== "confirming" && props.links?.signIn && /* @__PURE__ */ jsx("a", { className: f.cx("link"), href: props.links.signIn, children: f.t.backToSignIn })
1630
1707
  ] });
1631
1708
  }
1709
+ function AcceptInvite(props) {
1710
+ const auth = useAuth();
1711
+ const f = useForm(props);
1712
+ const [token, setToken] = useState("");
1713
+ const [invitation, setInvitation] = useState(null);
1714
+ const [state, setState] = useState("opening");
1715
+ const [password, setPassword] = useState("");
1716
+ usePasswordPolicy(f, useMethods(auth));
1717
+ useMailedToken("token", (t) => {
1718
+ if (!t) return setState("invalid");
1719
+ setToken(t);
1720
+ void auth.invitation(t).then((r) => {
1721
+ if (r.ok) {
1722
+ setInvitation(r.invitation);
1723
+ return setState("ready");
1724
+ }
1725
+ if (r.error.code !== "invalid_token") f.setError(r.error);
1726
+ setState("invalid");
1727
+ });
1728
+ });
1729
+ const submit = async (e) => {
1730
+ e.preventDefault();
1731
+ f.setBusy(true);
1732
+ f.setError(null);
1733
+ const r = await auth.acceptInvitation(token, password);
1734
+ f.setBusy(false);
1735
+ if (!r.ok) {
1736
+ if (r.error.code === "invalid_token") return setState("invalid");
1737
+ return f.setError(r.error);
1738
+ }
1739
+ if (r.status === "mfa_required") return setState("code");
1740
+ f.done();
1741
+ };
1742
+ const general = f.error && !f.error.field ? f.error : null;
1743
+ const back = props.links?.signIn && /* @__PURE__ */ jsx("a", { className: f.cx("link"), href: props.links.signIn, children: f.t.backToSignIn });
1744
+ if (state === "opening") {
1745
+ return /* @__PURE__ */ jsx("div", { className: f.cx("form"), ...f.root, "aria-busy": "true", children: /* @__PURE__ */ jsx("p", { className: f.cx("notice"), children: f.t.openingInvitation }) });
1746
+ }
1747
+ if (state === "invalid") {
1748
+ return /* @__PURE__ */ jsxs("div", { className: f.cx("form"), ...f.root, children: [
1749
+ /* @__PURE__ */ jsx("p", { role: "alert", className: f.cx("error"), children: general ? f.say(general) : f.t.invitationInvalid }),
1750
+ back
1751
+ ] });
1752
+ }
1753
+ if (state === "code") return /* @__PURE__ */ jsx(SecondStepForm, { f, auth });
1754
+ const workspace = invitation?.workspace ?? "";
1755
+ const heading = invitation?.organization ? f.t.invitedToOrganization.replace("{organization}", invitation.organization).replace("{workspace}", workspace) : f.t.invitedTo.replace("{workspace}", workspace);
1756
+ return /* @__PURE__ */ jsxs("form", { className: f.cx("form"), ...f.root, onSubmit: submit, noValidate: true, children: [
1757
+ /* @__PURE__ */ jsx("h2", { className: f.cx("heading"), children: heading }),
1758
+ /* @__PURE__ */ jsx("p", { className: f.cx("notice"), children: f.t.invitedAs.replace("{email}", invitation?.email ?? "") }),
1759
+ general && /* @__PURE__ */ jsx("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
1760
+ /* @__PURE__ */ jsx("input", { type: "email", name: "username", autoComplete: "username", value: invitation?.email ?? "", readOnly: true, hidden: true }),
1761
+ /* @__PURE__ */ jsx(Field, { f, name: "password", type: "password", label: f.t.choosePassword, value: password, autoComplete: "new-password", required: true, onChange: setPassword, hint: f.passwordRule }),
1762
+ /* @__PURE__ */ jsx(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.acceptInvite })
1763
+ ] });
1764
+ }
1632
1765
  function useSection(props) {
1633
1766
  const f = useForm({ ...props, unstyled: true });
1634
1767
  const [done, setDone] = useState(null);
@@ -2276,6 +2409,8 @@ function useAuth() {
2276
2409
  googleOneTap: (o) => then(client.signIn.googleOneTap(o))
2277
2410
  },
2278
2411
  signUp: { email: (f) => then(client.signUp.email(f)) },
2412
+ // The invited person is signed in once they accept.
2413
+ acceptInvitation: (token, password) => then(client.acceptInvitation(token, password)),
2279
2414
  signOut: () => then(client.signOut()),
2280
2415
  account: {
2281
2416
  ...client.account,
@@ -2293,6 +2428,7 @@ function useAuth() {
2293
2428
  }, [basePath, refresh, session, status]);
2294
2429
  }
2295
2430
  export {
2431
+ AcceptInvite,
2296
2432
  Account,
2297
2433
  CauthyProvider,
2298
2434
  ForgotPassword,