cauthy-auth 0.26.0 → 0.27.1

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
@@ -277,6 +277,30 @@ interface AuthMethods {
277
277
  * found by their work address (`signIn.ssoDiscover`, `signIn.sso`).
278
278
  */
279
279
  sso: boolean;
280
+ /**
281
+ * Set when the workspace asks something of a sign-up: the legal documents
282
+ * to accept (the terms, the privacy policy: show the links and a box, send
283
+ * `acceptLegal: true`) and the consents to ask (one box each, sent as
284
+ * `consents`; a required one must be ticked). The ready-made form shows them.
285
+ */
286
+ legal?: {
287
+ documents: {
288
+ kind: string;
289
+ version: string;
290
+ url: string;
291
+ }[];
292
+ consentPurposes: {
293
+ key: string;
294
+ label: string;
295
+ required: boolean;
296
+ }[];
297
+ };
298
+ }
299
+ /** A legal document the person owes: not yet accepted in its current version. */
300
+ interface PendingLegal {
301
+ kind: string;
302
+ version: string;
303
+ url: string;
280
304
  }
281
305
  /** A company's sign-in (an SSO connection of the workspace). */
282
306
  interface SSOConnection {
@@ -457,6 +481,18 @@ interface AccountClient {
457
481
  }): Promise<AuthResult<'changed', {
458
482
  signedIn: boolean;
459
483
  }>>;
484
+ /**
485
+ * The legal documents the person has not accepted in their current version
486
+ * (a new version of the terms, say): show them and call `acceptLegal`.
487
+ * Nothing stops the person meanwhile; the application decides what waits.
488
+ */
489
+ legal(): Promise<AuthResult<'ok', {
490
+ pending: PendingLegal[];
491
+ }>>;
492
+ /** Records the person's acceptance of the documents in force (the kinds named, or every one). */
493
+ acceptLegal(form?: {
494
+ kinds?: string[];
495
+ }): Promise<AuthResult<'accepted'>>;
460
496
  mfa: {
461
497
  /** Starts turning the second step on: show `secret` (or `otpauthUri` as a QR code) to add to an authenticator app, then call `verify`. */
462
498
  setup(form: {
@@ -784,12 +820,17 @@ interface AuthClient {
784
820
  }): string;
785
821
  };
786
822
  signUp: {
787
- /** `verification_required`: the account exists and must open the link Cauthy mailed before it can sign in. */
823
+ /**
824
+ * `verification_required`: the account exists and must open the link Cauthy mailed before it can sign in.
825
+ * `acceptLegal` and `consents` answer what `methods().legal` asks; a sign-up that leaves them out is refused.
826
+ */
788
827
  email(form: {
789
828
  email: string;
790
829
  password: string;
791
830
  name?: string;
792
831
  organization?: string;
832
+ acceptLegal?: boolean;
833
+ consents?: Record<string, boolean>;
793
834
  captchaToken?: string;
794
835
  }): Promise<AuthResult<'signed_in', {
795
836
  session: PublicSession;
@@ -857,6 +898,11 @@ interface AuthTexts {
857
898
  createAccount: string;
858
899
  /** The label of the company's name, asked when the application's system makes an organization per sign-up. */
859
900
  company: string;
901
+ /** The legal box: "I have read and accept" + "the Terms of Service" + "and" + "the Privacy Policy", linked. */
902
+ iAccept: string;
903
+ and: string;
904
+ termsOfService: string;
905
+ privacyPolicy: string;
860
906
  haveAccount: string;
861
907
  sendLink: string;
862
908
  linkSent: string;
@@ -960,7 +1006,7 @@ interface AuthTexts {
960
1006
  /** By error code; one missing shows what the server said. */
961
1007
  errors: Record<string, string>;
962
1008
  }
963
- type Slot = 'form' | 'field' | 'label' | 'input' | 'button' | 'providerButton' | 'link' | 'error' | 'fieldError' | 'notice' | 'separator' | 'footer' | 'account' | 'section' | 'heading' | 'secret' | 'codes' | 'sessions' | 'session';
1009
+ type Slot = 'form' | 'field' | 'label' | 'input' | 'button' | 'providerButton' | 'link' | 'error' | 'fieldError' | 'notice' | 'separator' | 'footer' | 'check' | 'account' | 'section' | 'heading' | 'secret' | 'codes' | 'sessions' | 'session';
964
1010
  interface AuthFormProps {
965
1011
  /** The language of the built-in sentences. Default: `en`. */
966
1012
  locale?: 'en' | 'es';
package/dist/react.d.ts CHANGED
@@ -277,6 +277,30 @@ interface AuthMethods {
277
277
  * found by their work address (`signIn.ssoDiscover`, `signIn.sso`).
278
278
  */
279
279
  sso: boolean;
280
+ /**
281
+ * Set when the workspace asks something of a sign-up: the legal documents
282
+ * to accept (the terms, the privacy policy: show the links and a box, send
283
+ * `acceptLegal: true`) and the consents to ask (one box each, sent as
284
+ * `consents`; a required one must be ticked). The ready-made form shows them.
285
+ */
286
+ legal?: {
287
+ documents: {
288
+ kind: string;
289
+ version: string;
290
+ url: string;
291
+ }[];
292
+ consentPurposes: {
293
+ key: string;
294
+ label: string;
295
+ required: boolean;
296
+ }[];
297
+ };
298
+ }
299
+ /** A legal document the person owes: not yet accepted in its current version. */
300
+ interface PendingLegal {
301
+ kind: string;
302
+ version: string;
303
+ url: string;
280
304
  }
281
305
  /** A company's sign-in (an SSO connection of the workspace). */
282
306
  interface SSOConnection {
@@ -457,6 +481,18 @@ interface AccountClient {
457
481
  }): Promise<AuthResult<'changed', {
458
482
  signedIn: boolean;
459
483
  }>>;
484
+ /**
485
+ * The legal documents the person has not accepted in their current version
486
+ * (a new version of the terms, say): show them and call `acceptLegal`.
487
+ * Nothing stops the person meanwhile; the application decides what waits.
488
+ */
489
+ legal(): Promise<AuthResult<'ok', {
490
+ pending: PendingLegal[];
491
+ }>>;
492
+ /** Records the person's acceptance of the documents in force (the kinds named, or every one). */
493
+ acceptLegal(form?: {
494
+ kinds?: string[];
495
+ }): Promise<AuthResult<'accepted'>>;
460
496
  mfa: {
461
497
  /** Starts turning the second step on: show `secret` (or `otpauthUri` as a QR code) to add to an authenticator app, then call `verify`. */
462
498
  setup(form: {
@@ -784,12 +820,17 @@ interface AuthClient {
784
820
  }): string;
785
821
  };
786
822
  signUp: {
787
- /** `verification_required`: the account exists and must open the link Cauthy mailed before it can sign in. */
823
+ /**
824
+ * `verification_required`: the account exists and must open the link Cauthy mailed before it can sign in.
825
+ * `acceptLegal` and `consents` answer what `methods().legal` asks; a sign-up that leaves them out is refused.
826
+ */
788
827
  email(form: {
789
828
  email: string;
790
829
  password: string;
791
830
  name?: string;
792
831
  organization?: string;
832
+ acceptLegal?: boolean;
833
+ consents?: Record<string, boolean>;
793
834
  captchaToken?: string;
794
835
  }): Promise<AuthResult<'signed_in', {
795
836
  session: PublicSession;
@@ -857,6 +898,11 @@ interface AuthTexts {
857
898
  createAccount: string;
858
899
  /** The label of the company's name, asked when the application's system makes an organization per sign-up. */
859
900
  company: string;
901
+ /** The legal box: "I have read and accept" + "the Terms of Service" + "and" + "the Privacy Policy", linked. */
902
+ iAccept: string;
903
+ and: string;
904
+ termsOfService: string;
905
+ privacyPolicy: string;
860
906
  haveAccount: string;
861
907
  sendLink: string;
862
908
  linkSent: string;
@@ -960,7 +1006,7 @@ interface AuthTexts {
960
1006
  /** By error code; one missing shows what the server said. */
961
1007
  errors: Record<string, string>;
962
1008
  }
963
- type Slot = 'form' | 'field' | 'label' | 'input' | 'button' | 'providerButton' | 'link' | 'error' | 'fieldError' | 'notice' | 'separator' | 'footer' | 'account' | 'section' | 'heading' | 'secret' | 'codes' | 'sessions' | 'session';
1009
+ type Slot = 'form' | 'field' | 'label' | 'input' | 'button' | 'providerButton' | 'link' | 'error' | 'fieldError' | 'notice' | 'separator' | 'footer' | 'check' | 'account' | 'section' | 'heading' | 'secret' | 'codes' | 'sessions' | 'session';
964
1010
  interface AuthFormProps {
965
1011
  /** The language of the built-in sentences. Default: `en`. */
966
1012
  locale?: 'en' | 'es';
package/dist/react.js CHANGED
@@ -183,8 +183,8 @@ function fromWire(wire) {
183
183
  }
184
184
 
185
185
  // src/validate.ts
186
- var MIN_PASSWORD = 8;
187
- var MAX_PASSWORD = 256;
186
+ var MIN_PASSWORD = 12;
187
+ var MAX_PASSWORD = 128;
188
188
  var MAX_NAME = 120;
189
189
  var EMAIL = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
190
190
  function checkEmail(value) {
@@ -443,7 +443,7 @@ function createAuthClient(options = {}) {
443
443
  signUp: {
444
444
  email: (f) => post(
445
445
  "/sign-up/email",
446
- { email: f.email, password: f.password, name: f.name, organization: f.organization },
446
+ { email: f.email, password: f.password, name: f.name, organization: f.organization, acceptLegal: f.acceptLegal, consents: f.consents },
447
447
  firstProblem(checkName(f.name), checkEmail(f.email), checkNewPassword(f.password), checkOrganization(f.organization)),
448
448
  f.captchaToken
449
449
  )
@@ -476,6 +476,12 @@ function createAuthClient(options = {}) {
476
476
  return body && typeof body.account === "object" ? body.account : null;
477
477
  },
478
478
  update: (f) => post("/me", { name: f.name }, checkName(f.name)),
479
+ async legal() {
480
+ const body = await read("/me/legal");
481
+ if (!body) return { ok: false, error: { code: "not_signed_in", message: "Nobody is signed in." } };
482
+ return { ok: true, status: "ok", pending: Array.isArray(body.pending) ? body.pending : [] };
483
+ },
484
+ acceptLegal: (f = {}) => post("/me/legal/accept", { kinds: f.kinds ?? [] }, null),
479
485
  changePassword: (f) => {
480
486
  const bad = checkNewPassword(f.newPassword);
481
487
  return post("/me/password", { password: f.password, newPassword: f.newPassword }, bad ? { ...bad, field: "newPassword" } : null);
@@ -616,6 +622,10 @@ var TEXTS = {
616
622
  noAccount: "No account yet?",
617
623
  createAccount: "Create one",
618
624
  haveAccount: "Already have an account?",
625
+ iAccept: "I have read and accept",
626
+ and: "and",
627
+ termsOfService: "the Terms of Service",
628
+ privacyPolicy: "the Privacy Policy",
619
629
  sendLink: "Send the link",
620
630
  linkSent: "If that address has an account, a link to choose a new password is on its way.",
621
631
  codeHelp: "Enter the code from your authenticator app, or a recovery code.",
@@ -718,7 +728,7 @@ var TEXTS = {
718
728
  user_not_active: "That account is disabled.",
719
729
  required: "This is required.",
720
730
  invalid_email: "That does not look like an email address.",
721
- too_short: "Use at least 8 characters.",
731
+ too_short: `Use at least ${MIN_PASSWORD} characters.`,
722
732
  invalid_credentials: "The address or the password is not right.",
723
733
  unavailable: "Sign-in is unavailable right now. Try again in a moment.",
724
734
  state_mismatch: "That sign-in could not be completed. Try again.",
@@ -768,6 +778,10 @@ var TEXTS = {
768
778
  noAccount: "\xBFNo tienes cuenta?",
769
779
  createAccount: "Crear una",
770
780
  haveAccount: "\xBFYa tienes cuenta?",
781
+ iAccept: "He le\xEDdo y acepto",
782
+ and: "y",
783
+ termsOfService: "los T\xE9rminos del servicio",
784
+ privacyPolicy: "la Pol\xEDtica de privacidad",
771
785
  sendLink: "Enviar el enlace",
772
786
  linkSent: "Si ese correo tiene una cuenta, ya va en camino un enlace para elegir una contrase\xF1a nueva.",
773
787
  codeHelp: "Escribe el c\xF3digo de tu aplicaci\xF3n de autenticaci\xF3n, o un c\xF3digo de recuperaci\xF3n.",
@@ -870,7 +884,7 @@ var TEXTS = {
870
884
  user_not_active: "Esa cuenta est\xE1 desactivada.",
871
885
  required: "Este dato es obligatorio.",
872
886
  invalid_email: "Eso no parece un correo.",
873
- too_short: "Usa al menos 8 caracteres.",
887
+ too_short: `Usa al menos ${MIN_PASSWORD} caracteres.`,
874
888
  invalid_credentials: "El correo o la contrase\xF1a no son correctos.",
875
889
  unavailable: "No se puede entrar en este momento. Intenta de nuevo en un momento.",
876
890
  state_mismatch: "No se pudo completar el ingreso. Intenta de nuevo.",
@@ -1315,11 +1329,21 @@ function SignUp(props) {
1315
1329
  const [sent, setSent] = (0, import_react.useState)(false);
1316
1330
  const methods = useMethods(auth);
1317
1331
  const challenge = useChallenge(methods?.captchaSiteKey, props.locale);
1332
+ const [acceptLegal, setAcceptLegal] = (0, import_react.useState)(false);
1333
+ const [consents, setConsents] = (0, import_react.useState)({});
1334
+ const legal = methods?.legal;
1318
1335
  const submit = async (e) => {
1319
1336
  e.preventDefault();
1320
1337
  f.setBusy(true);
1321
1338
  f.setError(null);
1322
- const r = auth.session?.user.isAnonymous === true ? await auth.account.upgrade({ name, email, password }) : await auth.signUp.email({ name, email, password, ...methods?.organization?.ask ? { organization } : {}, captchaToken: challenge.token });
1339
+ const r = auth.session?.user.isAnonymous === true ? await auth.account.upgrade({ name, email, password }) : await auth.signUp.email({
1340
+ name,
1341
+ email,
1342
+ password,
1343
+ ...methods?.organization?.ask ? { organization } : {},
1344
+ ...legal ? { acceptLegal, consents } : {},
1345
+ captchaToken: challenge.token
1346
+ });
1323
1347
  f.setBusy(false);
1324
1348
  challenge.renew();
1325
1349
  if (!r.ok) return f.setError(r.error);
@@ -1339,6 +1363,25 @@ function SignUp(props) {
1339
1363
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "name", type: "text", label: f.t.name, value: name, autoComplete: "name", onChange: setName }),
1340
1364
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "email", type: "email", label: f.t.email, value: email, autoComplete: "username", required: true, onChange: setEmail }),
1341
1365
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "password", type: "password", label: f.t.password, value: password, autoComplete: "new-password", required: true, onChange: setPassword }),
1366
+ legal && legal.documents.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: f.cx("check"), children: [
1367
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "checkbox", name: "acceptLegal", checked: acceptLegal, required: true, onChange: (e) => setAcceptLegal(e.target.checked) }),
1368
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
1369
+ f.t.iAccept,
1370
+ " ",
1371
+ legal.documents.map((d, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
1372
+ i > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1373
+ f.t.and,
1374
+ " "
1375
+ ] }),
1376
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { className: f.cx("link"), href: d.url, target: "_blank", rel: "noopener", children: d.kind === "terms" ? f.t.termsOfService : f.t.privacyPolicy }),
1377
+ " "
1378
+ ] }, d.kind))
1379
+ ] })
1380
+ ] }),
1381
+ legal?.consentPurposes.map((p) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: f.cx("check"), children: [
1382
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "checkbox", name: `consent_${p.key}`, checked: consents[p.key] === true, required: p.required, onChange: (e) => setConsents({ ...consents, [p.key]: e.target.checked }) }),
1383
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: p.label })
1384
+ ] }, p.key)),
1342
1385
  challenge.node,
1343
1386
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.signUp }),
1344
1387
  props.links?.signIn && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: f.cx("footer"), children: [