@open-mercato/enterprise 0.7.0 → 0.7.1-develop.7103.1.41ff100d93

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 (33) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/modules/security/api/interceptors.js +5 -2
  3. package/dist/modules/security/api/interceptors.js.map +2 -2
  4. package/dist/modules/security/api/mfa/_shared.js +1 -0
  5. package/dist/modules/security/api/mfa/_shared.js.map +2 -2
  6. package/dist/modules/security/data/entities.js +18 -0
  7. package/dist/modules/security/data/entities.js.map +2 -2
  8. package/dist/modules/security/index.js +3 -0
  9. package/dist/modules/security/index.js.map +2 -2
  10. package/dist/modules/security/lib/enforcement-redirect.js +28 -2
  11. package/dist/modules/security/lib/enforcement-redirect.js.map +2 -2
  12. package/dist/modules/security/lib/mfaCompletionRoutes.js +17 -0
  13. package/dist/modules/security/lib/mfaCompletionRoutes.js.map +7 -0
  14. package/dist/modules/security/lib/security-config.js +27 -0
  15. package/dist/modules/security/lib/security-config.js.map +2 -2
  16. package/dist/modules/security/migrations/Migration20260811143757_security.js +13 -0
  17. package/dist/modules/security/migrations/Migration20260811143757_security.js.map +7 -0
  18. package/dist/modules/security/services/SudoChallengeService.js +130 -41
  19. package/dist/modules/security/services/SudoChallengeService.js.map +2 -2
  20. package/dist/modules/sso/backend/page.js +1 -0
  21. package/dist/modules/sso/backend/page.js.map +2 -2
  22. package/package.json +7 -6
  23. package/src/modules/security/api/interceptors.ts +6 -2
  24. package/src/modules/security/api/mfa/_shared.ts +1 -0
  25. package/src/modules/security/data/entities.ts +18 -0
  26. package/src/modules/security/index.ts +4 -0
  27. package/src/modules/security/lib/enforcement-redirect.ts +30 -2
  28. package/src/modules/security/lib/mfaCompletionRoutes.ts +25 -0
  29. package/src/modules/security/lib/security-config.ts +29 -0
  30. package/src/modules/security/migrations/.snapshot-open-mercato.json +848 -275
  31. package/src/modules/security/migrations/Migration20260811143757_security.ts +13 -0
  32. package/src/modules/security/services/SudoChallengeService.ts +170 -49
  33. package/src/modules/sso/backend/page.tsx +2 -0
@@ -1,2 +1,2 @@
1
- [build:enterprise] found 328 entry points
1
+ [build:enterprise] found 336 entry points
2
2
  [build:enterprise] built successfully
@@ -1,5 +1,5 @@
1
1
  import { signJwt, verifyJwt } from "@open-mercato/shared/lib/auth/jwt";
2
- import { readSecurityModuleConfig } from "../lib/security-config.js";
2
+ import { emitMfaEmergencyBypassActiveWarning, 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,11 +35,14 @@ 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 {};
39
38
  const claims = readClaims(response.body.token);
40
39
  if (!claims) return {};
41
40
  const mfaVerificationService = resolveMfaVerificationService(context.container);
42
41
  if (!mfaVerificationService) return {};
42
+ if (readSecurityModuleConfig().mfa.emergencyBypass) {
43
+ emitMfaEmergencyBypassActiveWarning("login MFA challenge bypassed", { userId: claims.sub });
44
+ return {};
45
+ }
43
46
  try {
44
47
  const challenge = await mfaVerificationService.createChallenge(claims.sub);
45
48
  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 { 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;",
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;",
6
6
  "names": []
7
7
  }
@@ -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
  }
@@ -212,6 +212,21 @@ __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);
215
230
  __decorateClass([
216
231
  Property({ name: "session_token", type: "text" })
217
232
  ], SudoSession.prototype, "sessionToken", 2);
@@ -221,6 +236,9 @@ __decorateClass([
221
236
  __decorateClass([
222
237
  Property({ name: "expires_at", type: Date })
223
238
  ], SudoSession.prototype, "expiresAt", 2);
239
+ __decorateClass([
240
+ Property({ name: "verified_at", type: Date, nullable: true })
241
+ ], SudoSession.prototype, "verifiedAt", 2);
224
242
  __decorateClass([
225
243
  Property({ name: "created_at", type: Date, onCreate: () => /* @__PURE__ */ new Date() })
226
244
  ], 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: '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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",
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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",
6
6
  "names": ["ChallengeMethod", "EnforcementScope", "MfaMethodType", "SudoChallengeMethodUsed"]
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
  }
@@ -1,6 +1,6 @@
1
1
  import { createLogger } from "@open-mercato/shared/lib/logger";
2
2
  import { isEnforcementDeadlineOverdue } from "../services/MfaEnforcementService.js";
3
- import { readSecurityModuleConfig } from "./security-config.js";
3
+ import { emitMfaEmergencyBypassActiveWarning, 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,10 +46,18 @@ 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
- if (readSecurityModuleConfig().mfa.emergencyBypass) return null;
49
+ const emergencyBypass = readSecurityModuleConfig().mfa.emergencyBypass;
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
+ }
53
61
  logger.error("Unable to resolve MFA enforcement service; redirecting to enrollment", {
54
62
  err: enforcementService.error
55
63
  });
@@ -60,9 +68,27 @@ async function resolveMfaEnrollmentRedirect(args) {
60
68
  if (!compliance.enforced || compliance.compliant) return null;
61
69
  const deadlineState = resolveDeadlineRedirectState(compliance.deadline);
62
70
  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
+ }
63
81
  return createEnrollmentRedirect(pathname, deadlineState.overdue);
64
82
  } catch (error) {
65
83
  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
+ }
66
92
  logger.error("Unable to verify MFA enforcement compliance; redirecting to enrollment", { err: error });
67
93
  return createEnrollmentRedirect(pathname);
68
94
  }
@@ -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 { 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;",
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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",
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
+ }
@@ -1,10 +1,13 @@
1
1
  import { parseBooleanWithDefault } from "@open-mercato/shared/lib/boolean";
2
+ import { createLogger } from "@open-mercato/shared/lib/logger";
2
3
  const TOTP_PERIOD_SECONDS = 30;
3
4
  const TOTP_DIGITS = 6;
4
5
  const DEFAULT_SETUP_TTL_MS = 10 * 60 * 1e3;
5
6
  const DEFAULT_WEBAUTHN_CHALLENGE_TTL_MS = 5 * 60 * 1e3;
6
7
  const DEFAULT_SUDO_PENDING_CHALLENGE_TTL_MS = 10 * 60 * 1e3;
7
8
  const MIN_SUDO_TTL_SECONDS = 30;
9
+ const securityBypassLogger = createLogger("security").child({ component: "mfa-emergency-bypass" });
10
+ let startupBypassWarningEmitted = false;
8
11
  const defaultSecurityModuleConfig = {
9
12
  totp: {
10
13
  issuer: "Open Mercato",
@@ -110,6 +113,27 @@ function resolveSecurityModuleConfigForRequest(baseConfig, request, env = proces
110
113
  }
111
114
  };
112
115
  }
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();
113
137
  function readSecuritySetupTokenSecret(env = process.env) {
114
138
  const secret = readText(env.OM_SECURITY_MFA_SETUP_SECRET) ?? readText(env.AUTH_JWT_SECRET) ?? readText(env.AUTH_SECRET) ?? readText(env.JWT_SECRET);
115
139
  if (secret) {
@@ -210,8 +234,11 @@ function clamp(value, min, max) {
210
234
  }
211
235
  export {
212
236
  defaultSecurityModuleConfig,
237
+ emitMfaEmergencyBypassActiveWarning,
238
+ emitMfaEmergencyBypassStartupWarningIfNeeded,
213
239
  readSecurityModuleConfig,
214
240
  readSecuritySetupTokenSecret,
241
+ resetMfaEmergencyBypassStartupWarningForTests,
215
242
  resolveSecurityModuleConfigForRequest
216
243
  };
217
244
  //# sourceMappingURL=security-config.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/security/lib/security-config.ts"],
4
- "sourcesContent": ["import { parseBooleanWithDefault } from '@open-mercato/shared/lib/boolean'\n\nconst TOTP_PERIOD_SECONDS = 30\nconst TOTP_DIGITS = 6\nconst DEFAULT_SETUP_TTL_MS = 10 * 60 * 1000\nconst DEFAULT_WEBAUTHN_CHALLENGE_TTL_MS = 5 * 60 * 1000\nconst DEFAULT_SUDO_PENDING_CHALLENGE_TTL_MS = 10 * 60 * 1000\nconst MIN_SUDO_TTL_SECONDS = 30\n\nexport type SecurityModuleConfig = {\n totp: {\n issuer: string\n window: number\n digits: number\n periodSeconds: number\n setupTtlMs: number\n }\n otpEmail: {\n expirySeconds: number\n maxAttempts: number\n setupTtlMs: number\n challengeTtlMs: number\n subject: string\n }\n mfa: {\n challengeTtlMs: number\n maxAttempts: number\n emergencyBypass: boolean\n }\n sudo: {\n minTtlSeconds: number\n defaultTtlSeconds: number\n maxTtlSeconds: number\n pendingChallengeTtlMs: number\n }\n webauthn: {\n rpName: string\n rpId: string\n expectedOrigins: string[]\n setupTtlMs: number\n challengeTtlMs: number\n }\n recoveryCodes: {\n count: number\n bcryptCost: number\n }\n}\n\nexport const defaultSecurityModuleConfig: SecurityModuleConfig = {\n totp: {\n issuer: 'Open Mercato',\n window: 1,\n digits: TOTP_DIGITS,\n periodSeconds: TOTP_PERIOD_SECONDS,\n setupTtlMs: DEFAULT_SETUP_TTL_MS,\n },\n otpEmail: {\n expirySeconds: 600,\n maxAttempts: 5,\n setupTtlMs: DEFAULT_SETUP_TTL_MS,\n challengeTtlMs: 600 * 1000,\n subject: 'Your Open Mercato verification code',\n },\n mfa: {\n challengeTtlMs: 600 * 1000,\n maxAttempts: 5,\n emergencyBypass: false,\n },\n sudo: {\n minTtlSeconds: MIN_SUDO_TTL_SECONDS,\n defaultTtlSeconds: 300,\n maxTtlSeconds: 1800,\n pendingChallengeTtlMs: DEFAULT_SUDO_PENDING_CHALLENGE_TTL_MS,\n },\n webauthn: {\n rpName: 'Open Mercato',\n rpId: 'localhost',\n expectedOrigins: ['http://localhost:3000'],\n setupTtlMs: DEFAULT_SETUP_TTL_MS,\n challengeTtlMs: DEFAULT_WEBAUTHN_CHALLENGE_TTL_MS,\n },\n recoveryCodes: {\n count: 10,\n bcryptCost: 10,\n },\n}\n\nexport function readSecurityModuleConfig(\n env: NodeJS.ProcessEnv = process.env,\n): SecurityModuleConfig {\n const otpExpirySeconds = parsePositiveInteger(env.OM_SECURITY_OTP_EXPIRY_SECONDS)\n ?? defaultSecurityModuleConfig.otpEmail.expirySeconds\n const otpMaxAttempts = parsePositiveInteger(env.OM_SECURITY_OTP_MAX_ATTEMPTS)\n ?? defaultSecurityModuleConfig.otpEmail.maxAttempts\n const sudoMaxTtlSeconds = Math.max(\n parsePositiveInteger(env.OM_SECURITY_SUDO_MAX_TTL) ?? defaultSecurityModuleConfig.sudo.maxTtlSeconds,\n defaultSecurityModuleConfig.sudo.minTtlSeconds,\n )\n const sudoDefaultTtlSeconds = clamp(\n parsePositiveInteger(env.OM_SECURITY_SUDO_DEFAULT_TTL) ?? defaultSecurityModuleConfig.sudo.defaultTtlSeconds,\n defaultSecurityModuleConfig.sudo.minTtlSeconds,\n sudoMaxTtlSeconds,\n )\n const webauthnRpId = readText(env.OM_SECURITY_WEBAUTHN_RP_ID)\n ?? readHostname(env.APP_URL)\n ?? readHostname(env.NEXT_PUBLIC_APP_URL)\n ?? defaultSecurityModuleConfig.webauthn.rpId\n\n return {\n totp: {\n ...defaultSecurityModuleConfig.totp,\n issuer: readText(env.OM_SECURITY_TOTP_ISSUER) ?? defaultSecurityModuleConfig.totp.issuer,\n window: parseNonNegativeInteger(env.OM_SECURITY_TOTP_WINDOW) ?? defaultSecurityModuleConfig.totp.window,\n },\n otpEmail: {\n ...defaultSecurityModuleConfig.otpEmail,\n expirySeconds: otpExpirySeconds,\n maxAttempts: otpMaxAttempts,\n challengeTtlMs: otpExpirySeconds * 1000,\n },\n mfa: {\n challengeTtlMs: otpExpirySeconds * 1000,\n maxAttempts: otpMaxAttempts,\n emergencyBypass: parseBooleanWithDefault(\n env.OM_SECURITY_MFA_EMERGENCY_BYPASS,\n defaultSecurityModuleConfig.mfa.emergencyBypass,\n ),\n },\n sudo: {\n ...defaultSecurityModuleConfig.sudo,\n defaultTtlSeconds: sudoDefaultTtlSeconds,\n maxTtlSeconds: sudoMaxTtlSeconds,\n },\n webauthn: {\n ...defaultSecurityModuleConfig.webauthn,\n rpName: readText(env.OM_SECURITY_WEBAUTHN_RP_NAME) ?? defaultSecurityModuleConfig.webauthn.rpName,\n rpId: webauthnRpId,\n expectedOrigins: readWebAuthnOrigins(env, webauthnRpId),\n },\n recoveryCodes: {\n ...defaultSecurityModuleConfig.recoveryCodes,\n count: parsePositiveInteger(env.OM_SECURITY_RECOVERY_CODE_COUNT)\n ?? parsePositiveInteger(env.OM_SECURITY_RECOVERY_CODES_COUNT)\n ?? defaultSecurityModuleConfig.recoveryCodes.count,\n },\n }\n}\n\nexport function resolveSecurityModuleConfigForRequest(\n baseConfig: SecurityModuleConfig,\n request?: Request,\n env: NodeJS.ProcessEnv = process.env,\n): SecurityModuleConfig {\n const explicitRpId = readText(env.OM_SECURITY_WEBAUTHN_RP_ID)\n const requestRpId = readRequestHostname(request)\n const rpId = explicitRpId ?? requestRpId ?? baseConfig.webauthn.rpId\n const expectedOrigins = readWebAuthnOrigins(env, rpId, request, baseConfig.webauthn.expectedOrigins)\n\n if (rpId === baseConfig.webauthn.rpId && areOriginsEqual(expectedOrigins, baseConfig.webauthn.expectedOrigins)) {\n return baseConfig\n }\n\n return {\n ...baseConfig,\n webauthn: {\n ...baseConfig.webauthn,\n rpId,\n expectedOrigins,\n },\n }\n}\n\nexport function readSecuritySetupTokenSecret(env: NodeJS.ProcessEnv = process.env): string {\n const secret = readText(env.OM_SECURITY_MFA_SETUP_SECRET)\n ?? readText(env.AUTH_JWT_SECRET)\n ?? readText(env.AUTH_SECRET)\n ?? readText(env.JWT_SECRET)\n\n if (secret) {\n return secret\n }\n\n throw new Error(\n 'Security MFA setup tokens require OM_SECURITY_MFA_SETUP_SECRET, AUTH_JWT_SECRET, AUTH_SECRET, or JWT_SECRET.',\n )\n}\n\nfunction readWebAuthnOrigins(\n env: NodeJS.ProcessEnv,\n rpId: string,\n request?: Request,\n fallbackOrigins?: string[],\n): string[] {\n const fromList = (env.OM_SECURITY_WEBAUTHN_ORIGINS ?? '')\n .split(',')\n .map((value) => value.trim())\n .filter((value) => value.length > 0)\n const explicitOrigin = readText(env.OM_SECURITY_WEBAUTHN_ORIGIN)\n\n if (fromList.length > 0 || explicitOrigin) {\n return [...new Set([...fromList, ...(explicitOrigin ? [explicitOrigin] : [])])]\n }\n\n const requestOrigin = readRequestOrigin(request)\n const singletons = [\n requestOrigin,\n env.APP_URL,\n env.NEXT_PUBLIC_APP_URL,\n ]\n .map((value) => readText(value))\n .filter((value): value is string => typeof value === 'string')\n\n const origins = [...new Set([...fromList, ...singletons])]\n if (origins.length > 0) {\n return origins\n }\n\n if (fallbackOrigins && fallbackOrigins.length > 0) {\n return [...fallbackOrigins]\n }\n\n if (rpId === 'localhost') {\n return [...defaultSecurityModuleConfig.webauthn.expectedOrigins]\n }\n\n return [`https://${rpId}`]\n}\n\nfunction readText(value: string | undefined): string | undefined {\n const trimmed = value?.trim()\n return trimmed ? trimmed : undefined\n}\n\nfunction readHostname(value: string | undefined): string | undefined {\n const trimmed = readText(value)\n if (!trimmed) return undefined\n\n try {\n const url = new URL(trimmed)\n return readText(url.hostname)\n } catch {\n return undefined\n }\n}\n\nfunction readRequestHostname(request: Request | undefined): string | undefined {\n const url = readRequestUrl(request)\n return url ? readText(url.hostname) : undefined\n}\n\nfunction readRequestOrigin(request: Request | undefined): string | undefined {\n const url = readRequestUrl(request)\n return url ? readText(url.origin) : undefined\n}\n\nfunction readRequestUrl(request: Request | undefined): URL | null {\n if (!request) return null\n\n const forwardedHost = readForwardedHeader(request.headers.get('x-forwarded-host'))\n const forwardedProto = readForwardedHeader(request.headers.get('x-forwarded-proto'))\n if (forwardedHost) {\n const protocol = forwardedProto ?? 'https'\n try {\n return new URL(`${protocol}://${forwardedHost}`)\n } catch {\n return null\n }\n }\n\n try {\n return new URL(request.url)\n } catch {\n return null\n }\n}\n\nfunction readForwardedHeader(value: string | null): string | undefined {\n const trimmed = readText(value ?? undefined)\n if (!trimmed) return undefined\n const [first] = trimmed.split(',')\n return readText(first)\n}\n\nfunction areOriginsEqual(left: string[], right: string[]): boolean {\n if (left.length !== right.length) return false\n return left.every((value, index) => value === right[index])\n}\n\nfunction parsePositiveInteger(value: string | undefined): number | undefined {\n if (!value) return undefined\n const parsed = Number(value)\n if (!Number.isFinite(parsed) || parsed <= 0) return undefined\n return Math.trunc(parsed)\n}\n\nfunction parseNonNegativeInteger(value: string | undefined): number | undefined {\n if (!value) return undefined\n const parsed = Number(value)\n if (!Number.isFinite(parsed) || parsed < 0) return undefined\n return Math.trunc(parsed)\n}\n\nfunction clamp(value: number, min: number, max: number): number {\n return Math.min(Math.max(value, min), max)\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { parseBooleanWithDefault } from '@open-mercato/shared/lib/boolean'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst TOTP_PERIOD_SECONDS = 30\nconst TOTP_DIGITS = 6\nconst DEFAULT_SETUP_TTL_MS = 10 * 60 * 1000\nconst DEFAULT_WEBAUTHN_CHALLENGE_TTL_MS = 5 * 60 * 1000\nconst DEFAULT_SUDO_PENDING_CHALLENGE_TTL_MS = 10 * 60 * 1000\nconst MIN_SUDO_TTL_SECONDS = 30\n\nconst securityBypassLogger = createLogger('security').child({ component: 'mfa-emergency-bypass' })\nlet startupBypassWarningEmitted = false\n\nexport type SecurityModuleConfig = {\n totp: {\n issuer: string\n window: number\n digits: number\n periodSeconds: number\n setupTtlMs: number\n }\n otpEmail: {\n expirySeconds: number\n maxAttempts: number\n setupTtlMs: number\n challengeTtlMs: number\n subject: string\n }\n mfa: {\n challengeTtlMs: number\n maxAttempts: number\n emergencyBypass: boolean\n }\n sudo: {\n minTtlSeconds: number\n defaultTtlSeconds: number\n maxTtlSeconds: number\n pendingChallengeTtlMs: number\n }\n webauthn: {\n rpName: string\n rpId: string\n expectedOrigins: string[]\n setupTtlMs: number\n challengeTtlMs: number\n }\n recoveryCodes: {\n count: number\n bcryptCost: number\n }\n}\n\nexport const defaultSecurityModuleConfig: SecurityModuleConfig = {\n totp: {\n issuer: 'Open Mercato',\n window: 1,\n digits: TOTP_DIGITS,\n periodSeconds: TOTP_PERIOD_SECONDS,\n setupTtlMs: DEFAULT_SETUP_TTL_MS,\n },\n otpEmail: {\n expirySeconds: 600,\n maxAttempts: 5,\n setupTtlMs: DEFAULT_SETUP_TTL_MS,\n challengeTtlMs: 600 * 1000,\n subject: 'Your Open Mercato verification code',\n },\n mfa: {\n challengeTtlMs: 600 * 1000,\n maxAttempts: 5,\n emergencyBypass: false,\n },\n sudo: {\n minTtlSeconds: MIN_SUDO_TTL_SECONDS,\n defaultTtlSeconds: 300,\n maxTtlSeconds: 1800,\n pendingChallengeTtlMs: DEFAULT_SUDO_PENDING_CHALLENGE_TTL_MS,\n },\n webauthn: {\n rpName: 'Open Mercato',\n rpId: 'localhost',\n expectedOrigins: ['http://localhost:3000'],\n setupTtlMs: DEFAULT_SETUP_TTL_MS,\n challengeTtlMs: DEFAULT_WEBAUTHN_CHALLENGE_TTL_MS,\n },\n recoveryCodes: {\n count: 10,\n bcryptCost: 10,\n },\n}\n\nexport function readSecurityModuleConfig(\n env: NodeJS.ProcessEnv = process.env,\n): SecurityModuleConfig {\n const otpExpirySeconds = parsePositiveInteger(env.OM_SECURITY_OTP_EXPIRY_SECONDS)\n ?? defaultSecurityModuleConfig.otpEmail.expirySeconds\n const otpMaxAttempts = parsePositiveInteger(env.OM_SECURITY_OTP_MAX_ATTEMPTS)\n ?? defaultSecurityModuleConfig.otpEmail.maxAttempts\n const sudoMaxTtlSeconds = Math.max(\n parsePositiveInteger(env.OM_SECURITY_SUDO_MAX_TTL) ?? defaultSecurityModuleConfig.sudo.maxTtlSeconds,\n defaultSecurityModuleConfig.sudo.minTtlSeconds,\n )\n const sudoDefaultTtlSeconds = clamp(\n parsePositiveInteger(env.OM_SECURITY_SUDO_DEFAULT_TTL) ?? defaultSecurityModuleConfig.sudo.defaultTtlSeconds,\n defaultSecurityModuleConfig.sudo.minTtlSeconds,\n sudoMaxTtlSeconds,\n )\n const webauthnRpId = readText(env.OM_SECURITY_WEBAUTHN_RP_ID)\n ?? readHostname(env.APP_URL)\n ?? readHostname(env.NEXT_PUBLIC_APP_URL)\n ?? defaultSecurityModuleConfig.webauthn.rpId\n\n return {\n totp: {\n ...defaultSecurityModuleConfig.totp,\n issuer: readText(env.OM_SECURITY_TOTP_ISSUER) ?? defaultSecurityModuleConfig.totp.issuer,\n window: parseNonNegativeInteger(env.OM_SECURITY_TOTP_WINDOW) ?? defaultSecurityModuleConfig.totp.window,\n },\n otpEmail: {\n ...defaultSecurityModuleConfig.otpEmail,\n expirySeconds: otpExpirySeconds,\n maxAttempts: otpMaxAttempts,\n challengeTtlMs: otpExpirySeconds * 1000,\n },\n mfa: {\n challengeTtlMs: otpExpirySeconds * 1000,\n maxAttempts: otpMaxAttempts,\n emergencyBypass: parseBooleanWithDefault(\n env.OM_SECURITY_MFA_EMERGENCY_BYPASS,\n defaultSecurityModuleConfig.mfa.emergencyBypass,\n ),\n },\n sudo: {\n ...defaultSecurityModuleConfig.sudo,\n defaultTtlSeconds: sudoDefaultTtlSeconds,\n maxTtlSeconds: sudoMaxTtlSeconds,\n },\n webauthn: {\n ...defaultSecurityModuleConfig.webauthn,\n rpName: readText(env.OM_SECURITY_WEBAUTHN_RP_NAME) ?? defaultSecurityModuleConfig.webauthn.rpName,\n rpId: webauthnRpId,\n expectedOrigins: readWebAuthnOrigins(env, webauthnRpId),\n },\n recoveryCodes: {\n ...defaultSecurityModuleConfig.recoveryCodes,\n count: parsePositiveInteger(env.OM_SECURITY_RECOVERY_CODE_COUNT)\n ?? parsePositiveInteger(env.OM_SECURITY_RECOVERY_CODES_COUNT)\n ?? defaultSecurityModuleConfig.recoveryCodes.count,\n },\n }\n}\n\nexport function resolveSecurityModuleConfigForRequest(\n baseConfig: SecurityModuleConfig,\n request?: Request,\n env: NodeJS.ProcessEnv = process.env,\n): SecurityModuleConfig {\n const explicitRpId = readText(env.OM_SECURITY_WEBAUTHN_RP_ID)\n const requestRpId = readRequestHostname(request)\n const rpId = explicitRpId ?? requestRpId ?? baseConfig.webauthn.rpId\n const expectedOrigins = readWebAuthnOrigins(env, rpId, request, baseConfig.webauthn.expectedOrigins)\n\n if (rpId === baseConfig.webauthn.rpId && areOriginsEqual(expectedOrigins, baseConfig.webauthn.expectedOrigins)) {\n return baseConfig\n }\n\n return {\n ...baseConfig,\n webauthn: {\n ...baseConfig.webauthn,\n rpId,\n expectedOrigins,\n },\n }\n}\n\nexport function emitMfaEmergencyBypassActiveWarning(context: string, extra?: Record<string, unknown>): void {\n securityBypassLogger.warn('MFA emergency bypass is active', {\n emergencyBypass: true,\n context,\n ...extra,\n })\n}\n\nexport function emitMfaEmergencyBypassStartupWarningIfNeeded(env: NodeJS.ProcessEnv = process.env): void {\n if (startupBypassWarningEmitted) return\n const bypass = parseBooleanWithDefault(\n env.OM_SECURITY_MFA_EMERGENCY_BYPASS,\n defaultSecurityModuleConfig.mfa.emergencyBypass,\n )\n if (!bypass) return\n startupBypassWarningEmitted = true\n emitMfaEmergencyBypassActiveWarning('startup', { startup: true })\n}\n\nexport function resetMfaEmergencyBypassStartupWarningForTests(): void {\n startupBypassWarningEmitted = false\n}\n\nemitMfaEmergencyBypassStartupWarningIfNeeded()\n\nexport function readSecuritySetupTokenSecret(env: NodeJS.ProcessEnv = process.env): string {\n const secret = readText(env.OM_SECURITY_MFA_SETUP_SECRET)\n ?? readText(env.AUTH_JWT_SECRET)\n ?? readText(env.AUTH_SECRET)\n ?? readText(env.JWT_SECRET)\n\n if (secret) {\n return secret\n }\n\n throw new Error(\n 'Security MFA setup tokens require OM_SECURITY_MFA_SETUP_SECRET, AUTH_JWT_SECRET, AUTH_SECRET, or JWT_SECRET.',\n )\n}\n\nfunction readWebAuthnOrigins(\n env: NodeJS.ProcessEnv,\n rpId: string,\n request?: Request,\n fallbackOrigins?: string[],\n): string[] {\n const fromList = (env.OM_SECURITY_WEBAUTHN_ORIGINS ?? '')\n .split(',')\n .map((value) => value.trim())\n .filter((value) => value.length > 0)\n const explicitOrigin = readText(env.OM_SECURITY_WEBAUTHN_ORIGIN)\n\n if (fromList.length > 0 || explicitOrigin) {\n return [...new Set([...fromList, ...(explicitOrigin ? [explicitOrigin] : [])])]\n }\n\n const requestOrigin = readRequestOrigin(request)\n const singletons = [\n requestOrigin,\n env.APP_URL,\n env.NEXT_PUBLIC_APP_URL,\n ]\n .map((value) => readText(value))\n .filter((value): value is string => typeof value === 'string')\n\n const origins = [...new Set([...fromList, ...singletons])]\n if (origins.length > 0) {\n return origins\n }\n\n if (fallbackOrigins && fallbackOrigins.length > 0) {\n return [...fallbackOrigins]\n }\n\n if (rpId === 'localhost') {\n return [...defaultSecurityModuleConfig.webauthn.expectedOrigins]\n }\n\n return [`https://${rpId}`]\n}\n\nfunction readText(value: string | undefined): string | undefined {\n const trimmed = value?.trim()\n return trimmed ? trimmed : undefined\n}\n\nfunction readHostname(value: string | undefined): string | undefined {\n const trimmed = readText(value)\n if (!trimmed) return undefined\n\n try {\n const url = new URL(trimmed)\n return readText(url.hostname)\n } catch {\n return undefined\n }\n}\n\nfunction readRequestHostname(request: Request | undefined): string | undefined {\n const url = readRequestUrl(request)\n return url ? readText(url.hostname) : undefined\n}\n\nfunction readRequestOrigin(request: Request | undefined): string | undefined {\n const url = readRequestUrl(request)\n return url ? readText(url.origin) : undefined\n}\n\nfunction readRequestUrl(request: Request | undefined): URL | null {\n if (!request) return null\n\n const forwardedHost = readForwardedHeader(request.headers.get('x-forwarded-host'))\n const forwardedProto = readForwardedHeader(request.headers.get('x-forwarded-proto'))\n if (forwardedHost) {\n const protocol = forwardedProto ?? 'https'\n try {\n return new URL(`${protocol}://${forwardedHost}`)\n } catch {\n return null\n }\n }\n\n try {\n return new URL(request.url)\n } catch {\n return null\n }\n}\n\nfunction readForwardedHeader(value: string | null): string | undefined {\n const trimmed = readText(value ?? undefined)\n if (!trimmed) return undefined\n const [first] = trimmed.split(',')\n return readText(first)\n}\n\nfunction areOriginsEqual(left: string[], right: string[]): boolean {\n if (left.length !== right.length) return false\n return left.every((value, index) => value === right[index])\n}\n\nfunction parsePositiveInteger(value: string | undefined): number | undefined {\n if (!value) return undefined\n const parsed = Number(value)\n if (!Number.isFinite(parsed) || parsed <= 0) return undefined\n return Math.trunc(parsed)\n}\n\nfunction parseNonNegativeInteger(value: string | undefined): number | undefined {\n if (!value) return undefined\n const parsed = Number(value)\n if (!Number.isFinite(parsed) || parsed < 0) return undefined\n return Math.trunc(parsed)\n}\n\nfunction clamp(value: number, min: number, max: number): number {\n return Math.min(Math.max(value, min), max)\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,13 @@
1
+ import { Migration } from "@mikro-orm/migrations";
2
+ class Migration20260811143757_security extends Migration {
3
+ up() {
4
+ this.addSql(`alter table "sudo_sessions" add "scope_tenant_id" uuid null, add "scope_organization_id" uuid null, add "target_identifier" text null, add "sudo_config_id" uuid null, add "sudo_config_updated_at" timestamptz null, add "verified_at" timestamptz null;`);
5
+ }
6
+ down() {
7
+ this.addSql(`alter table "sudo_sessions" drop column "scope_tenant_id", drop column "scope_organization_id", drop column "target_identifier", drop column "sudo_config_id", drop column "sudo_config_updated_at", drop column "verified_at";`);
8
+ }
9
+ }
10
+ export {
11
+ Migration20260811143757_security
12
+ };
13
+ //# sourceMappingURL=Migration20260811143757_security.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/security/migrations/Migration20260811143757_security.ts"],
4
+ "sourcesContent": ["import { Migration } from '@mikro-orm/migrations';\n\nexport class Migration20260811143757_security extends Migration {\n\n override up(): void | Promise<void> {\n this.addSql(`alter table \"sudo_sessions\" add \"scope_tenant_id\" uuid null, add \"scope_organization_id\" uuid null, add \"target_identifier\" text null, add \"sudo_config_id\" uuid null, add \"sudo_config_updated_at\" timestamptz null, add \"verified_at\" timestamptz null;`);\n }\n\n override down(): void | Promise<void> {\n this.addSql(`alter table \"sudo_sessions\" drop column \"scope_tenant_id\", drop column \"scope_organization_id\", drop column \"target_identifier\", drop column \"sudo_config_id\", drop column \"sudo_config_updated_at\", drop column \"verified_at\";`);\n }\n\n}\n"],
5
+ "mappings": "AAAA,SAAS,iBAAiB;AAEnB,MAAM,yCAAyC,UAAU;AAAA,EAErD,KAA2B;AAClC,SAAK,OAAO,2PAA2P;AAAA,EACzQ;AAAA,EAES,OAA6B;AACpC,SAAK,OAAO,iOAAiO;AAAA,EAC/O;AAEF;",
6
+ "names": []
7
+ }