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