@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,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Email OTP code utilities.
|
|
3
|
+
*
|
|
4
|
+
* Generates, hashes, and verifies 6-digit one-time passwords sent via email.
|
|
5
|
+
* Codes are cryptographically random (not sequential) and stored as SHA-256
|
|
6
|
+
* hashes — the plaintext is only ever sent to the user's email and never
|
|
7
|
+
* persisted in the database.
|
|
8
|
+
*
|
|
9
|
+
* SHA-256 is appropriate here (rather than Argon2id) because OTP codes are
|
|
10
|
+
* short-lived (10 minutes) and brute-forcing a 6-digit code against a fast
|
|
11
|
+
* hash is still rate-limited at the application level.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Generate a cryptographically random 6-digit OTP code.
|
|
15
|
+
*
|
|
16
|
+
* Uses Node.js `crypto.randomInt()` for uniform random distribution.
|
|
17
|
+
* The result is zero-padded to always be exactly 6 characters.
|
|
18
|
+
*
|
|
19
|
+
* @returns A 6-digit string (e.g., "042819", "123456")
|
|
20
|
+
*/
|
|
21
|
+
export declare function generateOtpCode(): string;
|
|
22
|
+
/**
|
|
23
|
+
* Hash an OTP code with SHA-256 for database storage.
|
|
24
|
+
*
|
|
25
|
+
* @param code - The 6-digit OTP code to hash
|
|
26
|
+
* @returns Hex-encoded SHA-256 hash (64 characters)
|
|
27
|
+
*/
|
|
28
|
+
export declare function hashOtpCode(code: string): string;
|
|
29
|
+
/**
|
|
30
|
+
* Verify an OTP code against its SHA-256 hash.
|
|
31
|
+
*
|
|
32
|
+
* Performs a constant-time-equivalent comparison by computing the hash
|
|
33
|
+
* of the provided code and comparing hex strings. While timing attacks
|
|
34
|
+
* on SHA-256 hex comparisons are extremely unlikely to be practical for
|
|
35
|
+
* 6-digit codes, we hash-then-compare rather than using the raw input
|
|
36
|
+
* to avoid any theoretical leakage.
|
|
37
|
+
*
|
|
38
|
+
* @param code - The OTP code to verify
|
|
39
|
+
* @param hash - The stored SHA-256 hash to verify against
|
|
40
|
+
* @returns True if the code matches the hash
|
|
41
|
+
*/
|
|
42
|
+
export declare function verifyOtpCode(code: string, hash: string): boolean;
|
|
43
|
+
//# sourceMappingURL=otp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"otp.d.ts","sourceRoot":"","sources":["../../src/two-factor/otp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAUH;;;;;;;GAOG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAIxC;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAGjE"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Email OTP code utilities.
|
|
3
|
+
*
|
|
4
|
+
* Generates, hashes, and verifies 6-digit one-time passwords sent via email.
|
|
5
|
+
* Codes are cryptographically random (not sequential) and stored as SHA-256
|
|
6
|
+
* hashes — the plaintext is only ever sent to the user's email and never
|
|
7
|
+
* persisted in the database.
|
|
8
|
+
*
|
|
9
|
+
* SHA-256 is appropriate here (rather than Argon2id) because OTP codes are
|
|
10
|
+
* short-lived (10 minutes) and brute-forcing a 6-digit code against a fast
|
|
11
|
+
* hash is still rate-limited at the application level.
|
|
12
|
+
*/
|
|
13
|
+
import { createHash, randomInt } from 'node:crypto';
|
|
14
|
+
/** OTP code length — 6 digits, zero-padded. */
|
|
15
|
+
const OTP_CODE_LENGTH = 6;
|
|
16
|
+
/** Maximum value for a 6-digit code (exclusive). */
|
|
17
|
+
const OTP_MAX_VALUE = 1_000_000;
|
|
18
|
+
/**
|
|
19
|
+
* Generate a cryptographically random 6-digit OTP code.
|
|
20
|
+
*
|
|
21
|
+
* Uses Node.js `crypto.randomInt()` for uniform random distribution.
|
|
22
|
+
* The result is zero-padded to always be exactly 6 characters.
|
|
23
|
+
*
|
|
24
|
+
* @returns A 6-digit string (e.g., "042819", "123456")
|
|
25
|
+
*/
|
|
26
|
+
export function generateOtpCode() {
|
|
27
|
+
// randomInt(max) returns a uniform random integer in [0, max)
|
|
28
|
+
const code = randomInt(OTP_MAX_VALUE);
|
|
29
|
+
return code.toString().padStart(OTP_CODE_LENGTH, '0');
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Hash an OTP code with SHA-256 for database storage.
|
|
33
|
+
*
|
|
34
|
+
* @param code - The 6-digit OTP code to hash
|
|
35
|
+
* @returns Hex-encoded SHA-256 hash (64 characters)
|
|
36
|
+
*/
|
|
37
|
+
export function hashOtpCode(code) {
|
|
38
|
+
return createHash('sha256').update(code).digest('hex');
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Verify an OTP code against its SHA-256 hash.
|
|
42
|
+
*
|
|
43
|
+
* Performs a constant-time-equivalent comparison by computing the hash
|
|
44
|
+
* of the provided code and comparing hex strings. While timing attacks
|
|
45
|
+
* on SHA-256 hex comparisons are extremely unlikely to be practical for
|
|
46
|
+
* 6-digit codes, we hash-then-compare rather than using the raw input
|
|
47
|
+
* to avoid any theoretical leakage.
|
|
48
|
+
*
|
|
49
|
+
* @param code - The OTP code to verify
|
|
50
|
+
* @param hash - The stored SHA-256 hash to verify against
|
|
51
|
+
* @returns True if the code matches the hash
|
|
52
|
+
*/
|
|
53
|
+
export function verifyOtpCode(code, hash) {
|
|
54
|
+
const computedHash = hashOtpCode(code);
|
|
55
|
+
return computedHash === hash;
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=otp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"otp.js","sourceRoot":"","sources":["../../src/two-factor/otp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAEpD,+CAA+C;AAC/C,MAAM,eAAe,GAAG,CAAC,CAAC;AAE1B,oDAAoD;AACpD,MAAM,aAAa,GAAG,SAAS,CAAC;AAEhC;;;;;;;GAOG;AACH,MAAM,UAAU,eAAe;IAC7B,8DAA8D;IAC9D,MAAM,IAAI,GAAG,SAAS,CAAC,aAAa,CAAC,CAAC;IACtC,OAAO,IAAI,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,eAAe,EAAE,GAAG,CAAC,CAAC;AACxD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACzD,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,aAAa,CAAC,IAAY,EAAE,IAAY;IACtD,MAAM,YAAY,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;IACvC,OAAO,YAAY,KAAK,IAAI,CAAC;AAC/B,CAAC"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Recovery code utilities.
|
|
3
|
+
*
|
|
4
|
+
* Generates, hashes, and verifies single-use recovery codes for 2FA backup.
|
|
5
|
+
* Users receive 10 codes during 2FA setup, each in XXXX-XXXX format
|
|
6
|
+
* (8 alphanumeric characters). Codes are hashed with Argon2id before storage
|
|
7
|
+
* because they are long-lived and relatively high-entropy — they must resist
|
|
8
|
+
* offline brute-force attacks if the database is compromised.
|
|
9
|
+
*
|
|
10
|
+
* Uses the same Argon2id parameters as password hashing (via the `argon2`
|
|
11
|
+
* library's defaults) for consistency and security.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Generate a set of recovery codes in XXXX-XXXX format.
|
|
15
|
+
*
|
|
16
|
+
* Each code is 8 characters (split into two 4-character groups with a
|
|
17
|
+
* dash separator) drawn from an unambiguous alphanumeric alphabet.
|
|
18
|
+
* The dash is cosmetic and stripped during verification.
|
|
19
|
+
*
|
|
20
|
+
* @param count - Number of codes to generate (default: 10)
|
|
21
|
+
* @returns Array of plaintext recovery codes
|
|
22
|
+
*/
|
|
23
|
+
export declare function generateRecoveryCodes(count?: number): string[];
|
|
24
|
+
/**
|
|
25
|
+
* Hash a recovery code with Argon2id for database storage.
|
|
26
|
+
*
|
|
27
|
+
* The code is normalized (uppercase, dash stripped) before hashing
|
|
28
|
+
* so that verification is case-insensitive and dash-insensitive.
|
|
29
|
+
*
|
|
30
|
+
* @param code - Plaintext recovery code (e.g., "ABCD-1234")
|
|
31
|
+
* @returns Argon2id hash string
|
|
32
|
+
*/
|
|
33
|
+
export declare function hashRecoveryCode(code: string): Promise<string>;
|
|
34
|
+
/**
|
|
35
|
+
* Verify a recovery code against an Argon2id hash.
|
|
36
|
+
*
|
|
37
|
+
* The code is normalized before verification so that "abcd-1234",
|
|
38
|
+
* "ABCD1234", and "ABCD-1234" are all equivalent.
|
|
39
|
+
*
|
|
40
|
+
* @param code - Plaintext recovery code to verify
|
|
41
|
+
* @param codeHash - Stored Argon2id hash
|
|
42
|
+
* @returns True if the code matches the hash
|
|
43
|
+
*/
|
|
44
|
+
export declare function verifyRecoveryCode(code: string, codeHash: string): Promise<boolean>;
|
|
45
|
+
//# sourceMappingURL=recovery.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recovery.d.ts","sourceRoot":"","sources":["../../src/two-factor/recovery.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAiBH;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,GAAE,MAAoC,GAAG,MAAM,EAAE,CAQ3F;AAED;;;;;;;;GAQG;AACH,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAGpE;AAED;;;;;;;;;GASG;AACH,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAQzF"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Recovery code utilities.
|
|
3
|
+
*
|
|
4
|
+
* Generates, hashes, and verifies single-use recovery codes for 2FA backup.
|
|
5
|
+
* Users receive 10 codes during 2FA setup, each in XXXX-XXXX format
|
|
6
|
+
* (8 alphanumeric characters). Codes are hashed with Argon2id before storage
|
|
7
|
+
* because they are long-lived and relatively high-entropy — they must resist
|
|
8
|
+
* offline brute-force attacks if the database is compromised.
|
|
9
|
+
*
|
|
10
|
+
* Uses the same Argon2id parameters as password hashing (via the `argon2`
|
|
11
|
+
* library's defaults) for consistency and security.
|
|
12
|
+
*/
|
|
13
|
+
import { hash, verify } from 'argon2';
|
|
14
|
+
import { randomBytes } from 'node:crypto';
|
|
15
|
+
/** Default number of recovery codes to generate. */
|
|
16
|
+
const DEFAULT_RECOVERY_CODE_COUNT = 10;
|
|
17
|
+
/**
|
|
18
|
+
* Characters used in recovery codes — uppercase alphanumeric, excluding
|
|
19
|
+
* visually ambiguous characters (0/O, 1/I/L) for easier manual entry.
|
|
20
|
+
*/
|
|
21
|
+
const CODE_CHARS = 'ABCDEFGHJKMNPQRSTUVWXYZ23456789';
|
|
22
|
+
/** Length of each half of a recovery code (XXXX-XXXX). */
|
|
23
|
+
const HALF_LENGTH = 4;
|
|
24
|
+
/**
|
|
25
|
+
* Generate a set of recovery codes in XXXX-XXXX format.
|
|
26
|
+
*
|
|
27
|
+
* Each code is 8 characters (split into two 4-character groups with a
|
|
28
|
+
* dash separator) drawn from an unambiguous alphanumeric alphabet.
|
|
29
|
+
* The dash is cosmetic and stripped during verification.
|
|
30
|
+
*
|
|
31
|
+
* @param count - Number of codes to generate (default: 10)
|
|
32
|
+
* @returns Array of plaintext recovery codes
|
|
33
|
+
*/
|
|
34
|
+
export function generateRecoveryCodes(count = DEFAULT_RECOVERY_CODE_COUNT) {
|
|
35
|
+
const codes = [];
|
|
36
|
+
for (let i = 0; i < count; i++) {
|
|
37
|
+
codes.push(generateSingleCode());
|
|
38
|
+
}
|
|
39
|
+
return codes;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Hash a recovery code with Argon2id for database storage.
|
|
43
|
+
*
|
|
44
|
+
* The code is normalized (uppercase, dash stripped) before hashing
|
|
45
|
+
* so that verification is case-insensitive and dash-insensitive.
|
|
46
|
+
*
|
|
47
|
+
* @param code - Plaintext recovery code (e.g., "ABCD-1234")
|
|
48
|
+
* @returns Argon2id hash string
|
|
49
|
+
*/
|
|
50
|
+
export async function hashRecoveryCode(code) {
|
|
51
|
+
const normalized = normalizeCode(code);
|
|
52
|
+
return hash(normalized);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Verify a recovery code against an Argon2id hash.
|
|
56
|
+
*
|
|
57
|
+
* The code is normalized before verification so that "abcd-1234",
|
|
58
|
+
* "ABCD1234", and "ABCD-1234" are all equivalent.
|
|
59
|
+
*
|
|
60
|
+
* @param code - Plaintext recovery code to verify
|
|
61
|
+
* @param codeHash - Stored Argon2id hash
|
|
62
|
+
* @returns True if the code matches the hash
|
|
63
|
+
*/
|
|
64
|
+
export async function verifyRecoveryCode(code, codeHash) {
|
|
65
|
+
const normalized = normalizeCode(code);
|
|
66
|
+
try {
|
|
67
|
+
return await verify(codeHash, normalized);
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
// Argon2id verify throws on malformed hashes — treat as non-match
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Generate a single recovery code in XXXX-XXXX format.
|
|
76
|
+
*
|
|
77
|
+
* Uses crypto.randomBytes to generate random indices into the
|
|
78
|
+
* character set, then formats as two 4-character groups.
|
|
79
|
+
*/
|
|
80
|
+
function generateSingleCode() {
|
|
81
|
+
const totalLength = HALF_LENGTH * 2;
|
|
82
|
+
const bytes = randomBytes(totalLength);
|
|
83
|
+
let code = '';
|
|
84
|
+
for (let i = 0; i < totalLength; i++) {
|
|
85
|
+
// Use modulo to map each random byte to a character index.
|
|
86
|
+
// Slight bias is negligible for this use case (recovery codes
|
|
87
|
+
// don't need perfectly uniform distribution).
|
|
88
|
+
code += CODE_CHARS[bytes[i] % CODE_CHARS.length];
|
|
89
|
+
}
|
|
90
|
+
// Insert dash between halves: "ABCD1234" → "ABCD-1234"
|
|
91
|
+
return `${code.slice(0, HALF_LENGTH)}-${code.slice(HALF_LENGTH)}`;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Normalize a recovery code for hashing/verification.
|
|
95
|
+
* Converts to uppercase and strips dashes so that entry format
|
|
96
|
+
* doesn't matter ("abcd-1234" === "ABCD1234" === "ABCD-1234").
|
|
97
|
+
*
|
|
98
|
+
* @param code - Raw recovery code input
|
|
99
|
+
* @returns Normalized uppercase string without dashes
|
|
100
|
+
*/
|
|
101
|
+
function normalizeCode(code) {
|
|
102
|
+
return code.toUpperCase().replace(/-/g, '');
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=recovery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recovery.js","sourceRoot":"","sources":["../../src/two-factor/recovery.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,oDAAoD;AACpD,MAAM,2BAA2B,GAAG,EAAE,CAAC;AAEvC;;;GAGG;AACH,MAAM,UAAU,GAAG,iCAAiC,CAAC;AAErD,0DAA0D;AAC1D,MAAM,WAAW,GAAG,CAAC,CAAC;AAEtB;;;;;;;;;GASG;AACH,MAAM,UAAU,qBAAqB,CAAC,KAAK,GAAW,2BAA2B;IAC/E,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;QAC/B,KAAK,CAAC,IAAI,CAAC,kBAAkB,EAAE,CAAC,CAAC;IACnC,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,IAAY;IACjD,MAAM,UAAU,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IACvC,OAAO,IAAI,CAAC,UAAU,CAAC,CAAC;AAC1B,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,IAAY,EAAE,QAAgB;IACrE,MAAM,UAAU,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IACvC,IAAI,CAAC;QACH,OAAO,MAAM,MAAM,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;IAC5C,CAAC;IAAC,MAAM,CAAC;QACP,kEAAkE;QAClE,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,SAAS,kBAAkB;IACzB,MAAM,WAAW,GAAG,WAAW,GAAG,CAAC,CAAC;IACpC,MAAM,KAAK,GAAG,WAAW,CAAC,WAAW,CAAC,CAAC;IACvC,IAAI,IAAI,GAAG,EAAE,CAAC;IAEd,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,WAAW,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,2DAA2D;QAC3D,8DAA8D;QAC9D,8CAA8C;QAC9C,IAAI,IAAI,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC;IACnD,CAAC;IAED,uDAAuD;IACvD,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,WAAW,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,EAAE,CAAC;AACpE,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,aAAa,CAAC,IAAY;IACjC,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;AAC9C,CAAC"}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Two-factor authentication repository — PostgreSQL data access layer.
|
|
3
|
+
*
|
|
4
|
+
* Provides CRUD operations for the three 2FA tables:
|
|
5
|
+
* - user_totp: TOTP configuration per user (encrypted secrets)
|
|
6
|
+
* - two_factor_otp_codes: Email OTP codes (SHA-256 hashed, short-lived)
|
|
7
|
+
* - two_factor_recovery_codes: Single-use recovery codes (Argon2id hashed)
|
|
8
|
+
*
|
|
9
|
+
* Each function acquires the pool via `getPool()` and returns mapped
|
|
10
|
+
* objects using the mapping functions from types.ts. Follows the same
|
|
11
|
+
* patterns as the organization and user repositories.
|
|
12
|
+
*/
|
|
13
|
+
import type { UserTotp, OtpCode, RecoveryCode, InsertTotpData } from './types.js';
|
|
14
|
+
/**
|
|
15
|
+
* Insert a new TOTP configuration for a user.
|
|
16
|
+
*
|
|
17
|
+
* The encrypted secret, IV, and auth tag are stored as-is (hex-encoded
|
|
18
|
+
* strings from the crypto module). The UNIQUE constraint on user_id
|
|
19
|
+
* ensures only one TOTP config per user.
|
|
20
|
+
*
|
|
21
|
+
* @param data - TOTP configuration data (secrets already encrypted)
|
|
22
|
+
* @returns The newly created TOTP configuration
|
|
23
|
+
* @throws If user already has a TOTP config (unique constraint violation)
|
|
24
|
+
*/
|
|
25
|
+
export declare function insertTotp(data: InsertTotpData): Promise<UserTotp>;
|
|
26
|
+
/**
|
|
27
|
+
* Find TOTP configuration for a user.
|
|
28
|
+
*
|
|
29
|
+
* Returns the encrypted TOTP config if one exists, or null.
|
|
30
|
+
* The caller must decrypt the secret using the crypto module.
|
|
31
|
+
*
|
|
32
|
+
* @param userId - User UUID
|
|
33
|
+
* @returns TOTP configuration or null if not configured
|
|
34
|
+
*/
|
|
35
|
+
export declare function findTotpByUserId(userId: string): Promise<UserTotp | null>;
|
|
36
|
+
/**
|
|
37
|
+
* Mark a user's TOTP configuration as verified.
|
|
38
|
+
*
|
|
39
|
+
* Called after the user successfully enters their first TOTP code
|
|
40
|
+
* during setup. This confirms that the user's authenticator app
|
|
41
|
+
* is correctly configured.
|
|
42
|
+
*
|
|
43
|
+
* @param userId - User UUID
|
|
44
|
+
*/
|
|
45
|
+
export declare function markTotpVerified(userId: string): Promise<void>;
|
|
46
|
+
/**
|
|
47
|
+
* Delete a user's TOTP configuration.
|
|
48
|
+
*
|
|
49
|
+
* Called when disabling 2FA or resetting TOTP setup.
|
|
50
|
+
* The CASCADE on user_id means this is also cleaned up if the user is deleted.
|
|
51
|
+
*
|
|
52
|
+
* @param userId - User UUID
|
|
53
|
+
*/
|
|
54
|
+
export declare function deleteTotp(userId: string): Promise<void>;
|
|
55
|
+
/**
|
|
56
|
+
* Insert a new email OTP code.
|
|
57
|
+
*
|
|
58
|
+
* The code is stored as a SHA-256 hash — the plaintext is only sent
|
|
59
|
+
* via email. The expires_at timestamp determines when the code becomes
|
|
60
|
+
* invalid (typically 10 minutes from creation).
|
|
61
|
+
*
|
|
62
|
+
* @param userId - User UUID
|
|
63
|
+
* @param codeHash - SHA-256 hex hash of the 6-digit OTP code
|
|
64
|
+
* @param expiresAt - Expiration timestamp
|
|
65
|
+
* @returns The newly created OTP code record
|
|
66
|
+
*/
|
|
67
|
+
export declare function insertOtpCode(userId: string, codeHash: string, expiresAt: Date): Promise<OtpCode>;
|
|
68
|
+
/**
|
|
69
|
+
* Find all active (unused, not expired) OTP codes for a user.
|
|
70
|
+
*
|
|
71
|
+
* Returns codes that are both unused (used_at IS NULL) and not yet
|
|
72
|
+
* expired (expires_at > NOW()). Ordered by creation time descending
|
|
73
|
+
* so the most recent code is first.
|
|
74
|
+
*
|
|
75
|
+
* @param userId - User UUID
|
|
76
|
+
* @returns Array of active OTP codes (may be empty)
|
|
77
|
+
*/
|
|
78
|
+
export declare function findActiveOtpCodes(userId: string): Promise<OtpCode[]>;
|
|
79
|
+
/**
|
|
80
|
+
* Mark an OTP code as used.
|
|
81
|
+
*
|
|
82
|
+
* Sets the used_at timestamp to the current time, preventing reuse.
|
|
83
|
+
* Called after successful verification.
|
|
84
|
+
*
|
|
85
|
+
* @param codeId - OTP code UUID
|
|
86
|
+
*/
|
|
87
|
+
export declare function markOtpCodeUsed(codeId: string): Promise<void>;
|
|
88
|
+
/**
|
|
89
|
+
* Delete expired OTP codes for a user.
|
|
90
|
+
*
|
|
91
|
+
* Cleans up codes that have passed their expires_at timestamp or
|
|
92
|
+
* have already been used. Prevents table bloat over time.
|
|
93
|
+
*
|
|
94
|
+
* @param userId - User UUID
|
|
95
|
+
* @returns Number of codes deleted
|
|
96
|
+
*/
|
|
97
|
+
export declare function deleteExpiredOtpCodes(userId: string): Promise<number>;
|
|
98
|
+
/**
|
|
99
|
+
* Count active (unused, not expired) OTP codes for a user.
|
|
100
|
+
*
|
|
101
|
+
* Used for rate limiting — prevents generating too many codes
|
|
102
|
+
* in a short time period.
|
|
103
|
+
*
|
|
104
|
+
* @param userId - User UUID
|
|
105
|
+
* @returns Number of active OTP codes
|
|
106
|
+
*/
|
|
107
|
+
export declare function countActiveOtpCodes(userId: string): Promise<number>;
|
|
108
|
+
/**
|
|
109
|
+
* Insert a batch of recovery codes for a user.
|
|
110
|
+
*
|
|
111
|
+
* Inserts all code hashes in a single query using a multi-row VALUES
|
|
112
|
+
* clause for efficiency. Called during 2FA setup and regeneration.
|
|
113
|
+
*
|
|
114
|
+
* @param userId - User UUID
|
|
115
|
+
* @param codeHashes - Array of Argon2id hashes of recovery codes
|
|
116
|
+
*/
|
|
117
|
+
export declare function insertRecoveryCodes(userId: string, codeHashes: string[]): Promise<void>;
|
|
118
|
+
/**
|
|
119
|
+
* Find all unused recovery codes for a user.
|
|
120
|
+
*
|
|
121
|
+
* Returns codes where used_at IS NULL. The caller iterates these
|
|
122
|
+
* and verifies each hash to find a match (since Argon2id hashes
|
|
123
|
+
* include random salt, we can't do a direct DB lookup).
|
|
124
|
+
*
|
|
125
|
+
* @param userId - User UUID
|
|
126
|
+
* @returns Array of unused recovery codes (may be empty)
|
|
127
|
+
*/
|
|
128
|
+
export declare function findUnusedRecoveryCodes(userId: string): Promise<RecoveryCode[]>;
|
|
129
|
+
/**
|
|
130
|
+
* Mark a recovery code as used.
|
|
131
|
+
*
|
|
132
|
+
* Sets the used_at timestamp to the current time, preventing reuse.
|
|
133
|
+
* Recovery codes are single-use — once used, they cannot be used again.
|
|
134
|
+
*
|
|
135
|
+
* @param codeId - Recovery code UUID
|
|
136
|
+
*/
|
|
137
|
+
export declare function markRecoveryCodeUsed(codeId: string): Promise<void>;
|
|
138
|
+
/**
|
|
139
|
+
* Delete all recovery codes for a user.
|
|
140
|
+
*
|
|
141
|
+
* Called when disabling 2FA or regenerating recovery codes
|
|
142
|
+
* (old codes are deleted, then new ones are inserted).
|
|
143
|
+
*
|
|
144
|
+
* @param userId - User UUID
|
|
145
|
+
*/
|
|
146
|
+
export declare function deleteAllRecoveryCodes(userId: string): Promise<void>;
|
|
147
|
+
/**
|
|
148
|
+
* Count unused recovery codes for a user.
|
|
149
|
+
*
|
|
150
|
+
* Used in the 2FA status response to show how many codes remain.
|
|
151
|
+
*
|
|
152
|
+
* @param userId - User UUID
|
|
153
|
+
* @returns Number of unused recovery codes
|
|
154
|
+
*/
|
|
155
|
+
export declare function countUnusedRecoveryCodes(userId: string): Promise<number>;
|
|
156
|
+
//# sourceMappingURL=repository.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repository.d.ts","sourceRoot":"","sources":["../../src/two-factor/repository.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,KAAK,EACV,QAAQ,EAER,OAAO,EAEP,YAAY,EAEZ,cAAc,EACf,MAAM,YAAY,CAAC;AAWpB;;;;;;;;;;GAUG;AACH,wBAAsB,UAAU,CAAC,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,QAAQ,CAAC,CAsBxE;AAED;;;;;;;;GAQG;AACH,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAU/E;AAED;;;;;;;;GAQG;AACH,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAOpE;AAED;;;;;;;GAOG;AACH,wBAAsB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAO9D;AAMD;;;;;;;;;;;GAWG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,IAAI,GACd,OAAO,CAAC,OAAO,CAAC,CAWlB;AAED;;;;;;;;;GASG;AACH,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAW3E;AAED;;;;;;;GAOG;AACH,wBAAsB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAOnE;AAED;;;;;;;;GAQG;AACH,wBAAsB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAU3E;AAED;;;;;;;;GAQG;AACH,wBAAsB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAUzE;AAMD;;;;;;;;GAQG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,EAAE,GACnB,OAAO,CAAC,IAAI,CAAC,CAoBf;AAED;;;;;;;;;GASG;AACH,wBAAsB,uBAAuB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAWrF;AAED;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAOxE;AAED;;;;;;;GAOG;AACH,wBAAsB,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAO1E;AAED;;;;;;;GAOG;AACH,wBAAsB,wBAAwB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAU9E"}
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Two-factor authentication repository — PostgreSQL data access layer.
|
|
3
|
+
*
|
|
4
|
+
* Provides CRUD operations for the three 2FA tables:
|
|
5
|
+
* - user_totp: TOTP configuration per user (encrypted secrets)
|
|
6
|
+
* - two_factor_otp_codes: Email OTP codes (SHA-256 hashed, short-lived)
|
|
7
|
+
* - two_factor_recovery_codes: Single-use recovery codes (Argon2id hashed)
|
|
8
|
+
*
|
|
9
|
+
* Each function acquires the pool via `getPool()` and returns mapped
|
|
10
|
+
* objects using the mapping functions from types.ts. Follows the same
|
|
11
|
+
* patterns as the organization and user repositories.
|
|
12
|
+
*/
|
|
13
|
+
import { getPool } from '../lib/database.js';
|
|
14
|
+
import { mapRowToUserTotp, mapRowToOtpCode, mapRowToRecoveryCode, } from './types.js';
|
|
15
|
+
// ===========================================================================
|
|
16
|
+
// TOTP operations
|
|
17
|
+
// ===========================================================================
|
|
18
|
+
/**
|
|
19
|
+
* Insert a new TOTP configuration for a user.
|
|
20
|
+
*
|
|
21
|
+
* The encrypted secret, IV, and auth tag are stored as-is (hex-encoded
|
|
22
|
+
* strings from the crypto module). The UNIQUE constraint on user_id
|
|
23
|
+
* ensures only one TOTP config per user.
|
|
24
|
+
*
|
|
25
|
+
* @param data - TOTP configuration data (secrets already encrypted)
|
|
26
|
+
* @returns The newly created TOTP configuration
|
|
27
|
+
* @throws If user already has a TOTP config (unique constraint violation)
|
|
28
|
+
*/
|
|
29
|
+
export async function insertTotp(data) {
|
|
30
|
+
const pool = getPool();
|
|
31
|
+
const result = await pool.query(`INSERT INTO user_totp (
|
|
32
|
+
user_id, encrypted_secret, encryption_iv, encryption_tag,
|
|
33
|
+
algorithm, digits, period
|
|
34
|
+
)
|
|
35
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
|
36
|
+
RETURNING *`, [
|
|
37
|
+
data.userId,
|
|
38
|
+
data.encryptedSecret,
|
|
39
|
+
data.encryptionIv,
|
|
40
|
+
data.encryptionTag,
|
|
41
|
+
data.algorithm ?? 'SHA1',
|
|
42
|
+
data.digits ?? 6,
|
|
43
|
+
data.period ?? 30,
|
|
44
|
+
]);
|
|
45
|
+
return mapRowToUserTotp(result.rows[0]);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Find TOTP configuration for a user.
|
|
49
|
+
*
|
|
50
|
+
* Returns the encrypted TOTP config if one exists, or null.
|
|
51
|
+
* The caller must decrypt the secret using the crypto module.
|
|
52
|
+
*
|
|
53
|
+
* @param userId - User UUID
|
|
54
|
+
* @returns TOTP configuration or null if not configured
|
|
55
|
+
*/
|
|
56
|
+
export async function findTotpByUserId(userId) {
|
|
57
|
+
const pool = getPool();
|
|
58
|
+
const result = await pool.query('SELECT * FROM user_totp WHERE user_id = $1', [userId]);
|
|
59
|
+
if (result.rows.length === 0)
|
|
60
|
+
return null;
|
|
61
|
+
return mapRowToUserTotp(result.rows[0]);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Mark a user's TOTP configuration as verified.
|
|
65
|
+
*
|
|
66
|
+
* Called after the user successfully enters their first TOTP code
|
|
67
|
+
* during setup. This confirms that the user's authenticator app
|
|
68
|
+
* is correctly configured.
|
|
69
|
+
*
|
|
70
|
+
* @param userId - User UUID
|
|
71
|
+
*/
|
|
72
|
+
export async function markTotpVerified(userId) {
|
|
73
|
+
const pool = getPool();
|
|
74
|
+
await pool.query('UPDATE user_totp SET verified = true WHERE user_id = $1', [userId]);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Delete a user's TOTP configuration.
|
|
78
|
+
*
|
|
79
|
+
* Called when disabling 2FA or resetting TOTP setup.
|
|
80
|
+
* The CASCADE on user_id means this is also cleaned up if the user is deleted.
|
|
81
|
+
*
|
|
82
|
+
* @param userId - User UUID
|
|
83
|
+
*/
|
|
84
|
+
export async function deleteTotp(userId) {
|
|
85
|
+
const pool = getPool();
|
|
86
|
+
await pool.query('DELETE FROM user_totp WHERE user_id = $1', [userId]);
|
|
87
|
+
}
|
|
88
|
+
// ===========================================================================
|
|
89
|
+
// OTP code operations
|
|
90
|
+
// ===========================================================================
|
|
91
|
+
/**
|
|
92
|
+
* Insert a new email OTP code.
|
|
93
|
+
*
|
|
94
|
+
* The code is stored as a SHA-256 hash — the plaintext is only sent
|
|
95
|
+
* via email. The expires_at timestamp determines when the code becomes
|
|
96
|
+
* invalid (typically 10 minutes from creation).
|
|
97
|
+
*
|
|
98
|
+
* @param userId - User UUID
|
|
99
|
+
* @param codeHash - SHA-256 hex hash of the 6-digit OTP code
|
|
100
|
+
* @param expiresAt - Expiration timestamp
|
|
101
|
+
* @returns The newly created OTP code record
|
|
102
|
+
*/
|
|
103
|
+
export async function insertOtpCode(userId, codeHash, expiresAt) {
|
|
104
|
+
const pool = getPool();
|
|
105
|
+
const result = await pool.query(`INSERT INTO two_factor_otp_codes (user_id, code_hash, expires_at)
|
|
106
|
+
VALUES ($1, $2, $3)
|
|
107
|
+
RETURNING *`, [userId, codeHash, expiresAt]);
|
|
108
|
+
return mapRowToOtpCode(result.rows[0]);
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Find all active (unused, not expired) OTP codes for a user.
|
|
112
|
+
*
|
|
113
|
+
* Returns codes that are both unused (used_at IS NULL) and not yet
|
|
114
|
+
* expired (expires_at > NOW()). Ordered by creation time descending
|
|
115
|
+
* so the most recent code is first.
|
|
116
|
+
*
|
|
117
|
+
* @param userId - User UUID
|
|
118
|
+
* @returns Array of active OTP codes (may be empty)
|
|
119
|
+
*/
|
|
120
|
+
export async function findActiveOtpCodes(userId) {
|
|
121
|
+
const pool = getPool();
|
|
122
|
+
const result = await pool.query(`SELECT * FROM two_factor_otp_codes
|
|
123
|
+
WHERE user_id = $1 AND used_at IS NULL AND expires_at > NOW()
|
|
124
|
+
ORDER BY created_at DESC`, [userId]);
|
|
125
|
+
return result.rows.map(mapRowToOtpCode);
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Mark an OTP code as used.
|
|
129
|
+
*
|
|
130
|
+
* Sets the used_at timestamp to the current time, preventing reuse.
|
|
131
|
+
* Called after successful verification.
|
|
132
|
+
*
|
|
133
|
+
* @param codeId - OTP code UUID
|
|
134
|
+
*/
|
|
135
|
+
export async function markOtpCodeUsed(codeId) {
|
|
136
|
+
const pool = getPool();
|
|
137
|
+
await pool.query('UPDATE two_factor_otp_codes SET used_at = NOW() WHERE id = $1', [codeId]);
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Delete expired OTP codes for a user.
|
|
141
|
+
*
|
|
142
|
+
* Cleans up codes that have passed their expires_at timestamp or
|
|
143
|
+
* have already been used. Prevents table bloat over time.
|
|
144
|
+
*
|
|
145
|
+
* @param userId - User UUID
|
|
146
|
+
* @returns Number of codes deleted
|
|
147
|
+
*/
|
|
148
|
+
export async function deleteExpiredOtpCodes(userId) {
|
|
149
|
+
const pool = getPool();
|
|
150
|
+
const result = await pool.query(`DELETE FROM two_factor_otp_codes
|
|
151
|
+
WHERE user_id = $1 AND (expires_at <= NOW() OR used_at IS NOT NULL)`, [userId]);
|
|
152
|
+
return result.rowCount ?? 0;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Count active (unused, not expired) OTP codes for a user.
|
|
156
|
+
*
|
|
157
|
+
* Used for rate limiting — prevents generating too many codes
|
|
158
|
+
* in a short time period.
|
|
159
|
+
*
|
|
160
|
+
* @param userId - User UUID
|
|
161
|
+
* @returns Number of active OTP codes
|
|
162
|
+
*/
|
|
163
|
+
export async function countActiveOtpCodes(userId) {
|
|
164
|
+
const pool = getPool();
|
|
165
|
+
const result = await pool.query(`SELECT COUNT(*) as count FROM two_factor_otp_codes
|
|
166
|
+
WHERE user_id = $1 AND used_at IS NULL AND expires_at > NOW()`, [userId]);
|
|
167
|
+
return parseInt(result.rows[0].count, 10);
|
|
168
|
+
}
|
|
169
|
+
// ===========================================================================
|
|
170
|
+
// Recovery code operations
|
|
171
|
+
// ===========================================================================
|
|
172
|
+
/**
|
|
173
|
+
* Insert a batch of recovery codes for a user.
|
|
174
|
+
*
|
|
175
|
+
* Inserts all code hashes in a single query using a multi-row VALUES
|
|
176
|
+
* clause for efficiency. Called during 2FA setup and regeneration.
|
|
177
|
+
*
|
|
178
|
+
* @param userId - User UUID
|
|
179
|
+
* @param codeHashes - Array of Argon2id hashes of recovery codes
|
|
180
|
+
*/
|
|
181
|
+
export async function insertRecoveryCodes(userId, codeHashes) {
|
|
182
|
+
if (codeHashes.length === 0)
|
|
183
|
+
return;
|
|
184
|
+
const pool = getPool();
|
|
185
|
+
// Build multi-row VALUES clause: ($1, $2), ($1, $3), ($1, $4), ...
|
|
186
|
+
// $1 is always the userId; each hash gets its own parameter
|
|
187
|
+
const valuePlaceholders = [];
|
|
188
|
+
const params = [userId];
|
|
189
|
+
for (let i = 0; i < codeHashes.length; i++) {
|
|
190
|
+
valuePlaceholders.push(`($1, $${i + 2})`);
|
|
191
|
+
params.push(codeHashes[i]);
|
|
192
|
+
}
|
|
193
|
+
await pool.query(`INSERT INTO two_factor_recovery_codes (user_id, code_hash)
|
|
194
|
+
VALUES ${valuePlaceholders.join(', ')}`, params);
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Find all unused recovery codes for a user.
|
|
198
|
+
*
|
|
199
|
+
* Returns codes where used_at IS NULL. The caller iterates these
|
|
200
|
+
* and verifies each hash to find a match (since Argon2id hashes
|
|
201
|
+
* include random salt, we can't do a direct DB lookup).
|
|
202
|
+
*
|
|
203
|
+
* @param userId - User UUID
|
|
204
|
+
* @returns Array of unused recovery codes (may be empty)
|
|
205
|
+
*/
|
|
206
|
+
export async function findUnusedRecoveryCodes(userId) {
|
|
207
|
+
const pool = getPool();
|
|
208
|
+
const result = await pool.query(`SELECT * FROM two_factor_recovery_codes
|
|
209
|
+
WHERE user_id = $1 AND used_at IS NULL
|
|
210
|
+
ORDER BY created_at ASC`, [userId]);
|
|
211
|
+
return result.rows.map(mapRowToRecoveryCode);
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Mark a recovery code as used.
|
|
215
|
+
*
|
|
216
|
+
* Sets the used_at timestamp to the current time, preventing reuse.
|
|
217
|
+
* Recovery codes are single-use — once used, they cannot be used again.
|
|
218
|
+
*
|
|
219
|
+
* @param codeId - Recovery code UUID
|
|
220
|
+
*/
|
|
221
|
+
export async function markRecoveryCodeUsed(codeId) {
|
|
222
|
+
const pool = getPool();
|
|
223
|
+
await pool.query('UPDATE two_factor_recovery_codes SET used_at = NOW() WHERE id = $1', [codeId]);
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Delete all recovery codes for a user.
|
|
227
|
+
*
|
|
228
|
+
* Called when disabling 2FA or regenerating recovery codes
|
|
229
|
+
* (old codes are deleted, then new ones are inserted).
|
|
230
|
+
*
|
|
231
|
+
* @param userId - User UUID
|
|
232
|
+
*/
|
|
233
|
+
export async function deleteAllRecoveryCodes(userId) {
|
|
234
|
+
const pool = getPool();
|
|
235
|
+
await pool.query('DELETE FROM two_factor_recovery_codes WHERE user_id = $1', [userId]);
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Count unused recovery codes for a user.
|
|
239
|
+
*
|
|
240
|
+
* Used in the 2FA status response to show how many codes remain.
|
|
241
|
+
*
|
|
242
|
+
* @param userId - User UUID
|
|
243
|
+
* @returns Number of unused recovery codes
|
|
244
|
+
*/
|
|
245
|
+
export async function countUnusedRecoveryCodes(userId) {
|
|
246
|
+
const pool = getPool();
|
|
247
|
+
const result = await pool.query(`SELECT COUNT(*) as count FROM two_factor_recovery_codes
|
|
248
|
+
WHERE user_id = $1 AND used_at IS NULL`, [userId]);
|
|
249
|
+
return parseInt(result.rows[0].count, 10);
|
|
250
|
+
}
|
|
251
|
+
//# sourceMappingURL=repository.js.map
|