@open-mercato/enterprise 0.6.8-develop.7099.1.7ba2771d06 → 0.7.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 (36) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/modules/record_locks/backend/settings/record-locks/page.js +2 -2
  3. package/dist/modules/record_locks/backend/settings/record-locks/page.js.map +2 -2
  4. package/dist/modules/security/api/interceptors.js +2 -5
  5. package/dist/modules/security/api/interceptors.js.map +2 -2
  6. package/dist/modules/security/api/mfa/_shared.js +0 -1
  7. package/dist/modules/security/api/mfa/_shared.js.map +2 -2
  8. package/dist/modules/security/data/entities.js +0 -18
  9. package/dist/modules/security/data/entities.js.map +2 -2
  10. package/dist/modules/security/index.js +0 -3
  11. package/dist/modules/security/index.js.map +2 -2
  12. package/dist/modules/security/lib/enforcement-redirect.js +2 -28
  13. package/dist/modules/security/lib/enforcement-redirect.js.map +2 -2
  14. package/dist/modules/security/lib/security-config.js +0 -27
  15. package/dist/modules/security/lib/security-config.js.map +2 -2
  16. package/dist/modules/security/services/SudoChallengeService.js +41 -130
  17. package/dist/modules/security/services/SudoChallengeService.js.map +2 -2
  18. package/dist/modules/sso/backend/page.js +0 -1
  19. package/dist/modules/sso/backend/page.js.map +2 -2
  20. package/package.json +6 -7
  21. package/src/modules/record_locks/backend/settings/record-locks/page.tsx +4 -4
  22. package/src/modules/security/api/interceptors.ts +2 -6
  23. package/src/modules/security/api/mfa/_shared.ts +0 -1
  24. package/src/modules/security/data/entities.ts +0 -18
  25. package/src/modules/security/index.ts +0 -4
  26. package/src/modules/security/lib/enforcement-redirect.ts +2 -30
  27. package/src/modules/security/lib/security-config.ts +0 -29
  28. package/src/modules/security/migrations/.snapshot-open-mercato.json +276 -849
  29. package/src/modules/security/services/SudoChallengeService.ts +49 -170
  30. package/src/modules/sso/backend/page.tsx +0 -2
  31. package/dist/modules/security/lib/mfaCompletionRoutes.js +0 -17
  32. package/dist/modules/security/lib/mfaCompletionRoutes.js.map +0 -7
  33. package/dist/modules/security/migrations/Migration20260811143757_security.js +0 -13
  34. package/dist/modules/security/migrations/Migration20260811143757_security.js.map +0 -7
  35. package/src/modules/security/lib/mfaCompletionRoutes.ts +0 -25
  36. package/src/modules/security/migrations/Migration20260811143757_security.ts +0 -13
@@ -1,2 +1,2 @@
1
- [build:enterprise] found 336 entry points
1
+ [build:enterprise] found 328 entry points
2
2
  [build:enterprise] built successfully
@@ -136,7 +136,7 @@ function RecordLockingSettingsPage() {
136
136
  }
137
137
  ),
138
138
  /* @__PURE__ */ jsx(Alert, { status: "information", children: /* @__PURE__ */ jsxs(AlertDescription, { children: [
139
- /* @__PURE__ */ jsxs("p", { children: [
139
+ /* @__PURE__ */ jsxs("span", { className: "block", children: [
140
140
  /* @__PURE__ */ jsxs("strong", { children: [
141
141
  t("record_locks.settings.strategy_optimistic", "Optimistic"),
142
142
  ":"
@@ -147,7 +147,7 @@ function RecordLockingSettingsPage() {
147
147
  "Multiple users can edit at the same time; conflicts are checked on save."
148
148
  )
149
149
  ] }),
150
- /* @__PURE__ */ jsxs("p", { className: "mt-1", children: [
150
+ /* @__PURE__ */ jsxs("span", { className: "mt-1 block", children: [
151
151
  /* @__PURE__ */ jsxs("strong", { children: [
152
152
  t("record_locks.settings.strategy_pessimistic", "Pessimistic"),
153
153
  ":"
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../src/modules/record_locks/backend/settings/record-locks/page.tsx"],
4
- "sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { apiCall, apiCallOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { TagsInput } from '@open-mercato/ui/backend/inputs'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { Label } from '@open-mercato/ui/primitives/label'\nimport { Alert, AlertDescription } from '@open-mercato/ui/primitives/alert'\nimport { Switch } from '@open-mercato/ui/primitives/switch'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { E } from '@open-mercato/core/generated-shims/entities.ids.generated'\nimport { DEFAULT_RECORD_LOCK_SETTINGS } from '../../../lib/config'\n\ntype RecordLockSettings = {\n enabled: boolean\n strategy: 'optimistic' | 'pessimistic'\n timeoutSeconds: number\n heartbeatSeconds: number\n enabledResources: string[]\n allowForceUnlock: boolean\n allowIncomingOverride: boolean\n notifyOnConflict: boolean\n}\n\nconst DEFAULT_SETTINGS: RecordLockSettings = {\n ...DEFAULT_RECORD_LOCK_SETTINGS,\n}\n\nconst RECORD_LOCK_RESOURCE_ALIASES = [\n 'customers.person',\n 'customers.company',\n 'customers.deal',\n 'sales.quote',\n 'sales.order',\n]\n\nconst RECORD_LOCK_RESOURCE_SUGGESTIONS = Array.from(\n new Set([\n ...Object.values(E).flatMap((moduleEntities) =>\n Object.values(moduleEntities).flatMap((entityId) => {\n if (typeof entityId !== 'string' || !entityId.includes(':')) return []\n return [entityId.replace(':', '.')]\n })\n ),\n ...RECORD_LOCK_RESOURCE_ALIASES,\n ]),\n).sort((left, right) => left.localeCompare(right))\n\nexport default function RecordLockingSettingsPage() {\n const t = useT()\n const [settings, setSettings] = React.useState<RecordLockSettings>(DEFAULT_SETTINGS)\n const [loading, setLoading] = React.useState(true)\n const [saving, setSaving] = React.useState(false)\n\n const normalizeResources = React.useCallback((input: string[]): string[] => {\n const seen = new Set<string>()\n const out: string[] = []\n for (const raw of input) {\n const resource = typeof raw === 'string' ? raw.trim() : ''\n if (!resource || seen.has(resource)) continue\n seen.add(resource)\n out.push(resource)\n }\n return out\n }, [])\n\n const load = React.useCallback(async () => {\n setLoading(true)\n try {\n const call = await apiCall<{ settings?: RecordLockSettings }>('/api/record_locks/settings')\n const next = call.result?.settings ?? DEFAULT_SETTINGS\n setSettings({\n ...next,\n enabledResources: normalizeResources(next.enabledResources ?? []),\n })\n } catch {\n flash(t('record_locks.settings.save_error', 'Failed to load settings'), 'error')\n } finally {\n setLoading(false)\n }\n }, [normalizeResources, t])\n\n React.useEffect(() => {\n void load()\n }, [load])\n\n const onSave = React.useCallback(async () => {\n setSaving(true)\n try {\n const payload: RecordLockSettings = {\n ...settings,\n enabledResources: normalizeResources(settings.enabledResources ?? []),\n }\n const call = await apiCallOrThrow<{ settings?: RecordLockSettings }>(\n '/api/record_locks/settings',\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n },\n { errorMessage: t('record_locks.settings.save_error', 'Failed to save settings') },\n )\n\n const next = call.result?.settings ?? payload\n setSettings({\n ...next,\n enabledResources: normalizeResources(next.enabledResources ?? []),\n })\n flash(t('record_locks.settings.saved', 'Settings saved'), 'success')\n } catch {\n flash(t('record_locks.settings.save_error', 'Failed to save settings'), 'error')\n } finally {\n setSaving(false)\n }\n }, [normalizeResources, settings, t])\n\n if (loading) {\n return (\n <div className=\"p-6\">\n <Spinner />\n </div>\n )\n }\n\n return (\n <div className=\"max-w-2xl space-y-6 p-6\">\n <div>\n <h1 className=\"text-2xl font-semibold\">{t('record_locks.settings.title', 'Record locking')}</h1>\n <p className=\"mt-1 text-sm text-muted-foreground\">\n {t('record_locks.settings.description', 'Configure optimistic/pessimistic locking and conflict handling.')}\n </p>\n </div>\n\n <div className=\"space-y-5 rounded-lg border p-4\">\n <div className=\"flex items-center justify-between gap-4\">\n <Label htmlFor=\"record-lock-enabled\">{t('record_locks.settings.enabled', 'Enable record locking')}</Label>\n <Switch\n id=\"record-lock-enabled\"\n checked={settings.enabled}\n onCheckedChange={(checked) => setSettings((prev) => ({ ...prev, enabled: checked }))}\n />\n </div>\n\n <div className=\"space-y-2\">\n <Label htmlFor=\"record-lock-strategy\">{t('record_locks.settings.strategy', 'Strategy')}</Label>\n <select\n id=\"record-lock-strategy\"\n className=\"h-10 w-full rounded-md border bg-background px-3 text-sm\"\n value={settings.strategy}\n onChange={(event) => setSettings((prev) => ({\n ...prev,\n strategy: event.target.value === 'pessimistic' ? 'pessimistic' : 'optimistic',\n }))}\n >\n <option value=\"optimistic\">{t('record_locks.settings.strategy_optimistic', 'Optimistic')}</option>\n <option value=\"pessimistic\">{t('record_locks.settings.strategy_pessimistic', 'Pessimistic')}</option>\n </select>\n <Alert status=\"information\">\n <AlertDescription>\n <p>\n <strong>{t('record_locks.settings.strategy_optimistic', 'Optimistic')}:</strong>{' '}\n {t(\n 'record_locks.settings.strategy_help_optimistic',\n 'Multiple users can edit at the same time; conflicts are checked on save.',\n )}\n </p>\n <p className=\"mt-1\">\n <strong>{t('record_locks.settings.strategy_pessimistic', 'Pessimistic')}:</strong>{' '}\n {t(\n 'record_locks.settings.strategy_help_pessimistic',\n 'First editor acquires the lock and blocks concurrent edits until release.',\n )}\n </p>\n </AlertDescription>\n </Alert>\n </div>\n\n <div className=\"grid gap-4 sm:grid-cols-2\">\n <div className=\"space-y-2\">\n <Label htmlFor=\"record-lock-timeout\">{t('record_locks.settings.timeout_seconds', 'Lock timeout (seconds)')}</Label>\n <Input\n id=\"record-lock-timeout\"\n type=\"number\"\n min={30}\n max={3600}\n value={settings.timeoutSeconds}\n onChange={(event) => setSettings((prev) => ({\n ...prev,\n timeoutSeconds: Math.max(30, Math.min(3600, Number(event.target.value) || 30)),\n }))}\n />\n </div>\n <div className=\"space-y-2\">\n <Label htmlFor=\"record-lock-heartbeat\">{t('record_locks.settings.heartbeat_seconds', 'Heartbeat interval (seconds)')}</Label>\n <Input\n id=\"record-lock-heartbeat\"\n type=\"number\"\n min={5}\n max={300}\n value={settings.heartbeatSeconds}\n onChange={(event) => setSettings((prev) => ({\n ...prev,\n heartbeatSeconds: Math.max(5, Math.min(300, Number(event.target.value) || 5)),\n }))}\n />\n </div>\n </div>\n\n <div className=\"space-y-2\">\n <Label>{t('record_locks.settings.enabled_resources', 'Enabled resources')}</Label>\n <TagsInput\n value={settings.enabledResources}\n onChange={(next) => setSettings((prev) => ({ ...prev, enabledResources: normalizeResources(next) }))}\n suggestions={RECORD_LOCK_RESOURCE_SUGGESTIONS}\n allowCustomValues\n disabled={saving}\n placeholder={t(\n 'record_locks.settings.enabled_resources_placeholder',\n 'Add resource kind and press Enter',\n )}\n />\n <p className=\"text-xs text-muted-foreground\">\n {t(\n 'record_locks.settings.enabled_resources_hint',\n 'Pick from suggestions or type a custom resource kind.',\n )}\n </p>\n </div>\n\n <div className=\"flex items-center justify-between gap-4\">\n <Label htmlFor=\"record-lock-force-unlock\">{t('record_locks.settings.allow_force_unlock', 'Allow force unlock')}</Label>\n <Switch\n id=\"record-lock-force-unlock\"\n checked={settings.allowForceUnlock}\n onCheckedChange={(checked) => setSettings((prev) => ({ ...prev, allowForceUnlock: checked }))}\n />\n </div>\n\n <div className=\"flex items-center justify-between gap-4\">\n <Label htmlFor=\"record-lock-allow-incoming-override\">\n {t('record_locks.settings.allow_incoming_override', 'Allow overriding incoming changes')}\n </Label>\n <Switch\n id=\"record-lock-allow-incoming-override\"\n checked={settings.allowIncomingOverride}\n onCheckedChange={(checked) => setSettings((prev) => ({ ...prev, allowIncomingOverride: checked }))}\n />\n </div>\n <p className=\"text-xs text-muted-foreground\">\n {t(\n 'record_locks.settings.allow_incoming_override_hint',\n 'Users still need the record_locks.override_incoming feature in their role or user ACL to keep their version during conflicts.'\n )}{' '}\n <Link href=\"/backend/users\" className=\"underline underline-offset-2 hover:text-foreground\">\n {t('record_locks.settings.allow_incoming_override_permissions_link', 'Check permissions')}\n </Link>\n </p>\n\n <div className=\"flex items-center justify-between gap-4\">\n <Label htmlFor=\"record-lock-notify-conflict\">{t('record_locks.settings.notify_on_conflict', 'Notify on conflict')}</Label>\n <Switch\n id=\"record-lock-notify-conflict\"\n checked={settings.notifyOnConflict}\n onCheckedChange={(checked) => setSettings((prev) => ({ ...prev, notifyOnConflict: checked }))}\n />\n </div>\n\n <div className=\"pt-2\">\n <Button onClick={() => void onSave()} disabled={saving}>\n {saving ? t('ui.forms.status.saving', 'Saving...') : t('record_locks.settings.save', 'Save settings')}\n </Button>\n </div>\n </div>\n </div>\n )\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { apiCall, apiCallOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { TagsInput } from '@open-mercato/ui/backend/inputs'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { Label } from '@open-mercato/ui/primitives/label'\nimport { Alert, AlertDescription } from '@open-mercato/ui/primitives/alert'\nimport { Switch } from '@open-mercato/ui/primitives/switch'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { E } from '@open-mercato/core/generated-shims/entities.ids.generated'\nimport { DEFAULT_RECORD_LOCK_SETTINGS } from '../../../lib/config'\n\ntype RecordLockSettings = {\n enabled: boolean\n strategy: 'optimistic' | 'pessimistic'\n timeoutSeconds: number\n heartbeatSeconds: number\n enabledResources: string[]\n allowForceUnlock: boolean\n allowIncomingOverride: boolean\n notifyOnConflict: boolean\n}\n\nconst DEFAULT_SETTINGS: RecordLockSettings = {\n ...DEFAULT_RECORD_LOCK_SETTINGS,\n}\n\nconst RECORD_LOCK_RESOURCE_ALIASES = [\n 'customers.person',\n 'customers.company',\n 'customers.deal',\n 'sales.quote',\n 'sales.order',\n]\n\nconst RECORD_LOCK_RESOURCE_SUGGESTIONS = Array.from(\n new Set([\n ...Object.values(E).flatMap((moduleEntities) =>\n Object.values(moduleEntities).flatMap((entityId) => {\n if (typeof entityId !== 'string' || !entityId.includes(':')) return []\n return [entityId.replace(':', '.')]\n })\n ),\n ...RECORD_LOCK_RESOURCE_ALIASES,\n ]),\n).sort((left, right) => left.localeCompare(right))\n\nexport default function RecordLockingSettingsPage() {\n const t = useT()\n const [settings, setSettings] = React.useState<RecordLockSettings>(DEFAULT_SETTINGS)\n const [loading, setLoading] = React.useState(true)\n const [saving, setSaving] = React.useState(false)\n\n const normalizeResources = React.useCallback((input: string[]): string[] => {\n const seen = new Set<string>()\n const out: string[] = []\n for (const raw of input) {\n const resource = typeof raw === 'string' ? raw.trim() : ''\n if (!resource || seen.has(resource)) continue\n seen.add(resource)\n out.push(resource)\n }\n return out\n }, [])\n\n const load = React.useCallback(async () => {\n setLoading(true)\n try {\n const call = await apiCall<{ settings?: RecordLockSettings }>('/api/record_locks/settings')\n const next = call.result?.settings ?? DEFAULT_SETTINGS\n setSettings({\n ...next,\n enabledResources: normalizeResources(next.enabledResources ?? []),\n })\n } catch {\n flash(t('record_locks.settings.save_error', 'Failed to load settings'), 'error')\n } finally {\n setLoading(false)\n }\n }, [normalizeResources, t])\n\n React.useEffect(() => {\n void load()\n }, [load])\n\n const onSave = React.useCallback(async () => {\n setSaving(true)\n try {\n const payload: RecordLockSettings = {\n ...settings,\n enabledResources: normalizeResources(settings.enabledResources ?? []),\n }\n const call = await apiCallOrThrow<{ settings?: RecordLockSettings }>(\n '/api/record_locks/settings',\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n },\n { errorMessage: t('record_locks.settings.save_error', 'Failed to save settings') },\n )\n\n const next = call.result?.settings ?? payload\n setSettings({\n ...next,\n enabledResources: normalizeResources(next.enabledResources ?? []),\n })\n flash(t('record_locks.settings.saved', 'Settings saved'), 'success')\n } catch {\n flash(t('record_locks.settings.save_error', 'Failed to save settings'), 'error')\n } finally {\n setSaving(false)\n }\n }, [normalizeResources, settings, t])\n\n if (loading) {\n return (\n <div className=\"p-6\">\n <Spinner />\n </div>\n )\n }\n\n return (\n <div className=\"max-w-2xl space-y-6 p-6\">\n <div>\n <h1 className=\"text-2xl font-semibold\">{t('record_locks.settings.title', 'Record locking')}</h1>\n <p className=\"mt-1 text-sm text-muted-foreground\">\n {t('record_locks.settings.description', 'Configure optimistic/pessimistic locking and conflict handling.')}\n </p>\n </div>\n\n <div className=\"space-y-5 rounded-lg border p-4\">\n <div className=\"flex items-center justify-between gap-4\">\n <Label htmlFor=\"record-lock-enabled\">{t('record_locks.settings.enabled', 'Enable record locking')}</Label>\n <Switch\n id=\"record-lock-enabled\"\n checked={settings.enabled}\n onCheckedChange={(checked) => setSettings((prev) => ({ ...prev, enabled: checked }))}\n />\n </div>\n\n <div className=\"space-y-2\">\n <Label htmlFor=\"record-lock-strategy\">{t('record_locks.settings.strategy', 'Strategy')}</Label>\n <select\n id=\"record-lock-strategy\"\n className=\"h-10 w-full rounded-md border bg-background px-3 text-sm\"\n value={settings.strategy}\n onChange={(event) => setSettings((prev) => ({\n ...prev,\n strategy: event.target.value === 'pessimistic' ? 'pessimistic' : 'optimistic',\n }))}\n >\n <option value=\"optimistic\">{t('record_locks.settings.strategy_optimistic', 'Optimistic')}</option>\n <option value=\"pessimistic\">{t('record_locks.settings.strategy_pessimistic', 'Pessimistic')}</option>\n </select>\n <Alert status=\"information\">\n <AlertDescription>\n <span className=\"block\">\n <strong>{t('record_locks.settings.strategy_optimistic', 'Optimistic')}:</strong>{' '}\n {t(\n 'record_locks.settings.strategy_help_optimistic',\n 'Multiple users can edit at the same time; conflicts are checked on save.',\n )}\n </span>\n <span className=\"mt-1 block\">\n <strong>{t('record_locks.settings.strategy_pessimistic', 'Pessimistic')}:</strong>{' '}\n {t(\n 'record_locks.settings.strategy_help_pessimistic',\n 'First editor acquires the lock and blocks concurrent edits until release.',\n )}\n </span>\n </AlertDescription>\n </Alert>\n </div>\n\n <div className=\"grid gap-4 sm:grid-cols-2\">\n <div className=\"space-y-2\">\n <Label htmlFor=\"record-lock-timeout\">{t('record_locks.settings.timeout_seconds', 'Lock timeout (seconds)')}</Label>\n <Input\n id=\"record-lock-timeout\"\n type=\"number\"\n min={30}\n max={3600}\n value={settings.timeoutSeconds}\n onChange={(event) => setSettings((prev) => ({\n ...prev,\n timeoutSeconds: Math.max(30, Math.min(3600, Number(event.target.value) || 30)),\n }))}\n />\n </div>\n <div className=\"space-y-2\">\n <Label htmlFor=\"record-lock-heartbeat\">{t('record_locks.settings.heartbeat_seconds', 'Heartbeat interval (seconds)')}</Label>\n <Input\n id=\"record-lock-heartbeat\"\n type=\"number\"\n min={5}\n max={300}\n value={settings.heartbeatSeconds}\n onChange={(event) => setSettings((prev) => ({\n ...prev,\n heartbeatSeconds: Math.max(5, Math.min(300, Number(event.target.value) || 5)),\n }))}\n />\n </div>\n </div>\n\n <div className=\"space-y-2\">\n <Label>{t('record_locks.settings.enabled_resources', 'Enabled resources')}</Label>\n <TagsInput\n value={settings.enabledResources}\n onChange={(next) => setSettings((prev) => ({ ...prev, enabledResources: normalizeResources(next) }))}\n suggestions={RECORD_LOCK_RESOURCE_SUGGESTIONS}\n allowCustomValues\n disabled={saving}\n placeholder={t(\n 'record_locks.settings.enabled_resources_placeholder',\n 'Add resource kind and press Enter',\n )}\n />\n <p className=\"text-xs text-muted-foreground\">\n {t(\n 'record_locks.settings.enabled_resources_hint',\n 'Pick from suggestions or type a custom resource kind.',\n )}\n </p>\n </div>\n\n <div className=\"flex items-center justify-between gap-4\">\n <Label htmlFor=\"record-lock-force-unlock\">{t('record_locks.settings.allow_force_unlock', 'Allow force unlock')}</Label>\n <Switch\n id=\"record-lock-force-unlock\"\n checked={settings.allowForceUnlock}\n onCheckedChange={(checked) => setSettings((prev) => ({ ...prev, allowForceUnlock: checked }))}\n />\n </div>\n\n <div className=\"flex items-center justify-between gap-4\">\n <Label htmlFor=\"record-lock-allow-incoming-override\">\n {t('record_locks.settings.allow_incoming_override', 'Allow overriding incoming changes')}\n </Label>\n <Switch\n id=\"record-lock-allow-incoming-override\"\n checked={settings.allowIncomingOverride}\n onCheckedChange={(checked) => setSettings((prev) => ({ ...prev, allowIncomingOverride: checked }))}\n />\n </div>\n <p className=\"text-xs text-muted-foreground\">\n {t(\n 'record_locks.settings.allow_incoming_override_hint',\n 'Users still need the record_locks.override_incoming feature in their role or user ACL to keep their version during conflicts.'\n )}{' '}\n <Link href=\"/backend/users\" className=\"underline underline-offset-2 hover:text-foreground\">\n {t('record_locks.settings.allow_incoming_override_permissions_link', 'Check permissions')}\n </Link>\n </p>\n\n <div className=\"flex items-center justify-between gap-4\">\n <Label htmlFor=\"record-lock-notify-conflict\">{t('record_locks.settings.notify_on_conflict', 'Notify on conflict')}</Label>\n <Switch\n id=\"record-lock-notify-conflict\"\n checked={settings.notifyOnConflict}\n onCheckedChange={(checked) => setSettings((prev) => ({ ...prev, notifyOnConflict: checked }))}\n />\n </div>\n\n <div className=\"pt-2\">\n <Button onClick={() => void onSave()} disabled={saving}>\n {saving ? t('ui.forms.status.saving', 'Saving...') : t('record_locks.settings.save', 'Save settings')}\n </Button>\n </div>\n </div>\n </div>\n )\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,5 +1,5 @@
1
1
  import { signJwt, verifyJwt } from "@open-mercato/shared/lib/auth/jwt";
2
- import { emitMfaEmergencyBypassActiveWarning, readSecurityModuleConfig } from "../lib/security-config.js";
2
+ import { readSecurityModuleConfig } from "../lib/security-config.js";
3
3
  function readClaims(token) {
4
4
  const payload = verifyJwt(token);
5
5
  if (!payload || typeof payload !== "object") return null;
@@ -35,14 +35,11 @@ const interceptors = [
35
35
  if (response.statusCode !== 200) return {};
36
36
  if (response.body.ok !== true || response.body.mfa_required === true) return {};
37
37
  if (typeof response.body.token !== "string" || response.body.token.length === 0) return {};
38
+ if (readSecurityModuleConfig().mfa.emergencyBypass) return {};
38
39
  const claims = readClaims(response.body.token);
39
40
  if (!claims) return {};
40
41
  const mfaVerificationService = resolveMfaVerificationService(context.container);
41
42
  if (!mfaVerificationService) return {};
42
- if (readSecurityModuleConfig().mfa.emergencyBypass) {
43
- emitMfaEmergencyBypassActiveWarning("login MFA challenge bypassed", { userId: claims.sub });
44
- return {};
45
- }
46
43
  try {
47
44
  const challenge = await mfaVerificationService.createChallenge(claims.sub);
48
45
  const pendingToken = signJwt(
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/security/api/interceptors.ts"],
4
- "sourcesContent": ["import type { ApiInterceptor } from '@open-mercato/shared/lib/crud/api-interceptor'\nimport { signJwt, verifyJwt } from '@open-mercato/shared/lib/auth/jwt'\nimport { emitMfaEmergencyBypassActiveWarning, readSecurityModuleConfig } from '../lib/security-config'\n\ntype JwtClaims = {\n sub: string\n sid?: string | null\n tenantId?: string | null\n orgId?: string | null\n email?: string | null\n roles?: string[]\n}\n\ntype MfaVerificationServiceLike = {\n createChallenge: (userId: string) => Promise<{\n challengeId: string\n availableMethods: Array<{ type: string; label: string; icon: string }>\n }>\n}\n\nfunction readClaims(token: string): JwtClaims | null {\n const payload = verifyJwt(token)\n if (!payload || typeof payload !== 'object') return null\n const sub = typeof payload.sub === 'string' ? payload.sub : null\n if (!sub) return null\n return {\n sub,\n sid: typeof payload.sid === 'string' ? payload.sid : null,\n tenantId: typeof payload.tenantId === 'string' ? payload.tenantId : null,\n orgId: typeof payload.orgId === 'string' ? payload.orgId : null,\n email: typeof payload.email === 'string' ? payload.email : null,\n roles: Array.isArray(payload.roles) ? payload.roles.filter((value: unknown): value is string => typeof value === 'string') : [],\n }\n}\n\nfunction resolveMfaVerificationService(container: { resolve: (name: string) => unknown }): MfaVerificationServiceLike | null {\n try {\n const resolved = container.resolve('mfaVerificationService')\n if (\n !resolved\n || typeof resolved !== 'object'\n || typeof (resolved as { createChallenge?: unknown }).createChallenge !== 'function'\n ) {\n return null\n }\n return resolved as MfaVerificationServiceLike\n } catch {\n return null\n }\n}\n\nexport const interceptors: ApiInterceptor[] = [\n {\n id: 'security.auth.login.mfa-challenge',\n targetRoute: 'auth/login',\n methods: ['POST'],\n priority: 50,\n async after(_request, response, context) {\n if (response.statusCode !== 200) return {}\n if (response.body.ok !== true || response.body.mfa_required === true) return {}\n if (typeof response.body.token !== 'string' || response.body.token.length === 0) return {}\n\n const claims = readClaims(response.body.token)\n if (!claims) return {}\n\n const mfaVerificationService = resolveMfaVerificationService(context.container as { resolve: (name: string) => unknown })\n if (!mfaVerificationService) return {}\n\n if (readSecurityModuleConfig().mfa.emergencyBypass) {\n emitMfaEmergencyBypassActiveWarning('login MFA challenge bypassed', { userId: claims.sub })\n return {}\n }\n\n try {\n const challenge = await mfaVerificationService.createChallenge(claims.sub)\n const pendingToken = signJwt(\n {\n sub: claims.sub,\n sid: claims.sid ?? undefined,\n tenantId: claims.tenantId ?? null,\n orgId: claims.orgId ?? null,\n email: claims.email ?? null,\n roles: claims.roles ?? [],\n mfa_pending: true,\n mfa_verified: false,\n },\n undefined,\n 60 * 10,\n )\n return {\n replace: {\n ok: true,\n mfa_required: true,\n challenge_id: challenge.challengeId,\n available_methods: challenge.availableMethods,\n token: pendingToken,\n },\n }\n } catch {\n return {}\n }\n },\n },\n]\n"],
5
- "mappings": "AACA,SAAS,SAAS,iBAAiB;AACnC,SAAS,qCAAqC,gCAAgC;AAkB9E,SAAS,WAAW,OAAiC;AACnD,QAAM,UAAU,UAAU,KAAK;AAC/B,MAAI,CAAC,WAAW,OAAO,YAAY,SAAU,QAAO;AACpD,QAAM,MAAM,OAAO,QAAQ,QAAQ,WAAW,QAAQ,MAAM;AAC5D,MAAI,CAAC,IAAK,QAAO;AACjB,SAAO;AAAA,IACL;AAAA,IACA,KAAK,OAAO,QAAQ,QAAQ,WAAW,QAAQ,MAAM;AAAA,IACrD,UAAU,OAAO,QAAQ,aAAa,WAAW,QAAQ,WAAW;AAAA,IACpE,OAAO,OAAO,QAAQ,UAAU,WAAW,QAAQ,QAAQ;AAAA,IAC3D,OAAO,OAAO,QAAQ,UAAU,WAAW,QAAQ,QAAQ;AAAA,IAC3D,OAAO,MAAM,QAAQ,QAAQ,KAAK,IAAI,QAAQ,MAAM,OAAO,CAAC,UAAoC,OAAO,UAAU,QAAQ,IAAI,CAAC;AAAA,EAChI;AACF;AAEA,SAAS,8BAA8B,WAAsF;AAC3H,MAAI;AACF,UAAM,WAAW,UAAU,QAAQ,wBAAwB;AAC3D,QACE,CAAC,YACE,OAAO,aAAa,YACpB,OAAQ,SAA2C,oBAAoB,YAC1E;AACA,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,MAAM,eAAiC;AAAA,EAC5C;AAAA,IACE,IAAI;AAAA,IACJ,aAAa;AAAA,IACb,SAAS,CAAC,MAAM;AAAA,IAChB,UAAU;AAAA,IACV,MAAM,MAAM,UAAU,UAAU,SAAS;AACvC,UAAI,SAAS,eAAe,IAAK,QAAO,CAAC;AACzC,UAAI,SAAS,KAAK,OAAO,QAAQ,SAAS,KAAK,iBAAiB,KAAM,QAAO,CAAC;AAC9E,UAAI,OAAO,SAAS,KAAK,UAAU,YAAY,SAAS,KAAK,MAAM,WAAW,EAAG,QAAO,CAAC;AAEzF,YAAM,SAAS,WAAW,SAAS,KAAK,KAAK;AAC7C,UAAI,CAAC,OAAQ,QAAO,CAAC;AAErB,YAAM,yBAAyB,8BAA8B,QAAQ,SAAmD;AACxH,UAAI,CAAC,uBAAwB,QAAO,CAAC;AAErC,UAAI,yBAAyB,EAAE,IAAI,iBAAiB;AAClD,4CAAoC,gCAAgC,EAAE,QAAQ,OAAO,IAAI,CAAC;AAC1F,eAAO,CAAC;AAAA,MACV;AAEA,UAAI;AACF,cAAM,YAAY,MAAM,uBAAuB,gBAAgB,OAAO,GAAG;AACzE,cAAM,eAAe;AAAA,UACnB;AAAA,YACE,KAAK,OAAO;AAAA,YACZ,KAAK,OAAO,OAAO;AAAA,YACnB,UAAU,OAAO,YAAY;AAAA,YAC7B,OAAO,OAAO,SAAS;AAAA,YACvB,OAAO,OAAO,SAAS;AAAA,YACvB,OAAO,OAAO,SAAS,CAAC;AAAA,YACxB,aAAa;AAAA,YACb,cAAc;AAAA,UAChB;AAAA,UACA;AAAA,UACA,KAAK;AAAA,QACP;AACA,eAAO;AAAA,UACL,SAAS;AAAA,YACP,IAAI;AAAA,YACJ,cAAc;AAAA,YACd,cAAc,UAAU;AAAA,YACxB,mBAAmB,UAAU;AAAA,YAC7B,OAAO;AAAA,UACT;AAAA,QACF;AAAA,MACF,QAAQ;AACN,eAAO,CAAC;AAAA,MACV;AAAA,IACF;AAAA,EACF;AACF;",
4
+ "sourcesContent": ["import type { ApiInterceptor } from '@open-mercato/shared/lib/crud/api-interceptor'\nimport { signJwt, verifyJwt } from '@open-mercato/shared/lib/auth/jwt'\nimport { readSecurityModuleConfig } from '../lib/security-config'\n\ntype JwtClaims = {\n sub: string\n sid?: string | null\n tenantId?: string | null\n orgId?: string | null\n email?: string | null\n roles?: string[]\n}\n\ntype MfaVerificationServiceLike = {\n createChallenge: (userId: string) => Promise<{\n challengeId: string\n availableMethods: Array<{ type: string; label: string; icon: string }>\n }>\n}\n\nfunction readClaims(token: string): JwtClaims | null {\n const payload = verifyJwt(token)\n if (!payload || typeof payload !== 'object') return null\n const sub = typeof payload.sub === 'string' ? payload.sub : null\n if (!sub) return null\n return {\n sub,\n sid: typeof payload.sid === 'string' ? payload.sid : null,\n tenantId: typeof payload.tenantId === 'string' ? payload.tenantId : null,\n orgId: typeof payload.orgId === 'string' ? payload.orgId : null,\n email: typeof payload.email === 'string' ? payload.email : null,\n roles: Array.isArray(payload.roles) ? payload.roles.filter((value: unknown): value is string => typeof value === 'string') : [],\n }\n}\n\nfunction resolveMfaVerificationService(container: { resolve: (name: string) => unknown }): MfaVerificationServiceLike | null {\n try {\n const resolved = container.resolve('mfaVerificationService')\n if (\n !resolved\n || typeof resolved !== 'object'\n || typeof (resolved as { createChallenge?: unknown }).createChallenge !== 'function'\n ) {\n return null\n }\n return resolved as MfaVerificationServiceLike\n } catch {\n return null\n }\n}\n\nexport const interceptors: ApiInterceptor[] = [\n {\n id: 'security.auth.login.mfa-challenge',\n targetRoute: 'auth/login',\n methods: ['POST'],\n priority: 50,\n async after(_request, response, context) {\n if (response.statusCode !== 200) return {}\n if (response.body.ok !== true || response.body.mfa_required === true) return {}\n if (typeof response.body.token !== 'string' || response.body.token.length === 0) return {}\n if (readSecurityModuleConfig().mfa.emergencyBypass) return {}\n\n const claims = readClaims(response.body.token)\n if (!claims) return {}\n\n const mfaVerificationService = resolveMfaVerificationService(context.container as { resolve: (name: string) => unknown })\n if (!mfaVerificationService) return {}\n\n try {\n const challenge = await mfaVerificationService.createChallenge(claims.sub)\n const pendingToken = signJwt(\n {\n sub: claims.sub,\n sid: claims.sid ?? undefined,\n tenantId: claims.tenantId ?? null,\n orgId: claims.orgId ?? null,\n email: claims.email ?? null,\n roles: claims.roles ?? [],\n mfa_pending: true,\n mfa_verified: false,\n },\n undefined,\n 60 * 10,\n )\n return {\n replace: {\n ok: true,\n mfa_required: true,\n challenge_id: challenge.challengeId,\n available_methods: challenge.availableMethods,\n token: pendingToken,\n },\n }\n } catch {\n return {}\n }\n },\n },\n]\n"],
5
+ "mappings": "AACA,SAAS,SAAS,iBAAiB;AACnC,SAAS,gCAAgC;AAkBzC,SAAS,WAAW,OAAiC;AACnD,QAAM,UAAU,UAAU,KAAK;AAC/B,MAAI,CAAC,WAAW,OAAO,YAAY,SAAU,QAAO;AACpD,QAAM,MAAM,OAAO,QAAQ,QAAQ,WAAW,QAAQ,MAAM;AAC5D,MAAI,CAAC,IAAK,QAAO;AACjB,SAAO;AAAA,IACL;AAAA,IACA,KAAK,OAAO,QAAQ,QAAQ,WAAW,QAAQ,MAAM;AAAA,IACrD,UAAU,OAAO,QAAQ,aAAa,WAAW,QAAQ,WAAW;AAAA,IACpE,OAAO,OAAO,QAAQ,UAAU,WAAW,QAAQ,QAAQ;AAAA,IAC3D,OAAO,OAAO,QAAQ,UAAU,WAAW,QAAQ,QAAQ;AAAA,IAC3D,OAAO,MAAM,QAAQ,QAAQ,KAAK,IAAI,QAAQ,MAAM,OAAO,CAAC,UAAoC,OAAO,UAAU,QAAQ,IAAI,CAAC;AAAA,EAChI;AACF;AAEA,SAAS,8BAA8B,WAAsF;AAC3H,MAAI;AACF,UAAM,WAAW,UAAU,QAAQ,wBAAwB;AAC3D,QACE,CAAC,YACE,OAAO,aAAa,YACpB,OAAQ,SAA2C,oBAAoB,YAC1E;AACA,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,MAAM,eAAiC;AAAA,EAC5C;AAAA,IACE,IAAI;AAAA,IACJ,aAAa;AAAA,IACb,SAAS,CAAC,MAAM;AAAA,IAChB,UAAU;AAAA,IACV,MAAM,MAAM,UAAU,UAAU,SAAS;AACvC,UAAI,SAAS,eAAe,IAAK,QAAO,CAAC;AACzC,UAAI,SAAS,KAAK,OAAO,QAAQ,SAAS,KAAK,iBAAiB,KAAM,QAAO,CAAC;AAC9E,UAAI,OAAO,SAAS,KAAK,UAAU,YAAY,SAAS,KAAK,MAAM,WAAW,EAAG,QAAO,CAAC;AACzF,UAAI,yBAAyB,EAAE,IAAI,gBAAiB,QAAO,CAAC;AAE5D,YAAM,SAAS,WAAW,SAAS,KAAK,KAAK;AAC7C,UAAI,CAAC,OAAQ,QAAO,CAAC;AAErB,YAAM,yBAAyB,8BAA8B,QAAQ,SAAmD;AACxH,UAAI,CAAC,uBAAwB,QAAO,CAAC;AAErC,UAAI;AACF,cAAM,YAAY,MAAM,uBAAuB,gBAAgB,OAAO,GAAG;AACzE,cAAM,eAAe;AAAA,UACnB;AAAA,YACE,KAAK,OAAO;AAAA,YACZ,KAAK,OAAO,OAAO;AAAA,YACnB,UAAU,OAAO,YAAY;AAAA,YAC7B,OAAO,OAAO,SAAS;AAAA,YACvB,OAAO,OAAO,SAAS;AAAA,YACvB,OAAO,OAAO,SAAS,CAAC;AAAA,YACxB,aAAa;AAAA,YACb,cAAc;AAAA,UAChB;AAAA,UACA;AAAA,UACA,KAAK;AAAA,QACP;AACA,eAAO;AAAA,UACL,SAAS;AAAA,YACP,IAAI;AAAA,YACJ,cAAc;AAAA,YACd,cAAc,UAAU;AAAA,YACxB,mBAAmB,UAAU;AAAA,YAC7B,OAAO;AAAA,UACT;AAAA,QACF;AAAA,MACF,QAAQ;AACN,eAAO,CAAC;AAAA,MACV;AAAA,IACF;AAAA,EACF;AACF;",
6
6
  "names": []
7
7
  }
@@ -2,7 +2,6 @@ import { NextResponse } from "next/server";
2
2
  import { z } from "zod";
3
3
  import { CrudHttpError } from "@open-mercato/shared/lib/crud/errors";
4
4
  import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
5
- import "../../lib/mfaCompletionRoutes.js";
6
5
  import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
7
6
  import { signJwt } from "@open-mercato/shared/lib/auth/jwt";
8
7
  import { localizeSecurityApiBody, securityApiError } from "../i18n.js";
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/security/api/mfa/_shared.ts"],
4
- "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { CommandRuntimeContext } from '@open-mercato/shared/lib/commands'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport '../../lib/mfaCompletionRoutes'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { signJwt } from '@open-mercato/shared/lib/auth/jwt'\nimport type { MfaService, MfaServiceError } from '../../services/MfaService'\nimport type { MfaVerificationService, MfaVerificationServiceError } from '../../services/MfaVerificationService'\nimport { localizeSecurityApiBody, securityApiError } from '../i18n'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('security').child({ component: 'mfa' })\nconst MFA_MANAGE_FEATURE = 'security.mfa.manage'\n\nconst jsonRecordSchema = z.record(z.string(), z.unknown())\n\nexport type MfaRequestContext = {\n auth: NonNullable<Awaited<ReturnType<typeof getAuthFromRequest>>>\n container: Awaited<ReturnType<typeof createRequestContainer>>\n commandContext: CommandRuntimeContext\n mfaService: MfaService\n mfaVerificationService: MfaVerificationService\n}\n\ntype MfaEnrollmentRbacService = {\n userHasAllFeatures: (\n userId: string,\n features: string[],\n scope: { tenantId: string | null; organizationId: string | null },\n ) => Promise<boolean>\n}\n\ntype MfaEnrollmentEnforcementService = {\n checkUserCompliance: (userId: string) => Promise<{\n compliant: boolean\n enforced: boolean\n }>\n}\n\nexport async function resolveMfaRequestContext(req: Request): Promise<MfaRequestContext | NextResponse> {\n const auth = await getAuthFromRequest(req)\n if (!auth?.sub) {\n return securityApiError(401, 'Unauthorized')\n }\n\n const container = await createRequestContainer()\n return {\n auth,\n container,\n commandContext: {\n container,\n auth,\n organizationScope: null,\n selectedOrganizationId: auth.orgId ?? null,\n organizationIds: auth.orgId ? [auth.orgId] : null,\n request: req,\n },\n mfaService: container.resolve<MfaService>('mfaService'),\n mfaVerificationService: container.resolve<MfaVerificationService>('mfaVerificationService'),\n }\n}\n\nexport async function authorizeMfaEnrollmentMutation(\n context: Pick<MfaRequestContext, 'auth' | 'container'>,\n): Promise<NextResponse | null> {\n const tenantId = typeof context.auth.tenantId === 'string' && context.auth.tenantId.trim().length > 0\n ? context.auth.tenantId.trim()\n : null\n const organizationId = typeof context.auth.orgId === 'string' && context.auth.orgId.trim().length > 0\n ? context.auth.orgId.trim()\n : null\n const rbacService = context.container.resolve<MfaEnrollmentRbacService>('rbacService')\n const canManageMfa = await rbacService.userHasAllFeatures(\n context.auth.sub,\n [MFA_MANAGE_FEATURE],\n { tenantId, organizationId },\n )\n if (canManageMfa) return null\n if (!tenantId) return securityApiError(403, 'Forbidden')\n\n try {\n const enforcementService = context.container.resolve<MfaEnrollmentEnforcementService>('mfaEnforcementService')\n const compliance = await enforcementService.checkUserCompliance(context.auth.sub)\n if (compliance.enforced && !compliance.compliant) return null\n } catch (error) {\n logger.error('Unable to verify compelled MFA enrollment authorization; allowing enrollment recovery path', {\n err: error,\n })\n return null\n }\n\n return securityApiError(403, 'Forbidden')\n}\n\nexport async function readJsonRecord(req: Request): Promise<Record<string, unknown>> {\n try {\n const parsed = jsonRecordSchema.safeParse(await req.json())\n return parsed.success ? parsed.data : {}\n } catch {\n return {}\n }\n}\n\nexport function readUuidParam(value: string | string[] | undefined): string | null {\n if (typeof value !== 'string') return null\n const parsed = z.string().uuid().safeParse(value)\n return parsed.success ? parsed.data : null\n}\n\nexport function readString(value: unknown): string | null {\n if (typeof value !== 'string') return null\n const trimmed = value.trim()\n return trimmed.length > 0 ? trimmed : null\n}\n\nexport async function mapMfaError(error: unknown): Promise<NextResponse> {\n if (error instanceof CrudHttpError) {\n return NextResponse.json(await localizeSecurityApiBody(error.body), { status: error.status })\n }\n if (isMfaServiceError(error) || isMfaVerificationServiceError(error)) {\n return securityApiError(error.statusCode, error.message)\n }\n logger.error('MFA route failure', { err: error })\n return securityApiError(500, 'Failed to process MFA request.')\n}\n\nfunction isMfaServiceError(error: unknown): error is MfaServiceError {\n return error instanceof Error\n && error.name === 'MfaServiceError'\n && typeof (error as Partial<MfaServiceError>).statusCode === 'number'\n}\n\nfunction isMfaVerificationServiceError(error: unknown): error is MfaVerificationServiceError {\n return error instanceof Error\n && error.name === 'MfaVerificationServiceError'\n && typeof (error as Partial<MfaVerificationServiceError>).statusCode === 'number'\n}\n\nexport function issueVerifiedMfaToken(auth: MfaRequestContext['auth'], methods: string[]): string {\n const nextPayload: Record<string, unknown> = {\n sub: auth.sub,\n sid: typeof auth.sid === 'string' ? auth.sid : undefined,\n tenantId: auth.tenantId ?? null,\n orgId: auth.orgId ?? null,\n email: typeof auth.email === 'string' ? auth.email : null,\n roles: Array.isArray(auth.roles) ? auth.roles : [],\n mfa_pending: false,\n mfa_verified: true,\n mfa_methods: methods,\n }\n if (typeof auth.actorTenantId === 'string') {\n nextPayload.actorTenantId = auth.actorTenantId\n }\n if (typeof auth.actorOrgId === 'string') {\n nextPayload.actorOrgId = auth.actorOrgId\n }\n if (auth.isSuperAdmin === true) {\n nextPayload.isSuperAdmin = true\n }\n return signJwt({\n ...nextPayload,\n })\n}\n\nexport function setAuthCookie(response: NextResponse, token: string): void {\n response.cookies.set('auth_token', token, {\n httpOnly: true,\n path: '/',\n sameSite: 'lax',\n secure: process.env.NODE_ENV === 'production',\n maxAge: 60 * 60 * 8,\n })\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { CommandRuntimeContext } from '@open-mercato/shared/lib/commands'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { signJwt } from '@open-mercato/shared/lib/auth/jwt'\nimport type { MfaService, MfaServiceError } from '../../services/MfaService'\nimport type { MfaVerificationService, MfaVerificationServiceError } from '../../services/MfaVerificationService'\nimport { localizeSecurityApiBody, securityApiError } from '../i18n'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('security').child({ component: 'mfa' })\nconst MFA_MANAGE_FEATURE = 'security.mfa.manage'\n\nconst jsonRecordSchema = z.record(z.string(), z.unknown())\n\nexport type MfaRequestContext = {\n auth: NonNullable<Awaited<ReturnType<typeof getAuthFromRequest>>>\n container: Awaited<ReturnType<typeof createRequestContainer>>\n commandContext: CommandRuntimeContext\n mfaService: MfaService\n mfaVerificationService: MfaVerificationService\n}\n\ntype MfaEnrollmentRbacService = {\n userHasAllFeatures: (\n userId: string,\n features: string[],\n scope: { tenantId: string | null; organizationId: string | null },\n ) => Promise<boolean>\n}\n\ntype MfaEnrollmentEnforcementService = {\n checkUserCompliance: (userId: string) => Promise<{\n compliant: boolean\n enforced: boolean\n }>\n}\n\nexport async function resolveMfaRequestContext(req: Request): Promise<MfaRequestContext | NextResponse> {\n const auth = await getAuthFromRequest(req)\n if (!auth?.sub) {\n return securityApiError(401, 'Unauthorized')\n }\n\n const container = await createRequestContainer()\n return {\n auth,\n container,\n commandContext: {\n container,\n auth,\n organizationScope: null,\n selectedOrganizationId: auth.orgId ?? null,\n organizationIds: auth.orgId ? [auth.orgId] : null,\n request: req,\n },\n mfaService: container.resolve<MfaService>('mfaService'),\n mfaVerificationService: container.resolve<MfaVerificationService>('mfaVerificationService'),\n }\n}\n\nexport async function authorizeMfaEnrollmentMutation(\n context: Pick<MfaRequestContext, 'auth' | 'container'>,\n): Promise<NextResponse | null> {\n const tenantId = typeof context.auth.tenantId === 'string' && context.auth.tenantId.trim().length > 0\n ? context.auth.tenantId.trim()\n : null\n const organizationId = typeof context.auth.orgId === 'string' && context.auth.orgId.trim().length > 0\n ? context.auth.orgId.trim()\n : null\n const rbacService = context.container.resolve<MfaEnrollmentRbacService>('rbacService')\n const canManageMfa = await rbacService.userHasAllFeatures(\n context.auth.sub,\n [MFA_MANAGE_FEATURE],\n { tenantId, organizationId },\n )\n if (canManageMfa) return null\n if (!tenantId) return securityApiError(403, 'Forbidden')\n\n try {\n const enforcementService = context.container.resolve<MfaEnrollmentEnforcementService>('mfaEnforcementService')\n const compliance = await enforcementService.checkUserCompliance(context.auth.sub)\n if (compliance.enforced && !compliance.compliant) return null\n } catch (error) {\n logger.error('Unable to verify compelled MFA enrollment authorization; allowing enrollment recovery path', {\n err: error,\n })\n return null\n }\n\n return securityApiError(403, 'Forbidden')\n}\n\nexport async function readJsonRecord(req: Request): Promise<Record<string, unknown>> {\n try {\n const parsed = jsonRecordSchema.safeParse(await req.json())\n return parsed.success ? parsed.data : {}\n } catch {\n return {}\n }\n}\n\nexport function readUuidParam(value: string | string[] | undefined): string | null {\n if (typeof value !== 'string') return null\n const parsed = z.string().uuid().safeParse(value)\n return parsed.success ? parsed.data : null\n}\n\nexport function readString(value: unknown): string | null {\n if (typeof value !== 'string') return null\n const trimmed = value.trim()\n return trimmed.length > 0 ? trimmed : null\n}\n\nexport async function mapMfaError(error: unknown): Promise<NextResponse> {\n if (error instanceof CrudHttpError) {\n return NextResponse.json(await localizeSecurityApiBody(error.body), { status: error.status })\n }\n if (isMfaServiceError(error) || isMfaVerificationServiceError(error)) {\n return securityApiError(error.statusCode, error.message)\n }\n logger.error('MFA route failure', { err: error })\n return securityApiError(500, 'Failed to process MFA request.')\n}\n\nfunction isMfaServiceError(error: unknown): error is MfaServiceError {\n return error instanceof Error\n && error.name === 'MfaServiceError'\n && typeof (error as Partial<MfaServiceError>).statusCode === 'number'\n}\n\nfunction isMfaVerificationServiceError(error: unknown): error is MfaVerificationServiceError {\n return error instanceof Error\n && error.name === 'MfaVerificationServiceError'\n && typeof (error as Partial<MfaVerificationServiceError>).statusCode === 'number'\n}\n\nexport function issueVerifiedMfaToken(auth: MfaRequestContext['auth'], methods: string[]): string {\n const nextPayload: Record<string, unknown> = {\n sub: auth.sub,\n sid: typeof auth.sid === 'string' ? auth.sid : undefined,\n tenantId: auth.tenantId ?? null,\n orgId: auth.orgId ?? null,\n email: typeof auth.email === 'string' ? auth.email : null,\n roles: Array.isArray(auth.roles) ? auth.roles : [],\n mfa_pending: false,\n mfa_verified: true,\n mfa_methods: methods,\n }\n if (typeof auth.actorTenantId === 'string') {\n nextPayload.actorTenantId = auth.actorTenantId\n }\n if (typeof auth.actorOrgId === 'string') {\n nextPayload.actorOrgId = auth.actorOrgId\n }\n if (auth.isSuperAdmin === true) {\n nextPayload.isSuperAdmin = true\n }\n return signJwt({\n ...nextPayload,\n })\n}\n\nexport function setAuthCookie(response: NextResponse, token: string): void {\n response.cookies.set('auth_token', token, {\n httpOnly: true,\n path: '/',\n sameSite: 'lax',\n secure: process.env.NODE_ENV === 'production',\n maxAge: 60 * 60 * 8,\n })\n}\n"],
5
+ "mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAElB,SAAS,qBAAqB;AAC9B,SAAS,8BAA8B;AACvC,SAAS,0BAA0B;AACnC,SAAS,eAAe;AAGxB,SAAS,yBAAyB,wBAAwB;AAC1D,SAAS,oBAAoB;AAE7B,MAAM,SAAS,aAAa,UAAU,EAAE,MAAM,EAAE,WAAW,MAAM,CAAC;AAClE,MAAM,qBAAqB;AAE3B,MAAM,mBAAmB,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,QAAQ,CAAC;AAyBzD,eAAsB,yBAAyB,KAAyD;AACtG,QAAM,OAAO,MAAM,mBAAmB,GAAG;AACzC,MAAI,CAAC,MAAM,KAAK;AACd,WAAO,iBAAiB,KAAK,cAAc;AAAA,EAC7C;AAEA,QAAM,YAAY,MAAM,uBAAuB;AAC/C,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,gBAAgB;AAAA,MACd;AAAA,MACA;AAAA,MACA,mBAAmB;AAAA,MACnB,wBAAwB,KAAK,SAAS;AAAA,MACtC,iBAAiB,KAAK,QAAQ,CAAC,KAAK,KAAK,IAAI;AAAA,MAC7C,SAAS;AAAA,IACX;AAAA,IACA,YAAY,UAAU,QAAoB,YAAY;AAAA,IACtD,wBAAwB,UAAU,QAAgC,wBAAwB;AAAA,EAC5F;AACF;AAEA,eAAsB,+BACpB,SAC8B;AAC9B,QAAM,WAAW,OAAO,QAAQ,KAAK,aAAa,YAAY,QAAQ,KAAK,SAAS,KAAK,EAAE,SAAS,IAChG,QAAQ,KAAK,SAAS,KAAK,IAC3B;AACJ,QAAM,iBAAiB,OAAO,QAAQ,KAAK,UAAU,YAAY,QAAQ,KAAK,MAAM,KAAK,EAAE,SAAS,IAChG,QAAQ,KAAK,MAAM,KAAK,IACxB;AACJ,QAAM,cAAc,QAAQ,UAAU,QAAkC,aAAa;AACrF,QAAM,eAAe,MAAM,YAAY;AAAA,IACrC,QAAQ,KAAK;AAAA,IACb,CAAC,kBAAkB;AAAA,IACnB,EAAE,UAAU,eAAe;AAAA,EAC7B;AACA,MAAI,aAAc,QAAO;AACzB,MAAI,CAAC,SAAU,QAAO,iBAAiB,KAAK,WAAW;AAEvD,MAAI;AACF,UAAM,qBAAqB,QAAQ,UAAU,QAAyC,uBAAuB;AAC7G,UAAM,aAAa,MAAM,mBAAmB,oBAAoB,QAAQ,KAAK,GAAG;AAChF,QAAI,WAAW,YAAY,CAAC,WAAW,UAAW,QAAO;AAAA,EAC3D,SAAS,OAAO;AACd,WAAO,MAAM,8FAA8F;AAAA,MACzG,KAAK;AAAA,IACP,CAAC;AACD,WAAO;AAAA,EACT;AAEA,SAAO,iBAAiB,KAAK,WAAW;AAC1C;AAEA,eAAsB,eAAe,KAAgD;AACnF,MAAI;AACF,UAAM,SAAS,iBAAiB,UAAU,MAAM,IAAI,KAAK,CAAC;AAC1D,WAAO,OAAO,UAAU,OAAO,OAAO,CAAC;AAAA,EACzC,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAEO,SAAS,cAAc,OAAqD;AACjF,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,QAAM,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,KAAK;AAChD,SAAO,OAAO,UAAU,OAAO,OAAO;AACxC;AAEO,SAAS,WAAW,OAA+B;AACxD,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,QAAM,UAAU,MAAM,KAAK;AAC3B,SAAO,QAAQ,SAAS,IAAI,UAAU;AACxC;AAEA,eAAsB,YAAY,OAAuC;AACvE,MAAI,iBAAiB,eAAe;AAClC,WAAO,aAAa,KAAK,MAAM,wBAAwB,MAAM,IAAI,GAAG,EAAE,QAAQ,MAAM,OAAO,CAAC;AAAA,EAC9F;AACA,MAAI,kBAAkB,KAAK,KAAK,8BAA8B,KAAK,GAAG;AACpE,WAAO,iBAAiB,MAAM,YAAY,MAAM,OAAO;AAAA,EACzD;AACA,SAAO,MAAM,qBAAqB,EAAE,KAAK,MAAM,CAAC;AAChD,SAAO,iBAAiB,KAAK,gCAAgC;AAC/D;AAEA,SAAS,kBAAkB,OAA0C;AACnE,SAAO,iBAAiB,SACnB,MAAM,SAAS,qBACf,OAAQ,MAAmC,eAAe;AACjE;AAEA,SAAS,8BAA8B,OAAsD;AAC3F,SAAO,iBAAiB,SACnB,MAAM,SAAS,iCACf,OAAQ,MAA+C,eAAe;AAC7E;AAEO,SAAS,sBAAsB,MAAiC,SAA2B;AAChG,QAAM,cAAuC;AAAA,IAC3C,KAAK,KAAK;AAAA,IACV,KAAK,OAAO,KAAK,QAAQ,WAAW,KAAK,MAAM;AAAA,IAC/C,UAAU,KAAK,YAAY;AAAA,IAC3B,OAAO,KAAK,SAAS;AAAA,IACrB,OAAO,OAAO,KAAK,UAAU,WAAW,KAAK,QAAQ;AAAA,IACrD,OAAO,MAAM,QAAQ,KAAK,KAAK,IAAI,KAAK,QAAQ,CAAC;AAAA,IACjD,aAAa;AAAA,IACb,cAAc;AAAA,IACd,aAAa;AAAA,EACf;AACA,MAAI,OAAO,KAAK,kBAAkB,UAAU;AAC1C,gBAAY,gBAAgB,KAAK;AAAA,EACnC;AACA,MAAI,OAAO,KAAK,eAAe,UAAU;AACvC,gBAAY,aAAa,KAAK;AAAA,EAChC;AACA,MAAI,KAAK,iBAAiB,MAAM;AAC9B,gBAAY,eAAe;AAAA,EAC7B;AACA,SAAO,QAAQ;AAAA,IACb,GAAG;AAAA,EACL,CAAC;AACH;AAEO,SAAS,cAAc,UAAwB,OAAqB;AACzE,WAAS,QAAQ,IAAI,cAAc,OAAO;AAAA,IACxC,UAAU;AAAA,IACV,MAAM;AAAA,IACN,UAAU;AAAA,IACV,QAAQ,QAAQ,IAAI,aAAa;AAAA,IACjC,QAAQ,KAAK,KAAK;AAAA,EACpB,CAAC;AACH;",
6
6
  "names": []
7
7
  }
@@ -212,21 +212,6 @@ __decorateClass([
212
212
  __decorateClass([
213
213
  Property({ name: "tenant_id", type: "uuid" })
214
214
  ], SudoSession.prototype, "tenantId", 2);
215
- __decorateClass([
216
- Property({ name: "scope_tenant_id", type: "uuid", nullable: true })
217
- ], SudoSession.prototype, "scopeTenantId", 2);
218
- __decorateClass([
219
- Property({ name: "scope_organization_id", type: "uuid", nullable: true })
220
- ], SudoSession.prototype, "scopeOrganizationId", 2);
221
- __decorateClass([
222
- Property({ name: "target_identifier", type: "text", nullable: true })
223
- ], SudoSession.prototype, "targetIdentifier", 2);
224
- __decorateClass([
225
- Property({ name: "sudo_config_id", type: "uuid", nullable: true })
226
- ], SudoSession.prototype, "sudoConfigId", 2);
227
- __decorateClass([
228
- Property({ name: "sudo_config_updated_at", type: Date, nullable: true })
229
- ], SudoSession.prototype, "sudoConfigUpdatedAt", 2);
230
215
  __decorateClass([
231
216
  Property({ name: "session_token", type: "text" })
232
217
  ], SudoSession.prototype, "sessionToken", 2);
@@ -236,9 +221,6 @@ __decorateClass([
236
221
  __decorateClass([
237
222
  Property({ name: "expires_at", type: Date })
238
223
  ], SudoSession.prototype, "expiresAt", 2);
239
- __decorateClass([
240
- Property({ name: "verified_at", type: Date, nullable: true })
241
- ], SudoSession.prototype, "verifiedAt", 2);
242
224
  __decorateClass([
243
225
  Property({ name: "created_at", type: Date, onCreate: () => /* @__PURE__ */ new Date() })
244
226
  ], SudoSession.prototype, "createdAt", 2);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/security/data/entities.ts"],
4
- "sourcesContent": ["import { Entity, Index, PrimaryKey, Property } from '@mikro-orm/decorators/legacy'\nimport {\n ChallengeMethod,\n EnforcementScope,\n MfaMethodType,\n SudoChallengeMethodUsed,\n} from './constants'\n\nexport {\n ChallengeMethod,\n EnforcementScope,\n MfaMethodType,\n SudoChallengeMethodUsed,\n} from './constants'\n\n@Entity({ tableName: 'user_mfa_methods' })\n@Index({ name: 'idx_user_mfa_methods_user_type', properties: ['userId', 'type', 'isActive'] })\n@Index({ name: 'idx_user_mfa_methods_tenant', properties: ['tenantId'] })\nexport class UserMfaMethod {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'user_id', type: 'uuid' })\n userId!: string\n\n @Property({ name: 'tenant_id', type: 'uuid' })\n tenantId!: string\n\n @Property({ name: 'organization_id', type: 'uuid', nullable: true })\n organizationId?: string | null\n\n @Property({ type: 'text' })\n type!: string\n\n @Property({ type: 'text', nullable: true })\n label?: string | null\n\n @Property({ type: 'text', nullable: true })\n secret?: string | null\n\n @Property({ name: 'provider_metadata', type: 'jsonb', nullable: true })\n providerMetadata?: Record<string, unknown> | null\n\n @Property({ name: 'is_active', type: 'boolean', default: true })\n isActive: boolean = true\n\n @Property({ name: 'last_used_at', type: Date, nullable: true })\n lastUsedAt?: Date | null\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n\n @Property({ name: 'updated_at', type: Date, onCreate: () => new Date(), onUpdate: () => new Date() })\n updatedAt: Date = new Date()\n\n @Property({ name: 'deleted_at', type: Date, nullable: true })\n deletedAt?: Date | null\n}\n\n@Entity({ tableName: 'mfa_recovery_codes' })\n@Index({ name: 'idx_mfa_recovery_codes_user', properties: ['userId', 'isUsed'] })\nexport class MfaRecoveryCode {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'user_id', type: 'uuid' })\n userId!: string\n\n @Property({ name: 'tenant_id', type: 'uuid' })\n tenantId!: string\n\n @Property({ name: 'code_hash', type: 'text' })\n codeHash!: string\n\n @Property({ name: 'is_used', type: 'boolean', default: false })\n isUsed: boolean = false\n\n @Property({ name: 'used_at', type: Date, nullable: true })\n usedAt?: Date | null\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n}\n\n@Entity({ tableName: 'mfa_enforcement_policies' })\n@Index({ name: 'idx_mfa_enforcement_scope', properties: ['scope', 'tenantId'] })\nexport class MfaEnforcementPolicy {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ type: 'text' })\n scope!: EnforcementScope\n\n @Property({ name: 'tenant_id', type: 'uuid', nullable: true })\n tenantId?: string | null\n\n @Property({ name: 'organization_id', type: 'uuid', nullable: true })\n organizationId?: string | null\n\n @Property({ name: 'is_enforced', type: 'boolean', default: true })\n isEnforced: boolean = true\n\n @Property({ name: 'allowed_methods', type: 'jsonb', nullable: true })\n allowedMethods?: string[] | null\n\n @Property({ name: 'enforcement_deadline', type: Date, nullable: true })\n enforcementDeadline?: Date | null\n\n @Property({ name: 'enforced_by', type: 'uuid' })\n enforcedBy!: string\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n\n @Property({ name: 'updated_at', type: Date, onCreate: () => new Date(), onUpdate: () => new Date() })\n updatedAt: Date = new Date()\n\n @Property({ name: 'deleted_at', type: Date, nullable: true })\n deletedAt?: Date | null\n}\n\n@Entity({ tableName: 'sudo_challenge_configs' })\n@Index({ name: 'idx_sudo_configs_target', properties: ['targetIdentifier'] })\nexport class SudoChallengeConfig {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'tenant_id', type: 'uuid', nullable: true })\n tenantId?: string | null\n\n @Property({ name: 'organization_id', type: 'uuid', nullable: true })\n organizationId?: string | null\n\n @Property({ name: 'label', type: 'text', nullable: true })\n label: string | null = null\n\n @Property({ name: 'target_identifier', type: 'text' })\n targetIdentifier!: string\n\n @Property({ name: 'is_enabled', type: 'boolean', default: true })\n isEnabled: boolean = true\n\n @Property({ name: 'is_developer_default', type: 'boolean', default: false })\n isDeveloperDefault: boolean = false\n\n @Property({ name: 'ttl_seconds', type: 'integer', default: 300 })\n ttlSeconds: number = 300\n\n @Property({ name: 'challenge_method', type: 'text', default: 'auto' })\n challengeMethod: ChallengeMethod = ChallengeMethod.AUTO\n\n @Property({ name: 'configured_by', type: 'uuid', nullable: true })\n configuredBy?: string | null\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n\n @Property({ name: 'updated_at', type: Date, onCreate: () => new Date(), onUpdate: () => new Date() })\n updatedAt: Date = new Date()\n\n @Property({ name: 'deleted_at', type: Date, nullable: true })\n deletedAt?: Date | null\n}\n\n@Entity({ tableName: 'sudo_sessions' })\n@Index({ name: 'idx_sudo_sessions_token', properties: ['sessionToken', 'expiresAt'] })\nexport class SudoSession {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'user_id', type: 'uuid' })\n userId!: string\n\n @Property({ name: 'tenant_id', type: 'uuid' })\n tenantId!: string\n\n @Property({ name: 'scope_tenant_id', type: 'uuid', nullable: true })\n scopeTenantId?: string | null\n\n @Property({ name: 'scope_organization_id', type: 'uuid', nullable: true })\n scopeOrganizationId?: string | null\n\n @Property({ name: 'target_identifier', type: 'text', nullable: true })\n targetIdentifier?: string | null\n\n @Property({ name: 'sudo_config_id', type: 'uuid', nullable: true })\n sudoConfigId?: string | null\n\n @Property({ name: 'sudo_config_updated_at', type: Date, nullable: true })\n sudoConfigUpdatedAt?: Date | null\n\n @Property({ name: 'session_token', type: 'text' })\n sessionToken!: string\n\n @Property({ name: 'challenge_method', type: 'text' })\n challengeMethod!: string\n\n @Property({ name: 'expires_at', type: Date })\n expiresAt!: Date\n\n @Property({ name: 'verified_at', type: Date, nullable: true })\n verifiedAt?: Date | null\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n}\n\n@Entity({ tableName: 'mfa_challenges' })\n@Index({ name: 'idx_mfa_challenges_lookup', properties: ['id', 'expiresAt'] })\nexport class MfaChallenge {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'user_id', type: 'uuid' })\n userId!: string\n\n @Property({ name: 'tenant_id', type: 'uuid' })\n tenantId!: string\n\n @Property({ name: 'otp_code_hash', type: 'text', nullable: true })\n otpCodeHash?: string | null\n\n @Property({ name: 'method_type', type: 'text', nullable: true })\n methodType?: string | null\n\n @Property({ name: 'method_id', type: 'uuid', nullable: true })\n methodId?: string | null\n\n @Property({ name: 'provider_challenge', type: 'jsonb', nullable: true })\n providerChallenge?: Record<string, unknown> | null\n\n @Property({ type: 'integer', default: 0 })\n attempts: number = 0\n\n @Property({ name: 'expires_at', type: Date })\n expiresAt!: Date\n\n @Property({ name: 'verified_at', type: Date, nullable: true })\n verifiedAt?: Date | null\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { Entity, Index, PrimaryKey, Property } from '@mikro-orm/decorators/legacy'\nimport {\n ChallengeMethod,\n EnforcementScope,\n MfaMethodType,\n SudoChallengeMethodUsed,\n} from './constants'\n\nexport {\n ChallengeMethod,\n EnforcementScope,\n MfaMethodType,\n SudoChallengeMethodUsed,\n} from './constants'\n\n@Entity({ tableName: 'user_mfa_methods' })\n@Index({ name: 'idx_user_mfa_methods_user_type', properties: ['userId', 'type', 'isActive'] })\n@Index({ name: 'idx_user_mfa_methods_tenant', properties: ['tenantId'] })\nexport class UserMfaMethod {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'user_id', type: 'uuid' })\n userId!: string\n\n @Property({ name: 'tenant_id', type: 'uuid' })\n tenantId!: string\n\n @Property({ name: 'organization_id', type: 'uuid', nullable: true })\n organizationId?: string | null\n\n @Property({ type: 'text' })\n type!: string\n\n @Property({ type: 'text', nullable: true })\n label?: string | null\n\n @Property({ type: 'text', nullable: true })\n secret?: string | null\n\n @Property({ name: 'provider_metadata', type: 'jsonb', nullable: true })\n providerMetadata?: Record<string, unknown> | null\n\n @Property({ name: 'is_active', type: 'boolean', default: true })\n isActive: boolean = true\n\n @Property({ name: 'last_used_at', type: Date, nullable: true })\n lastUsedAt?: Date | null\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n\n @Property({ name: 'updated_at', type: Date, onCreate: () => new Date(), onUpdate: () => new Date() })\n updatedAt: Date = new Date()\n\n @Property({ name: 'deleted_at', type: Date, nullable: true })\n deletedAt?: Date | null\n}\n\n@Entity({ tableName: 'mfa_recovery_codes' })\n@Index({ name: 'idx_mfa_recovery_codes_user', properties: ['userId', 'isUsed'] })\nexport class MfaRecoveryCode {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'user_id', type: 'uuid' })\n userId!: string\n\n @Property({ name: 'tenant_id', type: 'uuid' })\n tenantId!: string\n\n @Property({ name: 'code_hash', type: 'text' })\n codeHash!: string\n\n @Property({ name: 'is_used', type: 'boolean', default: false })\n isUsed: boolean = false\n\n @Property({ name: 'used_at', type: Date, nullable: true })\n usedAt?: Date | null\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n}\n\n@Entity({ tableName: 'mfa_enforcement_policies' })\n@Index({ name: 'idx_mfa_enforcement_scope', properties: ['scope', 'tenantId'] })\nexport class MfaEnforcementPolicy {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ type: 'text' })\n scope!: EnforcementScope\n\n @Property({ name: 'tenant_id', type: 'uuid', nullable: true })\n tenantId?: string | null\n\n @Property({ name: 'organization_id', type: 'uuid', nullable: true })\n organizationId?: string | null\n\n @Property({ name: 'is_enforced', type: 'boolean', default: true })\n isEnforced: boolean = true\n\n @Property({ name: 'allowed_methods', type: 'jsonb', nullable: true })\n allowedMethods?: string[] | null\n\n @Property({ name: 'enforcement_deadline', type: Date, nullable: true })\n enforcementDeadline?: Date | null\n\n @Property({ name: 'enforced_by', type: 'uuid' })\n enforcedBy!: string\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n\n @Property({ name: 'updated_at', type: Date, onCreate: () => new Date(), onUpdate: () => new Date() })\n updatedAt: Date = new Date()\n\n @Property({ name: 'deleted_at', type: Date, nullable: true })\n deletedAt?: Date | null\n}\n\n@Entity({ tableName: 'sudo_challenge_configs' })\n@Index({ name: 'idx_sudo_configs_target', properties: ['targetIdentifier'] })\nexport class SudoChallengeConfig {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'tenant_id', type: 'uuid', nullable: true })\n tenantId?: string | null\n\n @Property({ name: 'organization_id', type: 'uuid', nullable: true })\n organizationId?: string | null\n\n @Property({ name: 'label', type: 'text', nullable: true })\n label: string | null = null\n\n @Property({ name: 'target_identifier', type: 'text' })\n targetIdentifier!: string\n\n @Property({ name: 'is_enabled', type: 'boolean', default: true })\n isEnabled: boolean = true\n\n @Property({ name: 'is_developer_default', type: 'boolean', default: false })\n isDeveloperDefault: boolean = false\n\n @Property({ name: 'ttl_seconds', type: 'integer', default: 300 })\n ttlSeconds: number = 300\n\n @Property({ name: 'challenge_method', type: 'text', default: 'auto' })\n challengeMethod: ChallengeMethod = ChallengeMethod.AUTO\n\n @Property({ name: 'configured_by', type: 'uuid', nullable: true })\n configuredBy?: string | null\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n\n @Property({ name: 'updated_at', type: Date, onCreate: () => new Date(), onUpdate: () => new Date() })\n updatedAt: Date = new Date()\n\n @Property({ name: 'deleted_at', type: Date, nullable: true })\n deletedAt?: Date | null\n}\n\n@Entity({ tableName: 'sudo_sessions' })\n@Index({ name: 'idx_sudo_sessions_token', properties: ['sessionToken', 'expiresAt'] })\nexport class SudoSession {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'user_id', type: 'uuid' })\n userId!: string\n\n @Property({ name: 'tenant_id', type: 'uuid' })\n tenantId!: string\n\n @Property({ name: 'session_token', type: 'text' })\n sessionToken!: string\n\n @Property({ name: 'challenge_method', type: 'text' })\n challengeMethod!: string\n\n @Property({ name: 'expires_at', type: Date })\n expiresAt!: Date\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n}\n\n@Entity({ tableName: 'mfa_challenges' })\n@Index({ name: 'idx_mfa_challenges_lookup', properties: ['id', 'expiresAt'] })\nexport class MfaChallenge {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'user_id', type: 'uuid' })\n userId!: string\n\n @Property({ name: 'tenant_id', type: 'uuid' })\n tenantId!: string\n\n @Property({ name: 'otp_code_hash', type: 'text', nullable: true })\n otpCodeHash?: string | null\n\n @Property({ name: 'method_type', type: 'text', nullable: true })\n methodType?: string | null\n\n @Property({ name: 'method_id', type: 'uuid', nullable: true })\n methodId?: string | null\n\n @Property({ name: 'provider_challenge', type: 'jsonb', nullable: true })\n providerChallenge?: Record<string, unknown> | null\n\n @Property({ type: 'integer', default: 0 })\n attempts: number = 0\n\n @Property({ name: 'expires_at', type: Date })\n expiresAt!: Date\n\n @Property({ name: 'verified_at', type: Date, nullable: true })\n verifiedAt?: Date | null\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["ChallengeMethod", "EnforcementScope", "MfaMethodType", "SudoChallengeMethodUsed"]
7
7
  }
@@ -1,5 +1,3 @@
1
- import "./lib/mfaCompletionRoutes.js";
2
- import { registerCanonicalMfaCompletionRoutes } from "./lib/mfaCompletionRoutes.js";
3
1
  const metadata = {
4
2
  id: "security",
5
3
  version: "0.1.0",
@@ -22,7 +20,6 @@ export {
22
20
  features,
23
21
  isSudoRequiredError,
24
22
  metadata,
25
- registerCanonicalMfaCompletionRoutes,
26
23
  requireSudo,
27
24
  useSudoChallenge,
28
25
  withSudoProtection
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/modules/security/index.ts"],
4
- "sourcesContent": ["import './lib/mfaCompletionRoutes'\n\nexport { registerCanonicalMfaCompletionRoutes } from './lib/mfaCompletionRoutes'\n\nexport const metadata = {\n id: 'security',\n version: '0.1.0',\n enterprise: true,\n title: 'Security',\n} as const\n\nexport { features } from './acl'\nexport {\n ChallengeMethod,\n SudoChallengeMethodUsed,\n} from './data/constants'\nexport type {\n MfaProviderComponents,\n MfaProviderInterface,\n MfaProviderSetup,\n MfaProviderUser,\n MfaSetupComponentProps,\n MfaVerifyComponentProps,\n} from './lib/mfa-provider-interface'\nexport type {\n SecurityMfaProviderEntry,\n SecuritySudoTarget,\n SecuritySudoTargetEntry,\n} from './lib/module-security-registry'\nexport { requireSudo, SudoRequiredError, isSudoRequiredError } from './lib/sudo-middleware'\nexport { useSudoChallenge } from './components/hooks/useSudoChallenge'\nexport { SudoProvider, withSudoProtection } from './components/SudoProvider'\n"],
5
- "mappings": "AAAA,OAAO;AAEP,SAAS,4CAA4C;AAE9C,MAAM,WAAW;AAAA,EACtB,IAAI;AAAA,EACJ,SAAS;AAAA,EACT,YAAY;AAAA,EACZ,OAAO;AACT;AAEA,SAAS,gBAAgB;AACzB;AAAA,EACE;AAAA,EACA;AAAA,OACK;AAcP,SAAS,aAAa,mBAAmB,2BAA2B;AACpE,SAAS,wBAAwB;AACjC,SAAS,cAAc,0BAA0B;",
4
+ "sourcesContent": ["export const metadata = {\n id: 'security',\n version: '0.1.0',\n enterprise: true,\n title: 'Security',\n} as const\n\nexport { features } from './acl'\nexport {\n ChallengeMethod,\n SudoChallengeMethodUsed,\n} from './data/constants'\nexport type {\n MfaProviderComponents,\n MfaProviderInterface,\n MfaProviderSetup,\n MfaProviderUser,\n MfaSetupComponentProps,\n MfaVerifyComponentProps,\n} from './lib/mfa-provider-interface'\nexport type {\n SecurityMfaProviderEntry,\n SecuritySudoTarget,\n SecuritySudoTargetEntry,\n} from './lib/module-security-registry'\nexport { requireSudo, SudoRequiredError, isSudoRequiredError } from './lib/sudo-middleware'\nexport { useSudoChallenge } from './components/hooks/useSudoChallenge'\nexport { SudoProvider, withSudoProtection } from './components/SudoProvider'\n"],
5
+ "mappings": "AAAO,MAAM,WAAW;AAAA,EACtB,IAAI;AAAA,EACJ,SAAS;AAAA,EACT,YAAY;AAAA,EACZ,OAAO;AACT;AAEA,SAAS,gBAAgB;AACzB;AAAA,EACE;AAAA,EACA;AAAA,OACK;AAcP,SAAS,aAAa,mBAAmB,2BAA2B;AACpE,SAAS,wBAAwB;AACjC,SAAS,cAAc,0BAA0B;",
6
6
  "names": []
7
7
  }
@@ -1,6 +1,6 @@
1
1
  import { createLogger } from "@open-mercato/shared/lib/logger";
2
2
  import { isEnforcementDeadlineOverdue } from "../services/MfaEnforcementService.js";
3
- import { emitMfaEmergencyBypassActiveWarning, readSecurityModuleConfig } from "./security-config.js";
3
+ import { readSecurityModuleConfig } from "./security-config.js";
4
4
  const MFA_ENROLLMENT_PATH = "/backend/profile/security/mfa";
5
5
  const logger = createLogger("security").child({ component: "mfa-enforcement-redirect" });
6
6
  function resolveDeadlineRedirectState(deadline) {
@@ -46,18 +46,10 @@ async function resolveMfaEnrollmentRedirect(args) {
46
46
  if (!auth || typeof auth.sub !== "string" || auth.sub.length === 0) return null;
47
47
  if (auth.mfa_pending === true) return null;
48
48
  if (isExemptPath(pathname)) return null;
49
- const emergencyBypass = readSecurityModuleConfig().mfa.emergencyBypass;
49
+ if (readSecurityModuleConfig().mfa.emergencyBypass) return null;
50
50
  const enforcementService = resolveEnforcementService(container);
51
51
  if (!enforcementService.service) {
52
52
  if (!canFailClosed(auth)) return null;
53
- if (emergencyBypass) {
54
- emitMfaEmergencyBypassActiveWarning("mfa-enrollment-redirect bypassed", {
55
- userId: auth.sub,
56
- pathname,
57
- reason: "enforcement-service-unavailable"
58
- });
59
- return null;
60
- }
61
53
  logger.error("Unable to resolve MFA enforcement service; redirecting to enrollment", {
62
54
  err: enforcementService.error
63
55
  });
@@ -68,27 +60,9 @@ async function resolveMfaEnrollmentRedirect(args) {
68
60
  if (!compliance.enforced || compliance.compliant) return null;
69
61
  const deadlineState = resolveDeadlineRedirectState(compliance.deadline);
70
62
  if (!deadlineState.shouldRedirect) return null;
71
- if (emergencyBypass) {
72
- emitMfaEmergencyBypassActiveWarning("mfa-enrollment-redirect bypassed", {
73
- userId: auth.sub,
74
- pathname,
75
- enforced: compliance.enforced,
76
- compliant: compliance.compliant,
77
- overdue: deadlineState.overdue
78
- });
79
- return null;
80
- }
81
63
  return createEnrollmentRedirect(pathname, deadlineState.overdue);
82
64
  } catch (error) {
83
65
  if (canFailClosed(auth)) {
84
- if (emergencyBypass) {
85
- emitMfaEmergencyBypassActiveWarning("mfa-enrollment-redirect bypassed", {
86
- userId: auth.sub,
87
- pathname,
88
- reason: "compliance-check-failed"
89
- });
90
- return null;
91
- }
92
66
  logger.error("Unable to verify MFA enforcement compliance; redirecting to enrollment", { err: error });
93
67
  return createEnrollmentRedirect(pathname);
94
68
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/security/lib/enforcement-redirect.ts"],
4
- "sourcesContent": ["import type { AuthContext } from '@open-mercato/shared/lib/auth/server'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport { isEnforcementDeadlineOverdue } from '../services/MfaEnforcementService'\nimport { emitMfaEmergencyBypassActiveWarning, readSecurityModuleConfig } from './security-config'\n\nconst MFA_ENROLLMENT_PATH = '/backend/profile/security/mfa'\nconst logger = createLogger('security').child({ component: 'mfa-enforcement-redirect' })\n\ntype MfaComplianceResult = {\n compliant: boolean\n enforced: boolean\n deadline?: Date\n}\n\ntype MfaEnforcementServiceLike = {\n checkUserCompliance: (userId: string) => Promise<MfaComplianceResult>\n}\n\ntype ServiceContainerLike = {\n resolve: (name: string) => unknown\n}\n\ntype EnforcementServiceResolution = {\n error?: unknown\n service: MfaEnforcementServiceLike | null\n}\n\nfunction resolveDeadlineRedirectState(deadline?: Date): {\n overdue: boolean\n shouldRedirect: boolean\n} {\n if (!(deadline instanceof Date) || Number.isNaN(deadline.getTime())) {\n return { overdue: false, shouldRedirect: true }\n }\n\n const overdue = isEnforcementDeadlineOverdue(deadline)\n return { overdue, shouldRedirect: overdue }\n}\n\nfunction isExemptPath(pathname: string): boolean {\n return pathname.startsWith('/backend/profile/security')\n}\n\nfunction createEnrollmentRedirect(pathname: string, overdue = false): string {\n const searchParams = new URLSearchParams({\n redirect: pathname,\n reason: 'mfa_enrollment_required',\n })\n if (overdue) {\n searchParams.set('overdue', '1')\n }\n return `${MFA_ENROLLMENT_PATH}?${searchParams.toString()}`\n}\n\nfunction canFailClosed(auth: NonNullable<AuthContext>): boolean {\n return typeof auth.tenantId === 'string' && auth.tenantId.length > 0\n}\n\nfunction resolveEnforcementService(\n container: ServiceContainerLike,\n): EnforcementServiceResolution {\n try {\n const resolved = container.resolve('mfaEnforcementService')\n if (\n !resolved\n || typeof resolved !== 'object'\n || typeof (resolved as { checkUserCompliance?: unknown }).checkUserCompliance !== 'function'\n ) {\n const receivedShape = resolved === null\n ? 'null'\n : Array.isArray(resolved)\n ? 'array'\n : typeof resolved === 'object'\n ? `object with keys: ${Object.keys(resolved).sort((left, right) => left.localeCompare(right)).join(', ') || '(none)'}`\n : typeof resolved\n return {\n error: new TypeError(`[internal] Malformed MFA enforcement service (${receivedShape}); expected checkUserCompliance()`),\n service: null,\n }\n }\n return { service: resolved as MfaEnforcementServiceLike }\n } catch (error) {\n return { error, service: null }\n }\n}\n\nexport async function resolveMfaEnrollmentRedirect(args: {\n auth: AuthContext\n pathname: string\n container: ServiceContainerLike\n}): Promise<string | null> {\n const { auth, pathname, container } = args\n if (!auth || typeof auth.sub !== 'string' || auth.sub.length === 0) return null\n if (auth.mfa_pending === true) return null\n if (isExemptPath(pathname)) return null\n\n const emergencyBypass = readSecurityModuleConfig().mfa.emergencyBypass\n\n const enforcementService = resolveEnforcementService(container)\n if (!enforcementService.service) {\n if (!canFailClosed(auth)) return null\n if (emergencyBypass) {\n emitMfaEmergencyBypassActiveWarning('mfa-enrollment-redirect bypassed', {\n userId: auth.sub,\n pathname,\n reason: 'enforcement-service-unavailable',\n })\n return null\n }\n logger.error('Unable to resolve MFA enforcement service; redirecting to enrollment', {\n err: enforcementService.error,\n })\n return createEnrollmentRedirect(pathname)\n }\n\n try {\n const compliance = await enforcementService.service.checkUserCompliance(auth.sub)\n if (!compliance.enforced || compliance.compliant) return null\n\n const deadlineState = resolveDeadlineRedirectState(compliance.deadline)\n if (!deadlineState.shouldRedirect) return null\n\n if (emergencyBypass) {\n emitMfaEmergencyBypassActiveWarning('mfa-enrollment-redirect bypassed', {\n userId: auth.sub,\n pathname,\n enforced: compliance.enforced,\n compliant: compliance.compliant,\n overdue: deadlineState.overdue,\n })\n return null\n }\n\n return createEnrollmentRedirect(pathname, deadlineState.overdue)\n } catch (error) {\n if (canFailClosed(auth)) {\n if (emergencyBypass) {\n emitMfaEmergencyBypassActiveWarning('mfa-enrollment-redirect bypassed', {\n userId: auth.sub,\n pathname,\n reason: 'compliance-check-failed',\n })\n return null\n }\n logger.error('Unable to verify MFA enforcement compliance; redirecting to enrollment', { err: error })\n return createEnrollmentRedirect(pathname)\n }\n return null\n }\n}\n\nexport default resolveMfaEnrollmentRedirect\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { AuthContext } from '@open-mercato/shared/lib/auth/server'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport { isEnforcementDeadlineOverdue } from '../services/MfaEnforcementService'\nimport { readSecurityModuleConfig } from './security-config'\n\nconst MFA_ENROLLMENT_PATH = '/backend/profile/security/mfa'\nconst logger = createLogger('security').child({ component: 'mfa-enforcement-redirect' })\n\ntype MfaComplianceResult = {\n compliant: boolean\n enforced: boolean\n deadline?: Date\n}\n\ntype MfaEnforcementServiceLike = {\n checkUserCompliance: (userId: string) => Promise<MfaComplianceResult>\n}\n\ntype ServiceContainerLike = {\n resolve: (name: string) => unknown\n}\n\ntype EnforcementServiceResolution = {\n error?: unknown\n service: MfaEnforcementServiceLike | null\n}\n\nfunction resolveDeadlineRedirectState(deadline?: Date): {\n overdue: boolean\n shouldRedirect: boolean\n} {\n if (!(deadline instanceof Date) || Number.isNaN(deadline.getTime())) {\n return { overdue: false, shouldRedirect: true }\n }\n\n const overdue = isEnforcementDeadlineOverdue(deadline)\n return { overdue, shouldRedirect: overdue }\n}\n\nfunction isExemptPath(pathname: string): boolean {\n return pathname.startsWith('/backend/profile/security')\n}\n\nfunction createEnrollmentRedirect(pathname: string, overdue = false): string {\n const searchParams = new URLSearchParams({\n redirect: pathname,\n reason: 'mfa_enrollment_required',\n })\n if (overdue) {\n searchParams.set('overdue', '1')\n }\n return `${MFA_ENROLLMENT_PATH}?${searchParams.toString()}`\n}\n\nfunction canFailClosed(auth: NonNullable<AuthContext>): boolean {\n return typeof auth.tenantId === 'string' && auth.tenantId.length > 0\n}\n\nfunction resolveEnforcementService(\n container: ServiceContainerLike,\n): EnforcementServiceResolution {\n try {\n const resolved = container.resolve('mfaEnforcementService')\n if (\n !resolved\n || typeof resolved !== 'object'\n || typeof (resolved as { checkUserCompliance?: unknown }).checkUserCompliance !== 'function'\n ) {\n const receivedShape = resolved === null\n ? 'null'\n : Array.isArray(resolved)\n ? 'array'\n : typeof resolved === 'object'\n ? `object with keys: ${Object.keys(resolved).sort((left, right) => left.localeCompare(right)).join(', ') || '(none)'}`\n : typeof resolved\n return {\n error: new TypeError(`[internal] Malformed MFA enforcement service (${receivedShape}); expected checkUserCompliance()`),\n service: null,\n }\n }\n return { service: resolved as MfaEnforcementServiceLike }\n } catch (error) {\n return { error, service: null }\n }\n}\n\nexport async function resolveMfaEnrollmentRedirect(args: {\n auth: AuthContext\n pathname: string\n container: ServiceContainerLike\n}): Promise<string | null> {\n const { auth, pathname, container } = args\n if (!auth || typeof auth.sub !== 'string' || auth.sub.length === 0) return null\n if (auth.mfa_pending === true) return null\n if (isExemptPath(pathname)) return null\n if (readSecurityModuleConfig().mfa.emergencyBypass) return null\n\n const enforcementService = resolveEnforcementService(container)\n if (!enforcementService.service) {\n if (!canFailClosed(auth)) return null\n logger.error('Unable to resolve MFA enforcement service; redirecting to enrollment', {\n err: enforcementService.error,\n })\n return createEnrollmentRedirect(pathname)\n }\n\n try {\n const compliance = await enforcementService.service.checkUserCompliance(auth.sub)\n if (!compliance.enforced || compliance.compliant) return null\n\n const deadlineState = resolveDeadlineRedirectState(compliance.deadline)\n if (!deadlineState.shouldRedirect) return null\n\n return createEnrollmentRedirect(pathname, deadlineState.overdue)\n } catch (error) {\n if (canFailClosed(auth)) {\n logger.error('Unable to verify MFA enforcement compliance; redirecting to enrollment', { err: error })\n return createEnrollmentRedirect(pathname)\n }\n return null\n }\n}\n\nexport default resolveMfaEnrollmentRedirect\n"],
5
+ "mappings": "AACA,SAAS,oBAAoB;AAC7B,SAAS,oCAAoC;AAC7C,SAAS,gCAAgC;AAEzC,MAAM,sBAAsB;AAC5B,MAAM,SAAS,aAAa,UAAU,EAAE,MAAM,EAAE,WAAW,2BAA2B,CAAC;AAqBvF,SAAS,6BAA6B,UAGpC;AACA,MAAI,EAAE,oBAAoB,SAAS,OAAO,MAAM,SAAS,QAAQ,CAAC,GAAG;AACnE,WAAO,EAAE,SAAS,OAAO,gBAAgB,KAAK;AAAA,EAChD;AAEA,QAAM,UAAU,6BAA6B,QAAQ;AACrD,SAAO,EAAE,SAAS,gBAAgB,QAAQ;AAC5C;AAEA,SAAS,aAAa,UAA2B;AAC/C,SAAO,SAAS,WAAW,2BAA2B;AACxD;AAEA,SAAS,yBAAyB,UAAkB,UAAU,OAAe;AAC3E,QAAM,eAAe,IAAI,gBAAgB;AAAA,IACvC,UAAU;AAAA,IACV,QAAQ;AAAA,EACV,CAAC;AACD,MAAI,SAAS;AACX,iBAAa,IAAI,WAAW,GAAG;AAAA,EACjC;AACA,SAAO,GAAG,mBAAmB,IAAI,aAAa,SAAS,CAAC;AAC1D;AAEA,SAAS,cAAc,MAAyC;AAC9D,SAAO,OAAO,KAAK,aAAa,YAAY,KAAK,SAAS,SAAS;AACrE;AAEA,SAAS,0BACP,WAC8B;AAC9B,MAAI;AACF,UAAM,WAAW,UAAU,QAAQ,uBAAuB;AAC1D,QACE,CAAC,YACE,OAAO,aAAa,YACpB,OAAQ,SAA+C,wBAAwB,YAClF;AACA,YAAM,gBAAgB,aAAa,OAC/B,SACA,MAAM,QAAQ,QAAQ,IACpB,UACA,OAAO,aAAa,WAClB,qBAAqB,OAAO,KAAK,QAAQ,EAAE,KAAK,CAAC,MAAM,UAAU,KAAK,cAAc,KAAK,CAAC,EAAE,KAAK,IAAI,KAAK,QAAQ,KAClH,OAAO;AACf,aAAO;AAAA,QACL,OAAO,IAAI,UAAU,iDAAiD,aAAa,mCAAmC;AAAA,QACtH,SAAS;AAAA,MACX;AAAA,IACF;AACA,WAAO,EAAE,SAAS,SAAsC;AAAA,EAC1D,SAAS,OAAO;AACd,WAAO,EAAE,OAAO,SAAS,KAAK;AAAA,EAChC;AACF;AAEA,eAAsB,6BAA6B,MAIxB;AACzB,QAAM,EAAE,MAAM,UAAU,UAAU,IAAI;AACtC,MAAI,CAAC,QAAQ,OAAO,KAAK,QAAQ,YAAY,KAAK,IAAI,WAAW,EAAG,QAAO;AAC3E,MAAI,KAAK,gBAAgB,KAAM,QAAO;AACtC,MAAI,aAAa,QAAQ,EAAG,QAAO;AACnC,MAAI,yBAAyB,EAAE,IAAI,gBAAiB,QAAO;AAE3D,QAAM,qBAAqB,0BAA0B,SAAS;AAC9D,MAAI,CAAC,mBAAmB,SAAS;AAC/B,QAAI,CAAC,cAAc,IAAI,EAAG,QAAO;AACjC,WAAO,MAAM,wEAAwE;AAAA,MACnF,KAAK,mBAAmB;AAAA,IAC1B,CAAC;AACD,WAAO,yBAAyB,QAAQ;AAAA,EAC1C;AAEA,MAAI;AACF,UAAM,aAAa,MAAM,mBAAmB,QAAQ,oBAAoB,KAAK,GAAG;AAChF,QAAI,CAAC,WAAW,YAAY,WAAW,UAAW,QAAO;AAEzD,UAAM,gBAAgB,6BAA6B,WAAW,QAAQ;AACtE,QAAI,CAAC,cAAc,eAAgB,QAAO;AAE1C,WAAO,yBAAyB,UAAU,cAAc,OAAO;AAAA,EACjE,SAAS,OAAO;AACd,QAAI,cAAc,IAAI,GAAG;AACvB,aAAO,MAAM,0EAA0E,EAAE,KAAK,MAAM,CAAC;AACrG,aAAO,yBAAyB,QAAQ;AAAA,IAC1C;AACA,WAAO;AAAA,EACT;AACF;AAEA,IAAO,+BAAQ;",
6
6
  "names": []
7
7
  }
@@ -1,13 +1,10 @@
1
1
  import { parseBooleanWithDefault } from "@open-mercato/shared/lib/boolean";
2
- import { createLogger } from "@open-mercato/shared/lib/logger";
3
2
  const TOTP_PERIOD_SECONDS = 30;
4
3
  const TOTP_DIGITS = 6;
5
4
  const DEFAULT_SETUP_TTL_MS = 10 * 60 * 1e3;
6
5
  const DEFAULT_WEBAUTHN_CHALLENGE_TTL_MS = 5 * 60 * 1e3;
7
6
  const DEFAULT_SUDO_PENDING_CHALLENGE_TTL_MS = 10 * 60 * 1e3;
8
7
  const MIN_SUDO_TTL_SECONDS = 30;
9
- const securityBypassLogger = createLogger("security").child({ component: "mfa-emergency-bypass" });
10
- let startupBypassWarningEmitted = false;
11
8
  const defaultSecurityModuleConfig = {
12
9
  totp: {
13
10
  issuer: "Open Mercato",
@@ -113,27 +110,6 @@ function resolveSecurityModuleConfigForRequest(baseConfig, request, env = proces
113
110
  }
114
111
  };
115
112
  }
116
- function emitMfaEmergencyBypassActiveWarning(context, extra) {
117
- securityBypassLogger.warn("MFA emergency bypass is active", {
118
- emergencyBypass: true,
119
- context,
120
- ...extra
121
- });
122
- }
123
- function emitMfaEmergencyBypassStartupWarningIfNeeded(env = process.env) {
124
- if (startupBypassWarningEmitted) return;
125
- const bypass = parseBooleanWithDefault(
126
- env.OM_SECURITY_MFA_EMERGENCY_BYPASS,
127
- defaultSecurityModuleConfig.mfa.emergencyBypass
128
- );
129
- if (!bypass) return;
130
- startupBypassWarningEmitted = true;
131
- emitMfaEmergencyBypassActiveWarning("startup", { startup: true });
132
- }
133
- function resetMfaEmergencyBypassStartupWarningForTests() {
134
- startupBypassWarningEmitted = false;
135
- }
136
- emitMfaEmergencyBypassStartupWarningIfNeeded();
137
113
  function readSecuritySetupTokenSecret(env = process.env) {
138
114
  const secret = readText(env.OM_SECURITY_MFA_SETUP_SECRET) ?? readText(env.AUTH_JWT_SECRET) ?? readText(env.AUTH_SECRET) ?? readText(env.JWT_SECRET);
139
115
  if (secret) {
@@ -234,11 +210,8 @@ function clamp(value, min, max) {
234
210
  }
235
211
  export {
236
212
  defaultSecurityModuleConfig,
237
- emitMfaEmergencyBypassActiveWarning,
238
- emitMfaEmergencyBypassStartupWarningIfNeeded,
239
213
  readSecurityModuleConfig,
240
214
  readSecuritySetupTokenSecret,
241
- resetMfaEmergencyBypassStartupWarningForTests,
242
215
  resolveSecurityModuleConfigForRequest
243
216
  };
244
217
  //# sourceMappingURL=security-config.js.map