cauthy-auth 0.27.1 → 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/vue.mjs CHANGED
@@ -121,7 +121,111 @@ function fromWire(wire) {
121
121
  // src/validate.ts
122
122
  var MIN_PASSWORD = 12;
123
123
  var MAX_PASSWORD = 128;
124
+ var FLOOR_PASSWORD = 8;
124
125
  var MAX_NAME = 120;
126
+ var CLASSES = ["lowercase", "uppercase", "digit", "symbol"];
127
+ var DEFAULT_PASSWORD_POLICY = Object.freeze({ min: MIN_PASSWORD, max: MAX_PASSWORD, requires: [], min_classes: 0, reject_breached: false });
128
+ var int = (v, fallback) => typeof v === "number" && Number.isFinite(v) ? Math.trunc(v) : fallback;
129
+ function passwordPolicyOf(raw) {
130
+ const r = raw && typeof raw === "object" ? raw : {};
131
+ const min = Math.min(Math.max(int(r.min ?? r.min_length, MIN_PASSWORD) || MIN_PASSWORD, FLOOR_PASSWORD), MAX_PASSWORD);
132
+ const max = Math.max(Math.min(int(r.max ?? r.max_length, MAX_PASSWORD) || MAX_PASSWORD, MAX_PASSWORD), min);
133
+ const asked = Array.isArray(r.requires) ? r.requires : [];
134
+ return {
135
+ min,
136
+ max,
137
+ requires: CLASSES.filter((c) => asked.includes(c)),
138
+ min_classes: Math.min(Math.max(int(r.min_classes ?? r.minClasses, 0), 0), CLASSES.length),
139
+ reject_breached: (r.reject_breached ?? r.rejectBreached) === true
140
+ };
141
+ }
142
+ function utf8Length(v) {
143
+ let n = 0;
144
+ for (const ch of v) {
145
+ const c = ch.codePointAt(0);
146
+ n += c < 128 ? 1 : c < 2048 ? 2 : c < 65536 ? 3 : 4;
147
+ }
148
+ return n;
149
+ }
150
+ function classesOf(v) {
151
+ const has = /* @__PURE__ */ new Set();
152
+ for (const ch of v) {
153
+ if (/\p{Ll}/u.test(ch)) has.add("lowercase");
154
+ else if (/\p{Lu}/u.test(ch)) has.add("uppercase");
155
+ else if (/\p{Nd}/u.test(ch)) has.add("digit");
156
+ else if (!/\p{L}/u.test(ch)) has.add("symbol");
157
+ }
158
+ return has;
159
+ }
160
+ function passwordProblems(value, policy = DEFAULT_PASSWORD_POLICY) {
161
+ const p = passwordPolicyOf(policy);
162
+ const out = [];
163
+ const length = utf8Length(value);
164
+ if (length < p.min) out.push("too_short");
165
+ if (length > p.max) out.push("too_long");
166
+ const has = classesOf(value);
167
+ for (const c of p.requires) if (!has.has(c)) out.push(`needs_${c}`);
168
+ if (p.min_classes > 0 && CLASSES.filter((c) => has.has(c)).length < p.min_classes) out.push("needs_classes");
169
+ return out;
170
+ }
171
+ var RULES = {
172
+ en: {
173
+ between: "Between {min} and {max} characters.",
174
+ requires: "Include {kinds}.",
175
+ and: "and",
176
+ classes: "Use at least {n} of these: lowercase letters, uppercase letters, digits, symbols.",
177
+ breached: "Passwords found in known data breaches are not accepted.",
178
+ kinds: { lowercase: "a lowercase letter", uppercase: "an uppercase letter", digit: "a digit", symbol: "a symbol" },
179
+ reasons: {
180
+ too_short: "Use at least {min} characters.",
181
+ too_long: "Use at most {max} characters.",
182
+ needs_lowercase: "Include a lowercase letter.",
183
+ needs_uppercase: "Include an uppercase letter.",
184
+ needs_digit: "Include a digit.",
185
+ needs_symbol: "Include a symbol.",
186
+ needs_classes: "Use at least {n} of these: lowercase letters, uppercase letters, digits, symbols.",
187
+ breached: "This password appears in a known data breach. Choose a different one."
188
+ }
189
+ },
190
+ es: {
191
+ between: "Entre {min} y {max} caracteres.",
192
+ requires: "Incluye {kinds}.",
193
+ and: "y",
194
+ classes: "Usa al menos {n} de estos tipos: min\xFAsculas, may\xFAsculas, n\xFAmeros, s\xEDmbolos.",
195
+ breached: "No se aceptan contrase\xF1as que aparecen en filtraciones de datos conocidas.",
196
+ kinds: { lowercase: "una min\xFAscula", uppercase: "una may\xFAscula", digit: "un n\xFAmero", symbol: "un s\xEDmbolo" },
197
+ reasons: {
198
+ too_short: "Usa al menos {min} caracteres.",
199
+ too_long: "Usa como m\xE1ximo {max} caracteres.",
200
+ needs_lowercase: "Incluye una min\xFAscula.",
201
+ needs_uppercase: "Incluye una may\xFAscula.",
202
+ needs_digit: "Incluye un n\xFAmero.",
203
+ needs_symbol: "Incluye un s\xEDmbolo.",
204
+ needs_classes: "Usa al menos {n} de estos tipos: min\xFAsculas, may\xFAsculas, n\xFAmeros, s\xEDmbolos.",
205
+ breached: "Esta contrase\xF1a aparece en una filtraci\xF3n de datos conocida. Elige otra."
206
+ }
207
+ }
208
+ };
209
+ var fill = (s, p) => s.replace("{min}", String(p.min)).replace("{max}", String(p.max)).replace("{n}", String(p.min_classes));
210
+ function describePasswordPolicy(policy = DEFAULT_PASSWORD_POLICY, locale = "en") {
211
+ const p = passwordPolicyOf(policy);
212
+ const t = RULES[locale] ?? RULES.en;
213
+ const parts = [fill(t.between, p)];
214
+ if (p.requires.length > 0) {
215
+ const kinds = p.requires.map((c) => t.kinds[c]);
216
+ const listed = kinds.length === 1 ? kinds[0] : `${kinds.slice(0, -1).join(", ")} ${t.and} ${kinds[kinds.length - 1]}`;
217
+ parts.push(t.requires.replace("{kinds}", listed));
218
+ }
219
+ if (p.min_classes > 0) parts.push(fill(t.classes, p));
220
+ if (p.reject_breached) parts.push(t.breached);
221
+ return parts.join(" ");
222
+ }
223
+ function explainPasswordProblems(reasons, policy = DEFAULT_PASSWORD_POLICY, locale = "en") {
224
+ const p = passwordPolicyOf(policy);
225
+ const t = RULES[locale] ?? RULES.en;
226
+ const said = reasons.filter((r) => r in t.reasons).map((r) => fill(t.reasons[r], p));
227
+ return said.length > 0 ? said.join(" ") : describePasswordPolicy(p, locale);
228
+ }
125
229
  var EMAIL = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
126
230
  function checkEmail(value) {
127
231
  const v = typeof value === "string" ? value.trim() : "";
@@ -134,13 +238,12 @@ function checkPassword(value) {
134
238
  if (value.length > MAX_PASSWORD) return { field: "password", code: "too_long", message: "That password is too long." };
135
239
  return null;
136
240
  }
137
- function checkNewPassword(value) {
138
- const basic = checkPassword(value);
139
- if (basic) return basic;
140
- if (value.length < MIN_PASSWORD) {
141
- return { field: "password", code: "too_short", message: `Use at least ${MIN_PASSWORD} characters.` };
142
- }
143
- return null;
241
+ function checkNewPassword(value, policy) {
242
+ if (typeof value !== "string" || value === "") return { field: "password", code: "required", message: "Enter your password." };
243
+ const p = policy ? passwordPolicyOf(policy) : DEFAULT_PASSWORD_POLICY;
244
+ const reasons = passwordProblems(value, p);
245
+ if (reasons.length === 0) return null;
246
+ return { field: "password", code: reasons[0], message: explainPasswordProblems(reasons, p) };
144
247
  }
145
248
  function checkName(value) {
146
249
  if (value === void 0 || value === null || value === "") return null;
@@ -203,7 +306,7 @@ function createAuthClient(options = {}) {
203
306
  const body = await res.json().catch(() => null);
204
307
  if (res.ok && body && typeof body.status === "string") return { ok: true, ...body };
205
308
  const e = body?.error;
206
- return { ok: false, error: e && typeof e.code === "string" ? { code: e.code, message: String(e.message ?? ""), ...e.field ? { field: e.field } : {} } : UNAVAILABLE };
309
+ 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 };
207
310
  } catch {
208
311
  return { ok: false, error: UNAVAILABLE };
209
312
  }
@@ -231,12 +334,14 @@ function createAuthClient(options = {}) {
231
334
  if (o.locale) q.set("locale", o.locale);
232
335
  return `${base}/login?${q}`;
233
336
  };
337
+ let policy;
234
338
  async function methods() {
235
339
  try {
236
340
  const res = await doFetch(`${base}/methods`, { credentials: "same-origin", headers: { accept: "application/json" } });
237
341
  if (res.ok) {
238
342
  const m = await res.json();
239
- return { providers: [], signUp: true, magicLink: false, passkey: false, ...m, passkeyHere: m.passkeyHere === true, passkeyOnCauthy: m.passkeyOnCauthy ?? m.passkey === true, sso: m.sso === true };
343
+ if (m.password) policy = passwordPolicyOf(m.password);
344
+ 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 };
240
345
  }
241
346
  } catch {
242
347
  }
@@ -380,12 +485,12 @@ function createAuthClient(options = {}) {
380
485
  email: (f) => post(
381
486
  "/sign-up/email",
382
487
  { email: f.email, password: f.password, name: f.name, organization: f.organization, acceptLegal: f.acceptLegal, consents: f.consents },
383
- firstProblem(checkName(f.name), checkEmail(f.email), checkNewPassword(f.password), checkOrganization(f.organization)),
488
+ firstProblem(checkName(f.name), checkEmail(f.email), checkNewPassword(f.password, policy), checkOrganization(f.organization)),
384
489
  f.captchaToken
385
490
  )
386
491
  },
387
492
  forgotPassword: (f) => post("/forgot-password", { email: f.email }, checkEmail(f.email), f.captchaToken),
388
- resetPassword: (f) => post("/reset-password", { token: f.token, password: f.password }, checkNewPassword(f.password)),
493
+ resetPassword: (f) => post("/reset-password", { token: f.token, password: f.password }, checkNewPassword(f.password, policy)),
389
494
  verifyEmail: (f) => post("/verify-email", { token: f.token }, null),
390
495
  signOut: () => post("/sign-out", {}, null),
391
496
  organizations: {
@@ -419,7 +524,7 @@ function createAuthClient(options = {}) {
419
524
  },
420
525
  acceptLegal: (f = {}) => post("/me/legal/accept", { kinds: f.kinds ?? [] }, null),
421
526
  changePassword: (f) => {
422
- const bad = checkNewPassword(f.newPassword);
527
+ const bad = checkNewPassword(f.newPassword, policy);
423
528
  return post("/me/password", { password: f.password, newPassword: f.newPassword }, bad ? { ...bad, field: "newPassword" } : null);
424
529
  },
425
530
  mfa: {
@@ -450,7 +555,7 @@ function createAuthClient(options = {}) {
450
555
  return body && Array.isArray(body.sessions) ? body.sessions : [];
451
556
  },
452
557
  signOutEverywhere: () => post("/me/sessions/end", {}, null),
453
- upgrade: (f) => post("/me/upgrade", { email: f.email, password: f.password, name: f.name ?? "" }, firstProblem(checkName(f.name), checkEmail(f.email), checkNewPassword(f.password))),
558
+ 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))),
454
559
  upgradeUrl,
455
560
  upgradeWith(provider, o) {
456
561
  window.location.assign(upgradeUrl(provider, o));