@portaidentity/server 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/LICENSE +21 -0
- package/README.md +33 -0
- package/dist/applications/cache.d.ts +64 -0
- package/dist/applications/cache.d.ts.map +1 -0
- package/dist/applications/cache.js +161 -0
- package/dist/applications/cache.js.map +1 -0
- package/dist/applications/errors.d.ts +25 -0
- package/dist/applications/errors.d.ts.map +1 -0
- package/dist/applications/errors.js +31 -0
- package/dist/applications/errors.js.map +1 -0
- package/dist/applications/index.d.ts +17 -0
- package/dist/applications/index.d.ts.map +1 -0
- package/dist/applications/index.js +19 -0
- package/dist/applications/index.js.map +1 -0
- package/dist/applications/repository.d.ts +184 -0
- package/dist/applications/repository.d.ts.map +1 -0
- package/dist/applications/repository.js +367 -0
- package/dist/applications/repository.js.map +1 -0
- package/dist/applications/service.d.ts +160 -0
- package/dist/applications/service.d.ts.map +1 -0
- package/dist/applications/service.js +388 -0
- package/dist/applications/service.js.map +1 -0
- package/dist/applications/slugs.d.ts +61 -0
- package/dist/applications/slugs.d.ts.map +1 -0
- package/dist/applications/slugs.js +115 -0
- package/dist/applications/slugs.js.map +1 -0
- package/dist/applications/types.d.ts +129 -0
- package/dist/applications/types.d.ts.map +1 -0
- package/dist/applications/types.js +62 -0
- package/dist/applications/types.js.map +1 -0
- package/dist/auth/csrf.d.ts +71 -0
- package/dist/auth/csrf.d.ts.map +1 -0
- package/dist/auth/csrf.js +112 -0
- package/dist/auth/csrf.js.map +1 -0
- package/dist/auth/email-renderer.d.ts +37 -0
- package/dist/auth/email-renderer.d.ts.map +1 -0
- package/dist/auth/email-renderer.js +116 -0
- package/dist/auth/email-renderer.js.map +1 -0
- package/dist/auth/email-service.d.ts +154 -0
- package/dist/auth/email-service.d.ts.map +1 -0
- package/dist/auth/email-service.js +415 -0
- package/dist/auth/email-service.js.map +1 -0
- package/dist/auth/email-transport.d.ts +65 -0
- package/dist/auth/email-transport.d.ts.map +1 -0
- package/dist/auth/email-transport.js +82 -0
- package/dist/auth/email-transport.js.map +1 -0
- package/dist/auth/i18n.d.ts +81 -0
- package/dist/auth/i18n.d.ts.map +1 -0
- package/dist/auth/i18n.js +395 -0
- package/dist/auth/i18n.js.map +1 -0
- package/dist/auth/index.d.ts +28 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +31 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/interaction-authority.d.ts +39 -0
- package/dist/auth/interaction-authority.d.ts.map +1 -0
- package/dist/auth/interaction-authority.js +30 -0
- package/dist/auth/interaction-authority.js.map +1 -0
- package/dist/auth/magic-link-session.d.ts +81 -0
- package/dist/auth/magic-link-session.d.ts.map +1 -0
- package/dist/auth/magic-link-session.js +183 -0
- package/dist/auth/magic-link-session.js.map +1 -0
- package/dist/auth/rate-limiter.d.ts +143 -0
- package/dist/auth/rate-limiter.d.ts.map +1 -0
- package/dist/auth/rate-limiter.js +220 -0
- package/dist/auth/rate-limiter.js.map +1 -0
- package/dist/auth/recovery-crypto.d.ts +34 -0
- package/dist/auth/recovery-crypto.d.ts.map +1 -0
- package/dist/auth/recovery-crypto.js +103 -0
- package/dist/auth/recovery-crypto.js.map +1 -0
- package/dist/auth/recovery-job-processor.d.ts +24 -0
- package/dist/auth/recovery-job-processor.d.ts.map +1 -0
- package/dist/auth/recovery-job-processor.js +123 -0
- package/dist/auth/recovery-job-processor.js.map +1 -0
- package/dist/auth/recovery-job-repository.d.ts +219 -0
- package/dist/auth/recovery-job-repository.d.ts.map +1 -0
- package/dist/auth/recovery-job-repository.js +340 -0
- package/dist/auth/recovery-job-repository.js.map +1 -0
- package/dist/auth/recovery-service.d.ts +27 -0
- package/dist/auth/recovery-service.d.ts.map +1 -0
- package/dist/auth/recovery-service.js +53 -0
- package/dist/auth/recovery-service.js.map +1 -0
- package/dist/auth/recovery-worker.d.ts +158 -0
- package/dist/auth/recovery-worker.d.ts.map +1 -0
- package/dist/auth/recovery-worker.js +306 -0
- package/dist/auth/recovery-worker.js.map +1 -0
- package/dist/auth/template-engine.d.ts +134 -0
- package/dist/auth/template-engine.d.ts.map +1 -0
- package/dist/auth/template-engine.js +225 -0
- package/dist/auth/template-engine.js.map +1 -0
- package/dist/auth/token-repository.d.ts +238 -0
- package/dist/auth/token-repository.d.ts.map +1 -0
- package/dist/auth/token-repository.js +475 -0
- package/dist/auth/token-repository.js.map +1 -0
- package/dist/auth/tokens.d.ts +45 -0
- package/dist/auth/tokens.d.ts.map +1 -0
- package/dist/auth/tokens.js +51 -0
- package/dist/auth/tokens.js.map +1 -0
- package/dist/cli/bootstrap.d.ts +55 -0
- package/dist/cli/bootstrap.d.ts.map +1 -0
- package/dist/cli/bootstrap.js +90 -0
- package/dist/cli/bootstrap.js.map +1 -0
- package/dist/cli/commands/health.d.ts +20 -0
- package/dist/cli/commands/health.d.ts.map +1 -0
- package/dist/cli/commands/health.js +80 -0
- package/dist/cli/commands/health.js.map +1 -0
- package/dist/cli/commands/init.d.ts +48 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +349 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/migrate.d.ts +19 -0
- package/dist/cli/commands/migrate.d.ts.map +1 -0
- package/dist/cli/commands/migrate.js +88 -0
- package/dist/cli/commands/migrate.js.map +1 -0
- package/dist/cli/commands/seed.d.ts +19 -0
- package/dist/cli/commands/seed.d.ts.map +1 -0
- package/dist/cli/commands/seed.js +71 -0
- package/dist/cli/commands/seed.js.map +1 -0
- package/dist/cli/commands/user-2fa.d.ts +29 -0
- package/dist/cli/commands/user-2fa.d.ts.map +1 -0
- package/dist/cli/commands/user-2fa.js +113 -0
- package/dist/cli/commands/user-2fa.js.map +1 -0
- package/dist/cli/error-handler.d.ts +31 -0
- package/dist/cli/error-handler.d.ts.map +1 -0
- package/dist/cli/error-handler.js +65 -0
- package/dist/cli/error-handler.js.map +1 -0
- package/dist/cli/index.d.ts +48 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +99 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/output.d.ts +94 -0
- package/dist/cli/output.d.ts.map +1 -0
- package/dist/cli/output.js +124 -0
- package/dist/cli/output.js.map +1 -0
- package/dist/cli/prompt.d.ts +48 -0
- package/dist/cli/prompt.d.ts.map +1 -0
- package/dist/cli/prompt.js +96 -0
- package/dist/cli/prompt.js.map +1 -0
- package/dist/clients/cache.d.ts +63 -0
- package/dist/clients/cache.d.ts.map +1 -0
- package/dist/clients/cache.js +160 -0
- package/dist/clients/cache.js.map +1 -0
- package/dist/clients/crypto.d.ts +69 -0
- package/dist/clients/crypto.d.ts.map +1 -0
- package/dist/clients/crypto.js +83 -0
- package/dist/clients/crypto.js.map +1 -0
- package/dist/clients/errors.d.ts +27 -0
- package/dist/clients/errors.d.ts.map +1 -0
- package/dist/clients/errors.js +33 -0
- package/dist/clients/errors.js.map +1 -0
- package/dist/clients/index.d.ts +19 -0
- package/dist/clients/index.d.ts.map +1 -0
- package/dist/clients/index.js +23 -0
- package/dist/clients/index.js.map +1 -0
- package/dist/clients/repository.d.ts +161 -0
- package/dist/clients/repository.d.ts.map +1 -0
- package/dist/clients/repository.js +318 -0
- package/dist/clients/repository.js.map +1 -0
- package/dist/clients/resolve-login-methods.d.ts +74 -0
- package/dist/clients/resolve-login-methods.d.ts.map +1 -0
- package/dist/clients/resolve-login-methods.js +68 -0
- package/dist/clients/resolve-login-methods.js.map +1 -0
- package/dist/clients/secret-repository.d.ts +109 -0
- package/dist/clients/secret-repository.d.ts.map +1 -0
- package/dist/clients/secret-repository.js +166 -0
- package/dist/clients/secret-repository.js.map +1 -0
- package/dist/clients/secret-service.d.ts +87 -0
- package/dist/clients/secret-service.d.ts.map +1 -0
- package/dist/clients/secret-service.js +204 -0
- package/dist/clients/secret-service.js.map +1 -0
- package/dist/clients/service.d.ts +158 -0
- package/dist/clients/service.d.ts.map +1 -0
- package/dist/clients/service.js +523 -0
- package/dist/clients/service.js.map +1 -0
- package/dist/clients/types.d.ts +214 -0
- package/dist/clients/types.d.ts.map +1 -0
- package/dist/clients/types.js +77 -0
- package/dist/clients/types.js.map +1 -0
- package/dist/clients/validators.d.ts +87 -0
- package/dist/clients/validators.d.ts.map +1 -0
- package/dist/clients/validators.js +164 -0
- package/dist/clients/validators.js.map +1 -0
- package/dist/config/index.d.ts +23 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +52 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/schema.d.ts +35 -0
- package/dist/config/schema.d.ts.map +1 -0
- package/dist/config/schema.js +167 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/custom-claims/cache.d.ts +48 -0
- package/dist/custom-claims/cache.d.ts.map +1 -0
- package/dist/custom-claims/cache.js +122 -0
- package/dist/custom-claims/cache.js.map +1 -0
- package/dist/custom-claims/errors.d.ts +25 -0
- package/dist/custom-claims/errors.d.ts.map +1 -0
- package/dist/custom-claims/errors.js +31 -0
- package/dist/custom-claims/errors.js.map +1 -0
- package/dist/custom-claims/index.d.ts +14 -0
- package/dist/custom-claims/index.d.ts.map +1 -0
- package/dist/custom-claims/index.js +17 -0
- package/dist/custom-claims/index.js.map +1 -0
- package/dist/custom-claims/repository.d.ts +147 -0
- package/dist/custom-claims/repository.d.ts.map +1 -0
- package/dist/custom-claims/repository.js +338 -0
- package/dist/custom-claims/repository.js.map +1 -0
- package/dist/custom-claims/service.d.ts +129 -0
- package/dist/custom-claims/service.d.ts.map +1 -0
- package/dist/custom-claims/service.js +296 -0
- package/dist/custom-claims/service.js.map +1 -0
- package/dist/custom-claims/types.d.ts +132 -0
- package/dist/custom-claims/types.d.ts.map +1 -0
- package/dist/custom-claims/types.js +61 -0
- package/dist/custom-claims/types.js.map +1 -0
- package/dist/custom-claims/validators.d.ts +61 -0
- package/dist/custom-claims/validators.d.ts.map +1 -0
- package/dist/custom-claims/validators.js +136 -0
- package/dist/custom-claims/validators.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +91 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/admin-permissions.d.ts +147 -0
- package/dist/lib/admin-permissions.d.ts.map +1 -0
- package/dist/lib/admin-permissions.js +261 -0
- package/dist/lib/admin-permissions.js.map +1 -0
- package/dist/lib/audit-log.d.ts +45 -0
- package/dist/lib/audit-log.d.ts.map +1 -0
- package/dist/lib/audit-log.js +79 -0
- package/dist/lib/audit-log.js.map +1 -0
- package/dist/lib/branding-assets.d.ts +47 -0
- package/dist/lib/branding-assets.d.ts.map +1 -0
- package/dist/lib/branding-assets.js +93 -0
- package/dist/lib/branding-assets.js.map +1 -0
- package/dist/lib/bulk-operations.d.ts +54 -0
- package/dist/lib/bulk-operations.d.ts.map +1 -0
- package/dist/lib/bulk-operations.js +201 -0
- package/dist/lib/bulk-operations.js.map +1 -0
- package/dist/lib/cursor.d.ts +110 -0
- package/dist/lib/cursor.d.ts.map +1 -0
- package/dist/lib/cursor.js +115 -0
- package/dist/lib/cursor.js.map +1 -0
- package/dist/lib/data-export.d.ts +67 -0
- package/dist/lib/data-export.d.ts.map +1 -0
- package/dist/lib/data-export.js +272 -0
- package/dist/lib/data-export.js.map +1 -0
- package/dist/lib/data-import-plan.d.ts +50 -0
- package/dist/lib/data-import-plan.d.ts.map +1 -0
- package/dist/lib/data-import-plan.js +235 -0
- package/dist/lib/data-import-plan.js.map +1 -0
- package/dist/lib/data-import.d.ts +276 -0
- package/dist/lib/data-import.d.ts.map +1 -0
- package/dist/lib/data-import.js +1694 -0
- package/dist/lib/data-import.js.map +1 -0
- package/dist/lib/database.d.ts +22 -0
- package/dist/lib/database.d.ts.map +1 -0
- package/dist/lib/database.js +103 -0
- package/dist/lib/database.js.map +1 -0
- package/dist/lib/entity-history.d.ts +52 -0
- package/dist/lib/entity-history.d.ts.map +1 -0
- package/dist/lib/entity-history.js +121 -0
- package/dist/lib/entity-history.js.map +1 -0
- package/dist/lib/etag.d.ts +92 -0
- package/dist/lib/etag.d.ts.map +1 -0
- package/dist/lib/etag.js +155 -0
- package/dist/lib/etag.js.map +1 -0
- package/dist/lib/image-validator.d.ts +44 -0
- package/dist/lib/image-validator.d.ts.map +1 -0
- package/dist/lib/image-validator.js +204 -0
- package/dist/lib/image-validator.js.map +1 -0
- package/dist/lib/logger.d.ts +15 -0
- package/dist/lib/logger.d.ts.map +1 -0
- package/dist/lib/logger.js +84 -0
- package/dist/lib/logger.js.map +1 -0
- package/dist/lib/migrator.d.ts +14 -0
- package/dist/lib/migrator.d.ts.map +1 -0
- package/dist/lib/migrator.js +34 -0
- package/dist/lib/migrator.js.map +1 -0
- package/dist/lib/redis.d.ts +5 -0
- package/dist/lib/redis.d.ts.map +1 -0
- package/dist/lib/redis.js +29 -0
- package/dist/lib/redis.js.map +1 -0
- package/dist/lib/runtime-paths.d.ts +38 -0
- package/dist/lib/runtime-paths.d.ts.map +1 -0
- package/dist/lib/runtime-paths.js +50 -0
- package/dist/lib/runtime-paths.js.map +1 -0
- package/dist/lib/session-tracking.d.ts +81 -0
- package/dist/lib/session-tracking.d.ts.map +1 -0
- package/dist/lib/session-tracking.js +145 -0
- package/dist/lib/session-tracking.js.map +1 -0
- package/dist/lib/signing-key-crypto.d.ts +53 -0
- package/dist/lib/signing-key-crypto.d.ts.map +1 -0
- package/dist/lib/signing-key-crypto.js +108 -0
- package/dist/lib/signing-key-crypto.js.map +1 -0
- package/dist/lib/signing-keys.d.ts +141 -0
- package/dist/lib/signing-keys.d.ts.map +1 -0
- package/dist/lib/signing-keys.js +206 -0
- package/dist/lib/signing-keys.js.map +1 -0
- package/dist/lib/stats.d.ts +83 -0
- package/dist/lib/stats.d.ts.map +1 -0
- package/dist/lib/stats.js +187 -0
- package/dist/lib/stats.js.map +1 -0
- package/dist/lib/super-admin-protection.d.ts +69 -0
- package/dist/lib/super-admin-protection.d.ts.map +1 -0
- package/dist/lib/super-admin-protection.js +102 -0
- package/dist/lib/super-admin-protection.js.map +1 -0
- package/dist/lib/system-config.d.ts +79 -0
- package/dist/lib/system-config.d.ts.map +1 -0
- package/dist/lib/system-config.js +159 -0
- package/dist/lib/system-config.js.map +1 -0
- package/dist/middleware/admin-auth.d.ts +100 -0
- package/dist/middleware/admin-auth.d.ts.map +1 -0
- package/dist/middleware/admin-auth.js +241 -0
- package/dist/middleware/admin-auth.js.map +1 -0
- package/dist/middleware/admin-cors.d.ts +41 -0
- package/dist/middleware/admin-cors.d.ts.map +1 -0
- package/dist/middleware/admin-cors.js +83 -0
- package/dist/middleware/admin-cors.js.map +1 -0
- package/dist/middleware/admin-mutation-audit.d.ts +9 -0
- package/dist/middleware/admin-mutation-audit.d.ts.map +1 -0
- package/dist/middleware/admin-mutation-audit.js +76 -0
- package/dist/middleware/admin-mutation-audit.js.map +1 -0
- package/dist/middleware/admin-rate-limiter.d.ts +56 -0
- package/dist/middleware/admin-rate-limiter.d.ts.map +1 -0
- package/dist/middleware/admin-rate-limiter.js +97 -0
- package/dist/middleware/admin-rate-limiter.js.map +1 -0
- package/dist/middleware/client-secret-hash.d.ts +34 -0
- package/dist/middleware/client-secret-hash.d.ts.map +1 -0
- package/dist/middleware/client-secret-hash.js +74 -0
- package/dist/middleware/client-secret-hash.js.map +1 -0
- package/dist/middleware/error-handler.d.ts +3 -0
- package/dist/middleware/error-handler.d.ts.map +1 -0
- package/dist/middleware/error-handler.js +93 -0
- package/dist/middleware/error-handler.js.map +1 -0
- package/dist/middleware/health.d.ts +3 -0
- package/dist/middleware/health.d.ts.map +1 -0
- package/dist/middleware/health.js +35 -0
- package/dist/middleware/health.js.map +1 -0
- package/dist/middleware/metrics.d.ts +40 -0
- package/dist/middleware/metrics.d.ts.map +1 -0
- package/dist/middleware/metrics.js +75 -0
- package/dist/middleware/metrics.js.map +1 -0
- package/dist/middleware/oidc-client-tenant.d.ts +37 -0
- package/dist/middleware/oidc-client-tenant.d.ts.map +1 -0
- package/dist/middleware/oidc-client-tenant.js +156 -0
- package/dist/middleware/oidc-client-tenant.js.map +1 -0
- package/dist/middleware/oidc-cors.d.ts +33 -0
- package/dist/middleware/oidc-cors.d.ts.map +1 -0
- package/dist/middleware/oidc-cors.js +68 -0
- package/dist/middleware/oidc-cors.js.map +1 -0
- package/dist/middleware/oidc-preflight-cors.d.ts +54 -0
- package/dist/middleware/oidc-preflight-cors.d.ts.map +1 -0
- package/dist/middleware/oidc-preflight-cors.js +267 -0
- package/dist/middleware/oidc-preflight-cors.js.map +1 -0
- package/dist/middleware/prompt-login-reset.d.ts +103 -0
- package/dist/middleware/prompt-login-reset.d.ts.map +1 -0
- package/dist/middleware/prompt-login-reset.js +204 -0
- package/dist/middleware/prompt-login-reset.js.map +1 -0
- package/dist/middleware/ready.d.ts +23 -0
- package/dist/middleware/ready.d.ts.map +1 -0
- package/dist/middleware/ready.js +60 -0
- package/dist/middleware/ready.js.map +1 -0
- package/dist/middleware/request-logger.d.ts +27 -0
- package/dist/middleware/request-logger.d.ts.map +1 -0
- package/dist/middleware/request-logger.js +46 -0
- package/dist/middleware/request-logger.js.map +1 -0
- package/dist/middleware/require-permission.d.ts +28 -0
- package/dist/middleware/require-permission.d.ts.map +1 -0
- package/dist/middleware/require-permission.js +63 -0
- package/dist/middleware/require-permission.js.map +1 -0
- package/dist/middleware/require-user-organization.d.ts +18 -0
- package/dist/middleware/require-user-organization.d.ts.map +1 -0
- package/dist/middleware/require-user-organization.js +53 -0
- package/dist/middleware/require-user-organization.js.map +1 -0
- package/dist/middleware/root-page.d.ts +62 -0
- package/dist/middleware/root-page.d.ts.map +1 -0
- package/dist/middleware/root-page.js +134 -0
- package/dist/middleware/root-page.js.map +1 -0
- package/dist/middleware/security-headers.d.ts +98 -0
- package/dist/middleware/security-headers.d.ts.map +1 -0
- package/dist/middleware/security-headers.js +159 -0
- package/dist/middleware/security-headers.js.map +1 -0
- package/dist/middleware/tenant-resolver.d.ts +37 -0
- package/dist/middleware/tenant-resolver.d.ts.map +1 -0
- package/dist/middleware/tenant-resolver.js +71 -0
- package/dist/middleware/tenant-resolver.js.map +1 -0
- package/dist/middleware/token-rate-limiter.d.ts +89 -0
- package/dist/middleware/token-rate-limiter.d.ts.map +1 -0
- package/dist/middleware/token-rate-limiter.js +189 -0
- package/dist/middleware/token-rate-limiter.js.map +1 -0
- package/dist/oidc/account-finder.d.ts +52 -0
- package/dist/oidc/account-finder.d.ts.map +1 -0
- package/dist/oidc/account-finder.js +134 -0
- package/dist/oidc/account-finder.js.map +1 -0
- package/dist/oidc/adapter-factory.d.ts +90 -0
- package/dist/oidc/adapter-factory.d.ts.map +1 -0
- package/dist/oidc/adapter-factory.js +165 -0
- package/dist/oidc/adapter-factory.js.map +1 -0
- package/dist/oidc/configuration.d.ts +155 -0
- package/dist/oidc/configuration.d.ts.map +1 -0
- package/dist/oidc/configuration.js +531 -0
- package/dist/oidc/configuration.js.map +1 -0
- package/dist/oidc/postgres-adapter.d.ts +159 -0
- package/dist/oidc/postgres-adapter.d.ts.map +1 -0
- package/dist/oidc/postgres-adapter.js +222 -0
- package/dist/oidc/postgres-adapter.js.map +1 -0
- package/dist/oidc/protocol-security-observer.d.ts +38 -0
- package/dist/oidc/protocol-security-observer.d.ts.map +1 -0
- package/dist/oidc/protocol-security-observer.js +93 -0
- package/dist/oidc/protocol-security-observer.js.map +1 -0
- package/dist/oidc/provider.d.ts +37 -0
- package/dist/oidc/provider.d.ts.map +1 -0
- package/dist/oidc/provider.js +71 -0
- package/dist/oidc/provider.js.map +1 -0
- package/dist/oidc/redis-adapter.d.ts +140 -0
- package/dist/oidc/redis-adapter.d.ts.map +1 -0
- package/dist/oidc/redis-adapter.js +344 -0
- package/dist/oidc/redis-adapter.js.map +1 -0
- package/dist/organizations/cache.d.ts +60 -0
- package/dist/organizations/cache.d.ts.map +1 -0
- package/dist/organizations/cache.js +157 -0
- package/dist/organizations/cache.js.map +1 -0
- package/dist/organizations/errors.d.ts +25 -0
- package/dist/organizations/errors.d.ts.map +1 -0
- package/dist/organizations/errors.js +31 -0
- package/dist/organizations/errors.js.map +1 -0
- package/dist/organizations/index.d.ts +15 -0
- package/dist/organizations/index.d.ts.map +1 -0
- package/dist/organizations/index.js +15 -0
- package/dist/organizations/index.js.map +1 -0
- package/dist/organizations/repository.d.ts +183 -0
- package/dist/organizations/repository.d.ts.map +1 -0
- package/dist/organizations/repository.js +354 -0
- package/dist/organizations/repository.js.map +1 -0
- package/dist/organizations/service.d.ts +168 -0
- package/dist/organizations/service.d.ts.map +1 -0
- package/dist/organizations/service.js +511 -0
- package/dist/organizations/service.js.map +1 -0
- package/dist/organizations/slugs.d.ts +57 -0
- package/dist/organizations/slugs.d.ts.map +1 -0
- package/dist/organizations/slugs.js +130 -0
- package/dist/organizations/slugs.js.map +1 -0
- package/dist/organizations/types.d.ts +146 -0
- package/dist/organizations/types.d.ts.map +1 -0
- package/dist/organizations/types.js +49 -0
- package/dist/organizations/types.js.map +1 -0
- package/dist/rbac/cache.d.ts +110 -0
- package/dist/rbac/cache.d.ts.map +1 -0
- package/dist/rbac/cache.js +271 -0
- package/dist/rbac/cache.js.map +1 -0
- package/dist/rbac/errors.d.ts +34 -0
- package/dist/rbac/errors.d.ts.map +1 -0
- package/dist/rbac/errors.js +43 -0
- package/dist/rbac/errors.js.map +1 -0
- package/dist/rbac/index.d.ts +17 -0
- package/dist/rbac/index.d.ts.map +1 -0
- package/dist/rbac/index.js +21 -0
- package/dist/rbac/index.js.map +1 -0
- package/dist/rbac/mapping-repository.d.ts +121 -0
- package/dist/rbac/mapping-repository.d.ts.map +1 -0
- package/dist/rbac/mapping-repository.js +224 -0
- package/dist/rbac/mapping-repository.js.map +1 -0
- package/dist/rbac/permission-repository.d.ts +100 -0
- package/dist/rbac/permission-repository.d.ts.map +1 -0
- package/dist/rbac/permission-repository.js +190 -0
- package/dist/rbac/permission-repository.js.map +1 -0
- package/dist/rbac/permission-service.d.ts +85 -0
- package/dist/rbac/permission-service.d.ts.map +1 -0
- package/dist/rbac/permission-service.js +179 -0
- package/dist/rbac/permission-service.js.map +1 -0
- package/dist/rbac/role-repository.d.ts +104 -0
- package/dist/rbac/role-repository.d.ts.map +1 -0
- package/dist/rbac/role-repository.js +202 -0
- package/dist/rbac/role-repository.js.map +1 -0
- package/dist/rbac/role-service.d.ts +111 -0
- package/dist/rbac/role-service.d.ts.map +1 -0
- package/dist/rbac/role-service.js +256 -0
- package/dist/rbac/role-service.js.map +1 -0
- package/dist/rbac/slugs.d.ts +116 -0
- package/dist/rbac/slugs.d.ts.map +1 -0
- package/dist/rbac/slugs.js +178 -0
- package/dist/rbac/slugs.js.map +1 -0
- package/dist/rbac/types.d.ts +187 -0
- package/dist/rbac/types.d.ts.map +1 -0
- package/dist/rbac/types.js +75 -0
- package/dist/rbac/types.js.map +1 -0
- package/dist/rbac/user-role-service.d.ts +101 -0
- package/dist/rbac/user-role-service.d.ts.map +1 -0
- package/dist/rbac/user-role-service.js +166 -0
- package/dist/rbac/user-role-service.js.map +1 -0
- package/dist/routes/applications.d.ts +40 -0
- package/dist/routes/applications.d.ts.map +1 -0
- package/dist/routes/applications.js +284 -0
- package/dist/routes/applications.js.map +1 -0
- package/dist/routes/audit.d.ts +31 -0
- package/dist/routes/audit.d.ts.map +1 -0
- package/dist/routes/audit.js +140 -0
- package/dist/routes/audit.js.map +1 -0
- package/dist/routes/branding.d.ts +17 -0
- package/dist/routes/branding.d.ts.map +1 -0
- package/dist/routes/branding.js +104 -0
- package/dist/routes/branding.js.map +1 -0
- package/dist/routes/bulk.d.ts +37 -0
- package/dist/routes/bulk.d.ts.map +1 -0
- package/dist/routes/bulk.js +94 -0
- package/dist/routes/bulk.js.map +1 -0
- package/dist/routes/clients.d.ts +44 -0
- package/dist/routes/clients.d.ts.map +1 -0
- package/dist/routes/clients.js +353 -0
- package/dist/routes/clients.js.map +1 -0
- package/dist/routes/config.d.ts +35 -0
- package/dist/routes/config.d.ts.map +1 -0
- package/dist/routes/config.js +115 -0
- package/dist/routes/config.js.map +1 -0
- package/dist/routes/custom-claims.d.ts +38 -0
- package/dist/routes/custom-claims.d.ts.map +1 -0
- package/dist/routes/custom-claims.js +208 -0
- package/dist/routes/custom-claims.js.map +1 -0
- package/dist/routes/exports.d.ts +21 -0
- package/dist/routes/exports.d.ts.map +1 -0
- package/dist/routes/exports.js +98 -0
- package/dist/routes/exports.js.map +1 -0
- package/dist/routes/imports.d.ts +18 -0
- package/dist/routes/imports.d.ts.map +1 -0
- package/dist/routes/imports.js +72 -0
- package/dist/routes/imports.js.map +1 -0
- package/dist/routes/interactions.d.ts +111 -0
- package/dist/routes/interactions.d.ts.map +1 -0
- package/dist/routes/interactions.js +1074 -0
- package/dist/routes/interactions.js.map +1 -0
- package/dist/routes/invitation.d.ts +31 -0
- package/dist/routes/invitation.d.ts.map +1 -0
- package/dist/routes/invitation.js +342 -0
- package/dist/routes/invitation.js.map +1 -0
- package/dist/routes/keys.d.ts +28 -0
- package/dist/routes/keys.d.ts.map +1 -0
- package/dist/routes/keys.js +91 -0
- package/dist/routes/keys.js.map +1 -0
- package/dist/routes/magic-link.d.ts +43 -0
- package/dist/routes/magic-link.d.ts.map +1 -0
- package/dist/routes/magic-link.js +264 -0
- package/dist/routes/magic-link.js.map +1 -0
- package/dist/routes/organizations.d.ts +38 -0
- package/dist/routes/organizations.d.ts.map +1 -0
- package/dist/routes/organizations.js +347 -0
- package/dist/routes/organizations.js.map +1 -0
- package/dist/routes/password-reset.d.ts +65 -0
- package/dist/routes/password-reset.d.ts.map +1 -0
- package/dist/routes/password-reset.js +493 -0
- package/dist/routes/password-reset.js.map +1 -0
- package/dist/routes/permissions.d.ts +33 -0
- package/dist/routes/permissions.d.ts.map +1 -0
- package/dist/routes/permissions.js +166 -0
- package/dist/routes/permissions.js.map +1 -0
- package/dist/routes/roles.d.ts +37 -0
- package/dist/routes/roles.d.ts.map +1 -0
- package/dist/routes/roles.js +222 -0
- package/dist/routes/roles.js.map +1 -0
- package/dist/routes/sessions.d.ts +37 -0
- package/dist/routes/sessions.d.ts.map +1 -0
- package/dist/routes/sessions.js +121 -0
- package/dist/routes/sessions.js.map +1 -0
- package/dist/routes/stats.d.ts +28 -0
- package/dist/routes/stats.d.ts.map +1 -0
- package/dist/routes/stats.js +65 -0
- package/dist/routes/stats.js.map +1 -0
- package/dist/routes/two-factor-admin.d.ts +54 -0
- package/dist/routes/two-factor-admin.d.ts.map +1 -0
- package/dist/routes/two-factor-admin.js +344 -0
- package/dist/routes/two-factor-admin.js.map +1 -0
- package/dist/routes/two-factor.d.ts +31 -0
- package/dist/routes/two-factor.d.ts.map +1 -0
- package/dist/routes/two-factor.js +595 -0
- package/dist/routes/two-factor.js.map +1 -0
- package/dist/routes/user-roles.d.ts +33 -0
- package/dist/routes/user-roles.d.ts.map +1 -0
- package/dist/routes/user-roles.js +129 -0
- package/dist/routes/user-roles.js.map +1 -0
- package/dist/routes/users.d.ts +61 -0
- package/dist/routes/users.d.ts.map +1 -0
- package/dist/routes/users.js +868 -0
- package/dist/routes/users.js.map +1 -0
- package/dist/security/decision-context.d.ts +52 -0
- package/dist/security/decision-context.d.ts.map +1 -0
- package/dist/security/decision-context.js +150 -0
- package/dist/security/decision-context.js.map +1 -0
- package/dist/security/decision-event.d.ts +128 -0
- package/dist/security/decision-event.d.ts.map +1 -0
- package/dist/security/decision-event.js +225 -0
- package/dist/security/decision-event.js.map +1 -0
- package/dist/security/transport-decision.d.ts +6 -0
- package/dist/security/transport-decision.d.ts.map +1 -0
- package/dist/security/transport-decision.js +37 -0
- package/dist/security/transport-decision.js.map +1 -0
- package/dist/server.d.ts +50 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +491 -0
- package/dist/server.js.map +1 -0
- package/dist/two-factor/cache.d.ts +47 -0
- package/dist/two-factor/cache.d.ts.map +1 -0
- package/dist/two-factor/cache.js +122 -0
- package/dist/two-factor/cache.js.map +1 -0
- package/dist/two-factor/crypto.d.ts +41 -0
- package/dist/two-factor/crypto.d.ts.map +1 -0
- package/dist/two-factor/crypto.js +94 -0
- package/dist/two-factor/crypto.js.map +1 -0
- package/dist/two-factor/errors.d.ts +82 -0
- package/dist/two-factor/errors.d.ts.map +1 -0
- package/dist/two-factor/errors.js +112 -0
- package/dist/two-factor/errors.js.map +1 -0
- package/dist/two-factor/index.d.ts +18 -0
- package/dist/two-factor/index.d.ts.map +1 -0
- package/dist/two-factor/index.js +25 -0
- package/dist/two-factor/index.js.map +1 -0
- package/dist/two-factor/otp.d.ts +43 -0
- package/dist/two-factor/otp.d.ts.map +1 -0
- package/dist/two-factor/otp.js +57 -0
- package/dist/two-factor/otp.js.map +1 -0
- package/dist/two-factor/recovery.d.ts +45 -0
- package/dist/two-factor/recovery.d.ts.map +1 -0
- package/dist/two-factor/recovery.js +104 -0
- package/dist/two-factor/recovery.js.map +1 -0
- package/dist/two-factor/repository.d.ts +156 -0
- package/dist/two-factor/repository.d.ts.map +1 -0
- package/dist/two-factor/repository.js +251 -0
- package/dist/two-factor/repository.js.map +1 -0
- package/dist/two-factor/service.d.ts +232 -0
- package/dist/two-factor/service.d.ts.map +1 -0
- package/dist/two-factor/service.js +599 -0
- package/dist/two-factor/service.js.map +1 -0
- package/dist/two-factor/totp.d.ts +57 -0
- package/dist/two-factor/totp.d.ts.map +1 -0
- package/dist/two-factor/totp.js +96 -0
- package/dist/two-factor/totp.js.map +1 -0
- package/dist/two-factor/types.d.ts +152 -0
- package/dist/two-factor/types.d.ts.map +1 -0
- package/dist/two-factor/types.js +64 -0
- package/dist/two-factor/types.js.map +1 -0
- package/dist/users/cache.d.ts +49 -0
- package/dist/users/cache.d.ts.map +1 -0
- package/dist/users/cache.js +140 -0
- package/dist/users/cache.js.map +1 -0
- package/dist/users/claims.d.ts +67 -0
- package/dist/users/claims.d.ts.map +1 -0
- package/dist/users/claims.js +104 -0
- package/dist/users/claims.js.map +1 -0
- package/dist/users/errors.d.ts +25 -0
- package/dist/users/errors.d.ts.map +1 -0
- package/dist/users/errors.js +31 -0
- package/dist/users/errors.js.map +1 -0
- package/dist/users/gdpr.d.ts +110 -0
- package/dist/users/gdpr.d.ts.map +1 -0
- package/dist/users/gdpr.js +211 -0
- package/dist/users/gdpr.js.map +1 -0
- package/dist/users/index.d.ts +20 -0
- package/dist/users/index.d.ts.map +1 -0
- package/dist/users/index.js +19 -0
- package/dist/users/index.js.map +1 -0
- package/dist/users/password.d.ts +85 -0
- package/dist/users/password.d.ts.map +1 -0
- package/dist/users/password.js +138 -0
- package/dist/users/password.js.map +1 -0
- package/dist/users/repository.d.ts +263 -0
- package/dist/users/repository.d.ts.map +1 -0
- package/dist/users/repository.js +499 -0
- package/dist/users/repository.js.map +1 -0
- package/dist/users/service.d.ts +299 -0
- package/dist/users/service.d.ts.map +1 -0
- package/dist/users/service.js +698 -0
- package/dist/users/service.js.map +1 -0
- package/dist/users/types.d.ts +220 -0
- package/dist/users/types.d.ts.map +1 -0
- package/dist/users/types.js +77 -0
- package/dist/users/types.js.map +1 -0
- package/locales/default/en/common.json +10 -0
- package/locales/default/en/consent.json +13 -0
- package/locales/default/en/emails.json +17 -0
- package/locales/default/en/errors.json +21 -0
- package/locales/default/en/forgot-password.json +11 -0
- package/locales/default/en/invitation.json +18 -0
- package/locales/default/en/login.json +17 -0
- package/locales/default/en/logout.json +9 -0
- package/locales/default/en/magic-link.json +12 -0
- package/locales/default/en/reset-password.json +16 -0
- package/locales/default/en/two-factor.json +29 -0
- package/migrations/.gitkeep +0 -0
- package/migrations/001_extensions.sql +22 -0
- package/migrations/002_organizations.sql +43 -0
- package/migrations/003_applications.sql +50 -0
- package/migrations/004_clients.sql +74 -0
- package/migrations/005_users.sql +119 -0
- package/migrations/006_roles_permissions.sql +80 -0
- package/migrations/007_custom_claims.sql +55 -0
- package/migrations/008_config.sql +51 -0
- package/migrations/009_audit_log.sql +31 -0
- package/migrations/010_oidc_adapter.sql +34 -0
- package/migrations/011_seed.sql +53 -0
- package/migrations/012_two_factor.sql +88 -0
- package/migrations/013_client_secret_sha256.sql +22 -0
- package/migrations/014_login_methods.sql +34 -0
- package/migrations/015_signing_key_encryption.sql +20 -0
- package/migrations/016_failed_login_count.sql +27 -0
- package/migrations/017_audit_retention.sql +11 -0
- package/migrations/018_admin_api_enhancements.sql +78 -0
- package/migrations/019_invitation_details.sql +27 -0
- package/migrations/020_two_factor_admin_permission.sql +108 -0
- package/migrations/021_auth_recovery_jobs.sql +74 -0
- package/migrations/022_recovery_artifact_ownership.sql +25 -0
- package/migrations/023_magic_link_authority.sql +39 -0
- package/package.json +106 -0
- package/templates/default/emails/invitation.hbs +39 -0
- package/templates/default/emails/invitation.txt.hbs +18 -0
- package/templates/default/emails/magic-link.hbs +30 -0
- package/templates/default/emails/magic-link.txt.hbs +11 -0
- package/templates/default/emails/otp-code.hbs +31 -0
- package/templates/default/emails/otp-code.txt.hbs +15 -0
- package/templates/default/emails/password-changed.hbs +24 -0
- package/templates/default/emails/password-changed.txt.hbs +7 -0
- package/templates/default/emails/password-reset.hbs +30 -0
- package/templates/default/emails/password-reset.txt.hbs +11 -0
- package/templates/default/emails/welcome.hbs +25 -0
- package/templates/default/emails/welcome.txt.hbs +9 -0
- package/templates/default/layouts/main.hbs +49 -0
- package/templates/default/pages/accept-invite.hbs +25 -0
- package/templates/default/pages/consent.hbs +27 -0
- package/templates/default/pages/error.hbs +15 -0
- package/templates/default/pages/forgot-password.hbs +21 -0
- package/templates/default/pages/invite-expired.hbs +6 -0
- package/templates/default/pages/invite-success.hbs +9 -0
- package/templates/default/pages/login.hbs +108 -0
- package/templates/default/pages/logout-success.hbs +5 -0
- package/templates/default/pages/logout.hbs +24 -0
- package/templates/default/pages/magic-link-sent.hbs +10 -0
- package/templates/default/pages/magic-link-success.hbs +17 -0
- package/templates/default/pages/reset-password.hbs +20 -0
- package/templates/default/pages/reset-success.hbs +9 -0
- package/templates/default/pages/two-factor-setup.hbs +65 -0
- package/templates/default/pages/two-factor-verify.hbs +77 -0
- package/templates/default/partials/flash-messages.hbs +10 -0
- package/templates/default/partials/footer.hbs +4 -0
- package/templates/default/partials/header.hbs +8 -0
|
@@ -0,0 +1,599 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Two-factor authentication service — business logic orchestrator.
|
|
3
|
+
*
|
|
4
|
+
* Composes the repository, cache, crypto utilities, and audit log
|
|
5
|
+
* to provide the complete 2FA management API. Functions are grouped by:
|
|
6
|
+
*
|
|
7
|
+
* - **Setup**: Initialize email OTP or TOTP, confirm TOTP enrollment
|
|
8
|
+
* - **Verification**: Verify OTP codes, TOTP codes, recovery codes
|
|
9
|
+
* - **Sending**: Generate and send OTP codes via email
|
|
10
|
+
* - **Management**: Get status, disable 2FA, regenerate recovery codes
|
|
11
|
+
* - **Policy**: Check org policy requirements, determine required method
|
|
12
|
+
*
|
|
13
|
+
* All write operations follow the pattern:
|
|
14
|
+
* 1. Validate preconditions (user state, existing 2FA, etc.)
|
|
15
|
+
* 2. Perform DB operations (via repository)
|
|
16
|
+
* 3. Update user 2FA state (via user repository)
|
|
17
|
+
* 4. Invalidate cache
|
|
18
|
+
* 5. Write audit log (fire-and-forget)
|
|
19
|
+
*/
|
|
20
|
+
import { config } from '../config/index.js';
|
|
21
|
+
import { getPool } from '../lib/database.js';
|
|
22
|
+
import { writeAuditLog } from '../lib/audit-log.js';
|
|
23
|
+
import { logger } from '../lib/logger.js';
|
|
24
|
+
import { TwoFactorAlreadyEnabledError, TwoFactorNotEnabledError, TotpNotConfiguredError, OtpExpiredError, OtpInvalidError, RecoveryCodeInvalidError, RecoveryCodesExhaustedError, } from './errors.js';
|
|
25
|
+
import { encryptTotpSecret, decryptTotpSecret } from './crypto.js';
|
|
26
|
+
import { generateOtpCode, hashOtpCode, verifyOtpCode } from './otp.js';
|
|
27
|
+
import { generateTotpSecret, generateTotpUri, generateQrCodeDataUri, verifyTotpCode } from './totp.js';
|
|
28
|
+
import { generateRecoveryCodes, hashRecoveryCode, verifyRecoveryCode as verifyRecoveryCodeHash } from './recovery.js';
|
|
29
|
+
import { insertTotp, findTotpByUserId, markTotpVerified, deleteTotp, insertOtpCode, findActiveOtpCodes, markOtpCodeUsed, deleteExpiredOtpCodes, countActiveOtpCodes, findUnusedRecoveryCodes, markRecoveryCodeUsed, insertRecoveryCodes, deleteAllRecoveryCodes, countUnusedRecoveryCodes, } from './repository.js';
|
|
30
|
+
import { getCachedTwoFactorStatus, cacheTwoFactorStatus, invalidateTwoFactorCache, } from './cache.js';
|
|
31
|
+
import { findUserById, updateUser as repoUpdateUser } from '../users/repository.js';
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
// Constants
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
/** OTP code expiration time in minutes */
|
|
36
|
+
const OTP_EXPIRY_MINUTES = 10;
|
|
37
|
+
/** Maximum active OTP codes per user (rate limiting) */
|
|
38
|
+
const MAX_ACTIVE_OTP_CODES = 5;
|
|
39
|
+
/** Default dev/test encryption key — only used when config key is not set */
|
|
40
|
+
const DEV_ENCRYPTION_KEY = 'a'.repeat(64);
|
|
41
|
+
// ---------------------------------------------------------------------------
|
|
42
|
+
// Helpers
|
|
43
|
+
// ---------------------------------------------------------------------------
|
|
44
|
+
/**
|
|
45
|
+
* Get the 2FA encryption key from config, falling back to a dev key
|
|
46
|
+
* in non-production environments. Production requires a real key.
|
|
47
|
+
*
|
|
48
|
+
* @returns 64-character hex encryption key
|
|
49
|
+
*/
|
|
50
|
+
function getEncryptionKey() {
|
|
51
|
+
if (config.twoFactorEncryptionKey) {
|
|
52
|
+
return config.twoFactorEncryptionKey;
|
|
53
|
+
}
|
|
54
|
+
// In development/test, use a predictable default — this is safe because
|
|
55
|
+
// dev/test TOTP secrets are throwaway data, not real user secrets
|
|
56
|
+
return DEV_ENCRYPTION_KEY;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Generate and hash recovery codes, returning both plaintext and hashes.
|
|
60
|
+
*
|
|
61
|
+
* @returns Object with plaintext codes (for display) and hashes (for storage)
|
|
62
|
+
*/
|
|
63
|
+
async function generateAndHashRecoveryCodes() {
|
|
64
|
+
const plaintextCodes = generateRecoveryCodes();
|
|
65
|
+
const codeHashes = await Promise.all(plaintextCodes.map((code) => hashRecoveryCode(code)));
|
|
66
|
+
return { plaintextCodes, codeHashes };
|
|
67
|
+
}
|
|
68
|
+
// ===========================================================================
|
|
69
|
+
// Setup functions
|
|
70
|
+
// ===========================================================================
|
|
71
|
+
/**
|
|
72
|
+
* Set up email OTP as the 2FA method for a user.
|
|
73
|
+
*
|
|
74
|
+
* Enables 2FA with the 'email' method, generates recovery codes, and
|
|
75
|
+
* marks the user as 2FA-enabled in the users table. No TOTP config
|
|
76
|
+
* is created — email OTP codes are generated on-demand during login.
|
|
77
|
+
*
|
|
78
|
+
* @param userId - User UUID
|
|
79
|
+
* @param orgId - Organization UUID (for audit logging)
|
|
80
|
+
* @returns Setup result with recovery codes
|
|
81
|
+
* @throws TwoFactorAlreadyEnabledError if 2FA is already enabled
|
|
82
|
+
*/
|
|
83
|
+
export async function setupEmailOtp(userId, orgId) {
|
|
84
|
+
// Check preconditions — user must not already have 2FA enabled
|
|
85
|
+
const user = await findUserById(userId);
|
|
86
|
+
if (!user) {
|
|
87
|
+
throw new TwoFactorNotEnabledError(userId);
|
|
88
|
+
}
|
|
89
|
+
// Check if 2FA is already enabled by reading the DB columns directly
|
|
90
|
+
if (user.twoFactorEnabled) {
|
|
91
|
+
throw new TwoFactorAlreadyEnabledError(userId);
|
|
92
|
+
}
|
|
93
|
+
// Generate recovery codes
|
|
94
|
+
const { plaintextCodes, codeHashes } = await generateAndHashRecoveryCodes();
|
|
95
|
+
// Store recovery codes in the database
|
|
96
|
+
await insertRecoveryCodes(userId, codeHashes);
|
|
97
|
+
// Enable 2FA on the user record
|
|
98
|
+
await repoUpdateUser(userId, {
|
|
99
|
+
twoFactorEnabled: true,
|
|
100
|
+
twoFactorMethod: 'email',
|
|
101
|
+
});
|
|
102
|
+
// Invalidate cache since 2FA state changed
|
|
103
|
+
await invalidateTwoFactorCache(userId);
|
|
104
|
+
// Audit log (fire-and-forget)
|
|
105
|
+
await writeAuditLog({
|
|
106
|
+
organizationId: orgId,
|
|
107
|
+
userId,
|
|
108
|
+
eventType: '2fa.setup.email',
|
|
109
|
+
eventCategory: 'auth',
|
|
110
|
+
metadata: { method: 'email' },
|
|
111
|
+
});
|
|
112
|
+
logger.info({ userId, method: 'email' }, '2FA setup complete (email OTP)');
|
|
113
|
+
return {
|
|
114
|
+
method: 'email',
|
|
115
|
+
recoveryCodes: plaintextCodes,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Start TOTP setup for a user.
|
|
120
|
+
*
|
|
121
|
+
* Generates a TOTP secret, encrypts it, stores it as unverified,
|
|
122
|
+
* and returns the otpauth:// URI and QR code for the user to scan.
|
|
123
|
+
* The setup is not complete until the user confirms with a valid
|
|
124
|
+
* TOTP code via `confirmTotpSetup()`.
|
|
125
|
+
*
|
|
126
|
+
* @param userId - User UUID
|
|
127
|
+
* @param email - User email (for TOTP URI display)
|
|
128
|
+
* @param orgSlug - Organization slug (used as TOTP issuer)
|
|
129
|
+
* @returns Setup result with TOTP URI, QR code, and recovery codes
|
|
130
|
+
* @throws TwoFactorAlreadyEnabledError if 2FA is already enabled
|
|
131
|
+
*/
|
|
132
|
+
export async function setupTotp(userId, email, orgSlug) {
|
|
133
|
+
// Check preconditions — user must not already have 2FA enabled
|
|
134
|
+
const user = await findUserById(userId);
|
|
135
|
+
if (!user) {
|
|
136
|
+
throw new TwoFactorNotEnabledError(userId);
|
|
137
|
+
}
|
|
138
|
+
if (user.twoFactorEnabled) {
|
|
139
|
+
throw new TwoFactorAlreadyEnabledError(userId);
|
|
140
|
+
}
|
|
141
|
+
// Delete any existing unverified TOTP config (from previous incomplete setup)
|
|
142
|
+
await deleteTotp(userId);
|
|
143
|
+
// Generate a new TOTP secret and encrypt it for storage
|
|
144
|
+
const secret = generateTotpSecret();
|
|
145
|
+
const encryptionKey = getEncryptionKey();
|
|
146
|
+
const { encrypted, iv, tag } = encryptTotpSecret(secret, encryptionKey);
|
|
147
|
+
// Store the encrypted TOTP config (unverified — verified becomes true after confirm)
|
|
148
|
+
await insertTotp({
|
|
149
|
+
userId,
|
|
150
|
+
encryptedSecret: encrypted,
|
|
151
|
+
encryptionIv: iv,
|
|
152
|
+
encryptionTag: tag,
|
|
153
|
+
});
|
|
154
|
+
// Generate the otpauth URI and QR code for the user
|
|
155
|
+
const issuer = `Porta (${orgSlug})`;
|
|
156
|
+
const totpUri = generateTotpUri(secret, email, issuer);
|
|
157
|
+
const qrCodeDataUri = await generateQrCodeDataUri(totpUri);
|
|
158
|
+
// Generate recovery codes (stored but 2FA not yet enabled — that happens on confirm)
|
|
159
|
+
const { plaintextCodes, codeHashes } = await generateAndHashRecoveryCodes();
|
|
160
|
+
await deleteAllRecoveryCodes(userId); // Clear any old ones
|
|
161
|
+
await insertRecoveryCodes(userId, codeHashes);
|
|
162
|
+
// Invalidate cache since TOTP config changed
|
|
163
|
+
await invalidateTwoFactorCache(userId);
|
|
164
|
+
logger.info({ userId, method: 'totp' }, 'TOTP setup initiated (pending confirmation)');
|
|
165
|
+
return {
|
|
166
|
+
method: 'totp',
|
|
167
|
+
recoveryCodes: plaintextCodes,
|
|
168
|
+
totpUri,
|
|
169
|
+
qrCodeDataUri,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Retrieve existing pending (unverified) TOTP setup info.
|
|
174
|
+
*
|
|
175
|
+
* If a user has a pending TOTP setup (unverified record in DB), this
|
|
176
|
+
* decrypts the secret and regenerates the QR code for display.
|
|
177
|
+
* Does NOT regenerate the secret — preserves the existing one.
|
|
178
|
+
*
|
|
179
|
+
* Returns null if no pending setup exists.
|
|
180
|
+
*
|
|
181
|
+
* @param userId - User UUID
|
|
182
|
+
* @param email - User email (for TOTP URI display)
|
|
183
|
+
* @param orgSlug - Organization slug (used as TOTP issuer)
|
|
184
|
+
* @returns Setup info with QR code and secret, or null if no pending setup
|
|
185
|
+
*/
|
|
186
|
+
export async function getPendingTotpSetupInfo(userId, email, orgSlug) {
|
|
187
|
+
const totp = await findTotpByUserId(userId);
|
|
188
|
+
if (!totp || totp.verified) {
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
// Decrypt the existing secret
|
|
192
|
+
const encryptionKey = getEncryptionKey();
|
|
193
|
+
const secret = decryptTotpSecret(totp.encryptedSecret, totp.encryptionIv, totp.encryptionTag, encryptionKey);
|
|
194
|
+
// Regenerate the URI and QR code from the existing secret
|
|
195
|
+
const issuer = `Porta (${orgSlug})`;
|
|
196
|
+
const totpUri = generateTotpUri(secret, email, issuer);
|
|
197
|
+
const qrCodeDataUri = await generateQrCodeDataUri(totpUri);
|
|
198
|
+
return { totpUri, qrCodeDataUri, totpSecret: secret };
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Confirm TOTP setup by verifying the user's first TOTP code.
|
|
202
|
+
*
|
|
203
|
+
* Called after the user scans the QR code and enters a TOTP code
|
|
204
|
+
* from their authenticator app. This confirms the setup is correct
|
|
205
|
+
* and enables 2FA on the user record.
|
|
206
|
+
*
|
|
207
|
+
* @param userId - User UUID
|
|
208
|
+
* @param code - 6-digit TOTP code from the authenticator app
|
|
209
|
+
* @returns true if the code is valid and setup is confirmed
|
|
210
|
+
* @throws TotpNotConfiguredError if no pending TOTP setup exists
|
|
211
|
+
*/
|
|
212
|
+
export async function confirmTotpSetup(userId, code) {
|
|
213
|
+
// Find the unverified TOTP config
|
|
214
|
+
const totp = await findTotpByUserId(userId);
|
|
215
|
+
if (!totp || totp.verified) {
|
|
216
|
+
throw new TotpNotConfiguredError(userId);
|
|
217
|
+
}
|
|
218
|
+
// Decrypt the secret to verify the code
|
|
219
|
+
const encryptionKey = getEncryptionKey();
|
|
220
|
+
const secret = decryptTotpSecret(totp.encryptedSecret, totp.encryptionIv, totp.encryptionTag, encryptionKey);
|
|
221
|
+
// Verify the TOTP code
|
|
222
|
+
const isValid = verifyTotpCode(code, secret);
|
|
223
|
+
if (!isValid) {
|
|
224
|
+
return false;
|
|
225
|
+
}
|
|
226
|
+
// Mark the TOTP config as verified
|
|
227
|
+
await markTotpVerified(userId);
|
|
228
|
+
// Enable 2FA on the user record
|
|
229
|
+
await repoUpdateUser(userId, {
|
|
230
|
+
twoFactorEnabled: true,
|
|
231
|
+
twoFactorMethod: 'totp',
|
|
232
|
+
});
|
|
233
|
+
// Invalidate cache since 2FA state changed
|
|
234
|
+
await invalidateTwoFactorCache(userId);
|
|
235
|
+
// Audit log (fire-and-forget)
|
|
236
|
+
const user = await findUserById(userId);
|
|
237
|
+
await writeAuditLog({
|
|
238
|
+
organizationId: user?.organizationId,
|
|
239
|
+
userId,
|
|
240
|
+
eventType: '2fa.setup.totp',
|
|
241
|
+
eventCategory: 'auth',
|
|
242
|
+
metadata: { method: 'totp' },
|
|
243
|
+
});
|
|
244
|
+
logger.info({ userId, method: 'totp' }, '2FA setup confirmed (TOTP)');
|
|
245
|
+
return true;
|
|
246
|
+
}
|
|
247
|
+
// ===========================================================================
|
|
248
|
+
// OTP email sending
|
|
249
|
+
// ===========================================================================
|
|
250
|
+
/**
|
|
251
|
+
* Generate and send an OTP code via email.
|
|
252
|
+
*
|
|
253
|
+
* Creates a 6-digit code, hashes it, stores the hash in the database
|
|
254
|
+
* with a 10-minute expiry, and sends the plaintext code via email.
|
|
255
|
+
* Rate-limited to MAX_ACTIVE_OTP_CODES active codes per user.
|
|
256
|
+
*
|
|
257
|
+
* Note: This function generates the code and stores it. The actual email
|
|
258
|
+
* sending is handled by the auth email service (Phase 8). For now, this
|
|
259
|
+
* function prepares the code — the route layer will call the email service.
|
|
260
|
+
*
|
|
261
|
+
* @param userId - User UUID
|
|
262
|
+
* @param email - User email to send the code to
|
|
263
|
+
* @param orgId - Organization UUID (for audit logging)
|
|
264
|
+
* @returns The plaintext OTP code (caller sends via email service)
|
|
265
|
+
* @throws Error if too many active codes exist (rate limiting)
|
|
266
|
+
*/
|
|
267
|
+
export async function sendOtpCode(userId, email, orgId) {
|
|
268
|
+
// Rate limiting — prevent generating too many codes
|
|
269
|
+
const activeCount = await countActiveOtpCodes(userId);
|
|
270
|
+
if (activeCount >= MAX_ACTIVE_OTP_CODES) {
|
|
271
|
+
// Clean up expired codes first, then re-check
|
|
272
|
+
await deleteExpiredOtpCodes(userId);
|
|
273
|
+
const newCount = await countActiveOtpCodes(userId);
|
|
274
|
+
if (newCount >= MAX_ACTIVE_OTP_CODES) {
|
|
275
|
+
throw new Error('Too many active OTP codes. Please wait before requesting a new one.');
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
// Generate a new OTP code
|
|
279
|
+
const plaintext = generateOtpCode();
|
|
280
|
+
const codeHash = hashOtpCode(plaintext);
|
|
281
|
+
// Calculate expiration (10 minutes from now)
|
|
282
|
+
const expiresAt = new Date(Date.now() + OTP_EXPIRY_MINUTES * 60 * 1000);
|
|
283
|
+
// Store the hashed code in the database
|
|
284
|
+
await insertOtpCode(userId, codeHash, expiresAt);
|
|
285
|
+
// Audit log (fire-and-forget)
|
|
286
|
+
await writeAuditLog({
|
|
287
|
+
organizationId: orgId,
|
|
288
|
+
userId,
|
|
289
|
+
eventType: '2fa.otp.sent',
|
|
290
|
+
eventCategory: 'auth',
|
|
291
|
+
metadata: { email },
|
|
292
|
+
});
|
|
293
|
+
logger.debug({ userId, email }, 'OTP code generated for email sending');
|
|
294
|
+
// Return the plaintext code — the caller sends it via email
|
|
295
|
+
return plaintext;
|
|
296
|
+
}
|
|
297
|
+
// ===========================================================================
|
|
298
|
+
// Verification functions
|
|
299
|
+
// ===========================================================================
|
|
300
|
+
/**
|
|
301
|
+
* Verify an email OTP code.
|
|
302
|
+
*
|
|
303
|
+
* Checks all active (unused, not expired) OTP codes for the user,
|
|
304
|
+
* verifying the provided code against each hash. If a match is found,
|
|
305
|
+
* the code is marked as used.
|
|
306
|
+
*
|
|
307
|
+
* @param userId - User UUID
|
|
308
|
+
* @param code - 6-digit OTP code to verify
|
|
309
|
+
* @returns true if the code is valid
|
|
310
|
+
* @throws OtpInvalidError if no matching code is found
|
|
311
|
+
*/
|
|
312
|
+
export async function verifyOtp(userId, code) {
|
|
313
|
+
// Find all active (unused, unexpired) OTP codes for this user
|
|
314
|
+
const activeCodes = await findActiveOtpCodes(userId);
|
|
315
|
+
if (activeCodes.length === 0) {
|
|
316
|
+
throw new OtpExpiredError();
|
|
317
|
+
}
|
|
318
|
+
// Try to match the code against each active hash
|
|
319
|
+
for (const otpRecord of activeCodes) {
|
|
320
|
+
if (verifyOtpCode(code, otpRecord.codeHash)) {
|
|
321
|
+
// Mark the matching code as used
|
|
322
|
+
await markOtpCodeUsed(otpRecord.id);
|
|
323
|
+
// Clean up expired codes in the background
|
|
324
|
+
deleteExpiredOtpCodes(userId).catch((err) => {
|
|
325
|
+
logger.warn({ err, userId }, 'Failed to clean up expired OTP codes');
|
|
326
|
+
});
|
|
327
|
+
return true;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
// No match found
|
|
331
|
+
throw new OtpInvalidError();
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Verify a TOTP code from an authenticator app.
|
|
335
|
+
*
|
|
336
|
+
* Decrypts the user's TOTP secret from the database and verifies
|
|
337
|
+
* the code with a ±1 step time window.
|
|
338
|
+
*
|
|
339
|
+
* @param userId - User UUID
|
|
340
|
+
* @param code - 6-digit TOTP code to verify
|
|
341
|
+
* @returns true if the code is valid
|
|
342
|
+
* @throws TotpNotConfiguredError if no verified TOTP config exists
|
|
343
|
+
*/
|
|
344
|
+
export async function verifyTotp(userId, code) {
|
|
345
|
+
// Find the verified TOTP config
|
|
346
|
+
const totp = await findTotpByUserId(userId);
|
|
347
|
+
if (!totp || !totp.verified) {
|
|
348
|
+
throw new TotpNotConfiguredError(userId);
|
|
349
|
+
}
|
|
350
|
+
// Decrypt the secret
|
|
351
|
+
const encryptionKey = getEncryptionKey();
|
|
352
|
+
const secret = decryptTotpSecret(totp.encryptedSecret, totp.encryptionIv, totp.encryptionTag, encryptionKey);
|
|
353
|
+
// Verify the code with ±1 step window
|
|
354
|
+
return verifyTotpCode(code, secret);
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* Verify a recovery code.
|
|
358
|
+
*
|
|
359
|
+
* Recovery codes use Argon2id hashing with random salt, so we must
|
|
360
|
+
* iterate all unused codes and verify against each hash. If a match
|
|
361
|
+
* is found, the code is marked as used (single-use).
|
|
362
|
+
*
|
|
363
|
+
* @param userId - User UUID
|
|
364
|
+
* @param code - Recovery code in XXXX-XXXX format (case-insensitive)
|
|
365
|
+
* @returns true if the code is valid
|
|
366
|
+
* @throws RecoveryCodesExhaustedError if no unused codes remain
|
|
367
|
+
* @throws RecoveryCodeInvalidError if the code doesn't match any hash
|
|
368
|
+
*/
|
|
369
|
+
export async function verifyRecoveryCode(userId, code) {
|
|
370
|
+
// Find all unused recovery codes
|
|
371
|
+
const unusedCodes = await findUnusedRecoveryCodes(userId);
|
|
372
|
+
if (unusedCodes.length === 0) {
|
|
373
|
+
throw new RecoveryCodesExhaustedError(userId);
|
|
374
|
+
}
|
|
375
|
+
// Try to match against each unused code hash (Argon2id — must iterate)
|
|
376
|
+
for (const recoveryRecord of unusedCodes) {
|
|
377
|
+
const isMatch = await verifyRecoveryCodeHash(code, recoveryRecord.codeHash);
|
|
378
|
+
if (isMatch) {
|
|
379
|
+
// Mark the matching code as used
|
|
380
|
+
await markRecoveryCodeUsed(recoveryRecord.id);
|
|
381
|
+
// Invalidate cache since recovery code count changed
|
|
382
|
+
await invalidateTwoFactorCache(userId);
|
|
383
|
+
// Audit log
|
|
384
|
+
const remaining = unusedCodes.length - 1;
|
|
385
|
+
await writeAuditLog({
|
|
386
|
+
userId,
|
|
387
|
+
eventType: '2fa.recovery.used',
|
|
388
|
+
eventCategory: 'auth',
|
|
389
|
+
metadata: { remainingCodes: remaining },
|
|
390
|
+
});
|
|
391
|
+
logger.info({ userId, remainingCodes: remaining }, 'Recovery code used');
|
|
392
|
+
return true;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
// No match found
|
|
396
|
+
throw new RecoveryCodeInvalidError();
|
|
397
|
+
}
|
|
398
|
+
// ===========================================================================
|
|
399
|
+
// Management functions
|
|
400
|
+
// ===========================================================================
|
|
401
|
+
/**
|
|
402
|
+
* Get the current 2FA status for a user.
|
|
403
|
+
*
|
|
404
|
+
* Checks Redis cache first, falls back to building the status from
|
|
405
|
+
* the database. The status includes whether 2FA is enabled, the method,
|
|
406
|
+
* whether TOTP is configured (and verified), and remaining recovery codes.
|
|
407
|
+
*
|
|
408
|
+
* @param userId - User UUID
|
|
409
|
+
* @returns Current 2FA status
|
|
410
|
+
*/
|
|
411
|
+
export async function getTwoFactorStatus(userId) {
|
|
412
|
+
// Try cache first
|
|
413
|
+
const cached = await getCachedTwoFactorStatus(userId);
|
|
414
|
+
if (cached)
|
|
415
|
+
return cached;
|
|
416
|
+
// Build status from database
|
|
417
|
+
const user = await findUserById(userId);
|
|
418
|
+
// Default status for unknown users — not enabled
|
|
419
|
+
if (!user) {
|
|
420
|
+
return {
|
|
421
|
+
enabled: false,
|
|
422
|
+
method: null,
|
|
423
|
+
totpConfigured: false,
|
|
424
|
+
recoveryCodesRemaining: 0,
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
// Check TOTP config existence and verification status
|
|
428
|
+
const totp = await findTotpByUserId(userId);
|
|
429
|
+
const recoveryCount = await countUnusedRecoveryCodes(userId);
|
|
430
|
+
const status = {
|
|
431
|
+
enabled: user.twoFactorEnabled,
|
|
432
|
+
method: user.twoFactorMethod ?? null,
|
|
433
|
+
totpConfigured: totp !== null && totp.verified,
|
|
434
|
+
recoveryCodesRemaining: recoveryCount,
|
|
435
|
+
};
|
|
436
|
+
// Cache the result
|
|
437
|
+
await cacheTwoFactorStatus(userId, status);
|
|
438
|
+
return status;
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* Disable 2FA for a user.
|
|
442
|
+
*
|
|
443
|
+
* Removes all 2FA data: TOTP config, active OTP codes, and recovery codes.
|
|
444
|
+
* Resets the user's 2FA flags in the users table.
|
|
445
|
+
*
|
|
446
|
+
* @param userId - User UUID
|
|
447
|
+
* @throws TwoFactorNotEnabledError if 2FA is not enabled
|
|
448
|
+
*/
|
|
449
|
+
export async function disableTwoFactor(userId) {
|
|
450
|
+
const user = await findUserById(userId);
|
|
451
|
+
if (!user || !user.twoFactorEnabled) {
|
|
452
|
+
throw new TwoFactorNotEnabledError(userId);
|
|
453
|
+
}
|
|
454
|
+
// Delete all 2FA data
|
|
455
|
+
await deleteTotp(userId);
|
|
456
|
+
await deleteAllRecoveryCodes(userId);
|
|
457
|
+
await deleteExpiredOtpCodes(userId);
|
|
458
|
+
// Reset 2FA flags on the user record
|
|
459
|
+
await repoUpdateUser(userId, {
|
|
460
|
+
twoFactorEnabled: false,
|
|
461
|
+
twoFactorMethod: null,
|
|
462
|
+
});
|
|
463
|
+
// Invalidate cache
|
|
464
|
+
await invalidateTwoFactorCache(userId);
|
|
465
|
+
// Audit log
|
|
466
|
+
await writeAuditLog({
|
|
467
|
+
organizationId: user.organizationId,
|
|
468
|
+
userId,
|
|
469
|
+
eventType: '2fa.disabled',
|
|
470
|
+
eventCategory: 'auth',
|
|
471
|
+
metadata: { previousMethod: user.twoFactorMethod },
|
|
472
|
+
});
|
|
473
|
+
logger.info({ userId }, '2FA disabled');
|
|
474
|
+
}
|
|
475
|
+
/**
|
|
476
|
+
* Regenerate recovery codes for a user.
|
|
477
|
+
*
|
|
478
|
+
* Deletes all existing recovery codes and generates a fresh set of 10.
|
|
479
|
+
* The user must have 2FA enabled to regenerate codes.
|
|
480
|
+
*
|
|
481
|
+
* @param userId - User UUID
|
|
482
|
+
* @returns Array of new plaintext recovery codes (shown once to the user)
|
|
483
|
+
* @throws TwoFactorNotEnabledError if 2FA is not enabled
|
|
484
|
+
*/
|
|
485
|
+
export async function regenerateRecoveryCodes(userId) {
|
|
486
|
+
const user = await findUserById(userId);
|
|
487
|
+
if (!user || !user.twoFactorEnabled) {
|
|
488
|
+
throw new TwoFactorNotEnabledError(userId);
|
|
489
|
+
}
|
|
490
|
+
// Delete old recovery codes
|
|
491
|
+
await deleteAllRecoveryCodes(userId);
|
|
492
|
+
// Generate new recovery codes
|
|
493
|
+
const { plaintextCodes, codeHashes } = await generateAndHashRecoveryCodes();
|
|
494
|
+
await insertRecoveryCodes(userId, codeHashes);
|
|
495
|
+
// Invalidate cache since recovery code count changed
|
|
496
|
+
await invalidateTwoFactorCache(userId);
|
|
497
|
+
// Audit log
|
|
498
|
+
await writeAuditLog({
|
|
499
|
+
organizationId: user.organizationId,
|
|
500
|
+
userId,
|
|
501
|
+
eventType: '2fa.recovery.regenerated',
|
|
502
|
+
eventCategory: 'auth',
|
|
503
|
+
});
|
|
504
|
+
logger.info({ userId }, 'Recovery codes regenerated');
|
|
505
|
+
return plaintextCodes;
|
|
506
|
+
}
|
|
507
|
+
/**
|
|
508
|
+
* Check if the organization's 2FA policy requires 2FA for a user.
|
|
509
|
+
*
|
|
510
|
+
* Returns true if the org policy is anything other than 'optional'
|
|
511
|
+
* AND the user has not yet enabled 2FA.
|
|
512
|
+
*
|
|
513
|
+
* @param org - Organization with twoFactorPolicy field
|
|
514
|
+
* @param user - User with 2FA state fields
|
|
515
|
+
* @returns true if 2FA is required but not yet set up
|
|
516
|
+
*/
|
|
517
|
+
export function requiresTwoFactor(org, user) {
|
|
518
|
+
// If policy is optional, 2FA is never required
|
|
519
|
+
if (org.twoFactorPolicy === 'optional') {
|
|
520
|
+
return false;
|
|
521
|
+
}
|
|
522
|
+
// If user already has 2FA enabled, requirement is met
|
|
523
|
+
if (user.twoFactorEnabled) {
|
|
524
|
+
return false;
|
|
525
|
+
}
|
|
526
|
+
// Policy requires 2FA but user hasn't set it up yet
|
|
527
|
+
return true;
|
|
528
|
+
}
|
|
529
|
+
/**
|
|
530
|
+
* Determine which 2FA method the user should use based on org policy.
|
|
531
|
+
*
|
|
532
|
+
* If the user has 2FA enabled, returns their configured method.
|
|
533
|
+
* If the org requires a specific method, returns that method.
|
|
534
|
+
* If the org requires 'any', returns null (user chooses).
|
|
535
|
+
* If the policy is optional, returns null.
|
|
536
|
+
*
|
|
537
|
+
* @param org - Organization with twoFactorPolicy field
|
|
538
|
+
* @param user - User with 2FA state fields
|
|
539
|
+
* @returns The 2FA method to use, or null if no method is required/configured
|
|
540
|
+
*/
|
|
541
|
+
export function determineTwoFactorMethod(org, user) {
|
|
542
|
+
// If user already has 2FA enabled, use their method
|
|
543
|
+
if (user.twoFactorEnabled && user.twoFactorMethod) {
|
|
544
|
+
return user.twoFactorMethod;
|
|
545
|
+
}
|
|
546
|
+
// Map org policy to required method
|
|
547
|
+
switch (org.twoFactorPolicy) {
|
|
548
|
+
case 'required_email':
|
|
549
|
+
return 'email';
|
|
550
|
+
case 'required_totp':
|
|
551
|
+
return 'totp';
|
|
552
|
+
case 'required_any':
|
|
553
|
+
// User gets to choose — return null to indicate "choose any"
|
|
554
|
+
return null;
|
|
555
|
+
case 'optional':
|
|
556
|
+
default:
|
|
557
|
+
return null;
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
/**
|
|
561
|
+
* Get an aggregate 2FA enrollment summary for an organization.
|
|
562
|
+
*
|
|
563
|
+
* Runs a single SQL query with conditional aggregation to count
|
|
564
|
+
* users by 2FA status, method, and calculate compliance rate.
|
|
565
|
+
*
|
|
566
|
+
* @param orgId - Organization UUID
|
|
567
|
+
* @returns Aggregate summary with counts and compliance rate
|
|
568
|
+
*/
|
|
569
|
+
export async function getTwoFactorSummary(orgId) {
|
|
570
|
+
const pool = getPool();
|
|
571
|
+
const result = await pool.query(`SELECT
|
|
572
|
+
COUNT(*)::text AS total_users,
|
|
573
|
+
COUNT(*) FILTER (WHERE two_factor_enabled = TRUE)::text AS enabled_count,
|
|
574
|
+
COUNT(*) FILTER (WHERE two_factor_enabled = FALSE)::text AS disabled_count,
|
|
575
|
+
COUNT(*) FILTER (WHERE two_factor_method = 'totp')::text AS totp_count,
|
|
576
|
+
COUNT(*) FILTER (WHERE two_factor_method = 'email')::text AS email_count
|
|
577
|
+
FROM users
|
|
578
|
+
WHERE organization_id = $1
|
|
579
|
+
AND status != 'archived'`, [orgId]);
|
|
580
|
+
const row = result.rows[0];
|
|
581
|
+
const totalUsers = parseInt(row.total_users, 10);
|
|
582
|
+
const enabledCount = parseInt(row.enabled_count, 10);
|
|
583
|
+
const disabledCount = parseInt(row.disabled_count, 10);
|
|
584
|
+
const totpCount = parseInt(row.totp_count, 10);
|
|
585
|
+
const emailCount = parseInt(row.email_count, 10);
|
|
586
|
+
// Compliance rate: percentage of non-archived users with 2FA enabled (AR #92)
|
|
587
|
+
const complianceRate = totalUsers > 0
|
|
588
|
+
? Math.round((enabledCount / totalUsers) * 10000) / 10000
|
|
589
|
+
: 0;
|
|
590
|
+
return {
|
|
591
|
+
totalUsers,
|
|
592
|
+
enabledCount,
|
|
593
|
+
disabledCount,
|
|
594
|
+
totpCount,
|
|
595
|
+
emailCount,
|
|
596
|
+
complianceRate,
|
|
597
|
+
};
|
|
598
|
+
}
|
|
599
|
+
//# sourceMappingURL=service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/two-factor/service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE1C,OAAO,EACL,4BAA4B,EAC5B,wBAAwB,EACxB,sBAAsB,EACtB,eAAe,EACf,eAAe,EACf,wBAAwB,EACxB,2BAA2B,GAC5B,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACnE,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACvE,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,qBAAqB,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACvG,OAAO,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,kBAAkB,IAAI,sBAAsB,EAAE,MAAM,eAAe,CAAC;AACtH,OAAO,EACL,UAAU,EACV,gBAAgB,EAChB,gBAAgB,EAChB,UAAU,EACV,aAAa,EACb,kBAAkB,EAClB,eAAe,EACf,qBAAqB,EACrB,mBAAmB,EACnB,uBAAuB,EACvB,oBAAoB,EACpB,mBAAmB,EACnB,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,wBAAwB,EACxB,oBAAoB,EACpB,wBAAwB,GACzB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,YAAY,EAAE,UAAU,IAAI,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAEpF,8EAA8E;AAC9E,YAAY;AACZ,8EAA8E;AAE9E,0CAA0C;AAC1C,MAAM,kBAAkB,GAAG,EAAE,CAAC;AAE9B,wDAAwD;AACxD,MAAM,oBAAoB,GAAG,CAAC,CAAC;AAE/B,6EAA6E;AAC7E,MAAM,kBAAkB,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;AAE1C,8EAA8E;AAC9E,UAAU;AACV,8EAA8E;AAE9E;;;;;GAKG;AACH,SAAS,gBAAgB;IACvB,IAAI,MAAM,CAAC,sBAAsB,EAAE,CAAC;QAClC,OAAO,MAAM,CAAC,sBAAsB,CAAC;IACvC,CAAC;IACD,wEAAwE;IACxE,kEAAkE;IAClE,OAAO,kBAAkB,CAAC;AAC5B,CAAC;AAED;;;;GAIG;AACH,KAAK,UAAU,4BAA4B;IAIzC,MAAM,cAAc,GAAG,qBAAqB,EAAE,CAAC;IAC/C,MAAM,UAAU,GAAG,MAAM,OAAO,CAAC,GAAG,CAClC,cAAc,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,CACrD,CAAC;IACF,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,CAAC;AACxC,CAAC;AAED,8EAA8E;AAC9E,kBAAkB;AAClB,8EAA8E;AAE9E;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,MAAc,EACd,KAAa;IAEb,+DAA+D;IAC/D,MAAM,IAAI,GAAG,MAAM,YAAY,CAAC,MAAM,CAAC,CAAC;IACxC,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,wBAAwB,CAAC,MAAM,CAAC,CAAC;IAC7C,CAAC;IAED,qEAAqE;IACrE,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAC1B,MAAM,IAAI,4BAA4B,CAAC,MAAM,CAAC,CAAC;IACjD,CAAC;IAED,0BAA0B;IAC1B,MAAM,EAAE,cAAc,EAAE,UAAU,EAAE,GAAG,MAAM,4BAA4B,EAAE,CAAC;IAE5E,uCAAuC;IACvC,MAAM,mBAAmB,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAE9C,gCAAgC;IAChC,MAAM,cAAc,CAAC,MAAM,EAAE;QAC3B,gBAAgB,EAAE,IAAI;QACtB,eAAe,EAAE,OAAO;KACzB,CAAC,CAAC;IAEH,2CAA2C;IAC3C,MAAM,wBAAwB,CAAC,MAAM,CAAC,CAAC;IAEvC,8BAA8B;IAC9B,MAAM,aAAa,CAAC;QAClB,cAAc,EAAE,KAAK;QACrB,MAAM;QACN,SAAS,EAAE,iBAAiB;QAC5B,aAAa,EAAE,MAAM;QACrB,QAAQ,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE;KAC9B,CAAC,CAAC;IAEH,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,gCAAgC,CAAC,CAAC;IAE3E,OAAO;QACL,MAAM,EAAE,OAAO;QACf,aAAa,EAAE,cAAc;KAC9B,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,MAAc,EACd,KAAa,EACb,OAAe;IAEf,+DAA+D;IAC/D,MAAM,IAAI,GAAG,MAAM,YAAY,CAAC,MAAM,CAAC,CAAC;IACxC,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,wBAAwB,CAAC,MAAM,CAAC,CAAC;IAC7C,CAAC;IAED,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAC1B,MAAM,IAAI,4BAA4B,CAAC,MAAM,CAAC,CAAC;IACjD,CAAC;IAED,8EAA8E;IAC9E,MAAM,UAAU,CAAC,MAAM,CAAC,CAAC;IAEzB,wDAAwD;IACxD,MAAM,MAAM,GAAG,kBAAkB,EAAE,CAAC;IACpC,MAAM,aAAa,GAAG,gBAAgB,EAAE,CAAC;IACzC,MAAM,EAAE,SAAS,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,iBAAiB,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IAExE,qFAAqF;IACrF,MAAM,UAAU,CAAC;QACf,MAAM;QACN,eAAe,EAAE,SAAS;QAC1B,YAAY,EAAE,EAAE;QAChB,aAAa,EAAE,GAAG;KACnB,CAAC,CAAC;IAEH,oDAAoD;IACpD,MAAM,MAAM,GAAG,UAAU,OAAO,GAAG,CAAC;IACpC,MAAM,OAAO,GAAG,eAAe,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;IACvD,MAAM,aAAa,GAAG,MAAM,qBAAqB,CAAC,OAAO,CAAC,CAAC;IAE3D,qFAAqF;IACrF,MAAM,EAAE,cAAc,EAAE,UAAU,EAAE,GAAG,MAAM,4BAA4B,EAAE,CAAC;IAC5E,MAAM,sBAAsB,CAAC,MAAM,CAAC,CAAC,CAAC,qBAAqB;IAC3D,MAAM,mBAAmB,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAE9C,6CAA6C;IAC7C,MAAM,wBAAwB,CAAC,MAAM,CAAC,CAAC;IAEvC,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,6CAA6C,CAAC,CAAC;IAEvF,OAAO;QACL,MAAM,EAAE,MAAM;QACd,aAAa,EAAE,cAAc;QAC7B,OAAO;QACP,aAAa;KACd,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,MAAc,EACd,KAAa,EACb,OAAe;IAEf,MAAM,IAAI,GAAG,MAAM,gBAAgB,CAAC,MAAM,CAAC,CAAC;IAC5C,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QAC3B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,8BAA8B;IAC9B,MAAM,aAAa,GAAG,gBAAgB,EAAE,CAAC;IACzC,MAAM,MAAM,GAAG,iBAAiB,CAC9B,IAAI,CAAC,eAAe,EACpB,IAAI,CAAC,YAAY,EACjB,IAAI,CAAC,aAAa,EAClB,aAAa,CACd,CAAC;IAEF,0DAA0D;IAC1D,MAAM,MAAM,GAAG,UAAU,OAAO,GAAG,CAAC;IACpC,MAAM,OAAO,GAAG,eAAe,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;IACvD,MAAM,aAAa,GAAG,MAAM,qBAAqB,CAAC,OAAO,CAAC,CAAC;IAE3D,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC;AACxD,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,MAAc,EACd,IAAY;IAEZ,kCAAkC;IAClC,MAAM,IAAI,GAAG,MAAM,gBAAgB,CAAC,MAAM,CAAC,CAAC;IAC5C,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QAC3B,MAAM,IAAI,sBAAsB,CAAC,MAAM,CAAC,CAAC;IAC3C,CAAC;IAED,wCAAwC;IACxC,MAAM,aAAa,GAAG,gBAAgB,EAAE,CAAC;IACzC,MAAM,MAAM,GAAG,iBAAiB,CAC9B,IAAI,CAAC,eAAe,EACpB,IAAI,CAAC,YAAY,EACjB,IAAI,CAAC,aAAa,EAClB,aAAa,CACd,CAAC;IAEF,uBAAuB;IACvB,MAAM,OAAO,GAAG,cAAc,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAC7C,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,KAAK,CAAC;IACf,CAAC;IAED,mCAAmC;IACnC,MAAM,gBAAgB,CAAC,MAAM,CAAC,CAAC;IAE/B,gCAAgC;IAChC,MAAM,cAAc,CAAC,MAAM,EAAE;QAC3B,gBAAgB,EAAE,IAAI;QACtB,eAAe,EAAE,MAAM;KACxB,CAAC,CAAC;IAEH,2CAA2C;IAC3C,MAAM,wBAAwB,CAAC,MAAM,CAAC,CAAC;IAEvC,8BAA8B;IAC9B,MAAM,IAAI,GAAG,MAAM,YAAY,CAAC,MAAM,CAAC,CAAC;IACxC,MAAM,aAAa,CAAC;QAClB,cAAc,EAAE,IAAI,EAAE,cAAc;QACpC,MAAM;QACN,SAAS,EAAE,gBAAgB;QAC3B,aAAa,EAAE,MAAM;QACrB,QAAQ,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE;KAC7B,CAAC,CAAC;IAEH,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,4BAA4B,CAAC,CAAC;IAEtE,OAAO,IAAI,CAAC;AACd,CAAC;AAED,8EAA8E;AAC9E,oBAAoB;AACpB,8EAA8E;AAE9E;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,MAAc,EACd,KAAa,EACb,KAAa;IAEb,oDAAoD;IACpD,MAAM,WAAW,GAAG,MAAM,mBAAmB,CAAC,MAAM,CAAC,CAAC;IACtD,IAAI,WAAW,IAAI,oBAAoB,EAAE,CAAC;QACxC,8CAA8C;QAC9C,MAAM,qBAAqB,CAAC,MAAM,CAAC,CAAC;QACpC,MAAM,QAAQ,GAAG,MAAM,mBAAmB,CAAC,MAAM,CAAC,CAAC;QACnD,IAAI,QAAQ,IAAI,oBAAoB,EAAE,CAAC;YACrC,MAAM,IAAI,KAAK,CAAC,qEAAqE,CAAC,CAAC;QACzF,CAAC;IACH,CAAC;IAED,0BAA0B;IAC1B,MAAM,SAAS,GAAG,eAAe,EAAE,CAAC;IACpC,MAAM,QAAQ,GAAG,WAAW,CAAC,SAAS,CAAC,CAAC;IAExC,6CAA6C;IAC7C,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,kBAAkB,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;IAExE,wCAAwC;IACxC,MAAM,aAAa,CAAC,MAAM,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC;IAEjD,8BAA8B;IAC9B,MAAM,aAAa,CAAC;QAClB,cAAc,EAAE,KAAK;QACrB,MAAM;QACN,SAAS,EAAE,cAAc;QACzB,aAAa,EAAE,MAAM;QACrB,QAAQ,EAAE,EAAE,KAAK,EAAE;KACpB,CAAC,CAAC;IAEH,MAAM,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,sCAAsC,CAAC,CAAC;IAExE,4DAA4D;IAC5D,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,8EAA8E;AAC9E,yBAAyB;AACzB,8EAA8E;AAE9E;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,MAAc,EACd,IAAY;IAEZ,8DAA8D;IAC9D,MAAM,WAAW,GAAG,MAAM,kBAAkB,CAAC,MAAM,CAAC,CAAC;IAErD,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC7B,MAAM,IAAI,eAAe,EAAE,CAAC;IAC9B,CAAC;IAED,iDAAiD;IACjD,KAAK,MAAM,SAAS,IAAI,WAAW,EAAE,CAAC;QACpC,IAAI,aAAa,CAAC,IAAI,EAAE,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC5C,iCAAiC;YACjC,MAAM,eAAe,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;YAEpC,2CAA2C;YAC3C,qBAAqB,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;gBAC1C,MAAM,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,sCAAsC,CAAC,CAAC;YACvE,CAAC,CAAC,CAAC;YAEH,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED,iBAAiB;IACjB,MAAM,IAAI,eAAe,EAAE,CAAC;AAC9B,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,MAAc,EACd,IAAY;IAEZ,gCAAgC;IAChC,MAAM,IAAI,GAAG,MAAM,gBAAgB,CAAC,MAAM,CAAC,CAAC;IAC5C,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;QAC5B,MAAM,IAAI,sBAAsB,CAAC,MAAM,CAAC,CAAC;IAC3C,CAAC;IAED,qBAAqB;IACrB,MAAM,aAAa,GAAG,gBAAgB,EAAE,CAAC;IACzC,MAAM,MAAM,GAAG,iBAAiB,CAC9B,IAAI,CAAC,eAAe,EACpB,IAAI,CAAC,YAAY,EACjB,IAAI,CAAC,aAAa,EAClB,aAAa,CACd,CAAC;IAEF,sCAAsC;IACtC,OAAO,cAAc,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AACtC,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,MAAc,EACd,IAAY;IAEZ,iCAAiC;IACjC,MAAM,WAAW,GAAG,MAAM,uBAAuB,CAAC,MAAM,CAAC,CAAC;IAE1D,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC7B,MAAM,IAAI,2BAA2B,CAAC,MAAM,CAAC,CAAC;IAChD,CAAC;IAED,uEAAuE;IACvE,KAAK,MAAM,cAAc,IAAI,WAAW,EAAE,CAAC;QACzC,MAAM,OAAO,GAAG,MAAM,sBAAsB,CAAC,IAAI,EAAE,cAAc,CAAC,QAAQ,CAAC,CAAC;QAC5E,IAAI,OAAO,EAAE,CAAC;YACZ,iCAAiC;YACjC,MAAM,oBAAoB,CAAC,cAAc,CAAC,EAAE,CAAC,CAAC;YAE9C,qDAAqD;YACrD,MAAM,wBAAwB,CAAC,MAAM,CAAC,CAAC;YAEvC,YAAY;YACZ,MAAM,SAAS,GAAG,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC;YACzC,MAAM,aAAa,CAAC;gBAClB,MAAM;gBACN,SAAS,EAAE,mBAAmB;gBAC9B,aAAa,EAAE,MAAM;gBACrB,QAAQ,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE;aACxC,CAAC,CAAC;YAEH,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,cAAc,EAAE,SAAS,EAAE,EAAE,oBAAoB,CAAC,CAAC;YAEzE,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED,iBAAiB;IACjB,MAAM,IAAI,wBAAwB,EAAE,CAAC;AACvC,CAAC;AAED,8EAA8E;AAC9E,uBAAuB;AACvB,8EAA8E;AAE9E;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,MAAc;IACrD,kBAAkB;IAClB,MAAM,MAAM,GAAG,MAAM,wBAAwB,CAAC,MAAM,CAAC,CAAC;IACtD,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IAE1B,6BAA6B;IAC7B,MAAM,IAAI,GAAG,MAAM,YAAY,CAAC,MAAM,CAAC,CAAC;IAExC,iDAAiD;IACjD,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO;YACL,OAAO,EAAE,KAAK;YACd,MAAM,EAAE,IAAI;YACZ,cAAc,EAAE,KAAK;YACrB,sBAAsB,EAAE,CAAC;SAC1B,CAAC;IACJ,CAAC;IAED,sDAAsD;IACtD,MAAM,IAAI,GAAG,MAAM,gBAAgB,CAAC,MAAM,CAAC,CAAC;IAC5C,MAAM,aAAa,GAAG,MAAM,wBAAwB,CAAC,MAAM,CAAC,CAAC;IAE7D,MAAM,MAAM,GAAoB;QAC9B,OAAO,EAAE,IAAI,CAAC,gBAAgB;QAC9B,MAAM,EAAE,IAAI,CAAC,eAAe,IAAI,IAAI;QACpC,cAAc,EAAE,IAAI,KAAK,IAAI,IAAI,IAAI,CAAC,QAAQ;QAC9C,sBAAsB,EAAE,aAAa;KACtC,CAAC;IAEF,mBAAmB;IACnB,MAAM,oBAAoB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAE3C,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,MAAc;IACnD,MAAM,IAAI,GAAG,MAAM,YAAY,CAAC,MAAM,CAAC,CAAC;IACxC,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACpC,MAAM,IAAI,wBAAwB,CAAC,MAAM,CAAC,CAAC;IAC7C,CAAC;IAED,sBAAsB;IACtB,MAAM,UAAU,CAAC,MAAM,CAAC,CAAC;IACzB,MAAM,sBAAsB,CAAC,MAAM,CAAC,CAAC;IACrC,MAAM,qBAAqB,CAAC,MAAM,CAAC,CAAC;IAEpC,qCAAqC;IACrC,MAAM,cAAc,CAAC,MAAM,EAAE;QAC3B,gBAAgB,EAAE,KAAK;QACvB,eAAe,EAAE,IAAI;KACtB,CAAC,CAAC;IAEH,mBAAmB;IACnB,MAAM,wBAAwB,CAAC,MAAM,CAAC,CAAC;IAEvC,YAAY;IACZ,MAAM,aAAa,CAAC;QAClB,cAAc,EAAE,IAAI,CAAC,cAAc;QACnC,MAAM;QACN,SAAS,EAAE,cAAc;QACzB,aAAa,EAAE,MAAM;QACrB,QAAQ,EAAE,EAAE,cAAc,EAAE,IAAI,CAAC,eAAe,EAAE;KACnD,CAAC,CAAC;IAEH,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,EAAE,cAAc,CAAC,CAAC;AAC1C,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAAC,MAAc;IAC1D,MAAM,IAAI,GAAG,MAAM,YAAY,CAAC,MAAM,CAAC,CAAC;IACxC,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACpC,MAAM,IAAI,wBAAwB,CAAC,MAAM,CAAC,CAAC;IAC7C,CAAC;IAED,4BAA4B;IAC5B,MAAM,sBAAsB,CAAC,MAAM,CAAC,CAAC;IAErC,8BAA8B;IAC9B,MAAM,EAAE,cAAc,EAAE,UAAU,EAAE,GAAG,MAAM,4BAA4B,EAAE,CAAC;IAC5E,MAAM,mBAAmB,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAE9C,qDAAqD;IACrD,MAAM,wBAAwB,CAAC,MAAM,CAAC,CAAC;IAEvC,YAAY;IACZ,MAAM,aAAa,CAAC;QAClB,cAAc,EAAE,IAAI,CAAC,cAAc;QACnC,MAAM;QACN,SAAS,EAAE,0BAA0B;QACrC,aAAa,EAAE,MAAM;KACtB,CAAC,CAAC;IAEH,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,EAAE,4BAA4B,CAAC,CAAC;IAEtD,OAAO,cAAc,CAAC;AACxB,CAAC;AAuBD;;;;;;;;;GASG;AACH,MAAM,UAAU,iBAAiB,CAAC,GAAiB,EAAE,IAAmB;IACtE,+CAA+C;IAC/C,IAAI,GAAG,CAAC,eAAe,KAAK,UAAU,EAAE,CAAC;QACvC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,sDAAsD;IACtD,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAC1B,OAAO,KAAK,CAAC;IACf,CAAC;IAED,oDAAoD;IACpD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,wBAAwB,CACtC,GAAiB,EACjB,IAAmB;IAEnB,oDAAoD;IACpD,IAAI,IAAI,CAAC,gBAAgB,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;QAClD,OAAO,IAAI,CAAC,eAAe,CAAC;IAC9B,CAAC;IAED,oCAAoC;IACpC,QAAQ,GAAG,CAAC,eAAe,EAAE,CAAC;QAC5B,KAAK,gBAAgB;YACnB,OAAO,OAAO,CAAC;QACjB,KAAK,eAAe;YAClB,OAAO,MAAM,CAAC;QAChB,KAAK,cAAc;YACjB,6DAA6D;YAC7D,OAAO,IAAI,CAAC;QACd,KAAK,UAAU,CAAC;QAChB;YACE,OAAO,IAAI,CAAC;IAChB,CAAC;AACH,CAAC;AAmBD;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,KAAa;IACrD,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;IAEvB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAO7B;;;;;;;;gCAQ4B,EAC5B,CAAC,KAAK,CAAC,CACR,CAAC;IAEF,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC3B,MAAM,UAAU,GAAG,QAAQ,CAAC,GAAG,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;IACjD,MAAM,YAAY,GAAG,QAAQ,CAAC,GAAG,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC;IACrD,MAAM,aAAa,GAAG,QAAQ,CAAC,GAAG,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC;IACvD,MAAM,SAAS,GAAG,QAAQ,CAAC,GAAG,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IAC/C,MAAM,UAAU,GAAG,QAAQ,CAAC,GAAG,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;IAEjD,8EAA8E;IAC9E,MAAM,cAAc,GAAG,UAAU,GAAG,CAAC;QACnC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,YAAY,GAAG,UAAU,CAAC,GAAG,KAAK,CAAC,GAAG,KAAK;QACzD,CAAC,CAAC,CAAC,CAAC;IAEN,OAAO;QACL,UAAU;QACV,YAAY;QACZ,aAAa;QACb,SAAS;QACT,UAAU;QACV,cAAc;KACf,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TOTP (Time-based One-Time Password) utilities.
|
|
3
|
+
*
|
|
4
|
+
* Implements RFC 6238 TOTP using the `otpauth` library. Provides secret
|
|
5
|
+
* generation, otpauth:// URI construction, QR code generation for
|
|
6
|
+
* authenticator app enrollment, and code verification with a ±1 step
|
|
7
|
+
* time window to accommodate clock drift.
|
|
8
|
+
*
|
|
9
|
+
* QR code generation uses the `qrcode` library to produce data URIs
|
|
10
|
+
* that can be displayed inline in Handlebars templates.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Generate a new TOTP secret.
|
|
14
|
+
*
|
|
15
|
+
* Creates a 20-byte (160-bit) random secret, which is the standard
|
|
16
|
+
* size for SHA-1-based TOTP as recommended by RFC 4226 §4.
|
|
17
|
+
* The secret is returned as a base32-encoded string suitable for
|
|
18
|
+
* storage (after encryption) and otpauth:// URI construction.
|
|
19
|
+
*
|
|
20
|
+
* @returns Base32-encoded TOTP secret
|
|
21
|
+
*/
|
|
22
|
+
export declare function generateTotpSecret(): string;
|
|
23
|
+
/**
|
|
24
|
+
* Generate an otpauth:// URI for authenticator app enrollment.
|
|
25
|
+
*
|
|
26
|
+
* The URI encodes the secret, issuer, account name, and TOTP parameters
|
|
27
|
+
* in a format understood by Google Authenticator, Authy, 1Password, etc.
|
|
28
|
+
*
|
|
29
|
+
* @param secret - Base32-encoded TOTP secret
|
|
30
|
+
* @param userEmail - User's email address (displayed as account name)
|
|
31
|
+
* @param issuer - Service name (e.g., "Porta" or org slug)
|
|
32
|
+
* @returns otpauth:// URI string
|
|
33
|
+
*/
|
|
34
|
+
export declare function generateTotpUri(secret: string, userEmail: string, issuer: string): string;
|
|
35
|
+
/**
|
|
36
|
+
* Generate a QR code data URI from an otpauth:// URI.
|
|
37
|
+
*
|
|
38
|
+
* The returned data URI can be used directly in an <img src="..."> tag
|
|
39
|
+
* in Handlebars templates. Uses SVG format for crisp rendering at any size.
|
|
40
|
+
*
|
|
41
|
+
* @param uri - otpauth:// URI to encode
|
|
42
|
+
* @returns Data URI string (data:image/svg+xml;base64,...)
|
|
43
|
+
*/
|
|
44
|
+
export declare function generateQrCodeDataUri(uri: string): Promise<string>;
|
|
45
|
+
/**
|
|
46
|
+
* Verify a TOTP code against a secret.
|
|
47
|
+
*
|
|
48
|
+
* Allows a ±1 step time window (i.e., the current 30-second window
|
|
49
|
+
* plus one window before and one after) to accommodate minor clock
|
|
50
|
+
* drift between the server and the user's authenticator app.
|
|
51
|
+
*
|
|
52
|
+
* @param code - The 6-digit TOTP code entered by the user
|
|
53
|
+
* @param secret - Base32-encoded TOTP secret
|
|
54
|
+
* @returns True if the code is valid within the time window
|
|
55
|
+
*/
|
|
56
|
+
export declare function verifyTotpCode(code: string, secret: string): boolean;
|
|
57
|
+
//# sourceMappingURL=totp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"totp.d.ts","sourceRoot":"","sources":["../../src/two-factor/totp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAUH;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAI3C;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GACb,MAAM,CAWR;AAED;;;;;;;;GAQG;AACH,wBAAsB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAQxE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAYpE"}
|