@endora-commerce/mod-mfa 0.100.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.
Files changed (126) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +58 -0
  3. package/dist/admin/index.d.ts +37 -0
  4. package/dist/admin/index.d.ts.map +1 -0
  5. package/dist/admin/index.js +12 -0
  6. package/dist/admin/index.js.map +1 -0
  7. package/dist/admin/pages/AdminSecuritySettings.d.ts +3 -0
  8. package/dist/admin/pages/AdminSecuritySettings.d.ts.map +1 -0
  9. package/dist/admin/pages/AdminSecuritySettings.js +71 -0
  10. package/dist/admin/pages/AdminSecuritySettings.js.map +1 -0
  11. package/dist/backend/entities/mfa-enrolment.entity.d.ts +33 -0
  12. package/dist/backend/entities/mfa-enrolment.entity.d.ts.map +1 -0
  13. package/dist/backend/entities/mfa-enrolment.entity.js +95 -0
  14. package/dist/backend/entities/mfa-enrolment.entity.js.map +1 -0
  15. package/dist/backend/entities/mfa-organization-policy.entity.d.ts +19 -0
  16. package/dist/backend/entities/mfa-organization-policy.entity.d.ts.map +1 -0
  17. package/dist/backend/entities/mfa-organization-policy.entity.js +60 -0
  18. package/dist/backend/entities/mfa-organization-policy.entity.js.map +1 -0
  19. package/dist/backend/entities/mfa-recovery-code.entity.d.ts +17 -0
  20. package/dist/backend/entities/mfa-recovery-code.entity.d.ts.map +1 -0
  21. package/dist/backend/entities/mfa-recovery-code.entity.js +54 -0
  22. package/dist/backend/entities/mfa-recovery-code.entity.js.map +1 -0
  23. package/dist/backend/entities/mfa-social-identity.entity.d.ts +24 -0
  24. package/dist/backend/entities/mfa-social-identity.entity.d.ts.map +1 -0
  25. package/dist/backend/entities/mfa-social-identity.entity.js +73 -0
  26. package/dist/backend/entities/mfa-social-identity.entity.js.map +1 -0
  27. package/dist/backend/index.d.ts +131 -0
  28. package/dist/backend/index.d.ts.map +1 -0
  29. package/dist/backend/index.js +173 -0
  30. package/dist/backend/index.js.map +1 -0
  31. package/dist/backend/plugin.d.ts +99 -0
  32. package/dist/backend/plugin.d.ts.map +1 -0
  33. package/dist/backend/plugin.js +111 -0
  34. package/dist/backend/plugin.js.map +1 -0
  35. package/dist/backend/routes.admin.d.ts +23 -0
  36. package/dist/backend/routes.admin.d.ts.map +1 -0
  37. package/dist/backend/routes.admin.js +93 -0
  38. package/dist/backend/routes.admin.js.map +1 -0
  39. package/dist/backend/routes.oauth.d.ts +26 -0
  40. package/dist/backend/routes.oauth.d.ts.map +1 -0
  41. package/dist/backend/routes.oauth.js +155 -0
  42. package/dist/backend/routes.oauth.js.map +1 -0
  43. package/dist/backend/routes.org.d.ts +21 -0
  44. package/dist/backend/routes.org.d.ts.map +1 -0
  45. package/dist/backend/routes.org.js +17 -0
  46. package/dist/backend/routes.org.js.map +1 -0
  47. package/dist/backend/routes.public.d.ts +21 -0
  48. package/dist/backend/routes.public.d.ts.map +1 -0
  49. package/dist/backend/routes.public.js +118 -0
  50. package/dist/backend/routes.public.js.map +1 -0
  51. package/dist/backend/routes.self-service.d.ts +34 -0
  52. package/dist/backend/routes.self-service.d.ts.map +1 -0
  53. package/dist/backend/routes.self-service.js +113 -0
  54. package/dist/backend/routes.self-service.js.map +1 -0
  55. package/dist/backend/services/account-identity.d.ts +71 -0
  56. package/dist/backend/services/account-identity.d.ts.map +1 -0
  57. package/dist/backend/services/account-identity.js +87 -0
  58. package/dist/backend/services/account-identity.js.map +1 -0
  59. package/dist/backend/services/challenge-store.d.ts +40 -0
  60. package/dist/backend/services/challenge-store.d.ts.map +1 -0
  61. package/dist/backend/services/challenge-store.js +90 -0
  62. package/dist/backend/services/challenge-store.js.map +1 -0
  63. package/dist/backend/services/mfa-enrolment-count.service.d.ts +26 -0
  64. package/dist/backend/services/mfa-enrolment-count.service.d.ts.map +1 -0
  65. package/dist/backend/services/mfa-enrolment-count.service.js +34 -0
  66. package/dist/backend/services/mfa-enrolment-count.service.js.map +1 -0
  67. package/dist/backend/services/mfa-enrolment-service.d.ts +54 -0
  68. package/dist/backend/services/mfa-enrolment-service.d.ts.map +1 -0
  69. package/dist/backend/services/mfa-enrolment-service.js +217 -0
  70. package/dist/backend/services/mfa-enrolment-service.js.map +1 -0
  71. package/dist/backend/services/mfa-enrolment-state.service.d.ts +27 -0
  72. package/dist/backend/services/mfa-enrolment-state.service.d.ts.map +1 -0
  73. package/dist/backend/services/mfa-enrolment-state.service.js +33 -0
  74. package/dist/backend/services/mfa-enrolment-state.service.js.map +1 -0
  75. package/dist/backend/services/mfa-login-service.d.ts +56 -0
  76. package/dist/backend/services/mfa-login-service.d.ts.map +1 -0
  77. package/dist/backend/services/mfa-login-service.js +89 -0
  78. package/dist/backend/services/mfa-login-service.js.map +1 -0
  79. package/dist/backend/services/mfa-org-policy-service.d.ts +16 -0
  80. package/dist/backend/services/mfa-org-policy-service.d.ts.map +1 -0
  81. package/dist/backend/services/mfa-org-policy-service.js +43 -0
  82. package/dist/backend/services/mfa-org-policy-service.js.map +1 -0
  83. package/dist/backend/services/mfa-policy-resolver.d.ts +47 -0
  84. package/dist/backend/services/mfa-policy-resolver.d.ts.map +1 -0
  85. package/dist/backend/services/mfa-policy-resolver.js +82 -0
  86. package/dist/backend/services/mfa-policy-resolver.js.map +1 -0
  87. package/dist/backend/services/oauth-provider-service.d.ts +65 -0
  88. package/dist/backend/services/oauth-provider-service.d.ts.map +1 -0
  89. package/dist/backend/services/oauth-provider-service.js +80 -0
  90. package/dist/backend/services/oauth-provider-service.js.map +1 -0
  91. package/dist/backend/services/recovery-codes.d.ts +10 -0
  92. package/dist/backend/services/recovery-codes.d.ts.map +1 -0
  93. package/dist/backend/services/recovery-codes.js +18 -0
  94. package/dist/backend/services/recovery-codes.js.map +1 -0
  95. package/dist/backend/services/secret-cipher.d.ts +16 -0
  96. package/dist/backend/services/secret-cipher.d.ts.map +1 -0
  97. package/dist/backend/services/secret-cipher.js +52 -0
  98. package/dist/backend/services/secret-cipher.js.map +1 -0
  99. package/dist/backend/services/social-identity-service.d.ts +40 -0
  100. package/dist/backend/services/social-identity-service.d.ts.map +1 -0
  101. package/dist/backend/services/social-identity-service.js +69 -0
  102. package/dist/backend/services/social-identity-service.js.map +1 -0
  103. package/dist/backend/services/social-link-service.d.ts +63 -0
  104. package/dist/backend/services/social-link-service.d.ts.map +1 -0
  105. package/dist/backend/services/social-link-service.js +121 -0
  106. package/dist/backend/services/social-link-service.js.map +1 -0
  107. package/dist/backend/services/totp.d.ts +11 -0
  108. package/dist/backend/services/totp.d.ts.map +1 -0
  109. package/dist/backend/services/totp.js +46 -0
  110. package/dist/backend/services/totp.js.map +1 -0
  111. package/dist/manifest.d.ts +184 -0
  112. package/dist/manifest.d.ts.map +1 -0
  113. package/dist/manifest.js +264 -0
  114. package/dist/manifest.js.map +1 -0
  115. package/dist/migrations/20260611T140409_mfa_init.d.ts +22 -0
  116. package/dist/migrations/20260611T140409_mfa_init.d.ts.map +1 -0
  117. package/dist/migrations/20260611T140409_mfa_init.js +94 -0
  118. package/dist/migrations/20260611T140409_mfa_init.js.map +1 -0
  119. package/dist/migrations/index.d.ts +27 -0
  120. package/dist/migrations/index.d.ts.map +1 -0
  121. package/dist/migrations/index.js +29 -0
  122. package/dist/migrations/index.js.map +1 -0
  123. package/i18n/en.json +14 -0
  124. package/i18n/pl.json +14 -0
  125. package/package.json +102 -0
  126. package/tailwind.css +14 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Endora sp. z o.o. and the Endora Commerce contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,58 @@
1
+ # MFA module (feature 042)
2
+
3
+ Two-factor authentication (TOTP + recovery codes) and federated sign-in
4
+ (Google / Microsoft) for both the Storefront (customer accounts) and the
5
+ Admin UI (admin users).
6
+
7
+ See `specs/042-mfa-authentication/` for the full spec, plan, data model, and
8
+ contracts.
9
+
10
+ ## Status
11
+
12
+ Complete (US1–US6). Capabilities:
13
+
14
+ - **TOTP 2FA** (enrol / activate / disable / regenerate / status) on both
15
+ surfaces, with single-use recovery codes and a replay-guarded second step.
16
+ - **Two-step login** — credential step returns `authenticated` |
17
+ `mfaRequired` | `mfaSetupRequired`; a separate verify step issues the session.
18
+ - **Enforcement** — per-scope settings (admin global; storefront global/channel)
19
+ and per-organization policy (org-admin + platform-admin); enforced-but-
20
+ unenrolled accounts enrol via a setup ticket at login.
21
+ - **Federated sign-in** — Google / Microsoft (OIDC via `openid-client`);
22
+ storefront auto-creates a standalone account, admin matches existing only.
23
+ - **Admin reset** — single account + organization-wide.
24
+
25
+ Self-service setup is gated on the per-scope "2FA enabled" setting (FR-001).
26
+
27
+ ## Routes (by file)
28
+
29
+ - `routes.public.ts` — second-step verify + setup-ticket begin/complete (both surfaces)
30
+ - `routes.self-service.ts` — `/{account,admin/account}/mfa/*` (setup/activate/disable/regenerate/status)
31
+ - `routes.oauth.ts` — `/auth/{customer,admin}/oauth/:provider/{start,callback}`
32
+ - `routes.org.ts` — storefront org-admin enforcement
33
+ - `routes.admin.ts` — platform-admin reset (single/bulk) + org enforcement
34
+
35
+ ## Notable decisions
36
+
37
+ - **TOTP**: reuses the existing `otpauth` dependency (no new dep). New
38
+ dependency `openid-client` is for Google/Microsoft OIDC (research §R2).
39
+ - **Secret at rest**: AES-256-GCM (`SecretCipher`), key from
40
+ `MFA_SECRET_ENCRYPTION_KEY` (base64, 32 bytes). Recovery codes are stored as
41
+ SHA-256 hashes only.
42
+ - **Ephemeral login state**: Redis (`ChallengeStore`) — pending-login
43
+ challenge, setup ticket, OAuth transaction — all TTL-bounded.
44
+ - **Module name `mfa`**: a singular acronym; a documented Principle VI
45
+ carve-out (see plan Complexity Tracking).
46
+ - **Removability** (Principle I): login services consult MFA through an
47
+ injected port; absent ⇒ password-only fallback. The legacy
48
+ `*.two_factor_secret` columns are superseded by `mfa_enrolments` and left
49
+ dormant (research §R8) — dropping them is tracked tech-debt.
50
+
51
+ ## Environment
52
+
53
+ | Var | Purpose |
54
+ |---|---|
55
+ | `MFA_SECRET_ENCRYPTION_KEY` | base64-encoded 32-byte AES key for TOTP secrets |
56
+ | `MFA_OAUTH_GOOGLE_CLIENT_ID` / `_SECRET` | Google OIDC client |
57
+ | `MFA_OAUTH_MICROSOFT_CLIENT_ID` / `_SECRET` | Microsoft Entra OIDC client |
58
+ | `MFA_OAUTH_*_REDIRECT_URI` | per-surface callback URLs |
@@ -0,0 +1,37 @@
1
+ /**
2
+ * `mfa`' admin surface — one route and no sidebar entry, declared by the module
3
+ * that owns it (feature 091, Phase 4, batch four;
4
+ * `specs/091-module-owned-admin-surfaces/contracts/admin-contribution.md`).
5
+ *
6
+ * **The nav-less shape, and it is the reason this module is in the batch.**
7
+ * Batch two rejected `mfa` on the ground that it contributes no sidebar entry,
8
+ * so *"half the ratchet would not move and half the evidence would be
9
+ * missing"*. `plan.md`'s Ruling 1 measured that as sound pilot-evidence
10
+ * reasoning and wrong as a selection rule: `admin/src/App.tsx`'s `ModuleRoute`
11
+ * gates **every** registry-contributed route on `useSurfaceVisibility` — both
12
+ * axes, rendering `NotFoundPage` — so a nav-less module's off-state proof is
13
+ * driven over the **route**, which is what an operator following a stale deep
14
+ * link actually meets. `admin/test/modules/mfa.module-owned-surface.test.tsx`
15
+ * is that proof, and the ratchet's `{ routes: 1, nav: 0 }` entry is removed
16
+ * like any other: a zero that stays zero is a passing assertion, not a missing
17
+ * one.
18
+ *
19
+ * **The route carries no `requiredPermission`, and that is read from the route
20
+ * rather than guessed.** `/security` is the signed-in admin's own two-factor
21
+ * self-service (`packages/modules/mfa/src/backend/routes.self-service.ts`), and
22
+ * every one of its six endpoints is gated by the bare admin guard —
23
+ * `{ preHandler: requireGuard }`, no `requireAdmin(...)` anywhere. An operator
24
+ * manages their own second factor; there is no permission code that could gate
25
+ * it without locking somebody out of their own account. So the declaration
26
+ * omits the field, `isSurfaceVisible` treats an absent requirement as
27
+ * satisfied, and the module's presence is the whole of the gate.
28
+ *
29
+ * **This entry exports data and nothing else** (R2), like every `./admin`
30
+ * layer: `check:module-boundary`'s D-171 rule designates a subpath as contract
31
+ * surface when it emits no runtime binding, and `./admin` deliberately does not
32
+ * qualify, so a consumer reaching into another module's `./admin` stays a
33
+ * counted boundary reach.
34
+ */
35
+ import type { AdminContributions } from '@endora-commerce/admin-kit/contributions';
36
+ export declare const contributions: AdminContributions;
37
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/admin/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0CAA0C,CAAC;AAKnF,eAAO,MAAM,aAAa,EAAE,kBAQ3B,CAAC"}
@@ -0,0 +1,12 @@
1
+ /** The module's only admin route: the signed-in admin's own 2FA settings. */
2
+ const ROUTE_PATH = '/security';
3
+ export const contributions = {
4
+ routes: [
5
+ {
6
+ path: ROUTE_PATH,
7
+ component: () => import('./pages/AdminSecuritySettings.js'),
8
+ index: true,
9
+ },
10
+ ],
11
+ };
12
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/admin/index.ts"],"names":[],"mappings":"AAoCA,6EAA6E;AAC7E,MAAM,UAAU,GAAG,WAAW,CAAC;AAE/B,MAAM,CAAC,MAAM,aAAa,GAAuB;IAC/C,MAAM,EAAE;QACN;YACE,IAAI,EAAE,UAAU;YAChB,SAAS,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,kCAAkC,CAAC;YAC3D,KAAK,EAAE,IAAI;SACZ;KACF;CACF,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { type ReactNode } from 'react';
2
+ export default function AdminSecuritySettings(): ReactNode;
3
+ //# sourceMappingURL=AdminSecuritySettings.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AdminSecuritySettings.d.ts","sourceRoot":"","sources":["../../../src/admin/pages/AdminSecuritySettings.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAoD,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AA+BzF,MAAM,CAAC,OAAO,UAAU,qBAAqB,IAAI,SAAS,CAmKzD"}
@@ -0,0 +1,71 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useCallback, useEffect, useState } from 'react';
3
+ import { ShieldCheck, ShieldAlert } from 'lucide-react';
4
+ import { Alert, AlertDescription, AlertTitle, Button, Card, CardContent, CardDescription, CardHeader, CardTitle, Input, Label, } from '@endora-commerce/admin-kit/ui';
5
+ import { ApiError, apiClient } from '@endora-commerce/admin-kit/lib';
6
+ const BASE = '/api/v1/admin/account/mfa';
7
+ export default function AdminSecuritySettings() {
8
+ const [status, setStatus] = useState(null);
9
+ const [setup, setSetup] = useState(null);
10
+ const [recoveryCodes, setRecoveryCodes] = useState(null);
11
+ const [code, setCode] = useState('');
12
+ const [error, setError] = useState(null);
13
+ const [busy, setBusy] = useState(false);
14
+ const loadStatus = useCallback(async () => {
15
+ const res = await apiClient.get(`${BASE}/status`);
16
+ setStatus(res.data);
17
+ }, []);
18
+ useEffect(() => {
19
+ void loadStatus();
20
+ }, [loadStatus]);
21
+ const run = useCallback(async (fn) => {
22
+ setBusy(true);
23
+ setError(null);
24
+ try {
25
+ await fn();
26
+ }
27
+ catch (err) {
28
+ setError(err instanceof ApiError ? err.envelope.error.message : 'Something went wrong.');
29
+ }
30
+ finally {
31
+ setBusy(false);
32
+ }
33
+ }, []);
34
+ const startSetup = () => void run(async () => {
35
+ const res = await apiClient.post(`${BASE}/setup`);
36
+ setSetup(res.data);
37
+ setRecoveryCodes(null);
38
+ });
39
+ const confirm = (e) => {
40
+ e.preventDefault();
41
+ void run(async () => {
42
+ const res = await apiClient.post(`${BASE}/activate`, {
43
+ code,
44
+ });
45
+ setRecoveryCodes(res.data.recoveryCodes);
46
+ setSetup(null);
47
+ setCode('');
48
+ await loadStatus();
49
+ });
50
+ };
51
+ const disable = (e) => {
52
+ e.preventDefault();
53
+ void run(async () => {
54
+ await apiClient.post(`${BASE}/disable`, { code });
55
+ setCode('');
56
+ setRecoveryCodes(null);
57
+ await loadStatus();
58
+ });
59
+ };
60
+ const regenerate = (e) => {
61
+ e.preventDefault();
62
+ void run(async () => {
63
+ const res = await apiClient.post(`${BASE}/recovery-codes/regenerate`, { code });
64
+ setRecoveryCodes(res.data.recoveryCodes);
65
+ setCode('');
66
+ await loadStatus();
67
+ });
68
+ };
69
+ return (_jsx("div", { className: "mx-auto max-w-2xl space-y-6 p-4", children: _jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsxs(CardTitle, { className: "flex items-center gap-2 text-lg", children: [_jsx(ShieldCheck, { className: "size-5 text-primary" }), "Two-factor authentication"] }), _jsx(CardDescription, { children: "Protect your admin account with a time-based code from an authenticator app." })] }), _jsxs(CardContent, { className: "space-y-4", children: [error ? (_jsxs(Alert, { variant: "destructive", children: [_jsx(ShieldAlert, { className: "size-4" }), _jsx(AlertTitle, { children: "Error" }), _jsx(AlertDescription, { children: error })] })) : null, recoveryCodes ? (_jsxs(Alert, { children: [_jsx(AlertTitle, { children: "Save your recovery codes" }), _jsxs(AlertDescription, { children: [_jsx("p", { children: "Each code works once. They are shown only now." }), _jsx("ul", { className: "mt-2 grid grid-cols-2 gap-1 font-mono text-sm", children: recoveryCodes.map((c) => (_jsx("li", { children: c }, c))) })] })] })) : null, status === null ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "Loading\u2026" })) : status.totpActive ? (_jsxs("div", { className: "space-y-6", children: [_jsxs("p", { className: "text-sm", children: ["2FA is ", _jsx("strong", { children: "enabled" }), ". Unused recovery codes: ", status.recoveryCodesRemaining, "."] }), _jsxs("form", { className: "space-y-2", onSubmit: disable, children: [_jsx(Label, { htmlFor: "disable-code", children: "Enter a current code to disable 2FA" }), _jsx(Input, { id: "disable-code", value: code, onChange: (e) => setCode(e.target.value), required: true }), _jsx(Button, { type: "submit", variant: "destructive", disabled: busy, children: "Disable 2FA" })] }), _jsxs("form", { className: "space-y-2", onSubmit: regenerate, children: [_jsx(Label, { htmlFor: "regen-code", children: "Enter a current code to regenerate recovery codes" }), _jsx(Input, { id: "regen-code", value: code, onChange: (e) => setCode(e.target.value), required: true }), _jsx(Button, { type: "submit", variant: "outline", disabled: busy, children: "Regenerate recovery codes" })] })] })) : setup ? (_jsxs("form", { className: "space-y-3", onSubmit: confirm, children: [_jsx("p", { className: "text-sm", children: "Add this account to your authenticator app:" }), _jsx("p", { className: "break-all rounded bg-muted px-2 py-1 font-mono text-xs", children: setup.otpauthUri }), _jsxs("p", { className: "text-sm", children: ["Manual key: ", _jsx("code", { className: "font-mono", children: setup.secret })] }), _jsx(Label, { htmlFor: "confirm-code", children: "Enter the 6-digit code to confirm" }), _jsx(Input, { id: "confirm-code", inputMode: "numeric", value: code, onChange: (e) => setCode(e.target.value), required: true, placeholder: "123456" }), _jsx(Button, { type: "submit", disabled: busy, children: "Confirm and enable" })] })) : (_jsx(Button, { onClick: startSetup, disabled: busy, children: "Enable 2FA" }))] })] }) }));
70
+ }
71
+ //# sourceMappingURL=AdminSecuritySettings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AdminSecuritySettings.js","sourceRoot":"","sources":["../../../src/admin/pages/AdminSecuritySettings.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ import { OptionalProps } from '@mikro-orm/core';
2
+ /**
3
+ * Per-account TOTP enrolment (feature 042). At most one `active` row per
4
+ * subject (enforced by a partial unique index, see migration 067).
5
+ *
6
+ * The subject is polymorphic across the two identity stores
7
+ * (`customer_accounts` / `admin_users`); no DB FK is declared to those tables
8
+ * to keep the `mfa` module isolated (Constitution Principle I). The TOTP
9
+ * secret is stored encrypted at rest (AES-256-GCM); the plaintext base32
10
+ * secret never touches the database.
11
+ */
12
+ export declare class MfaEnrolment {
13
+ [OptionalProps]?: 'id' | 'status' | 'lastAcceptedStep' | 'confirmedAt' | 'createdAt' | 'updatedAt';
14
+ id: string;
15
+ /** `'customer'` | `'admin'`. */
16
+ subjectType: string;
17
+ /** `customer_accounts.id` or `admin_users.id` — service-enforced, no DB FK. */
18
+ subjectId: string;
19
+ /** `'pending'` (setup started, unconfirmed) | `'active'`. */
20
+ status: string;
21
+ /** AES-256-GCM ciphertext of the base32 TOTP secret. */
22
+ secretCiphertext: Buffer;
23
+ /** GCM initialisation vector (per row). */
24
+ secretIv: Buffer;
25
+ /** GCM authentication tag. */
26
+ secretAuthTag: Buffer;
27
+ /** Last accepted TOTP time-step — guards against in-window replay. */
28
+ lastAcceptedStep?: string | null;
29
+ confirmedAt?: Date | null;
30
+ createdAt: Date;
31
+ updatedAt: Date;
32
+ }
33
+ //# sourceMappingURL=mfa-enrolment.entity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mfa-enrolment.entity.d.ts","sourceRoot":"","sources":["../../../src/backend/entities/mfa-enrolment.entity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,aAAa,EAAwB,MAAM,iBAAiB,CAAC;AAIrF;;;;;;;;;GASG;AACH,qBAEa,YAAY;IACvB,CAAC,aAAa,CAAC,CAAC,EACZ,IAAI,GACJ,QAAQ,GACR,kBAAkB,GAClB,aAAa,GACb,WAAW,GACX,WAAW,CAAC;IAGhB,EAAE,EAAE,MAAM,CAAgB;IAE1B,gCAAgC;IAGhC,WAAW,EAAG,MAAM,CAAC;IAErB,+EAA+E;IAG/E,SAAS,EAAG,MAAM,CAAC;IAEnB,6DAA6D;IAE7D,MAAM,EAAE,MAAM,CAAa;IAE3B,wDAAwD;IAExD,gBAAgB,EAAG,MAAM,CAAC;IAE1B,2CAA2C;IAE3C,QAAQ,EAAG,MAAM,CAAC;IAElB,8BAA8B;IAE9B,aAAa,EAAG,MAAM,CAAC;IAEvB,sEAAsE;IAEtE,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAGjC,WAAW,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IAG1B,SAAS,EAAE,IAAI,CAAc;IAG7B,SAAS,EAAE,IAAI,CAAc;CAC9B"}
@@ -0,0 +1,95 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ import { Entity, Index, OptionalProps, PrimaryKey, Property } from '@mikro-orm/core';
11
+ import { GlobalEntity } from '@endora-commerce/platform/tenancy';
12
+ import { randomUUID } from 'crypto';
13
+ /**
14
+ * Per-account TOTP enrolment (feature 042). At most one `active` row per
15
+ * subject (enforced by a partial unique index, see migration 067).
16
+ *
17
+ * The subject is polymorphic across the two identity stores
18
+ * (`customer_accounts` / `admin_users`); no DB FK is declared to those tables
19
+ * to keep the `mfa` module isolated (Constitution Principle I). The TOTP
20
+ * secret is stored encrypted at rest (AES-256-GCM); the plaintext base32
21
+ * secret never touches the database.
22
+ */
23
+ let MfaEnrolment = class MfaEnrolment {
24
+ [OptionalProps];
25
+ id = randomUUID();
26
+ /** `'customer'` | `'admin'`. */
27
+ subjectType;
28
+ /** `customer_accounts.id` or `admin_users.id` — service-enforced, no DB FK. */
29
+ subjectId;
30
+ /** `'pending'` (setup started, unconfirmed) | `'active'`. */
31
+ status = 'pending';
32
+ /** AES-256-GCM ciphertext of the base32 TOTP secret. */
33
+ secretCiphertext;
34
+ /** GCM initialisation vector (per row). */
35
+ secretIv;
36
+ /** GCM authentication tag. */
37
+ secretAuthTag;
38
+ /** Last accepted TOTP time-step — guards against in-window replay. */
39
+ lastAcceptedStep;
40
+ confirmedAt;
41
+ createdAt = new Date();
42
+ updatedAt = new Date();
43
+ };
44
+ __decorate([
45
+ PrimaryKey({ type: 'uuid' }),
46
+ __metadata("design:type", String)
47
+ ], MfaEnrolment.prototype, "id", void 0);
48
+ __decorate([
49
+ Property({ type: 'string', length: 16 }),
50
+ Index(),
51
+ __metadata("design:type", String)
52
+ ], MfaEnrolment.prototype, "subjectType", void 0);
53
+ __decorate([
54
+ Property({ type: 'uuid' }),
55
+ Index(),
56
+ __metadata("design:type", String)
57
+ ], MfaEnrolment.prototype, "subjectId", void 0);
58
+ __decorate([
59
+ Property({ type: 'string', length: 16 }),
60
+ __metadata("design:type", String)
61
+ ], MfaEnrolment.prototype, "status", void 0);
62
+ __decorate([
63
+ Property({ type: 'bytea' }),
64
+ __metadata("design:type", Buffer)
65
+ ], MfaEnrolment.prototype, "secretCiphertext", void 0);
66
+ __decorate([
67
+ Property({ type: 'bytea' }),
68
+ __metadata("design:type", Buffer)
69
+ ], MfaEnrolment.prototype, "secretIv", void 0);
70
+ __decorate([
71
+ Property({ type: 'bytea' }),
72
+ __metadata("design:type", Buffer)
73
+ ], MfaEnrolment.prototype, "secretAuthTag", void 0);
74
+ __decorate([
75
+ Property({ type: 'bigint', nullable: true }),
76
+ __metadata("design:type", Object)
77
+ ], MfaEnrolment.prototype, "lastAcceptedStep", void 0);
78
+ __decorate([
79
+ Property({ type: 'datetime', nullable: true }),
80
+ __metadata("design:type", Object)
81
+ ], MfaEnrolment.prototype, "confirmedAt", void 0);
82
+ __decorate([
83
+ Property({ type: 'datetime', onCreate: () => new Date() }),
84
+ __metadata("design:type", Date)
85
+ ], MfaEnrolment.prototype, "createdAt", void 0);
86
+ __decorate([
87
+ Property({ type: 'datetime', onUpdate: () => new Date() }),
88
+ __metadata("design:type", Date)
89
+ ], MfaEnrolment.prototype, "updatedAt", void 0);
90
+ MfaEnrolment = __decorate([
91
+ GlobalEntity(),
92
+ Entity({ tableName: 'mfa_enrolments' })
93
+ ], MfaEnrolment);
94
+ export { MfaEnrolment };
95
+ //# sourceMappingURL=mfa-enrolment.entity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mfa-enrolment.entity.js","sourceRoot":"","sources":["../../../src/backend/entities/mfa-enrolment.entity.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AACrF,OAAO,EAAE,YAAY,EAAE,MAAM,mCAAmC,CAAC;AACjE,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAEpC;;;;;;;;;GASG;AAGI,IAAM,YAAY,GAAlB,MAAM,YAAY;IACvB,CAAC,aAAa,CAAC,CAMC;IAGhB,EAAE,GAAW,UAAU,EAAE,CAAC;IAE1B,gCAAgC;IAGhC,WAAW,CAAU;IAErB,+EAA+E;IAG/E,SAAS,CAAU;IAEnB,6DAA6D;IAE7D,MAAM,GAAW,SAAS,CAAC;IAE3B,wDAAwD;IAExD,gBAAgB,CAAU;IAE1B,2CAA2C;IAE3C,QAAQ,CAAU;IAElB,8BAA8B;IAE9B,aAAa,CAAU;IAEvB,sEAAsE;IAEtE,gBAAgB,CAAiB;IAGjC,WAAW,CAAe;IAG1B,SAAS,GAAS,IAAI,IAAI,EAAE,CAAC;IAG7B,SAAS,GAAS,IAAI,IAAI,EAAE,CAAC;CAC9B,CAAA;AAxCC;IADC,UAAU,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;wCACH;AAK1B;IAFC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IACxC,KAAK,EAAE;;iDACa;AAKrB;IAFC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAC1B,KAAK,EAAE;;+CACW;AAInB;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;;4CACd;AAI3B;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;8BACT,MAAM;sDAAC;AAI1B;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;8BACjB,MAAM;8CAAC;AAIlB;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;8BACZ,MAAM;mDAAC;AAIvB;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;sDACZ;AAGjC;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;iDACrB;AAG1B;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,EAAE,CAAC;8BAChD,IAAI;+CAAc;AAG7B;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,EAAE,CAAC;8BAChD,IAAI;+CAAc;AAjDlB,YAAY;IAFxB,YAAY,EAAE;IACd,MAAM,CAAC,EAAE,SAAS,EAAE,gBAAgB,EAAE,CAAC;GAC3B,YAAY,CAkDxB"}
@@ -0,0 +1,19 @@
1
+ import { OptionalProps } from '@mikro-orm/core';
2
+ /**
3
+ * Per-Organization 2FA enforcement flag (feature 042, FR-014). Managed by
4
+ * Organization Administrators (storefront) and Platform Administrators
5
+ * (admin). Owned by the `mfa` module for removability; `organizationId`
6
+ * references the organizations module by value (no hard FK) to preserve
7
+ * module isolation (Constitution Principle I). Absence of a row ⇒ not enforced.
8
+ */
9
+ export declare class MfaOrganizationPolicy {
10
+ [OptionalProps]?: 'id' | 'enforceTotp' | 'updatedByActor' | 'createdAt' | 'updatedAt';
11
+ id: string;
12
+ organizationId: string;
13
+ enforceTotp: boolean;
14
+ /** Audit hint — admin user id or org-admin customer id that last changed it. */
15
+ updatedByActor?: string | null;
16
+ createdAt: Date;
17
+ updatedAt: Date;
18
+ }
19
+ //# sourceMappingURL=mfa-organization-policy.entity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mfa-organization-policy.entity.d.ts","sourceRoot":"","sources":["../../../src/backend/entities/mfa-organization-policy.entity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,aAAa,EAAgC,MAAM,iBAAiB,CAAC;AAItF;;;;;;GAMG;AACH,qBAEa,qBAAqB;IAChC,CAAC,aAAa,CAAC,CAAC,EAAE,IAAI,GAAG,aAAa,GAAG,gBAAgB,GAAG,WAAW,GAAG,WAAW,CAAC;IAGtF,EAAE,EAAE,MAAM,CAAgB;IAI1B,cAAc,EAAG,MAAM,CAAC;IAGxB,WAAW,EAAE,OAAO,CAAS;IAE7B,gFAAgF;IAEhF,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAG/B,SAAS,EAAE,IAAI,CAAc;IAG7B,SAAS,EAAE,IAAI,CAAc;CAC9B"}
@@ -0,0 +1,60 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ import { Entity, OptionalProps, PrimaryKey, Property, Unique } from '@mikro-orm/core';
11
+ import { GlobalEntity } from '@endora-commerce/platform/tenancy';
12
+ import { randomUUID } from 'crypto';
13
+ /**
14
+ * Per-Organization 2FA enforcement flag (feature 042, FR-014). Managed by
15
+ * Organization Administrators (storefront) and Platform Administrators
16
+ * (admin). Owned by the `mfa` module for removability; `organizationId`
17
+ * references the organizations module by value (no hard FK) to preserve
18
+ * module isolation (Constitution Principle I). Absence of a row ⇒ not enforced.
19
+ */
20
+ let MfaOrganizationPolicy = class MfaOrganizationPolicy {
21
+ [OptionalProps];
22
+ id = randomUUID();
23
+ organizationId;
24
+ enforceTotp = false;
25
+ /** Audit hint — admin user id or org-admin customer id that last changed it. */
26
+ updatedByActor;
27
+ createdAt = new Date();
28
+ updatedAt = new Date();
29
+ };
30
+ __decorate([
31
+ PrimaryKey({ type: 'uuid' }),
32
+ __metadata("design:type", String)
33
+ ], MfaOrganizationPolicy.prototype, "id", void 0);
34
+ __decorate([
35
+ Property({ type: 'uuid' }),
36
+ Unique(),
37
+ __metadata("design:type", String)
38
+ ], MfaOrganizationPolicy.prototype, "organizationId", void 0);
39
+ __decorate([
40
+ Property({ type: 'boolean' }),
41
+ __metadata("design:type", Boolean)
42
+ ], MfaOrganizationPolicy.prototype, "enforceTotp", void 0);
43
+ __decorate([
44
+ Property({ type: 'string', length: 64, nullable: true }),
45
+ __metadata("design:type", Object)
46
+ ], MfaOrganizationPolicy.prototype, "updatedByActor", void 0);
47
+ __decorate([
48
+ Property({ type: 'datetime', onCreate: () => new Date() }),
49
+ __metadata("design:type", Date)
50
+ ], MfaOrganizationPolicy.prototype, "createdAt", void 0);
51
+ __decorate([
52
+ Property({ type: 'datetime', onUpdate: () => new Date() }),
53
+ __metadata("design:type", Date)
54
+ ], MfaOrganizationPolicy.prototype, "updatedAt", void 0);
55
+ MfaOrganizationPolicy = __decorate([
56
+ GlobalEntity(),
57
+ Entity({ tableName: 'mfa_organization_policies' })
58
+ ], MfaOrganizationPolicy);
59
+ export { MfaOrganizationPolicy };
60
+ //# sourceMappingURL=mfa-organization-policy.entity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mfa-organization-policy.entity.js","sourceRoot":"","sources":["../../../src/backend/entities/mfa-organization-policy.entity.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AACtF,OAAO,EAAE,YAAY,EAAE,MAAM,mCAAmC,CAAC;AACjE,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAEpC;;;;;;GAMG;AAGI,IAAM,qBAAqB,GAA3B,MAAM,qBAAqB;IAChC,CAAC,aAAa,CAAC,CAAuE;IAGtF,EAAE,GAAW,UAAU,EAAE,CAAC;IAI1B,cAAc,CAAU;IAGxB,WAAW,GAAY,KAAK,CAAC;IAE7B,gFAAgF;IAEhF,cAAc,CAAiB;IAG/B,SAAS,GAAS,IAAI,IAAI,EAAE,CAAC;IAG7B,SAAS,GAAS,IAAI,IAAI,EAAE,CAAC;CAC9B,CAAA;AAlBC;IADC,UAAU,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;iDACH;AAI1B;IAFC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAC1B,MAAM,EAAE;;6DACe;AAGxB;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;;0DACD;AAI7B;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;6DAC1B;AAG/B;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,EAAE,CAAC;8BAChD,IAAI;wDAAc;AAG7B;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,EAAE,CAAC;8BAChD,IAAI;wDAAc;AArBlB,qBAAqB;IAFjC,YAAY,EAAE;IACd,MAAM,CAAC,EAAE,SAAS,EAAE,2BAA2B,EAAE,CAAC;GACtC,qBAAqB,CAsBjC"}
@@ -0,0 +1,17 @@
1
+ import { OptionalProps } from '@mikro-orm/core';
2
+ /**
3
+ * Single-use backup code for a TOTP enrolment (feature 042). Stored only as a
4
+ * SHA-256 hash; the plaintext is shown once at activation/regeneration.
5
+ * Cascade-deleted with its parent enrolment (FK declared in migration 067).
6
+ */
7
+ export declare class MfaRecoveryCode {
8
+ [OptionalProps]?: 'id' | 'usedAt' | 'createdAt';
9
+ id: string;
10
+ enrolmentId: string;
11
+ /** SHA-256 hex of the plaintext recovery code. */
12
+ codeHash: string;
13
+ /** Set when the code is consumed; a used code is never re-accepted. */
14
+ usedAt?: Date | null;
15
+ createdAt: Date;
16
+ }
17
+ //# sourceMappingURL=mfa-recovery-code.entity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mfa-recovery-code.entity.d.ts","sourceRoot":"","sources":["../../../src/backend/entities/mfa-recovery-code.entity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,aAAa,EAAwB,MAAM,iBAAiB,CAAC;AAIrF;;;;GAIG;AACH,qBAEa,eAAe;IAC1B,CAAC,aAAa,CAAC,CAAC,EAAE,IAAI,GAAG,QAAQ,GAAG,WAAW,CAAC;IAGhD,EAAE,EAAE,MAAM,CAAgB;IAI1B,WAAW,EAAG,MAAM,CAAC;IAErB,kDAAkD;IAElD,QAAQ,EAAG,MAAM,CAAC;IAElB,uEAAuE;IAEvE,MAAM,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IAGrB,SAAS,EAAE,IAAI,CAAc;CAC9B"}
@@ -0,0 +1,54 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ import { Entity, Index, OptionalProps, PrimaryKey, Property } from '@mikro-orm/core';
11
+ import { GlobalEntity } from '@endora-commerce/platform/tenancy';
12
+ import { randomUUID } from 'crypto';
13
+ /**
14
+ * Single-use backup code for a TOTP enrolment (feature 042). Stored only as a
15
+ * SHA-256 hash; the plaintext is shown once at activation/regeneration.
16
+ * Cascade-deleted with its parent enrolment (FK declared in migration 067).
17
+ */
18
+ let MfaRecoveryCode = class MfaRecoveryCode {
19
+ [OptionalProps];
20
+ id = randomUUID();
21
+ enrolmentId;
22
+ /** SHA-256 hex of the plaintext recovery code. */
23
+ codeHash;
24
+ /** Set when the code is consumed; a used code is never re-accepted. */
25
+ usedAt;
26
+ createdAt = new Date();
27
+ };
28
+ __decorate([
29
+ PrimaryKey({ type: 'uuid' }),
30
+ __metadata("design:type", String)
31
+ ], MfaRecoveryCode.prototype, "id", void 0);
32
+ __decorate([
33
+ Property({ type: 'uuid' }),
34
+ Index(),
35
+ __metadata("design:type", String)
36
+ ], MfaRecoveryCode.prototype, "enrolmentId", void 0);
37
+ __decorate([
38
+ Property({ type: 'string', length: 128 }),
39
+ __metadata("design:type", String)
40
+ ], MfaRecoveryCode.prototype, "codeHash", void 0);
41
+ __decorate([
42
+ Property({ type: 'datetime', nullable: true }),
43
+ __metadata("design:type", Object)
44
+ ], MfaRecoveryCode.prototype, "usedAt", void 0);
45
+ __decorate([
46
+ Property({ type: 'datetime', onCreate: () => new Date() }),
47
+ __metadata("design:type", Date)
48
+ ], MfaRecoveryCode.prototype, "createdAt", void 0);
49
+ MfaRecoveryCode = __decorate([
50
+ GlobalEntity(),
51
+ Entity({ tableName: 'mfa_recovery_codes' })
52
+ ], MfaRecoveryCode);
53
+ export { MfaRecoveryCode };
54
+ //# sourceMappingURL=mfa-recovery-code.entity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mfa-recovery-code.entity.js","sourceRoot":"","sources":["../../../src/backend/entities/mfa-recovery-code.entity.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AACrF,OAAO,EAAE,YAAY,EAAE,MAAM,mCAAmC,CAAC;AACjE,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAEpC;;;;GAIG;AAGI,IAAM,eAAe,GAArB,MAAM,eAAe;IAC1B,CAAC,aAAa,CAAC,CAAiC;IAGhD,EAAE,GAAW,UAAU,EAAE,CAAC;IAI1B,WAAW,CAAU;IAErB,kDAAkD;IAElD,QAAQ,CAAU;IAElB,uEAAuE;IAEvE,MAAM,CAAe;IAGrB,SAAS,GAAS,IAAI,IAAI,EAAE,CAAC;CAC9B,CAAA;AAhBC;IADC,UAAU,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;2CACH;AAI1B;IAFC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAC1B,KAAK,EAAE;;oDACa;AAIrB;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;;iDACxB;AAIlB;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;+CAC1B;AAGrB;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,EAAE,CAAC;8BAChD,IAAI;kDAAc;AAnBlB,eAAe;IAF3B,YAAY,EAAE;IACd,MAAM,CAAC,EAAE,SAAS,EAAE,oBAAoB,EAAE,CAAC;GAC/B,eAAe,CAoB3B"}
@@ -0,0 +1,24 @@
1
+ import { OptionalProps } from '@mikro-orm/core';
2
+ /**
3
+ * Link between a platform account and an external provider identity
4
+ * (Google / Microsoft) — feature 042. Resolution is by the provider's
5
+ * verified primary email; the `providerSubject` (`sub` claim) is the stable
6
+ * provider-side id. Uniqueness constraints (declared in migration 067):
7
+ * - (provider, provider_subject): one platform account per IdP identity;
8
+ * - (subject_type, subject_id, provider): one link per provider per account.
9
+ */
10
+ export declare class MfaSocialIdentity {
11
+ [OptionalProps]?: 'id' | 'lastUsedAt' | 'linkedAt';
12
+ id: string;
13
+ subjectType: string;
14
+ subjectId: string;
15
+ /** `'google'` | `'microsoft'`. */
16
+ provider: string;
17
+ /** The IdP `sub` claim — stable provider-side identifier. */
18
+ providerSubject: string;
19
+ /** Verified email at link time (used for resolution). */
20
+ email: string;
21
+ linkedAt: Date;
22
+ lastUsedAt?: Date | null;
23
+ }
24
+ //# sourceMappingURL=mfa-social-identity.entity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mfa-social-identity.entity.d.ts","sourceRoot":"","sources":["../../../src/backend/entities/mfa-social-identity.entity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,aAAa,EAAwB,MAAM,iBAAiB,CAAC;AAIrF;;;;;;;GAOG;AACH,qBAEa,iBAAiB;IAC5B,CAAC,aAAa,CAAC,CAAC,EAAE,IAAI,GAAG,YAAY,GAAG,UAAU,CAAC;IAGnD,EAAE,EAAE,MAAM,CAAgB;IAG1B,WAAW,EAAG,MAAM,CAAC;IAGrB,SAAS,EAAG,MAAM,CAAC;IAEnB,kCAAkC;IAElC,QAAQ,EAAG,MAAM,CAAC;IAElB,6DAA6D;IAE7D,eAAe,EAAG,MAAM,CAAC;IAEzB,yDAAyD;IAGzD,KAAK,EAAG,MAAM,CAAC;IAGf,QAAQ,EAAE,IAAI,CAAc;IAG5B,UAAU,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;CAC1B"}