@camstack/addon-auth 1.1.5 → 1.1.6
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/{dist-BopfLZ9P.mjs → dist-BlBNW5nA.mjs} +31 -1
- package/dist/{dist-biwV9TkV.js → dist-CS-GzG-v.js} +42 -0
- package/dist/magic-link/auth-magic-link.addon.js +1 -1
- package/dist/magic-link/auth-magic-link.addon.mjs +1 -1
- package/dist/oidc/auth-oidc.addon.js +1 -1
- package/dist/oidc/auth-oidc.addon.mjs +1 -1
- package/dist/webauthn/_stub.js +58 -12
- package/dist/webauthn/{_virtual_mf-localSharedImportMap___mfe_internal__addon_auth_webauthn_widgets-DW5sgIHW.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_auth_webauthn_widgets-Du23U8IZ.mjs} +2 -2
- package/dist/webauthn/auth-webauthn.addon.js +77 -2
- package/dist/webauthn/auth-webauthn.addon.mjs +77 -2
- package/dist/webauthn/{hostInit-DKNSGMxo.mjs → hostInit-Cq4HnxlU.mjs} +2 -2
- package/dist/webauthn/remoteEntry.js +1 -1
- package/package.json +1 -1
|
@@ -16418,6 +16418,15 @@ method(_void(), array(TurnServerSchema).readonly());
|
|
|
16418
16418
|
* - `listPasskeys({userId})` — enumerate user's enrolled credentials.
|
|
16419
16419
|
* - `removePasskey({userId, credentialId})` — revoke one credential.
|
|
16420
16420
|
*
|
|
16421
|
+
* 4. Second-factor preference (opt-in, default OFF):
|
|
16422
|
+
* Enrolling a passkey only enables passkey-FIRST sign-in. It is
|
|
16423
|
+
* demanded as a second factor after a password login ONLY when the
|
|
16424
|
+
* user explicitly opts in via `setSecondFactorPreference`.
|
|
16425
|
+
* - `getSecondFactorPreference({userId})` → `{ enabled }` (missing
|
|
16426
|
+
* row ⇒ `enabled: false`).
|
|
16427
|
+
* - `setSecondFactorPreference({userId, enabled})` — persisted by
|
|
16428
|
+
* the providing addon beside its credentials.
|
|
16429
|
+
*
|
|
16421
16430
|
* Challenges are short-lived (5 min, in-memory). The cap is internal —
|
|
16422
16431
|
* the admin-ui composes the begin/finish round-trip and never exposes
|
|
16423
16432
|
* the cap to non-admins.
|
|
@@ -16491,6 +16500,15 @@ response: record(string(), unknown()) }), object({
|
|
|
16491
16500
|
kind: "mutation",
|
|
16492
16501
|
auth: "admin",
|
|
16493
16502
|
access: "delete"
|
|
16503
|
+
}),
|
|
16504
|
+
getSecondFactorPreference: method(object({ userId: string() }), object({ enabled: boolean() }), { auth: "admin" }),
|
|
16505
|
+
setSecondFactorPreference: method(object({
|
|
16506
|
+
userId: string(),
|
|
16507
|
+
enabled: boolean()
|
|
16508
|
+
}), object({ success: literal(true) }), {
|
|
16509
|
+
kind: "mutation",
|
|
16510
|
+
auth: "admin",
|
|
16511
|
+
access: "create"
|
|
16494
16512
|
})
|
|
16495
16513
|
}
|
|
16496
16514
|
};
|
|
@@ -23015,6 +23033,12 @@ Object.freeze({
|
|
|
23015
23033
|
addonId: null,
|
|
23016
23034
|
access: "create"
|
|
23017
23035
|
},
|
|
23036
|
+
"userPasskeys.getSecondFactorPreference": {
|
|
23037
|
+
capName: "user-passkeys",
|
|
23038
|
+
capScope: "system",
|
|
23039
|
+
addonId: null,
|
|
23040
|
+
access: "view"
|
|
23041
|
+
},
|
|
23018
23042
|
"userPasskeys.listPasskeys": {
|
|
23019
23043
|
capName: "user-passkeys",
|
|
23020
23044
|
capScope: "system",
|
|
@@ -23027,6 +23051,12 @@ Object.freeze({
|
|
|
23027
23051
|
addonId: null,
|
|
23028
23052
|
access: "delete"
|
|
23029
23053
|
},
|
|
23054
|
+
"userPasskeys.setSecondFactorPreference": {
|
|
23055
|
+
capName: "user-passkeys",
|
|
23056
|
+
capScope: "system",
|
|
23057
|
+
addonId: null,
|
|
23058
|
+
access: "create"
|
|
23059
|
+
},
|
|
23030
23060
|
"vacuumControl.locate": {
|
|
23031
23061
|
capName: "vacuum-control",
|
|
23032
23062
|
capScope: "device",
|
|
@@ -23279,4 +23309,4 @@ object({
|
|
|
23279
23309
|
schemaVersion: literal(1)
|
|
23280
23310
|
});
|
|
23281
23311
|
//#endregion
|
|
23282
|
-
export { loginMethodCapability as a, BaseAddon as c,
|
|
23312
|
+
export { loginMethodCapability as a, BaseAddon as c, number as d, object as f, buildAddonRouteProvider as i, array as l, addonWidgetsSourceCapability as n, userPasskeysCapability as o, string as p, authProviderCapability as r, errMsg as s, addonRoutesCapability as t, boolean as u };
|
|
@@ -16418,6 +16418,15 @@ method(_void(), array(TurnServerSchema).readonly());
|
|
|
16418
16418
|
* - `listPasskeys({userId})` — enumerate user's enrolled credentials.
|
|
16419
16419
|
* - `removePasskey({userId, credentialId})` — revoke one credential.
|
|
16420
16420
|
*
|
|
16421
|
+
* 4. Second-factor preference (opt-in, default OFF):
|
|
16422
|
+
* Enrolling a passkey only enables passkey-FIRST sign-in. It is
|
|
16423
|
+
* demanded as a second factor after a password login ONLY when the
|
|
16424
|
+
* user explicitly opts in via `setSecondFactorPreference`.
|
|
16425
|
+
* - `getSecondFactorPreference({userId})` → `{ enabled }` (missing
|
|
16426
|
+
* row ⇒ `enabled: false`).
|
|
16427
|
+
* - `setSecondFactorPreference({userId, enabled})` — persisted by
|
|
16428
|
+
* the providing addon beside its credentials.
|
|
16429
|
+
*
|
|
16421
16430
|
* Challenges are short-lived (5 min, in-memory). The cap is internal —
|
|
16422
16431
|
* the admin-ui composes the begin/finish round-trip and never exposes
|
|
16423
16432
|
* the cap to non-admins.
|
|
@@ -16491,6 +16500,15 @@ response: record(string(), unknown()) }), object({
|
|
|
16491
16500
|
kind: "mutation",
|
|
16492
16501
|
auth: "admin",
|
|
16493
16502
|
access: "delete"
|
|
16503
|
+
}),
|
|
16504
|
+
getSecondFactorPreference: method(object({ userId: string() }), object({ enabled: boolean() }), { auth: "admin" }),
|
|
16505
|
+
setSecondFactorPreference: method(object({
|
|
16506
|
+
userId: string(),
|
|
16507
|
+
enabled: boolean()
|
|
16508
|
+
}), object({ success: literal(true) }), {
|
|
16509
|
+
kind: "mutation",
|
|
16510
|
+
auth: "admin",
|
|
16511
|
+
access: "create"
|
|
16494
16512
|
})
|
|
16495
16513
|
}
|
|
16496
16514
|
};
|
|
@@ -23015,6 +23033,12 @@ Object.freeze({
|
|
|
23015
23033
|
addonId: null,
|
|
23016
23034
|
access: "create"
|
|
23017
23035
|
},
|
|
23036
|
+
"userPasskeys.getSecondFactorPreference": {
|
|
23037
|
+
capName: "user-passkeys",
|
|
23038
|
+
capScope: "system",
|
|
23039
|
+
addonId: null,
|
|
23040
|
+
access: "view"
|
|
23041
|
+
},
|
|
23018
23042
|
"userPasskeys.listPasskeys": {
|
|
23019
23043
|
capName: "user-passkeys",
|
|
23020
23044
|
capScope: "system",
|
|
@@ -23027,6 +23051,12 @@ Object.freeze({
|
|
|
23027
23051
|
addonId: null,
|
|
23028
23052
|
access: "delete"
|
|
23029
23053
|
},
|
|
23054
|
+
"userPasskeys.setSecondFactorPreference": {
|
|
23055
|
+
capName: "user-passkeys",
|
|
23056
|
+
capScope: "system",
|
|
23057
|
+
addonId: null,
|
|
23058
|
+
access: "create"
|
|
23059
|
+
},
|
|
23030
23060
|
"vacuumControl.locate": {
|
|
23031
23061
|
capName: "vacuum-control",
|
|
23032
23062
|
capScope: "device",
|
|
@@ -23309,6 +23339,12 @@ Object.defineProperty(exports, "authProviderCapability", {
|
|
|
23309
23339
|
return authProviderCapability;
|
|
23310
23340
|
}
|
|
23311
23341
|
});
|
|
23342
|
+
Object.defineProperty(exports, "boolean", {
|
|
23343
|
+
enumerable: true,
|
|
23344
|
+
get: function() {
|
|
23345
|
+
return boolean;
|
|
23346
|
+
}
|
|
23347
|
+
});
|
|
23312
23348
|
Object.defineProperty(exports, "buildAddonRouteProvider", {
|
|
23313
23349
|
enumerable: true,
|
|
23314
23350
|
get: function() {
|
|
@@ -23327,6 +23363,12 @@ Object.defineProperty(exports, "loginMethodCapability", {
|
|
|
23327
23363
|
return loginMethodCapability;
|
|
23328
23364
|
}
|
|
23329
23365
|
});
|
|
23366
|
+
Object.defineProperty(exports, "number", {
|
|
23367
|
+
enumerable: true,
|
|
23368
|
+
get: function() {
|
|
23369
|
+
return number;
|
|
23370
|
+
}
|
|
23371
|
+
});
|
|
23330
23372
|
Object.defineProperty(exports, "object", {
|
|
23331
23373
|
enumerable: true,
|
|
23332
23374
|
get: function() {
|
|
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
require("../chunk-Cek0wNdY.js");
|
|
6
|
-
const require_dist = require("../dist-
|
|
6
|
+
const require_dist = require("../dist-CS-GzG-v.js");
|
|
7
7
|
//#region src/magic-link/auth-magic-link.addon.ts
|
|
8
8
|
/**
|
|
9
9
|
* Magic-link authentication addon.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as loginMethodCapability, c as BaseAddon, i as buildAddonRouteProvider, r as authProviderCapability, s as errMsg, t as addonRoutesCapability } from "../dist-
|
|
1
|
+
import { a as loginMethodCapability, c as BaseAddon, i as buildAddonRouteProvider, r as authProviderCapability, s as errMsg, t as addonRoutesCapability } from "../dist-BlBNW5nA.mjs";
|
|
2
2
|
//#region src/magic-link/auth-magic-link.addon.ts
|
|
3
3
|
/**
|
|
4
4
|
* Magic-link authentication addon.
|
|
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
const require_chunk = require("../chunk-Cek0wNdY.js");
|
|
6
|
-
const require_dist = require("../dist-
|
|
6
|
+
const require_dist = require("../dist-CS-GzG-v.js");
|
|
7
7
|
let node_crypto = require("node:crypto");
|
|
8
8
|
node_crypto = require_chunk.__toESM(node_crypto);
|
|
9
9
|
//#region node_modules/jose/dist/webapi/lib/buffer_utils.js
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as loginMethodCapability, c as BaseAddon, i as buildAddonRouteProvider, r as authProviderCapability, s as errMsg, t as addonRoutesCapability } from "../dist-
|
|
1
|
+
import { a as loginMethodCapability, c as BaseAddon, i as buildAddonRouteProvider, r as authProviderCapability, s as errMsg, t as addonRoutesCapability } from "../dist-BlBNW5nA.mjs";
|
|
2
2
|
import * as crypto$1 from "node:crypto";
|
|
3
3
|
//#region node_modules/jose/dist/webapi/lib/buffer_utils.js
|
|
4
4
|
var encoder = new TextEncoder();
|
package/dist/webauthn/_stub.js
CHANGED
|
@@ -414,7 +414,17 @@ function B(e) {
|
|
|
414
414
|
let n = a(), l = r({
|
|
415
415
|
queryKey: ["auth", "listOwnPasskeys"],
|
|
416
416
|
queryFn: () => n.listOwnPasskeys()
|
|
417
|
-
}), u = l.data ?? [], [d, f] = t(""), [p, m] = t(!1), [h, g] = t(null), S =
|
|
417
|
+
}), u = l.data ?? [], [d, f] = t(""), [p, m] = t(!1), [h, g] = t(null), S = r({
|
|
418
|
+
queryKey: ["auth", "getOwnPasskeySecondFactorPreference"],
|
|
419
|
+
queryFn: () => n.getOwnPasskeySecondFactorPreference(),
|
|
420
|
+
enabled: u.length > 0
|
|
421
|
+
}), C = S.data?.enabled ?? !1, w = i({
|
|
422
|
+
mutationFn: (e) => n.setOwnPasskeySecondFactorPreference({ enabled: e }),
|
|
423
|
+
onSuccess: () => {
|
|
424
|
+
g(null), S.refetch();
|
|
425
|
+
},
|
|
426
|
+
onError: (e) => g(e instanceof Error ? e.message : "Could not update the preference")
|
|
427
|
+
}), T = async () => {
|
|
418
428
|
g(null), m(!0);
|
|
419
429
|
try {
|
|
420
430
|
let e = await z((await n.beginOwnPasskeyRegistration()).optionsJSON);
|
|
@@ -427,7 +437,7 @@ function B(e) {
|
|
|
427
437
|
} finally {
|
|
428
438
|
m(!1);
|
|
429
439
|
}
|
|
430
|
-
},
|
|
440
|
+
}, E = i({
|
|
431
441
|
mutationFn: (e) => n.removeOwnPasskey({ credentialId: e }),
|
|
432
442
|
onSuccess: () => {
|
|
433
443
|
g(null), l.refetch();
|
|
@@ -447,7 +457,7 @@ function B(e) {
|
|
|
447
457
|
children: [
|
|
448
458
|
/* @__PURE__ */ o("div", {
|
|
449
459
|
className: "text-xs text-foreground-subtle",
|
|
450
|
-
children: "Add a passkey (Touch ID, Windows Hello, or a security key)
|
|
460
|
+
children: "Add a passkey (Touch ID, Windows Hello, or a security key) to sign in directly — fast and phishing-resistant, no password needed."
|
|
451
461
|
}),
|
|
452
462
|
l.isLoading ? /* @__PURE__ */ s("div", {
|
|
453
463
|
className: "flex items-center gap-2 text-xs text-foreground-subtle",
|
|
@@ -473,8 +483,8 @@ function B(e) {
|
|
|
473
483
|
})]
|
|
474
484
|
}),
|
|
475
485
|
/* @__PURE__ */ s("button", {
|
|
476
|
-
onClick: () =>
|
|
477
|
-
disabled:
|
|
486
|
+
onClick: () => E.mutate(e.credentialId),
|
|
487
|
+
disabled: E.isPending,
|
|
478
488
|
className: "inline-flex items-center gap-1 rounded px-2 py-1 text-[11px] text-danger hover:bg-danger/10 disabled:opacity-50",
|
|
479
489
|
children: [/* @__PURE__ */ o(x, { className: "h-3 w-3" }), "Remove"]
|
|
480
490
|
})
|
|
@@ -484,6 +494,28 @@ function B(e) {
|
|
|
484
494
|
className: "text-[11px] text-foreground-subtle",
|
|
485
495
|
children: "No passkeys enrolled yet."
|
|
486
496
|
}),
|
|
497
|
+
u.length > 0 && /* @__PURE__ */ s("div", {
|
|
498
|
+
className: "flex items-start justify-between gap-3 rounded-md border border-border px-3 py-2.5",
|
|
499
|
+
children: [/* @__PURE__ */ s("div", {
|
|
500
|
+
className: "min-w-0",
|
|
501
|
+
children: [/* @__PURE__ */ o("div", {
|
|
502
|
+
className: "text-xs font-medium text-foreground",
|
|
503
|
+
children: "Require passkey as second factor"
|
|
504
|
+
}), /* @__PURE__ */ o("div", {
|
|
505
|
+
className: "text-[10px] text-foreground-subtle mt-0.5",
|
|
506
|
+
children: "Also ask for a passkey after every password sign-in. Off by default — enrolling a passkey on its own only enables direct passkey sign-in."
|
|
507
|
+
})]
|
|
508
|
+
}), /* @__PURE__ */ o("button", {
|
|
509
|
+
type: "button",
|
|
510
|
+
role: "switch",
|
|
511
|
+
"aria-checked": C,
|
|
512
|
+
"aria-label": "Require passkey as second factor",
|
|
513
|
+
onClick: () => w.mutate(!C),
|
|
514
|
+
disabled: S.isLoading || w.isPending,
|
|
515
|
+
className: `relative inline-flex h-5 w-9 shrink-0 items-center rounded-full transition-colors disabled:opacity-50 ${C ? "bg-primary" : "bg-foreground-subtle/30"}`,
|
|
516
|
+
children: /* @__PURE__ */ o("span", { className: `inline-block h-3.5 w-3.5 rounded-full bg-white shadow transition-transform ${C ? "translate-x-[18px]" : "translate-x-[3px]"}` })
|
|
517
|
+
})]
|
|
518
|
+
}),
|
|
487
519
|
h && /* @__PURE__ */ s("div", {
|
|
488
520
|
className: "text-[11px] text-danger flex items-center gap-1",
|
|
489
521
|
children: [/* @__PURE__ */ o(_, { className: "h-3 w-3" }), h]
|
|
@@ -503,7 +535,7 @@ function B(e) {
|
|
|
503
535
|
className: "w-full rounded border border-border bg-background px-2 py-1.5 text-xs text-foreground focus:outline-none focus:ring-1 focus:ring-primary"
|
|
504
536
|
})]
|
|
505
537
|
}), /* @__PURE__ */ o("button", {
|
|
506
|
-
onClick:
|
|
538
|
+
onClick: T,
|
|
507
539
|
disabled: p,
|
|
508
540
|
className: "inline-flex items-center gap-1.5 rounded-lg bg-primary px-3 py-2 text-xs font-semibold text-primary-foreground hover:opacity-90 disabled:opacity-50",
|
|
509
541
|
children: p ? /* @__PURE__ */ s(c, { children: [/* @__PURE__ */ o(b, { className: "h-3.5 w-3.5 animate-spin" }), "Enrolling…"] }) : /* @__PURE__ */ s(c, { children: [/* @__PURE__ */ o(v, { className: "h-3.5 w-3.5" }), "Add passkey"] })
|
|
@@ -564,12 +596,14 @@ var U = {
|
|
|
564
596
|
en: {
|
|
565
597
|
signInWithPasskey: "Sign in with a passkey",
|
|
566
598
|
unsupported: "This browser does not support passkeys.",
|
|
567
|
-
failed: "Passkey sign-in failed"
|
|
599
|
+
failed: "Passkey sign-in failed",
|
|
600
|
+
secondFactorRequired: "Additional verification is required — sign in with your password to continue."
|
|
568
601
|
},
|
|
569
602
|
it: {
|
|
570
603
|
signInWithPasskey: "Accedi con una passkey",
|
|
571
604
|
unsupported: "Questo browser non supporta le passkey.",
|
|
572
|
-
failed: "Accesso con passkey non riuscito"
|
|
605
|
+
failed: "Accesso con passkey non riuscito",
|
|
606
|
+
secondFactorRequired: "È richiesta una verifica aggiuntiva — accedi con la password per continuare."
|
|
573
607
|
}
|
|
574
608
|
};
|
|
575
609
|
function W(e) {
|
|
@@ -582,11 +616,12 @@ function G(e, t) {
|
|
|
582
616
|
//#region src/webauthn/widgets/PasskeyDirectLogin.tsx
|
|
583
617
|
function K(e) {
|
|
584
618
|
if (!e) return null;
|
|
585
|
-
let t = e.onComplete, n = e.
|
|
619
|
+
let t = e.onComplete, n = e.onSecondFactor, r = e.onError, i = e.locale;
|
|
586
620
|
return typeof t == "function" ? {
|
|
587
621
|
onComplete: t,
|
|
588
|
-
|
|
589
|
-
|
|
622
|
+
onSecondFactor: typeof n == "function" ? n : void 0,
|
|
623
|
+
onError: typeof r == "function" ? r : void 0,
|
|
624
|
+
locale: typeof i == "string" ? i : void 0
|
|
590
625
|
} : null;
|
|
591
626
|
}
|
|
592
627
|
function q(e) {
|
|
@@ -599,7 +634,18 @@ function q(e) {
|
|
|
599
634
|
onClick: async () => {
|
|
600
635
|
u(null), c(!0);
|
|
601
636
|
try {
|
|
602
|
-
let e = await R((await n.passkeyLoginBegin()).optionsJSON), t = await n.passkeyLoginFinish(e);
|
|
637
|
+
let e = await R((await n.passkeyLoginBegin()).optionsJSON), t = await n.passkeyLoginFinish(e), i = t.secondFactors ?? (t.requiresTotp === !0 ? ["totp"] : []);
|
|
638
|
+
if (i.length > 0) {
|
|
639
|
+
if (r.onSecondFactor) r.onSecondFactor({
|
|
640
|
+
challengeToken: t.token,
|
|
641
|
+
factors: i
|
|
642
|
+
});
|
|
643
|
+
else {
|
|
644
|
+
let e = G(d, "secondFactorRequired");
|
|
645
|
+
u(e), r.onError?.(e);
|
|
646
|
+
}
|
|
647
|
+
return;
|
|
648
|
+
}
|
|
603
649
|
r.onComplete(t.token);
|
|
604
650
|
} catch (e) {
|
|
605
651
|
let t = e instanceof Error ? e.message : G(d, "failed");
|
|
@@ -3,7 +3,7 @@ import "./dist-CYZr2fwk.mjs";
|
|
|
3
3
|
var e = {
|
|
4
4
|
"@camstack/sdk": {
|
|
5
5
|
name: "@camstack/sdk",
|
|
6
|
-
version: "1.1.
|
|
6
|
+
version: "1.1.22",
|
|
7
7
|
scope: ["default"],
|
|
8
8
|
loaded: !1,
|
|
9
9
|
from: "addon_auth_webauthn_widgets",
|
|
@@ -18,7 +18,7 @@ var e = {
|
|
|
18
18
|
},
|
|
19
19
|
"@camstack/types": {
|
|
20
20
|
name: "@camstack/types",
|
|
21
|
-
version: "1.1.
|
|
21
|
+
version: "1.1.39",
|
|
22
22
|
scope: ["default"],
|
|
23
23
|
loaded: !1,
|
|
24
24
|
from: "addon_auth_webauthn_widgets",
|
|
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
const require_chunk = require("../chunk-Cek0wNdY.js");
|
|
6
|
-
const require_dist = require("../dist-
|
|
6
|
+
const require_dist = require("../dist-CS-GzG-v.js");
|
|
7
7
|
let stream = require("stream");
|
|
8
8
|
stream = require_chunk.__toESM(stream, 1);
|
|
9
9
|
let http = require("http");
|
|
@@ -12123,6 +12123,36 @@ async function verifyAuthenticationResponse(options) {
|
|
|
12123
12123
|
* auth like Touch ID), `usb`, `nfc`, `ble`.
|
|
12124
12124
|
*/
|
|
12125
12125
|
var COLLECTION = "user_passkeys";
|
|
12126
|
+
/**
|
|
12127
|
+
* Per-user passkey preferences — one row per userId, keyed upserts via
|
|
12128
|
+
* the settings-store `set` surface. Today a single flag: whether the
|
|
12129
|
+
* user OPTED IN to their passkey doubling as a mandatory second factor
|
|
12130
|
+
* after a password login (default OFF — enrolling a passkey only
|
|
12131
|
+
* enables passkey-first sign-in).
|
|
12132
|
+
*/
|
|
12133
|
+
var PREF_COLLECTION = "user_passkey_prefs";
|
|
12134
|
+
var PREF_COLUMNS = [
|
|
12135
|
+
{
|
|
12136
|
+
name: "userId",
|
|
12137
|
+
type: "TEXT",
|
|
12138
|
+
primaryKey: true,
|
|
12139
|
+
notNull: true
|
|
12140
|
+
},
|
|
12141
|
+
{
|
|
12142
|
+
name: "secondFactorEnabled",
|
|
12143
|
+
type: "BOOLEAN",
|
|
12144
|
+
notNull: true
|
|
12145
|
+
},
|
|
12146
|
+
{
|
|
12147
|
+
name: "updatedAt",
|
|
12148
|
+
type: "INTEGER",
|
|
12149
|
+
notNull: true
|
|
12150
|
+
}
|
|
12151
|
+
];
|
|
12152
|
+
var StoredPreferenceSchema = require_dist.object({
|
|
12153
|
+
secondFactorEnabled: require_dist.boolean(),
|
|
12154
|
+
updatedAt: require_dist.number()
|
|
12155
|
+
});
|
|
12126
12156
|
var COLUMNS = [
|
|
12127
12157
|
{
|
|
12128
12158
|
name: "credentialId",
|
|
@@ -12173,6 +12203,10 @@ async function declarePasskeySchema(store) {
|
|
|
12173
12203
|
columns: ["userId"]
|
|
12174
12204
|
}]
|
|
12175
12205
|
});
|
|
12206
|
+
await store.declareCollection.mutate({
|
|
12207
|
+
collection: PREF_COLLECTION,
|
|
12208
|
+
columns: PREF_COLUMNS
|
|
12209
|
+
});
|
|
12176
12210
|
}
|
|
12177
12211
|
function parseRow(data) {
|
|
12178
12212
|
const credentialId = data["credentialId"];
|
|
@@ -12248,6 +12282,31 @@ var PasskeyStore = class {
|
|
|
12248
12282
|
key: credentialId
|
|
12249
12283
|
});
|
|
12250
12284
|
}
|
|
12285
|
+
/**
|
|
12286
|
+
* Whether `userId` opted in to the passkey-as-second-factor gate.
|
|
12287
|
+
* Fail-safe: a missing or malformed row reads as `false` — the
|
|
12288
|
+
* password login flow must never demand a factor the user did not
|
|
12289
|
+
* explicitly enable.
|
|
12290
|
+
*/
|
|
12291
|
+
async getSecondFactorPreference(userId) {
|
|
12292
|
+
const raw = await this.store.get.query({
|
|
12293
|
+
collection: PREF_COLLECTION,
|
|
12294
|
+
key: userId
|
|
12295
|
+
});
|
|
12296
|
+
const parsed = StoredPreferenceSchema.safeParse(raw);
|
|
12297
|
+
return parsed.success ? parsed.data.secondFactorEnabled : false;
|
|
12298
|
+
}
|
|
12299
|
+
async setSecondFactorPreference(userId, enabled) {
|
|
12300
|
+
const value = {
|
|
12301
|
+
secondFactorEnabled: enabled,
|
|
12302
|
+
updatedAt: Date.now()
|
|
12303
|
+
};
|
|
12304
|
+
await this.store.set.mutate({
|
|
12305
|
+
collection: PREF_COLLECTION,
|
|
12306
|
+
key: userId,
|
|
12307
|
+
value
|
|
12308
|
+
});
|
|
12309
|
+
}
|
|
12251
12310
|
};
|
|
12252
12311
|
//#endregion
|
|
12253
12312
|
//#region src/webauthn/widget-catalog.ts
|
|
@@ -12438,7 +12497,9 @@ var AuthWebauthnAddon = class extends require_dist.BaseAddon {
|
|
|
12438
12497
|
beginDiscoverableAuthentication: async () => this.beginDiscoverableAuthentication(),
|
|
12439
12498
|
finishDiscoverableAuthentication: async ({ response }) => this.finishDiscoverableAuthentication(response),
|
|
12440
12499
|
listPasskeys: async ({ userId }) => this.listPasskeys(userId),
|
|
12441
|
-
removePasskey: async ({ userId, credentialId }) => this.removePasskey(userId, credentialId)
|
|
12500
|
+
removePasskey: async ({ userId, credentialId }) => this.removePasskey(userId, credentialId),
|
|
12501
|
+
getSecondFactorPreference: async ({ userId }) => this.getSecondFactorPreference(userId),
|
|
12502
|
+
setSecondFactorPreference: async ({ userId, enabled }) => this.setSecondFactorPreference(userId, enabled)
|
|
12442
12503
|
};
|
|
12443
12504
|
const widgetsProvider = { listWidgets: async () => authWebauthnWidgets };
|
|
12444
12505
|
const loginMethodProvider = { getLoginMethods: async () => authWebauthnLoginMethods };
|
|
@@ -12794,6 +12855,20 @@ var AuthWebauthnAddon = class extends require_dist.BaseAddon {
|
|
|
12794
12855
|
transports: [...r.transports]
|
|
12795
12856
|
}));
|
|
12796
12857
|
}
|
|
12858
|
+
/**
|
|
12859
|
+
* Second-factor preference (opt-in, default OFF). Enrolling a passkey
|
|
12860
|
+
* only enables passkey-first sign-in; the password login flow demands
|
|
12861
|
+
* the passkey second leg ONLY when this flag is explicitly enabled.
|
|
12862
|
+
*/
|
|
12863
|
+
async getSecondFactorPreference(userId) {
|
|
12864
|
+
if (!this.store) return { enabled: false };
|
|
12865
|
+
return { enabled: await this.store.getSecondFactorPreference(userId) };
|
|
12866
|
+
}
|
|
12867
|
+
async setSecondFactorPreference(userId, enabled) {
|
|
12868
|
+
if (!this.store) throw new Error("passkey store unavailable");
|
|
12869
|
+
await this.store.setSecondFactorPreference(userId, enabled);
|
|
12870
|
+
return { success: true };
|
|
12871
|
+
}
|
|
12797
12872
|
async removePasskey(userId, credentialId) {
|
|
12798
12873
|
if (!this.store) throw new Error("passkey store unavailable");
|
|
12799
12874
|
const existing = await this.store.findByCredentialId(credentialId);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as loginMethodCapability, c as BaseAddon, d as
|
|
1
|
+
import { a as loginMethodCapability, c as BaseAddon, d as number, f as object, l as array, n as addonWidgetsSourceCapability, o as userPasskeysCapability, p as string, u as boolean } from "../dist-BlBNW5nA.mjs";
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
3
|
import Stream from "stream";
|
|
4
4
|
import http from "http";
|
|
@@ -12149,6 +12149,36 @@ async function verifyAuthenticationResponse(options) {
|
|
|
12149
12149
|
* auth like Touch ID), `usb`, `nfc`, `ble`.
|
|
12150
12150
|
*/
|
|
12151
12151
|
var COLLECTION = "user_passkeys";
|
|
12152
|
+
/**
|
|
12153
|
+
* Per-user passkey preferences — one row per userId, keyed upserts via
|
|
12154
|
+
* the settings-store `set` surface. Today a single flag: whether the
|
|
12155
|
+
* user OPTED IN to their passkey doubling as a mandatory second factor
|
|
12156
|
+
* after a password login (default OFF — enrolling a passkey only
|
|
12157
|
+
* enables passkey-first sign-in).
|
|
12158
|
+
*/
|
|
12159
|
+
var PREF_COLLECTION = "user_passkey_prefs";
|
|
12160
|
+
var PREF_COLUMNS = [
|
|
12161
|
+
{
|
|
12162
|
+
name: "userId",
|
|
12163
|
+
type: "TEXT",
|
|
12164
|
+
primaryKey: true,
|
|
12165
|
+
notNull: true
|
|
12166
|
+
},
|
|
12167
|
+
{
|
|
12168
|
+
name: "secondFactorEnabled",
|
|
12169
|
+
type: "BOOLEAN",
|
|
12170
|
+
notNull: true
|
|
12171
|
+
},
|
|
12172
|
+
{
|
|
12173
|
+
name: "updatedAt",
|
|
12174
|
+
type: "INTEGER",
|
|
12175
|
+
notNull: true
|
|
12176
|
+
}
|
|
12177
|
+
];
|
|
12178
|
+
var StoredPreferenceSchema = object({
|
|
12179
|
+
secondFactorEnabled: boolean(),
|
|
12180
|
+
updatedAt: number()
|
|
12181
|
+
});
|
|
12152
12182
|
var COLUMNS = [
|
|
12153
12183
|
{
|
|
12154
12184
|
name: "credentialId",
|
|
@@ -12199,6 +12229,10 @@ async function declarePasskeySchema(store) {
|
|
|
12199
12229
|
columns: ["userId"]
|
|
12200
12230
|
}]
|
|
12201
12231
|
});
|
|
12232
|
+
await store.declareCollection.mutate({
|
|
12233
|
+
collection: PREF_COLLECTION,
|
|
12234
|
+
columns: PREF_COLUMNS
|
|
12235
|
+
});
|
|
12202
12236
|
}
|
|
12203
12237
|
function parseRow(data) {
|
|
12204
12238
|
const credentialId = data["credentialId"];
|
|
@@ -12274,6 +12308,31 @@ var PasskeyStore = class {
|
|
|
12274
12308
|
key: credentialId
|
|
12275
12309
|
});
|
|
12276
12310
|
}
|
|
12311
|
+
/**
|
|
12312
|
+
* Whether `userId` opted in to the passkey-as-second-factor gate.
|
|
12313
|
+
* Fail-safe: a missing or malformed row reads as `false` — the
|
|
12314
|
+
* password login flow must never demand a factor the user did not
|
|
12315
|
+
* explicitly enable.
|
|
12316
|
+
*/
|
|
12317
|
+
async getSecondFactorPreference(userId) {
|
|
12318
|
+
const raw = await this.store.get.query({
|
|
12319
|
+
collection: PREF_COLLECTION,
|
|
12320
|
+
key: userId
|
|
12321
|
+
});
|
|
12322
|
+
const parsed = StoredPreferenceSchema.safeParse(raw);
|
|
12323
|
+
return parsed.success ? parsed.data.secondFactorEnabled : false;
|
|
12324
|
+
}
|
|
12325
|
+
async setSecondFactorPreference(userId, enabled) {
|
|
12326
|
+
const value = {
|
|
12327
|
+
secondFactorEnabled: enabled,
|
|
12328
|
+
updatedAt: Date.now()
|
|
12329
|
+
};
|
|
12330
|
+
await this.store.set.mutate({
|
|
12331
|
+
collection: PREF_COLLECTION,
|
|
12332
|
+
key: userId,
|
|
12333
|
+
value
|
|
12334
|
+
});
|
|
12335
|
+
}
|
|
12277
12336
|
};
|
|
12278
12337
|
//#endregion
|
|
12279
12338
|
//#region src/webauthn/widget-catalog.ts
|
|
@@ -12427,7 +12486,9 @@ var AuthWebauthnAddon = class extends BaseAddon {
|
|
|
12427
12486
|
beginDiscoverableAuthentication: async () => this.beginDiscoverableAuthentication(),
|
|
12428
12487
|
finishDiscoverableAuthentication: async ({ response }) => this.finishDiscoverableAuthentication(response),
|
|
12429
12488
|
listPasskeys: async ({ userId }) => this.listPasskeys(userId),
|
|
12430
|
-
removePasskey: async ({ userId, credentialId }) => this.removePasskey(userId, credentialId)
|
|
12489
|
+
removePasskey: async ({ userId, credentialId }) => this.removePasskey(userId, credentialId),
|
|
12490
|
+
getSecondFactorPreference: async ({ userId }) => this.getSecondFactorPreference(userId),
|
|
12491
|
+
setSecondFactorPreference: async ({ userId, enabled }) => this.setSecondFactorPreference(userId, enabled)
|
|
12431
12492
|
};
|
|
12432
12493
|
const widgetsProvider = { listWidgets: async () => authWebauthnWidgets };
|
|
12433
12494
|
const loginMethodProvider = { getLoginMethods: async () => authWebauthnLoginMethods };
|
|
@@ -12783,6 +12844,20 @@ var AuthWebauthnAddon = class extends BaseAddon {
|
|
|
12783
12844
|
transports: [...r.transports]
|
|
12784
12845
|
}));
|
|
12785
12846
|
}
|
|
12847
|
+
/**
|
|
12848
|
+
* Second-factor preference (opt-in, default OFF). Enrolling a passkey
|
|
12849
|
+
* only enables passkey-first sign-in; the password login flow demands
|
|
12850
|
+
* the passkey second leg ONLY when this flag is explicitly enabled.
|
|
12851
|
+
*/
|
|
12852
|
+
async getSecondFactorPreference(userId) {
|
|
12853
|
+
if (!this.store) return { enabled: false };
|
|
12854
|
+
return { enabled: await this.store.getSecondFactorPreference(userId) };
|
|
12855
|
+
}
|
|
12856
|
+
async setSecondFactorPreference(userId, enabled) {
|
|
12857
|
+
if (!this.store) throw new Error("passkey store unavailable");
|
|
12858
|
+
await this.store.setSecondFactorPreference(userId, enabled);
|
|
12859
|
+
return { success: true };
|
|
12860
|
+
}
|
|
12786
12861
|
async removePasskey(userId, credentialId) {
|
|
12787
12862
|
if (!this.store) throw new Error("passkey store unavailable");
|
|
12788
12863
|
const existing = await this.store.findByCredentialId(credentialId);
|
|
@@ -36,7 +36,7 @@ async function r() {
|
|
|
36
36
|
}
|
|
37
37
|
},
|
|
38
38
|
"@camstack/types": {
|
|
39
|
-
version: "1.1.
|
|
39
|
+
version: "1.1.39",
|
|
40
40
|
scope: "default",
|
|
41
41
|
shareConfig: {
|
|
42
42
|
singleton: !0,
|
|
@@ -45,7 +45,7 @@ async function r() {
|
|
|
45
45
|
}
|
|
46
46
|
},
|
|
47
47
|
"@camstack/sdk": {
|
|
48
|
-
version: "1.1.
|
|
48
|
+
version: "1.1.22",
|
|
49
49
|
scope: "default",
|
|
50
50
|
shareConfig: {
|
|
51
51
|
singleton: !0,
|
|
@@ -30,7 +30,7 @@ async function d(e) {
|
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
async function f() {
|
|
33
|
-
return l ||= d(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_auth_webauthn_widgets-
|
|
33
|
+
return l ||= d(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_auth_webauthn_widgets-Du23U8IZ.mjs")).catch((e) => {
|
|
34
34
|
throw l = void 0, e;
|
|
35
35
|
}), l;
|
|
36
36
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camstack/addon-auth",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.6",
|
|
4
4
|
"description": "CamStack authentication bundle — magic-link, OIDC, and WebAuthn/passkey. Multi-entry npm package shipping 3 addons under a single bundle; each addon keeps its own id, capabilities, and runner.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"camstack",
|