cauthy-auth 0.12.0 → 0.13.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/index.mjs CHANGED
@@ -985,6 +985,11 @@ function createCauthy(given = {}) {
985
985
  }
986
986
  const STEP_SECONDS = 600;
987
987
  const PASSKEY_SECONDS = 300;
988
+ const factorsOf = (d) => ({
989
+ totp: d?.totp === true,
990
+ phone: d?.phone ? { number: String(d.phone.number ?? ""), channel: String(d.phone.channel ?? "sms") } : null,
991
+ phoneChannels: Array.isArray(d?.phone_channels) ? d.phone_channels.map(String) : []
992
+ });
988
993
  const isCredential = (v) => typeof v === "object" && v !== null && !Array.isArray(v) && typeof v.id === "string" && typeof v.response === "object";
989
994
  const passkeyOf = (d) => ({
990
995
  id: String(d?.id ?? ""),
@@ -1035,6 +1040,9 @@ function createCauthy(given = {}) {
1035
1040
  ...clientId ? { "x-cauthy-client-id": clientId } : {},
1036
1041
  ...await passedOn(request),
1037
1042
  ...bearer2 ? { authorization: `Bearer ${bearer2}` } : {},
1043
+ // The answer to the workspace's Turnstile challenge, from the
1044
+ // person's browser: Cauthy checks it at the doors that ask for it.
1045
+ ...request.headers.get("x-cauthy-captcha") ? { "x-captcha-token": request.headers.get("x-cauthy-captcha") } : {},
1038
1046
  ...request.headers.get("accept-language") ? { "accept-language": request.headers.get("accept-language") } : {},
1039
1047
  // The language the application says its person reads, over the browser's.
1040
1048
  ...request.headers.get("x-cauthy-locale") ? { "x-cauthy-locale": request.headers.get("x-cauthy-locale") } : {},
@@ -1092,6 +1100,12 @@ function createCauthy(given = {}) {
1092
1100
  "/me/mfa/verify",
1093
1101
  "/me/mfa/disable",
1094
1102
  "/me/mfa/recovery-codes",
1103
+ "/me/mfa/factors",
1104
+ "/me/mfa/phone",
1105
+ "/me/mfa/phone/verify",
1106
+ "/me/mfa/phone/remove",
1107
+ "/sign-in/mfa/methods",
1108
+ "/sign-in/mfa/send",
1095
1109
  "/me/sessions",
1096
1110
  "/me/sessions/end",
1097
1111
  "/me/email",
@@ -1135,7 +1149,8 @@ function createCauthy(given = {}) {
1135
1149
  magicLink: settings.data?.allow_magic_link === true && pages.signIn !== void 0,
1136
1150
  passkey: place.error ? true : here || place.data?.on_cauthy === true,
1137
1151
  passkeyHere: here,
1138
- passkeyOnCauthy: place.error ? true : place.data?.on_cauthy === true
1152
+ passkeyOnCauthy: place.error ? true : place.data?.on_cauthy === true,
1153
+ ...settings.data?.captcha_enabled === true && settings.data?.captcha_site_key ? { captchaSiteKey: String(settings.data.captcha_site_key) } : {}
1139
1154
  });
1140
1155
  }
1141
1156
  if (route.startsWith("/sign-in/social/") && !post) {
@@ -1205,6 +1220,13 @@ function createCauthy(given = {}) {
1205
1220
  const list = Array.isArray(res.data) ? res.data : [];
1206
1221
  return json({ passkeys: list.map(passkeyOf) }, 200, mine.headers);
1207
1222
  }
1223
+ if (route === "/me/mfa/factors" && !post) {
1224
+ const mine = await signedInAs(request);
1225
+ if ("response" in mine) return mine.response;
1226
+ const res = await ask(request, "GET", "/v1/auth/mfa/factors", void 0, void 0, mine.token);
1227
+ if (res.error) return problem(res.status, res.error.code, res.error.message, void 0, mine.headers);
1228
+ return json(factorsOf(res.data), 200, mine.headers);
1229
+ }
1208
1230
  if ((route === "/me" || route === "/me/sessions") && !post) {
1209
1231
  const mine = await signedInAs(request);
1210
1232
  if ("response" in mine) return mine.response;
@@ -1253,6 +1275,20 @@ function createCauthy(given = {}) {
1253
1275
  if (res.error) return problem(res.status, res.error.code, res.error.message, "code");
1254
1276
  return signedIn(request, res.data, [writeCookie(stepCookie, "", attrs(request, 0))]);
1255
1277
  }
1278
+ if (route === "/sign-in/mfa/methods" || route === "/sign-in/mfa/send") {
1279
+ const sealed = readCookies(request.headers.get("cookie")).get(stepCookie);
1280
+ const step = sealed ? await unseal(sealed, await key) : null;
1281
+ if (step === null || step.kind !== "mfa") return problem(400, "step_expired", "Sign in again: the code was asked for too long ago.");
1282
+ if (route === "/sign-in/mfa/methods") {
1283
+ const res2 = await ask(request, "POST", "/v1/auth/mfa/methods", { mfa_token: step.token });
1284
+ if (res2.error) return problem(res2.status, res2.error.code, res2.error.message);
1285
+ const p = res2.data?.phone;
1286
+ return json({ status: "ok", totp: res2.data?.totp === true, phone: p ? { number: String(p.number ?? ""), channel: String(p.channel ?? "sms") } : null });
1287
+ }
1288
+ const res = await ask(request, "POST", "/v1/auth/mfa/send", { mfa_token: step.token });
1289
+ if (res.error) return problem(res.status, res.error.code, res.error.message);
1290
+ return json({ status: "sent", sentTo: String(res.data?.sent_to ?? ""), channel: String(res.data?.channel ?? "sms") });
1291
+ }
1256
1292
  if (route === "/sign-up/email") {
1257
1293
  const bad = firstProblem(checkName(b.name), checkEmail(b.email), checkNewPassword(b.password));
1258
1294
  if (bad) return invalid(bad);
@@ -1311,6 +1347,23 @@ function createCauthy(given = {}) {
1311
1347
  if (res.error) return refused(res, "code");
1312
1348
  return json({ status: "enabled", recoveryCodes: Array.isArray(res.data?.recovery_codes) ? res.data.recovery_codes.map(String) : [] }, 200, mine.headers);
1313
1349
  }
1350
+ if (route === "/me/mfa/phone") {
1351
+ const res = await as("/v1/auth/mfa/phone", { phone: typeof b.phone === "string" ? b.phone : "", channel: typeof b.channel === "string" ? b.channel : "sms", password });
1352
+ if (res.error) return refused(res, res.error.code === "step_up_required" ? "password" : res.error.code === "validation_error" ? "phone" : void 0);
1353
+ return json({ status: "sent", sentTo: String(res.data?.sent_to ?? ""), channel: String(res.data?.channel ?? "") }, 200, mine.headers);
1354
+ }
1355
+ if (route === "/me/mfa/phone/verify") {
1356
+ const bad = checkCode(b.code);
1357
+ if (bad) return invalid(bad);
1358
+ const res = await as("/v1/auth/mfa/phone/verify", { code: String(b.code).trim() });
1359
+ if (res.error) return refused(res, "code");
1360
+ return json({ status: "enabled", recoveryCodes: Array.isArray(res.data?.recovery_codes) ? res.data.recovery_codes.map(String) : [] }, 200, mine.headers);
1361
+ }
1362
+ if (route === "/me/mfa/phone/remove") {
1363
+ const res = await as("/v1/auth/mfa/phone/remove", { password });
1364
+ if (res.error) return refused(res, res.error.code === "step_up_required" ? "password" : void 0);
1365
+ return json({ status: "removed" }, 200, mine.headers);
1366
+ }
1314
1367
  if (route === "/me/mfa/disable") {
1315
1368
  const res = await as("/v1/auth/me/mfa/disable", { password });
1316
1369
  if (res.error) return refused(res, res.error.code === "step_up_required" ? "password" : void 0);