cauthy-auth 0.27.0 → 0.28.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/cli.js CHANGED
@@ -928,7 +928,9 @@ function createSystem(given = {}) {
928
928
  async function fail2(res, what) {
929
929
  const body = await res.json().catch(() => null);
930
930
  const first = body?.errors?.[0];
931
- throw new CauthyApiError(res.status, first?.code ?? "error", `Cauthy refused ${what} (${res.status}): ${first?.message ?? res.statusText}`);
931
+ const code = typeof body?.error === "string" ? body.error : first?.code;
932
+ const message = typeof body?.error === "string" ? body.error_description : first?.message;
933
+ throw new CauthyApiError(res.status, code ?? "error", `Cauthy refused ${what} (${res.status}): ${message ?? res.statusText}`);
932
934
  }
933
935
  let held;
934
936
  let asking;
package/dist/client.d.mts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { CauthyTheme } from './theme.mjs';
2
2
  import { i as PublicSession } from './system-BxMsSIu1.mjs';
3
- import { A as AuthField } from './validate-BTRit1O1.mjs';
4
- export { F as FieldProblem, M as MIN_PASSWORD, c as checkCode, a as checkEmail, b as checkName, d as checkNewPassword, e as checkPassword } from './validate-BTRit1O1.mjs';
3
+ import { A as AuthField, b as PasswordPolicy } from './validate-sEXG82po.mjs';
4
+ export { D as DEFAULT_PASSWORD_POLICY, F as FieldProblem, M as MIN_PASSWORD, P as PasswordClass, a as PasswordLocale, c as PasswordReason, d as checkCode, e as checkEmail, f as checkName, g as checkNewPassword, h as checkPassword, i as describePasswordPolicy, j as explainPasswordProblems, p as passwordPolicyOf, k as passwordProblems } from './validate-sEXG82po.mjs';
5
5
  import 'jose';
6
6
 
7
7
  /** Whether this browser can use a passkey at all. */
@@ -61,6 +61,8 @@ interface AuthError {
61
61
  message: string;
62
62
  /** The input it is about, when it is about one. */
63
63
  field?: AuthField;
64
+ /** `weak_password`: the rules of the workspace's password policy it failed (`too_short`, `needs_digit`, …). */
65
+ reasons?: string[];
64
66
  }
65
67
  type AuthResult<S extends string, More = unknown> = ({
66
68
  ok: true;
@@ -128,6 +130,13 @@ interface AuthMethods {
128
130
  required: boolean;
129
131
  }[];
130
132
  };
133
+ /**
134
+ * What a new password must be in the workspace. Once `methods()` has
135
+ * answered, this client checks new passwords against it before sending
136
+ * them; until then, against the defaults (12 to 128 characters). Absent
137
+ * from a server that does not say.
138
+ */
139
+ password?: PasswordPolicy;
131
140
  }
132
141
  /** A legal document the person owes: not yet accepted in its current version. */
133
142
  interface PendingLegal {
@@ -716,4 +725,4 @@ interface AuthClientOptions {
716
725
  }
717
726
  declare function createAuthClient(options?: AuthClientOptions): AuthClient;
718
727
 
719
- export { type Account, type AccountClient, type AccountSession, type AuthClient, type AuthClientOptions, type AuthError, AuthField, type AuthMethods, type AuthResult, type ConnectedAgent, type LinkedIdentity, type OrganizationPerson, type OrganizationRoleChoice, type OrganizationSeat, type OrganizationsClient, type Passkey, type PasskeyKind, type PendingLegal, type SSOConnection, type SecondStep, type SessionState, type SessionStore, type SessionStoreOptions, canAutofillPasskeys, canUsePasskeys, createAuthClient, createSessionStore };
728
+ export { type Account, type AccountClient, type AccountSession, type AuthClient, type AuthClientOptions, type AuthError, AuthField, type AuthMethods, type AuthResult, type ConnectedAgent, type LinkedIdentity, type OrganizationPerson, type OrganizationRoleChoice, type OrganizationSeat, type OrganizationsClient, type Passkey, type PasskeyKind, PasswordPolicy, type PendingLegal, type SSOConnection, type SecondStep, type SessionState, type SessionStore, type SessionStoreOptions, canAutofillPasskeys, canUsePasskeys, createAuthClient, createSessionStore };
package/dist/client.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { CauthyTheme } from './theme.js';
2
2
  import { i as PublicSession } from './system-DTIeDXDP.js';
3
- import { A as AuthField } from './validate-BTRit1O1.js';
4
- export { F as FieldProblem, M as MIN_PASSWORD, c as checkCode, a as checkEmail, b as checkName, d as checkNewPassword, e as checkPassword } from './validate-BTRit1O1.js';
3
+ import { A as AuthField, b as PasswordPolicy } from './validate-sEXG82po.js';
4
+ export { D as DEFAULT_PASSWORD_POLICY, F as FieldProblem, M as MIN_PASSWORD, P as PasswordClass, a as PasswordLocale, c as PasswordReason, d as checkCode, e as checkEmail, f as checkName, g as checkNewPassword, h as checkPassword, i as describePasswordPolicy, j as explainPasswordProblems, p as passwordPolicyOf, k as passwordProblems } from './validate-sEXG82po.js';
5
5
  import 'jose';
6
6
 
7
7
  /** Whether this browser can use a passkey at all. */
@@ -61,6 +61,8 @@ interface AuthError {
61
61
  message: string;
62
62
  /** The input it is about, when it is about one. */
63
63
  field?: AuthField;
64
+ /** `weak_password`: the rules of the workspace's password policy it failed (`too_short`, `needs_digit`, …). */
65
+ reasons?: string[];
64
66
  }
65
67
  type AuthResult<S extends string, More = unknown> = ({
66
68
  ok: true;
@@ -128,6 +130,13 @@ interface AuthMethods {
128
130
  required: boolean;
129
131
  }[];
130
132
  };
133
+ /**
134
+ * What a new password must be in the workspace. Once `methods()` has
135
+ * answered, this client checks new passwords against it before sending
136
+ * them; until then, against the defaults (12 to 128 characters). Absent
137
+ * from a server that does not say.
138
+ */
139
+ password?: PasswordPolicy;
131
140
  }
132
141
  /** A legal document the person owes: not yet accepted in its current version. */
133
142
  interface PendingLegal {
@@ -716,4 +725,4 @@ interface AuthClientOptions {
716
725
  }
717
726
  declare function createAuthClient(options?: AuthClientOptions): AuthClient;
718
727
 
719
- export { type Account, type AccountClient, type AccountSession, type AuthClient, type AuthClientOptions, type AuthError, AuthField, type AuthMethods, type AuthResult, type ConnectedAgent, type LinkedIdentity, type OrganizationPerson, type OrganizationRoleChoice, type OrganizationSeat, type OrganizationsClient, type Passkey, type PasskeyKind, type PendingLegal, type SSOConnection, type SecondStep, type SessionState, type SessionStore, type SessionStoreOptions, canAutofillPasskeys, canUsePasskeys, createAuthClient, createSessionStore };
728
+ export { type Account, type AccountClient, type AccountSession, type AuthClient, type AuthClientOptions, type AuthError, AuthField, type AuthMethods, type AuthResult, type ConnectedAgent, type LinkedIdentity, type OrganizationPerson, type OrganizationRoleChoice, type OrganizationSeat, type OrganizationsClient, type Passkey, type PasskeyKind, PasswordPolicy, type PendingLegal, type SSOConnection, type SecondStep, type SessionState, type SessionStore, type SessionStoreOptions, canAutofillPasskeys, canUsePasskeys, createAuthClient, createSessionStore };
package/dist/client.js CHANGED
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/client.ts
21
21
  var client_exports = {};
22
22
  __export(client_exports, {
23
+ DEFAULT_PASSWORD_POLICY: () => DEFAULT_PASSWORD_POLICY,
23
24
  MIN_PASSWORD: () => MIN_PASSWORD,
24
25
  canAutofillPasskeys: () => canAutofillPasskeys,
25
26
  canUsePasskeys: () => canUsePasskeys,
@@ -29,7 +30,11 @@ __export(client_exports, {
29
30
  checkNewPassword: () => checkNewPassword,
30
31
  checkPassword: () => checkPassword,
31
32
  createAuthClient: () => createAuthClient,
32
- createSessionStore: () => createSessionStore
33
+ createSessionStore: () => createSessionStore,
34
+ describePasswordPolicy: () => describePasswordPolicy,
35
+ explainPasswordProblems: () => explainPasswordProblems,
36
+ passwordPolicyOf: () => passwordPolicyOf,
37
+ passwordProblems: () => passwordProblems
33
38
  });
34
39
  module.exports = __toCommonJS(client_exports);
35
40
 
@@ -151,9 +156,113 @@ function fromWire(wire) {
151
156
  }
152
157
 
153
158
  // src/validate.ts
154
- var MIN_PASSWORD = 8;
155
- var MAX_PASSWORD = 256;
159
+ var MIN_PASSWORD = 12;
160
+ var MAX_PASSWORD = 128;
161
+ var FLOOR_PASSWORD = 8;
156
162
  var MAX_NAME = 120;
163
+ var CLASSES = ["lowercase", "uppercase", "digit", "symbol"];
164
+ var DEFAULT_PASSWORD_POLICY = Object.freeze({ min: MIN_PASSWORD, max: MAX_PASSWORD, requires: [], min_classes: 0, reject_breached: false });
165
+ var int = (v, fallback) => typeof v === "number" && Number.isFinite(v) ? Math.trunc(v) : fallback;
166
+ function passwordPolicyOf(raw) {
167
+ const r = raw && typeof raw === "object" ? raw : {};
168
+ const min = Math.min(Math.max(int(r.min ?? r.min_length, MIN_PASSWORD) || MIN_PASSWORD, FLOOR_PASSWORD), MAX_PASSWORD);
169
+ const max = Math.max(Math.min(int(r.max ?? r.max_length, MAX_PASSWORD) || MAX_PASSWORD, MAX_PASSWORD), min);
170
+ const asked = Array.isArray(r.requires) ? r.requires : [];
171
+ return {
172
+ min,
173
+ max,
174
+ requires: CLASSES.filter((c) => asked.includes(c)),
175
+ min_classes: Math.min(Math.max(int(r.min_classes ?? r.minClasses, 0), 0), CLASSES.length),
176
+ reject_breached: (r.reject_breached ?? r.rejectBreached) === true
177
+ };
178
+ }
179
+ function utf8Length(v) {
180
+ let n = 0;
181
+ for (const ch of v) {
182
+ const c = ch.codePointAt(0);
183
+ n += c < 128 ? 1 : c < 2048 ? 2 : c < 65536 ? 3 : 4;
184
+ }
185
+ return n;
186
+ }
187
+ function classesOf(v) {
188
+ const has = /* @__PURE__ */ new Set();
189
+ for (const ch of v) {
190
+ if (/\p{Ll}/u.test(ch)) has.add("lowercase");
191
+ else if (/\p{Lu}/u.test(ch)) has.add("uppercase");
192
+ else if (/\p{Nd}/u.test(ch)) has.add("digit");
193
+ else if (!/\p{L}/u.test(ch)) has.add("symbol");
194
+ }
195
+ return has;
196
+ }
197
+ function passwordProblems(value, policy = DEFAULT_PASSWORD_POLICY) {
198
+ const p = passwordPolicyOf(policy);
199
+ const out = [];
200
+ const length = utf8Length(value);
201
+ if (length < p.min) out.push("too_short");
202
+ if (length > p.max) out.push("too_long");
203
+ const has = classesOf(value);
204
+ for (const c of p.requires) if (!has.has(c)) out.push(`needs_${c}`);
205
+ if (p.min_classes > 0 && CLASSES.filter((c) => has.has(c)).length < p.min_classes) out.push("needs_classes");
206
+ return out;
207
+ }
208
+ var RULES = {
209
+ en: {
210
+ between: "Between {min} and {max} characters.",
211
+ requires: "Include {kinds}.",
212
+ and: "and",
213
+ classes: "Use at least {n} of these: lowercase letters, uppercase letters, digits, symbols.",
214
+ breached: "Passwords found in known data breaches are not accepted.",
215
+ kinds: { lowercase: "a lowercase letter", uppercase: "an uppercase letter", digit: "a digit", symbol: "a symbol" },
216
+ reasons: {
217
+ too_short: "Use at least {min} characters.",
218
+ too_long: "Use at most {max} characters.",
219
+ needs_lowercase: "Include a lowercase letter.",
220
+ needs_uppercase: "Include an uppercase letter.",
221
+ needs_digit: "Include a digit.",
222
+ needs_symbol: "Include a symbol.",
223
+ needs_classes: "Use at least {n} of these: lowercase letters, uppercase letters, digits, symbols.",
224
+ breached: "This password appears in a known data breach. Choose a different one."
225
+ }
226
+ },
227
+ es: {
228
+ between: "Entre {min} y {max} caracteres.",
229
+ requires: "Incluye {kinds}.",
230
+ and: "y",
231
+ classes: "Usa al menos {n} de estos tipos: min\xFAsculas, may\xFAsculas, n\xFAmeros, s\xEDmbolos.",
232
+ breached: "No se aceptan contrase\xF1as que aparecen en filtraciones de datos conocidas.",
233
+ kinds: { lowercase: "una min\xFAscula", uppercase: "una may\xFAscula", digit: "un n\xFAmero", symbol: "un s\xEDmbolo" },
234
+ reasons: {
235
+ too_short: "Usa al menos {min} caracteres.",
236
+ too_long: "Usa como m\xE1ximo {max} caracteres.",
237
+ needs_lowercase: "Incluye una min\xFAscula.",
238
+ needs_uppercase: "Incluye una may\xFAscula.",
239
+ needs_digit: "Incluye un n\xFAmero.",
240
+ needs_symbol: "Incluye un s\xEDmbolo.",
241
+ needs_classes: "Usa al menos {n} de estos tipos: min\xFAsculas, may\xFAsculas, n\xFAmeros, s\xEDmbolos.",
242
+ breached: "Esta contrase\xF1a aparece en una filtraci\xF3n de datos conocida. Elige otra."
243
+ }
244
+ }
245
+ };
246
+ var fill = (s, p) => s.replace("{min}", String(p.min)).replace("{max}", String(p.max)).replace("{n}", String(p.min_classes));
247
+ function describePasswordPolicy(policy = DEFAULT_PASSWORD_POLICY, locale = "en") {
248
+ const p = passwordPolicyOf(policy);
249
+ const t = RULES[locale] ?? RULES.en;
250
+ const parts = [fill(t.between, p)];
251
+ if (p.requires.length > 0) {
252
+ const kinds = p.requires.map((c) => t.kinds[c]);
253
+ const listed = kinds.length === 1 ? kinds[0] : `${kinds.slice(0, -1).join(", ")} ${t.and} ${kinds[kinds.length - 1]}`;
254
+ parts.push(t.requires.replace("{kinds}", listed));
255
+ }
256
+ if (p.min_classes > 0) parts.push(fill(t.classes, p));
257
+ if (p.reject_breached) parts.push(t.breached);
258
+ return parts.join(" ");
259
+ }
260
+ function explainPasswordProblems(reasons, policy = DEFAULT_PASSWORD_POLICY, locale = "en") {
261
+ const p = passwordPolicyOf(policy);
262
+ const t = RULES[locale] ?? RULES.en;
263
+ const said = reasons.filter((r) => r in t.reasons).map((r) => fill(t.reasons[r], p));
264
+ return said.length > 0 ? said.join(" ") : describePasswordPolicy(p, locale);
265
+ }
157
266
  var EMAIL = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
158
267
  function checkEmail(value) {
159
268
  const v = typeof value === "string" ? value.trim() : "";
@@ -166,13 +275,12 @@ function checkPassword(value) {
166
275
  if (value.length > MAX_PASSWORD) return { field: "password", code: "too_long", message: "That password is too long." };
167
276
  return null;
168
277
  }
169
- function checkNewPassword(value) {
170
- const basic = checkPassword(value);
171
- if (basic) return basic;
172
- if (value.length < MIN_PASSWORD) {
173
- return { field: "password", code: "too_short", message: `Use at least ${MIN_PASSWORD} characters.` };
174
- }
175
- return null;
278
+ function checkNewPassword(value, policy) {
279
+ if (typeof value !== "string" || value === "") return { field: "password", code: "required", message: "Enter your password." };
280
+ const p = policy ? passwordPolicyOf(policy) : DEFAULT_PASSWORD_POLICY;
281
+ const reasons = passwordProblems(value, p);
282
+ if (reasons.length === 0) return null;
283
+ return { field: "password", code: reasons[0], message: explainPasswordProblems(reasons, p) };
176
284
  }
177
285
  function checkName(value) {
178
286
  if (value === void 0 || value === null || value === "") return null;
@@ -343,7 +451,7 @@ function createAuthClient(options = {}) {
343
451
  const body = await res.json().catch(() => null);
344
452
  if (res.ok && body && typeof body.status === "string") return { ok: true, ...body };
345
453
  const e = body?.error;
346
- return { ok: false, error: e && typeof e.code === "string" ? { code: e.code, message: String(e.message ?? ""), ...e.field ? { field: e.field } : {} } : UNAVAILABLE };
454
+ return { ok: false, error: e && typeof e.code === "string" ? { code: e.code, message: String(e.message ?? ""), ...e.field ? { field: e.field } : {}, ...Array.isArray(e.reasons) ? { reasons: e.reasons.map(String) } : {} } : UNAVAILABLE };
347
455
  } catch {
348
456
  return { ok: false, error: UNAVAILABLE };
349
457
  }
@@ -371,12 +479,14 @@ function createAuthClient(options = {}) {
371
479
  if (o.locale) q.set("locale", o.locale);
372
480
  return `${base}/login?${q}`;
373
481
  };
482
+ let policy;
374
483
  async function methods() {
375
484
  try {
376
485
  const res = await doFetch(`${base}/methods`, { credentials: "same-origin", headers: { accept: "application/json" } });
377
486
  if (res.ok) {
378
487
  const m = await res.json();
379
- return { providers: [], signUp: true, magicLink: false, passkey: false, ...m, passkeyHere: m.passkeyHere === true, passkeyOnCauthy: m.passkeyOnCauthy ?? m.passkey === true, sso: m.sso === true };
488
+ if (m.password) policy = passwordPolicyOf(m.password);
489
+ 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 };
380
490
  }
381
491
  } catch {
382
492
  }
@@ -520,12 +630,12 @@ function createAuthClient(options = {}) {
520
630
  email: (f) => post(
521
631
  "/sign-up/email",
522
632
  { email: f.email, password: f.password, name: f.name, organization: f.organization, acceptLegal: f.acceptLegal, consents: f.consents },
523
- firstProblem(checkName(f.name), checkEmail(f.email), checkNewPassword(f.password), checkOrganization(f.organization)),
633
+ firstProblem(checkName(f.name), checkEmail(f.email), checkNewPassword(f.password, policy), checkOrganization(f.organization)),
524
634
  f.captchaToken
525
635
  )
526
636
  },
527
637
  forgotPassword: (f) => post("/forgot-password", { email: f.email }, checkEmail(f.email), f.captchaToken),
528
- resetPassword: (f) => post("/reset-password", { token: f.token, password: f.password }, checkNewPassword(f.password)),
638
+ resetPassword: (f) => post("/reset-password", { token: f.token, password: f.password }, checkNewPassword(f.password, policy)),
529
639
  verifyEmail: (f) => post("/verify-email", { token: f.token }, null),
530
640
  signOut: () => post("/sign-out", {}, null),
531
641
  organizations: {
@@ -559,7 +669,7 @@ function createAuthClient(options = {}) {
559
669
  },
560
670
  acceptLegal: (f = {}) => post("/me/legal/accept", { kinds: f.kinds ?? [] }, null),
561
671
  changePassword: (f) => {
562
- const bad = checkNewPassword(f.newPassword);
672
+ const bad = checkNewPassword(f.newPassword, policy);
563
673
  return post("/me/password", { password: f.password, newPassword: f.newPassword }, bad ? { ...bad, field: "newPassword" } : null);
564
674
  },
565
675
  mfa: {
@@ -590,7 +700,7 @@ function createAuthClient(options = {}) {
590
700
  return body && Array.isArray(body.sessions) ? body.sessions : [];
591
701
  },
592
702
  signOutEverywhere: () => post("/me/sessions/end", {}, null),
593
- upgrade: (f) => post("/me/upgrade", { email: f.email, password: f.password, name: f.name ?? "" }, firstProblem(checkName(f.name), checkEmail(f.email), checkNewPassword(f.password))),
703
+ upgrade: (f) => post("/me/upgrade", { email: f.email, password: f.password, name: f.name ?? "" }, firstProblem(checkName(f.name), checkEmail(f.email), checkNewPassword(f.password, policy))),
594
704
  upgradeUrl,
595
705
  upgradeWith(provider, o) {
596
706
  window.location.assign(upgradeUrl(provider, o));
@@ -628,6 +738,7 @@ function createAuthClient(options = {}) {
628
738
  }
629
739
  // Annotate the CommonJS export names for ESM import in node:
630
740
  0 && (module.exports = {
741
+ DEFAULT_PASSWORD_POLICY,
631
742
  MIN_PASSWORD,
632
743
  canAutofillPasskeys,
633
744
  canUsePasskeys,
@@ -637,6 +748,10 @@ function createAuthClient(options = {}) {
637
748
  checkNewPassword,
638
749
  checkPassword,
639
750
  createAuthClient,
640
- createSessionStore
751
+ createSessionStore,
752
+ describePasswordPolicy,
753
+ explainPasswordProblems,
754
+ passwordPolicyOf,
755
+ passwordProblems
641
756
  });
642
757
  //# sourceMappingURL=client.js.map