@open-mercato/enterprise 0.6.8-develop.7072.1.19c2a8bbe0 → 0.6.8-develop.7076.1.13d0af067b

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.
@@ -1,2 +1,2 @@
1
- [build:enterprise] found 334 entry points
1
+ [build:enterprise] found 336 entry points
2
2
  [build:enterprise] built successfully
@@ -2,6 +2,7 @@ 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";
5
6
  import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
6
7
  import { signJwt } from "@open-mercato/shared/lib/auth/jwt";
7
8
  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 { 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 '../../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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",
6
6
  "names": []
7
7
  }
@@ -1,3 +1,5 @@
1
+ import "./lib/mfaCompletionRoutes.js";
2
+ import { registerCanonicalMfaCompletionRoutes } from "./lib/mfaCompletionRoutes.js";
1
3
  const metadata = {
2
4
  id: "security",
3
5
  version: "0.1.0",
@@ -20,6 +22,7 @@ export {
20
22
  features,
21
23
  isSudoRequiredError,
22
24
  metadata,
25
+ registerCanonicalMfaCompletionRoutes,
23
26
  requireSudo,
24
27
  useSudoChallenge,
25
28
  withSudoProtection
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/modules/security/index.ts"],
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;",
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;",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,17 @@
1
+ import { registerMfaPendingAccessRoutes } from "@open-mercato/shared/lib/auth/mfaPendingAccess";
2
+ const CANONICAL_MFA_COMPLETION_ROUTES = [
3
+ { path: "/api/security/mfa/prepare", methods: ["POST"] },
4
+ { path: "/api/security/mfa/verify", methods: ["POST"] },
5
+ { path: "/api/security/mfa/recovery", methods: ["POST"] }
6
+ ];
7
+ let registered = false;
8
+ function registerCanonicalMfaCompletionRoutes() {
9
+ if (registered) return;
10
+ registerMfaPendingAccessRoutes(CANONICAL_MFA_COMPLETION_ROUTES);
11
+ registered = true;
12
+ }
13
+ registerCanonicalMfaCompletionRoutes();
14
+ export {
15
+ registerCanonicalMfaCompletionRoutes
16
+ };
17
+ //# sourceMappingURL=mfaCompletionRoutes.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/security/lib/mfaCompletionRoutes.ts"],
4
+ "sourcesContent": ["import { registerMfaPendingAccessRoutes } from '@open-mercato/shared/lib/auth/mfaPendingAccess'\n\nconst CANONICAL_MFA_COMPLETION_ROUTES = [\n { path: '/api/security/mfa/prepare', methods: ['POST'] },\n { path: '/api/security/mfa/verify', methods: ['POST'] },\n { path: '/api/security/mfa/recovery', methods: ['POST'] },\n]\n\nlet registered = false\n\n/**\n * Register the canonical MFA challenge-completion routes with the platform-wide pending-token\n * allowlist. The enterprise security module owns these routes, so it owns their registration:\n * shared stays generic (SPEC-ENT-007 \u00A7 Amendment 2026-08-21). Idempotent; runs once per process\n * on first import of this module \u2014 which happens at security-module bootstrap and, as a\n * belt-and-braces guarantee, on every load of the completion route modules themselves, so the\n * routes are always registered before request authentication resolves.\n */\nexport function registerCanonicalMfaCompletionRoutes(): void {\n if (registered) return\n registerMfaPendingAccessRoutes(CANONICAL_MFA_COMPLETION_ROUTES)\n registered = true\n}\n\nregisterCanonicalMfaCompletionRoutes()\n"],
5
+ "mappings": "AAAA,SAAS,sCAAsC;AAE/C,MAAM,kCAAkC;AAAA,EACtC,EAAE,MAAM,6BAA6B,SAAS,CAAC,MAAM,EAAE;AAAA,EACvD,EAAE,MAAM,4BAA4B,SAAS,CAAC,MAAM,EAAE;AAAA,EACtD,EAAE,MAAM,8BAA8B,SAAS,CAAC,MAAM,EAAE;AAC1D;AAEA,IAAI,aAAa;AAUV,SAAS,uCAA6C;AAC3D,MAAI,WAAY;AAChB,iCAA+B,+BAA+B;AAC9D,eAAa;AACf;AAEA,qCAAqC;",
6
+ "names": []
7
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-mercato/enterprise",
3
- "version": "0.6.8-develop.7072.1.19c2a8bbe0",
3
+ "version": "0.6.8-develop.7076.1.13d0af067b",
4
4
  "license": "SEE LICENSE IN LICENSE.md",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -65,8 +65,8 @@
65
65
  }
66
66
  },
67
67
  "dependencies": {
68
- "@open-mercato/core": "0.6.8-develop.7072.1.19c2a8bbe0",
69
- "@open-mercato/ui": "0.6.8-develop.7072.1.19c2a8bbe0",
68
+ "@open-mercato/core": "0.6.8-develop.7076.1.13d0af067b",
69
+ "@open-mercato/ui": "0.6.8-develop.7076.1.13d0af067b",
70
70
  "@simplewebauthn/browser": "^13.3.0",
71
71
  "@simplewebauthn/server": "^13.3.2",
72
72
  "@simplewebauthn/types": "^12.0.0",
@@ -76,12 +76,12 @@
76
76
  "qrcode": "^1.5.4"
77
77
  },
78
78
  "peerDependencies": {
79
- "@open-mercato/shared": "0.6.8-develop.7072.1.19c2a8bbe0",
79
+ "@open-mercato/shared": "0.6.8-develop.7076.1.13d0af067b",
80
80
  "react": "^19.0.0",
81
81
  "react-dom": "^19.0.0"
82
82
  },
83
83
  "devDependencies": {
84
- "@open-mercato/shared": "0.6.8-develop.7072.1.19c2a8bbe0",
84
+ "@open-mercato/shared": "0.6.8-develop.7076.1.13d0af067b",
85
85
  "@types/jest": "^30.0.0",
86
86
  "@types/react": "^19.2.17",
87
87
  "@types/react-dom": "^19.2.3",
@@ -3,6 +3,7 @@ import { z } from 'zod'
3
3
  import type { CommandRuntimeContext } from '@open-mercato/shared/lib/commands'
4
4
  import { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'
5
5
  import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
6
+ import '../../lib/mfaCompletionRoutes'
6
7
  import { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'
7
8
  import { signJwt } from '@open-mercato/shared/lib/auth/jwt'
8
9
  import type { MfaService, MfaServiceError } from '../../services/MfaService'
@@ -1,3 +1,7 @@
1
+ import './lib/mfaCompletionRoutes'
2
+
3
+ export { registerCanonicalMfaCompletionRoutes } from './lib/mfaCompletionRoutes'
4
+
1
5
  export const metadata = {
2
6
  id: 'security',
3
7
  version: '0.1.0',
@@ -0,0 +1,25 @@
1
+ import { registerMfaPendingAccessRoutes } from '@open-mercato/shared/lib/auth/mfaPendingAccess'
2
+
3
+ const CANONICAL_MFA_COMPLETION_ROUTES = [
4
+ { path: '/api/security/mfa/prepare', methods: ['POST'] },
5
+ { path: '/api/security/mfa/verify', methods: ['POST'] },
6
+ { path: '/api/security/mfa/recovery', methods: ['POST'] },
7
+ ]
8
+
9
+ let registered = false
10
+
11
+ /**
12
+ * Register the canonical MFA challenge-completion routes with the platform-wide pending-token
13
+ * allowlist. The enterprise security module owns these routes, so it owns their registration:
14
+ * shared stays generic (SPEC-ENT-007 § Amendment 2026-08-21). Idempotent; runs once per process
15
+ * on first import of this module — which happens at security-module bootstrap and, as a
16
+ * belt-and-braces guarantee, on every load of the completion route modules themselves, so the
17
+ * routes are always registered before request authentication resolves.
18
+ */
19
+ export function registerCanonicalMfaCompletionRoutes(): void {
20
+ if (registered) return
21
+ registerMfaPendingAccessRoutes(CANONICAL_MFA_COMPLETION_ROUTES)
22
+ registered = true
23
+ }
24
+
25
+ registerCanonicalMfaCompletionRoutes()