cauthy-auth 0.12.0 → 0.14.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 ?? ""),
@@ -992,6 +997,15 @@ function createCauthy(given = {}) {
992
997
  createdAt: String(d?.created_at ?? ""),
993
998
  lastUsedAt: d?.last_used_at ? String(d.last_used_at) : null
994
999
  });
1000
+ const agentOf = (d) => ({
1001
+ id: String(d?.id ?? ""),
1002
+ name: String(d?.agent?.name ?? ""),
1003
+ redirectHost: String(d?.agent?.redirect_host ?? ""),
1004
+ resource: { name: String(d?.resource?.name ?? ""), uri: String(d?.resource?.uri ?? "") },
1005
+ scopes: Array.isArray(d?.scopes) ? d.scopes.map((s) => ({ name: String(s?.name ?? ""), description: String(s?.description ?? "") })) : [],
1006
+ connectedAt: String(d?.created_at ?? ""),
1007
+ lastUsedAt: String(d?.last_used_at ?? "")
1008
+ });
995
1009
  const problem = (status2, code, message, field, headers) => json({ error: { code, message, ...field ? { field } : {} } }, status2, headers);
996
1010
  const invalid = (p) => problem(400, p.code, p.message, p.field);
997
1011
  function ownRequest(request) {
@@ -1035,6 +1049,9 @@ function createCauthy(given = {}) {
1035
1049
  ...clientId ? { "x-cauthy-client-id": clientId } : {},
1036
1050
  ...await passedOn(request),
1037
1051
  ...bearer2 ? { authorization: `Bearer ${bearer2}` } : {},
1052
+ // The answer to the workspace's Turnstile challenge, from the
1053
+ // person's browser: Cauthy checks it at the doors that ask for it.
1054
+ ...request.headers.get("x-cauthy-captcha") ? { "x-captcha-token": request.headers.get("x-cauthy-captcha") } : {},
1038
1055
  ...request.headers.get("accept-language") ? { "accept-language": request.headers.get("accept-language") } : {},
1039
1056
  // The language the application says its person reads, over the browser's.
1040
1057
  ...request.headers.get("x-cauthy-locale") ? { "x-cauthy-locale": request.headers.get("x-cauthy-locale") } : {},
@@ -1092,6 +1109,12 @@ function createCauthy(given = {}) {
1092
1109
  "/me/mfa/verify",
1093
1110
  "/me/mfa/disable",
1094
1111
  "/me/mfa/recovery-codes",
1112
+ "/me/mfa/factors",
1113
+ "/me/mfa/phone",
1114
+ "/me/mfa/phone/verify",
1115
+ "/me/mfa/phone/remove",
1116
+ "/sign-in/mfa/methods",
1117
+ "/sign-in/mfa/send",
1095
1118
  "/me/sessions",
1096
1119
  "/me/sessions/end",
1097
1120
  "/me/email",
@@ -1107,7 +1130,9 @@ function createCauthy(given = {}) {
1107
1130
  "/me/passkeys",
1108
1131
  "/me/passkeys/add/begin",
1109
1132
  "/me/passkeys/add/finish",
1110
- "/me/passkeys/remove"
1133
+ "/me/passkeys/remove",
1134
+ "/me/agents",
1135
+ "/me/agents/disconnect"
1111
1136
  ].includes(route) || route.startsWith("/sign-in/social/") || route.startsWith("/callback/") || route.startsWith("/me/identities/link/");
1112
1137
  if (!own) return null;
1113
1138
  if (tenant === "") {
@@ -1135,7 +1160,8 @@ function createCauthy(given = {}) {
1135
1160
  magicLink: settings.data?.allow_magic_link === true && pages.signIn !== void 0,
1136
1161
  passkey: place.error ? true : here || place.data?.on_cauthy === true,
1137
1162
  passkeyHere: here,
1138
- passkeyOnCauthy: place.error ? true : place.data?.on_cauthy === true
1163
+ passkeyOnCauthy: place.error ? true : place.data?.on_cauthy === true,
1164
+ ...settings.data?.captcha_enabled === true && settings.data?.captcha_site_key ? { captchaSiteKey: String(settings.data.captcha_site_key) } : {}
1139
1165
  });
1140
1166
  }
1141
1167
  if (route.startsWith("/sign-in/social/") && !post) {
@@ -1205,6 +1231,21 @@ function createCauthy(given = {}) {
1205
1231
  const list = Array.isArray(res.data) ? res.data : [];
1206
1232
  return json({ passkeys: list.map(passkeyOf) }, 200, mine.headers);
1207
1233
  }
1234
+ if (route === "/me/agents" && !post) {
1235
+ const mine = await signedInAs(request);
1236
+ if ("response" in mine) return mine.response;
1237
+ const res = await ask(request, "GET", "/v1/auth/me/agents", void 0, void 0, mine.token);
1238
+ if (res.error) return problem(res.status, res.error.code, res.error.message, void 0, mine.headers);
1239
+ const list = Array.isArray(res.data) ? res.data : [];
1240
+ return json({ agents: list.map(agentOf) }, 200, mine.headers);
1241
+ }
1242
+ if (route === "/me/mfa/factors" && !post) {
1243
+ const mine = await signedInAs(request);
1244
+ if ("response" in mine) return mine.response;
1245
+ const res = await ask(request, "GET", "/v1/auth/mfa/factors", void 0, void 0, mine.token);
1246
+ if (res.error) return problem(res.status, res.error.code, res.error.message, void 0, mine.headers);
1247
+ return json(factorsOf(res.data), 200, mine.headers);
1248
+ }
1208
1249
  if ((route === "/me" || route === "/me/sessions") && !post) {
1209
1250
  const mine = await signedInAs(request);
1210
1251
  if ("response" in mine) return mine.response;
@@ -1253,6 +1294,20 @@ function createCauthy(given = {}) {
1253
1294
  if (res.error) return problem(res.status, res.error.code, res.error.message, "code");
1254
1295
  return signedIn(request, res.data, [writeCookie(stepCookie, "", attrs(request, 0))]);
1255
1296
  }
1297
+ if (route === "/sign-in/mfa/methods" || route === "/sign-in/mfa/send") {
1298
+ const sealed = readCookies(request.headers.get("cookie")).get(stepCookie);
1299
+ const step = sealed ? await unseal(sealed, await key) : null;
1300
+ if (step === null || step.kind !== "mfa") return problem(400, "step_expired", "Sign in again: the code was asked for too long ago.");
1301
+ if (route === "/sign-in/mfa/methods") {
1302
+ const res2 = await ask(request, "POST", "/v1/auth/mfa/methods", { mfa_token: step.token });
1303
+ if (res2.error) return problem(res2.status, res2.error.code, res2.error.message);
1304
+ const p = res2.data?.phone;
1305
+ return json({ status: "ok", totp: res2.data?.totp === true, phone: p ? { number: String(p.number ?? ""), channel: String(p.channel ?? "sms") } : null });
1306
+ }
1307
+ const res = await ask(request, "POST", "/v1/auth/mfa/send", { mfa_token: step.token });
1308
+ if (res.error) return problem(res.status, res.error.code, res.error.message);
1309
+ return json({ status: "sent", sentTo: String(res.data?.sent_to ?? ""), channel: String(res.data?.channel ?? "sms") });
1310
+ }
1256
1311
  if (route === "/sign-up/email") {
1257
1312
  const bad = firstProblem(checkName(b.name), checkEmail(b.email), checkNewPassword(b.password));
1258
1313
  if (bad) return invalid(bad);
@@ -1311,6 +1366,23 @@ function createCauthy(given = {}) {
1311
1366
  if (res.error) return refused(res, "code");
1312
1367
  return json({ status: "enabled", recoveryCodes: Array.isArray(res.data?.recovery_codes) ? res.data.recovery_codes.map(String) : [] }, 200, mine.headers);
1313
1368
  }
1369
+ if (route === "/me/mfa/phone") {
1370
+ const res = await as("/v1/auth/mfa/phone", { phone: typeof b.phone === "string" ? b.phone : "", channel: typeof b.channel === "string" ? b.channel : "sms", password });
1371
+ if (res.error) return refused(res, res.error.code === "step_up_required" ? "password" : res.error.code === "validation_error" ? "phone" : void 0);
1372
+ return json({ status: "sent", sentTo: String(res.data?.sent_to ?? ""), channel: String(res.data?.channel ?? "") }, 200, mine.headers);
1373
+ }
1374
+ if (route === "/me/mfa/phone/verify") {
1375
+ const bad = checkCode(b.code);
1376
+ if (bad) return invalid(bad);
1377
+ const res = await as("/v1/auth/mfa/phone/verify", { code: String(b.code).trim() });
1378
+ if (res.error) return refused(res, "code");
1379
+ return json({ status: "enabled", recoveryCodes: Array.isArray(res.data?.recovery_codes) ? res.data.recovery_codes.map(String) : [] }, 200, mine.headers);
1380
+ }
1381
+ if (route === "/me/mfa/phone/remove") {
1382
+ const res = await as("/v1/auth/mfa/phone/remove", { password });
1383
+ if (res.error) return refused(res, res.error.code === "step_up_required" ? "password" : void 0);
1384
+ return json({ status: "removed" }, 200, mine.headers);
1385
+ }
1314
1386
  if (route === "/me/mfa/disable") {
1315
1387
  const res = await as("/v1/auth/me/mfa/disable", { password });
1316
1388
  if (res.error) return refused(res, res.error.code === "step_up_required" ? "password" : void 0);
@@ -1363,6 +1435,13 @@ function createCauthy(given = {}) {
1363
1435
  if (res.error) return refused(res);
1364
1436
  return json({ status: "removed", id }, 200, mine.headers);
1365
1437
  }
1438
+ if (route === "/me/agents/disconnect") {
1439
+ const id = typeof b.id === "string" ? b.id : "";
1440
+ if (!/^[0-9a-f-]{36}$/i.test(id)) return problem(400, "invalid_agent", "Say which agent to disconnect.", void 0, mine.headers);
1441
+ const res = await as(`/v1/auth/me/agents/${id}`, void 0, "DELETE");
1442
+ if (res.error) return refused(res);
1443
+ return json({ status: "disconnected", id }, 200, mine.headers);
1444
+ }
1366
1445
  if (route === "/me/sessions/end") {
1367
1446
  const res = await as("/v1/auth/me/sessions", void 0, "DELETE");
1368
1447
  if (res.error) return refused(res);