@trokky/trokky 2.0.3
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/dist/adapters/filesystem/filesystem-adapter.d.ts +80 -0
- package/dist/adapters/filesystem/filesystem-adapter.d.ts.map +1 -0
- package/dist/adapters/filesystem/filesystem-adapter.js +1370 -0
- package/dist/adapters/filesystem/filesystem-adapter.js.map +1 -0
- package/dist/adapters/filesystem/index.d.ts +29 -0
- package/dist/adapters/filesystem/index.d.ts.map +1 -0
- package/dist/adapters/filesystem/index.js +31 -0
- package/dist/adapters/filesystem/index.js.map +1 -0
- package/dist/adapters/filesystem/types.d.ts +111 -0
- package/dist/adapters/filesystem/types.d.ts.map +1 -0
- package/dist/adapters/filesystem/types.js +2 -0
- package/dist/adapters/filesystem/types.js.map +1 -0
- package/dist/adapters/filesystem-data/filesystem-data-adapter.d.ts +111 -0
- package/dist/adapters/filesystem-data/filesystem-data-adapter.d.ts.map +1 -0
- package/dist/adapters/filesystem-data/filesystem-data-adapter.js +1363 -0
- package/dist/adapters/filesystem-data/filesystem-data-adapter.js.map +1 -0
- package/dist/adapters/filesystem-data/index.d.ts +3 -0
- package/dist/adapters/filesystem-data/index.d.ts.map +1 -0
- package/dist/adapters/filesystem-data/index.js +13 -0
- package/dist/adapters/filesystem-data/index.js.map +1 -0
- package/dist/adapters/filesystem-data/types.d.ts +147 -0
- package/dist/adapters/filesystem-data/types.d.ts.map +1 -0
- package/dist/adapters/filesystem-data/types.js +2 -0
- package/dist/adapters/filesystem-data/types.js.map +1 -0
- package/dist/adapters/filesystem-media/filesystem-media-adapter.d.ts +32 -0
- package/dist/adapters/filesystem-media/filesystem-media-adapter.d.ts.map +1 -0
- package/dist/adapters/filesystem-media/filesystem-media-adapter.js +639 -0
- package/dist/adapters/filesystem-media/filesystem-media-adapter.js.map +1 -0
- package/dist/adapters/filesystem-media/index.d.ts +3 -0
- package/dist/adapters/filesystem-media/index.d.ts.map +1 -0
- package/dist/adapters/filesystem-media/index.js +13 -0
- package/dist/adapters/filesystem-media/index.js.map +1 -0
- package/dist/adapters/filesystem-media/types.d.ts +76 -0
- package/dist/adapters/filesystem-media/types.d.ts.map +1 -0
- package/dist/adapters/filesystem-media/types.js +2 -0
- package/dist/adapters/filesystem-media/types.js.map +1 -0
- package/dist/adapters/postgres-data/index.d.ts +3 -0
- package/dist/adapters/postgres-data/index.d.ts.map +1 -0
- package/dist/adapters/postgres-data/index.js +13 -0
- package/dist/adapters/postgres-data/index.js.map +1 -0
- package/dist/adapters/postgres-data/postgres-data-adapter.d.ts +154 -0
- package/dist/adapters/postgres-data/postgres-data-adapter.d.ts.map +1 -0
- package/dist/adapters/postgres-data/postgres-data-adapter.js +1432 -0
- package/dist/adapters/postgres-data/postgres-data-adapter.js.map +1 -0
- package/dist/adapters/postgres-data/types.d.ts +139 -0
- package/dist/adapters/postgres-data/types.d.ts.map +1 -0
- package/dist/adapters/postgres-data/types.js +2 -0
- package/dist/adapters/postgres-data/types.js.map +1 -0
- package/dist/core/adapters/registry.d.ts +79 -0
- package/dist/core/adapters/registry.d.ts.map +1 -0
- package/dist/core/adapters/registry.js +149 -0
- package/dist/core/adapters/registry.js.map +1 -0
- package/dist/core/core/engine.d.ts +551 -0
- package/dist/core/core/engine.d.ts.map +1 -0
- package/dist/core/core/engine.js +1074 -0
- package/dist/core/core/engine.js.map +1 -0
- package/dist/core/crypto/adapter.d.ts +63 -0
- package/dist/core/crypto/adapter.d.ts.map +1 -0
- package/dist/core/crypto/adapter.js +63 -0
- package/dist/core/crypto/adapter.js.map +1 -0
- package/dist/core/crypto/fallback-adapter.d.ts +23 -0
- package/dist/core/crypto/fallback-adapter.d.ts.map +1 -0
- package/dist/core/crypto/fallback-adapter.js +195 -0
- package/dist/core/crypto/fallback-adapter.js.map +1 -0
- package/dist/core/crypto/node-adapter.d.ts +20 -0
- package/dist/core/crypto/node-adapter.d.ts.map +1 -0
- package/dist/core/crypto/node-adapter.js +85 -0
- package/dist/core/crypto/node-adapter.js.map +1 -0
- package/dist/core/crypto/password-hash.d.ts +73 -0
- package/dist/core/crypto/password-hash.d.ts.map +1 -0
- package/dist/core/crypto/password-hash.js +244 -0
- package/dist/core/crypto/password-hash.js.map +1 -0
- package/dist/core/crypto/webcrypto-adapter.d.ts +24 -0
- package/dist/core/crypto/webcrypto-adapter.d.ts.map +1 -0
- package/dist/core/crypto/webcrypto-adapter.js +158 -0
- package/dist/core/crypto/webcrypto-adapter.js.map +1 -0
- package/dist/core/errors/index.d.ts +30 -0
- package/dist/core/errors/index.d.ts.map +1 -0
- package/dist/core/errors/index.js +61 -0
- package/dist/core/errors/index.js.map +1 -0
- package/dist/core/events/event-bus.d.ts +121 -0
- package/dist/core/events/event-bus.d.ts.map +1 -0
- package/dist/core/events/event-bus.js +631 -0
- package/dist/core/events/event-bus.js.map +1 -0
- package/dist/core/events/index.d.ts +17 -0
- package/dist/core/events/index.d.ts.map +1 -0
- package/dist/core/events/index.js +22 -0
- package/dist/core/events/index.js.map +1 -0
- package/dist/core/events/memory-storage.d.ts +57 -0
- package/dist/core/events/memory-storage.d.ts.map +1 -0
- package/dist/core/events/memory-storage.js +175 -0
- package/dist/core/events/memory-storage.js.map +1 -0
- package/dist/core/events/types.d.ts +434 -0
- package/dist/core/events/types.d.ts.map +1 -0
- package/dist/core/events/types.js +9 -0
- package/dist/core/events/types.js.map +1 -0
- package/dist/core/events/utils.d.ts +134 -0
- package/dist/core/events/utils.d.ts.map +1 -0
- package/dist/core/events/utils.js +333 -0
- package/dist/core/events/utils.js.map +1 -0
- package/dist/core/fields/conditional.d.ts +115 -0
- package/dist/core/fields/conditional.d.ts.map +1 -0
- package/dist/core/fields/conditional.js +296 -0
- package/dist/core/fields/conditional.js.map +1 -0
- package/dist/core/fields/field-type.d.ts +218 -0
- package/dist/core/fields/field-type.d.ts.map +1 -0
- package/dist/core/fields/field-type.js +17 -0
- package/dist/core/fields/field-type.js.map +1 -0
- package/dist/core/fields/helpers.d.ts +86 -0
- package/dist/core/fields/helpers.d.ts.map +1 -0
- package/dist/core/fields/helpers.js +260 -0
- package/dist/core/fields/helpers.js.map +1 -0
- package/dist/core/fields/index.d.ts +5 -0
- package/dist/core/fields/index.d.ts.map +1 -0
- package/dist/core/fields/index.js +6 -0
- package/dist/core/fields/index.js.map +1 -0
- package/dist/core/fields/registry.d.ts +78 -0
- package/dist/core/fields/registry.d.ts.map +1 -0
- package/dist/core/fields/registry.js +185 -0
- package/dist/core/fields/registry.js.map +1 -0
- package/dist/core/index.d.ts +39 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +57 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/media/image-processor.d.ts +24 -0
- package/dist/core/media/image-processor.d.ts.map +1 -0
- package/dist/core/media/image-processor.js +69 -0
- package/dist/core/media/image-processor.js.map +1 -0
- package/dist/core/media/processors/cloudflare-images.d.ts +83 -0
- package/dist/core/media/processors/cloudflare-images.d.ts.map +1 -0
- package/dist/core/media/processors/cloudflare-images.js +241 -0
- package/dist/core/media/processors/cloudflare-images.js.map +1 -0
- package/dist/core/media/processors/cloudflare-transform-store.d.ts +127 -0
- package/dist/core/media/processors/cloudflare-transform-store.d.ts.map +1 -0
- package/dist/core/media/processors/cloudflare-transform-store.js +237 -0
- package/dist/core/media/processors/cloudflare-transform-store.js.map +1 -0
- package/dist/core/media/processors/cloudflare-transformations.d.ts +81 -0
- package/dist/core/media/processors/cloudflare-transformations.d.ts.map +1 -0
- package/dist/core/media/processors/cloudflare-transformations.js +144 -0
- package/dist/core/media/processors/cloudflare-transformations.js.map +1 -0
- package/dist/core/media/processors/index.d.ts +9 -0
- package/dist/core/media/processors/index.d.ts.map +1 -0
- package/dist/core/media/processors/index.js +13 -0
- package/dist/core/media/processors/index.js.map +1 -0
- package/dist/core/media/processors/noop.d.ts +22 -0
- package/dist/core/media/processors/noop.d.ts.map +1 -0
- package/dist/core/media/processors/noop.js +38 -0
- package/dist/core/media/processors/noop.js.map +1 -0
- package/dist/core/media/processors/sharp.d.ts +30 -0
- package/dist/core/media/processors/sharp.d.ts.map +1 -0
- package/dist/core/media/processors/sharp.js +126 -0
- package/dist/core/media/processors/sharp.js.map +1 -0
- package/dist/core/media/processors/types.d.ts +67 -0
- package/dist/core/media/processors/types.d.ts.map +1 -0
- package/dist/core/media/processors/types.js +42 -0
- package/dist/core/media/processors/types.js.map +1 -0
- package/dist/core/media/processors/workers-images.d.ts +112 -0
- package/dist/core/media/processors/workers-images.d.ts.map +1 -0
- package/dist/core/media/processors/workers-images.js +257 -0
- package/dist/core/media/processors/workers-images.js.map +1 -0
- package/dist/core/schema/registry.d.ts +41 -0
- package/dist/core/schema/registry.d.ts.map +1 -0
- package/dist/core/schema/registry.js +248 -0
- package/dist/core/schema/registry.js.map +1 -0
- package/dist/core/schema/singleton.d.ts +107 -0
- package/dist/core/schema/singleton.d.ts.map +1 -0
- package/dist/core/schema/singleton.js +180 -0
- package/dist/core/schema/singleton.js.map +1 -0
- package/dist/core/security/auth.d.ts +120 -0
- package/dist/core/security/auth.d.ts.map +1 -0
- package/dist/core/security/auth.js +274 -0
- package/dist/core/security/auth.js.map +1 -0
- package/dist/core/security/captcha/index.d.ts +21 -0
- package/dist/core/security/captcha/index.d.ts.map +1 -0
- package/dist/core/security/captcha/index.js +34 -0
- package/dist/core/security/captcha/index.js.map +1 -0
- package/dist/core/security/captcha/recaptcha.d.ts +41 -0
- package/dist/core/security/captcha/recaptcha.d.ts.map +1 -0
- package/dist/core/security/captcha/recaptcha.js +89 -0
- package/dist/core/security/captcha/recaptcha.js.map +1 -0
- package/dist/core/security/captcha/turnstile.d.ts +41 -0
- package/dist/core/security/captcha/turnstile.d.ts.map +1 -0
- package/dist/core/security/captcha/turnstile.js +90 -0
- package/dist/core/security/captcha/turnstile.js.map +1 -0
- package/dist/core/security/captcha/types.d.ts +66 -0
- package/dist/core/security/captcha/types.d.ts.map +1 -0
- package/dist/core/security/captcha/types.js +7 -0
- package/dist/core/security/captcha/types.js.map +1 -0
- package/dist/core/security/mfa/email-otp.d.ts +91 -0
- package/dist/core/security/mfa/email-otp.d.ts.map +1 -0
- package/dist/core/security/mfa/email-otp.js +264 -0
- package/dist/core/security/mfa/email-otp.js.map +1 -0
- package/dist/core/security/mfa/index.d.ts +8 -0
- package/dist/core/security/mfa/index.d.ts.map +1 -0
- package/dist/core/security/mfa/index.js +8 -0
- package/dist/core/security/mfa/index.js.map +1 -0
- package/dist/core/security/mfa/totp.d.ts +87 -0
- package/dist/core/security/mfa/totp.d.ts.map +1 -0
- package/dist/core/security/mfa/totp.js +293 -0
- package/dist/core/security/mfa/totp.js.map +1 -0
- package/dist/core/security/middleware.d.ts +102 -0
- package/dist/core/security/middleware.d.ts.map +1 -0
- package/dist/core/security/middleware.js +261 -0
- package/dist/core/security/middleware.js.map +1 -0
- package/dist/core/security/oauth/google.d.ts +76 -0
- package/dist/core/security/oauth/google.d.ts.map +1 -0
- package/dist/core/security/oauth/google.js +154 -0
- package/dist/core/security/oauth/google.js.map +1 -0
- package/dist/core/security/oauth/index.d.ts +8 -0
- package/dist/core/security/oauth/index.d.ts.map +1 -0
- package/dist/core/security/oauth/index.js +7 -0
- package/dist/core/security/oauth/index.js.map +1 -0
- package/dist/core/security/oauth2/authorization-server.d.ts +195 -0
- package/dist/core/security/oauth2/authorization-server.d.ts.map +1 -0
- package/dist/core/security/oauth2/authorization-server.js +569 -0
- package/dist/core/security/oauth2/authorization-server.js.map +1 -0
- package/dist/core/security/oauth2/index.d.ts +9 -0
- package/dist/core/security/oauth2/index.d.ts.map +1 -0
- package/dist/core/security/oauth2/index.js +9 -0
- package/dist/core/security/oauth2/index.js.map +1 -0
- package/dist/core/security/rate-limiter.d.ts +15 -0
- package/dist/core/security/rate-limiter.d.ts.map +1 -0
- package/dist/core/security/rate-limiter.js +48 -0
- package/dist/core/security/rate-limiter.js.map +1 -0
- package/dist/core/security/validation.d.ts +20 -0
- package/dist/core/security/validation.d.ts.map +1 -0
- package/dist/core/security/validation.js +273 -0
- package/dist/core/security/validation.js.map +1 -0
- package/dist/core/services/auth-service.d.ts +128 -0
- package/dist/core/services/auth-service.d.ts.map +1 -0
- package/dist/core/services/auth-service.js +530 -0
- package/dist/core/services/auth-service.js.map +1 -0
- package/dist/core/services/captcha-service.d.ts +44 -0
- package/dist/core/services/captcha-service.d.ts.map +1 -0
- package/dist/core/services/captcha-service.js +82 -0
- package/dist/core/services/captcha-service.js.map +1 -0
- package/dist/core/services/document-service.d.ts +59 -0
- package/dist/core/services/document-service.d.ts.map +1 -0
- package/dist/core/services/document-service.js +269 -0
- package/dist/core/services/document-service.js.map +1 -0
- package/dist/core/services/media-service.d.ts +40 -0
- package/dist/core/services/media-service.d.ts.map +1 -0
- package/dist/core/services/media-service.js +396 -0
- package/dist/core/services/media-service.js.map +1 -0
- package/dist/core/services/mfa-service.d.ts +123 -0
- package/dist/core/services/mfa-service.d.ts.map +1 -0
- package/dist/core/services/mfa-service.js +606 -0
- package/dist/core/services/mfa-service.js.map +1 -0
- package/dist/core/services/oauth-service.d.ts +58 -0
- package/dist/core/services/oauth-service.d.ts.map +1 -0
- package/dist/core/services/oauth-service.js +253 -0
- package/dist/core/services/oauth-service.js.map +1 -0
- package/dist/core/services/oauth2-server-service.d.ts +61 -0
- package/dist/core/services/oauth2-server-service.d.ts.map +1 -0
- package/dist/core/services/oauth2-server-service.js +259 -0
- package/dist/core/services/oauth2-server-service.js.map +1 -0
- package/dist/core/services/passkey-service.d.ts +59 -0
- package/dist/core/services/passkey-service.d.ts.map +1 -0
- package/dist/core/services/passkey-service.js +238 -0
- package/dist/core/services/passkey-service.js.map +1 -0
- package/dist/core/services/token-service.d.ts +31 -0
- package/dist/core/services/token-service.d.ts.map +1 -0
- package/dist/core/services/token-service.js +132 -0
- package/dist/core/services/token-service.js.map +1 -0
- package/dist/core/services/trusted-device-service.d.ts +39 -0
- package/dist/core/services/trusted-device-service.d.ts.map +1 -0
- package/dist/core/services/trusted-device-service.js +123 -0
- package/dist/core/services/trusted-device-service.js.map +1 -0
- package/dist/core/services/user-service.d.ts +38 -0
- package/dist/core/services/user-service.d.ts.map +1 -0
- package/dist/core/services/user-service.js +265 -0
- package/dist/core/services/user-service.js.map +1 -0
- package/dist/core/studio/integration.d.ts +54 -0
- package/dist/core/studio/integration.d.ts.map +1 -0
- package/dist/core/studio/integration.js +326 -0
- package/dist/core/studio/integration.js.map +1 -0
- package/dist/core/types/index.d.ts +282 -0
- package/dist/core/types/index.d.ts.map +1 -0
- package/dist/core/types/index.js +83 -0
- package/dist/core/types/index.js.map +1 -0
- package/dist/core/types/mail.d.ts +90 -0
- package/dist/core/types/mail.d.ts.map +1 -0
- package/dist/core/types/mail.js +7 -0
- package/dist/core/types/mail.js.map +1 -0
- package/dist/core/types/oauth2.d.ts +9 -0
- package/dist/core/types/oauth2.d.ts.map +1 -0
- package/dist/core/types/oauth2.js +7 -0
- package/dist/core/types/oauth2.js.map +1 -0
- package/dist/core/types/storage-adapters.d.ts +611 -0
- package/dist/core/types/storage-adapters.d.ts.map +1 -0
- package/dist/core/types/storage-adapters.js +14 -0
- package/dist/core/types/storage-adapters.js.map +1 -0
- package/dist/core/types/user.d.ts +10 -0
- package/dist/core/types/user.d.ts.map +1 -0
- package/dist/core/types/user.js +45 -0
- package/dist/core/types/user.js.map +1 -0
- package/dist/core/utils/id-generator.d.ts +16 -0
- package/dist/core/utils/id-generator.d.ts.map +1 -0
- package/dist/core/utils/id-generator.js +61 -0
- package/dist/core/utils/id-generator.js.map +1 -0
- package/dist/core/utils/logger.d.ts +100 -0
- package/dist/core/utils/logger.d.ts.map +1 -0
- package/dist/core/utils/logger.js +212 -0
- package/dist/core/utils/logger.js.map +1 -0
- package/dist/core/utils/reference-expander.d.ts +57 -0
- package/dist/core/utils/reference-expander.d.ts.map +1 -0
- package/dist/core/utils/reference-expander.js +192 -0
- package/dist/core/utils/reference-expander.js.map +1 -0
- package/dist/core/utils/universal-crypto.d.ts +46 -0
- package/dist/core/utils/universal-crypto.d.ts.map +1 -0
- package/dist/core/utils/universal-crypto.js +171 -0
- package/dist/core/utils/universal-crypto.js.map +1 -0
- package/dist/core/validation/validator.d.ts +11 -0
- package/dist/core/validation/validator.d.ts.map +1 -0
- package/dist/core/validation/validator.js +164 -0
- package/dist/core/validation/validator.js.map +1 -0
- package/dist/i18n/config.d.ts +4077 -0
- package/dist/i18n/config.d.ts.map +1 -0
- package/dist/i18n/config.js +116 -0
- package/dist/i18n/config.js.map +1 -0
- package/dist/i18n/hooks.d.ts +86 -0
- package/dist/i18n/hooks.d.ts.map +1 -0
- package/dist/i18n/hooks.js +122 -0
- package/dist/i18n/hooks.js.map +1 -0
- package/dist/i18n/index.d.ts +39 -0
- package/dist/i18n/index.d.ts.map +1 -0
- package/dist/i18n/index.js +40 -0
- package/dist/i18n/index.js.map +1 -0
- package/dist/i18n/locales/en/auth.json +108 -0
- package/dist/i18n/locales/en/common.json +100 -0
- package/dist/i18n/locales/en/errors.json +55 -0
- package/dist/i18n/locales/en/fields.json +456 -0
- package/dist/i18n/locales/en/studio.json +1329 -0
- package/dist/i18n/locales/fr/auth.json +108 -0
- package/dist/i18n/locales/fr/common.json +100 -0
- package/dist/i18n/locales/fr/errors.json +55 -0
- package/dist/i18n/locales/fr/fields.json +456 -0
- package/dist/i18n/locales/fr/studio.json +1274 -0
- package/dist/i18n/provider.d.ts +45 -0
- package/dist/i18n/provider.d.ts.map +1 -0
- package/dist/i18n/provider.js +49 -0
- package/dist/i18n/provider.js.map +1 -0
- package/dist/i18n/types.d.ts +8 -0
- package/dist/i18n/types.d.ts.map +1 -0
- package/dist/i18n/types.js +7 -0
- package/dist/i18n/types.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/integrations/express/adapter.d.ts +33 -0
- package/dist/integrations/express/adapter.d.ts.map +1 -0
- package/dist/integrations/express/adapter.js +203 -0
- package/dist/integrations/express/adapter.js.map +1 -0
- package/dist/integrations/express/config.d.ts +599 -0
- package/dist/integrations/express/config.d.ts.map +1 -0
- package/dist/integrations/express/config.js +166 -0
- package/dist/integrations/express/config.js.map +1 -0
- package/dist/integrations/express/index.d.ts +10 -0
- package/dist/integrations/express/index.d.ts.map +1 -0
- package/dist/integrations/express/index.js +9 -0
- package/dist/integrations/express/index.js.map +1 -0
- package/dist/integrations/express/integration.d.ts +111 -0
- package/dist/integrations/express/integration.d.ts.map +1 -0
- package/dist/integrations/express/integration.js +730 -0
- package/dist/integrations/express/integration.js.map +1 -0
- package/dist/integrations/express/media-rewrite-middleware.d.ts +78 -0
- package/dist/integrations/express/media-rewrite-middleware.d.ts.map +1 -0
- package/dist/integrations/express/media-rewrite-middleware.js +211 -0
- package/dist/integrations/express/media-rewrite-middleware.js.map +1 -0
- package/dist/integrations/express/media-url-generator.d.ts +52 -0
- package/dist/integrations/express/media-url-generator.d.ts.map +1 -0
- package/dist/integrations/express/media-url-generator.js +107 -0
- package/dist/integrations/express/media-url-generator.js.map +1 -0
- package/dist/integrations/express/middleware.d.ts +25 -0
- package/dist/integrations/express/middleware.d.ts.map +1 -0
- package/dist/integrations/express/middleware.js +130 -0
- package/dist/integrations/express/middleware.js.map +1 -0
- package/dist/integrations/express/server.d.ts +99 -0
- package/dist/integrations/express/server.d.ts.map +1 -0
- package/dist/integrations/express/server.js +597 -0
- package/dist/integrations/express/server.js.map +1 -0
- package/dist/integrations/express/types.d.ts +106 -0
- package/dist/integrations/express/types.d.ts.map +1 -0
- package/dist/integrations/express/types.js +2 -0
- package/dist/integrations/express/types.js.map +1 -0
- package/dist/mail/adapters/console.d.ts +30 -0
- package/dist/mail/adapters/console.d.ts.map +1 -0
- package/dist/mail/adapters/console.js +169 -0
- package/dist/mail/adapters/console.js.map +1 -0
- package/dist/mail/adapters/resend.d.ts +18 -0
- package/dist/mail/adapters/resend.d.ts.map +1 -0
- package/dist/mail/adapters/resend.js +148 -0
- package/dist/mail/adapters/resend.js.map +1 -0
- package/dist/mail/adapters/smtp.d.ts +22 -0
- package/dist/mail/adapters/smtp.d.ts.map +1 -0
- package/dist/mail/adapters/smtp.js +180 -0
- package/dist/mail/adapters/smtp.js.map +1 -0
- package/dist/mail/index.d.ts +15 -0
- package/dist/mail/index.d.ts.map +1 -0
- package/dist/mail/index.js +13 -0
- package/dist/mail/index.js.map +1 -0
- package/dist/mail/mail-service.d.ts +180 -0
- package/dist/mail/mail-service.d.ts.map +1 -0
- package/dist/mail/mail-service.js +370 -0
- package/dist/mail/mail-service.js.map +1 -0
- package/dist/mail/notification-service.d.ts +82 -0
- package/dist/mail/notification-service.d.ts.map +1 -0
- package/dist/mail/notification-service.js +262 -0
- package/dist/mail/notification-service.js.map +1 -0
- package/dist/mail/templates/built-in-templates.d.ts +47 -0
- package/dist/mail/templates/built-in-templates.d.ts.map +1 -0
- package/dist/mail/templates/built-in-templates.js +416 -0
- package/dist/mail/templates/built-in-templates.js.map +1 -0
- package/dist/mail/templates/custom-template-renderer.d.ts +115 -0
- package/dist/mail/templates/custom-template-renderer.d.ts.map +1 -0
- package/dist/mail/templates/custom-template-renderer.js +225 -0
- package/dist/mail/templates/custom-template-renderer.js.map +1 -0
- package/dist/mail/types.d.ts +7 -0
- package/dist/mail/types.d.ts.map +1 -0
- package/dist/mail/types.js +7 -0
- package/dist/mail/types.js.map +1 -0
- package/dist/routes/auth/captcha.d.ts +33 -0
- package/dist/routes/auth/captcha.d.ts.map +1 -0
- package/dist/routes/auth/captcha.js +102 -0
- package/dist/routes/auth/captcha.js.map +1 -0
- package/dist/routes/auth/change-password.d.ts +17 -0
- package/dist/routes/auth/change-password.d.ts.map +1 -0
- package/dist/routes/auth/change-password.js +121 -0
- package/dist/routes/auth/change-password.js.map +1 -0
- package/dist/routes/auth/index.d.ts +13 -0
- package/dist/routes/auth/index.d.ts.map +1 -0
- package/dist/routes/auth/index.js +35 -0
- package/dist/routes/auth/index.js.map +1 -0
- package/dist/routes/auth/mfa.d.ts +121 -0
- package/dist/routes/auth/mfa.d.ts.map +1 -0
- package/dist/routes/auth/mfa.js +1143 -0
- package/dist/routes/auth/mfa.js.map +1 -0
- package/dist/routes/auth/oauth.d.ts +37 -0
- package/dist/routes/auth/oauth.d.ts.map +1 -0
- package/dist/routes/auth/oauth.js +420 -0
- package/dist/routes/auth/oauth.js.map +1 -0
- package/dist/routes/auth/oauth2-server.d.ts +70 -0
- package/dist/routes/auth/oauth2-server.d.ts.map +1 -0
- package/dist/routes/auth/oauth2-server.js +775 -0
- package/dist/routes/auth/oauth2-server.js.map +1 -0
- package/dist/routes/auth/passkey.d.ts +65 -0
- package/dist/routes/auth/passkey.d.ts.map +1 -0
- package/dist/routes/auth/passkey.js +910 -0
- package/dist/routes/auth/passkey.js.map +1 -0
- package/dist/routes/auth/password-reset.d.ts +23 -0
- package/dist/routes/auth/password-reset.d.ts.map +1 -0
- package/dist/routes/auth/password-reset.js +336 -0
- package/dist/routes/auth/password-reset.js.map +1 -0
- package/dist/routes/handlers/audit.d.ts +21 -0
- package/dist/routes/handlers/audit.d.ts.map +1 -0
- package/dist/routes/handlers/audit.js +124 -0
- package/dist/routes/handlers/audit.js.map +1 -0
- package/dist/routes/handlers/auth.d.ts +150 -0
- package/dist/routes/handlers/auth.d.ts.map +1 -0
- package/dist/routes/handlers/auth.js +512 -0
- package/dist/routes/handlers/auth.js.map +1 -0
- package/dist/routes/handlers/base.d.ts +34 -0
- package/dist/routes/handlers/base.d.ts.map +1 -0
- package/dist/routes/handlers/base.js +366 -0
- package/dist/routes/handlers/base.js.map +1 -0
- package/dist/routes/handlers/config.d.ts +31 -0
- package/dist/routes/handlers/config.d.ts.map +1 -0
- package/dist/routes/handlers/config.js +483 -0
- package/dist/routes/handlers/config.js.map +1 -0
- package/dist/routes/handlers/documents.d.ts +50 -0
- package/dist/routes/handlers/documents.d.ts.map +1 -0
- package/dist/routes/handlers/documents.js +708 -0
- package/dist/routes/handlers/documents.js.map +1 -0
- package/dist/routes/handlers/index.d.ts +12 -0
- package/dist/routes/handlers/index.d.ts.map +1 -0
- package/dist/routes/handlers/index.js +11 -0
- package/dist/routes/handlers/index.js.map +1 -0
- package/dist/routes/handlers/media.d.ts +24 -0
- package/dist/routes/handlers/media.d.ts.map +1 -0
- package/dist/routes/handlers/media.js +432 -0
- package/dist/routes/handlers/media.js.map +1 -0
- package/dist/routes/handlers/search.d.ts +10 -0
- package/dist/routes/handlers/search.d.ts.map +1 -0
- package/dist/routes/handlers/search.js +153 -0
- package/dist/routes/handlers/search.js.map +1 -0
- package/dist/routes/handlers/tokens.d.ts +14 -0
- package/dist/routes/handlers/tokens.d.ts.map +1 -0
- package/dist/routes/handlers/tokens.js +109 -0
- package/dist/routes/handlers/tokens.js.map +1 -0
- package/dist/routes/handlers/types.d.ts +13 -0
- package/dist/routes/handlers/types.d.ts.map +1 -0
- package/dist/routes/handlers/types.js +5 -0
- package/dist/routes/handlers/types.js.map +1 -0
- package/dist/routes/handlers/users.d.ts +16 -0
- package/dist/routes/handlers/users.d.ts.map +1 -0
- package/dist/routes/handlers/users.js +198 -0
- package/dist/routes/handlers/users.js.map +1 -0
- package/dist/routes/handlers/webhooks.d.ts +37 -0
- package/dist/routes/handlers/webhooks.d.ts.map +1 -0
- package/dist/routes/handlers/webhooks.js +343 -0
- package/dist/routes/handlers/webhooks.js.map +1 -0
- package/dist/routes/index.d.ts +5 -0
- package/dist/routes/index.d.ts.map +1 -0
- package/dist/routes/index.js +5 -0
- package/dist/routes/index.js.map +1 -0
- package/dist/routes/routes.d.ts +29 -0
- package/dist/routes/routes.d.ts.map +1 -0
- package/dist/routes/routes.js +531 -0
- package/dist/routes/routes.js.map +1 -0
- package/dist/routes/slug-processor.d.ts +32 -0
- package/dist/routes/slug-processor.d.ts.map +1 -0
- package/dist/routes/slug-processor.js +328 -0
- package/dist/routes/slug-processor.js.map +1 -0
- package/dist/routes/types.d.ts +31 -0
- package/dist/routes/types.d.ts.map +1 -0
- package/dist/routes/types.js +7 -0
- package/dist/routes/types.js.map +1 -0
- package/dist/routes/users/index.d.ts +7 -0
- package/dist/routes/users/index.d.ts.map +1 -0
- package/dist/routes/users/index.js +7 -0
- package/dist/routes/users/index.js.map +1 -0
- package/dist/structure/builder/StructureBuilder.d.ts +209 -0
- package/dist/structure/builder/StructureBuilder.d.ts.map +1 -0
- package/dist/structure/builder/StructureBuilder.js +425 -0
- package/dist/structure/builder/StructureBuilder.js.map +1 -0
- package/dist/structure/builder/index.d.ts +8 -0
- package/dist/structure/builder/index.d.ts.map +1 -0
- package/dist/structure/builder/index.js +6 -0
- package/dist/structure/builder/index.js.map +1 -0
- package/dist/structure/builder/navigation-tree-builder.d.ts +33 -0
- package/dist/structure/builder/navigation-tree-builder.d.ts.map +1 -0
- package/dist/structure/builder/navigation-tree-builder.js +284 -0
- package/dist/structure/builder/navigation-tree-builder.js.map +1 -0
- package/dist/structure/errors/index.d.ts +72 -0
- package/dist/structure/errors/index.d.ts.map +1 -0
- package/dist/structure/errors/index.js +136 -0
- package/dist/structure/errors/index.js.map +1 -0
- package/dist/structure/index.d.ts +14 -0
- package/dist/structure/index.d.ts.map +1 -0
- package/dist/structure/index.js +16 -0
- package/dist/structure/index.js.map +1 -0
- package/dist/structure/types/filters.d.ts +167 -0
- package/dist/structure/types/filters.d.ts.map +1 -0
- package/dist/structure/types/filters.js +38 -0
- package/dist/structure/types/filters.js.map +1 -0
- package/dist/structure/types/index.d.ts +11 -0
- package/dist/structure/types/index.d.ts.map +1 -0
- package/dist/structure/types/index.js +7 -0
- package/dist/structure/types/index.js.map +1 -0
- package/dist/structure/types/permissions.d.ts +159 -0
- package/dist/structure/types/permissions.d.ts.map +1 -0
- package/dist/structure/types/permissions.js +49 -0
- package/dist/structure/types/permissions.js.map +1 -0
- package/dist/structure/types/structure.d.ts +342 -0
- package/dist/structure/types/structure.d.ts.map +1 -0
- package/dist/structure/types/structure.js +6 -0
- package/dist/structure/types/structure.js.map +1 -0
- package/dist/structure/types/views.d.ts +278 -0
- package/dist/structure/types/views.d.ts.map +1 -0
- package/dist/structure/types/views.js +6 -0
- package/dist/structure/types/views.js.map +1 -0
- package/dist/structure/utils/hash.d.ts +61 -0
- package/dist/structure/utils/hash.d.ts.map +1 -0
- package/dist/structure/utils/hash.js +118 -0
- package/dist/structure/utils/hash.js.map +1 -0
- package/dist/structure/utils/index.d.ts +10 -0
- package/dist/structure/utils/index.d.ts.map +1 -0
- package/dist/structure/utils/index.js +7 -0
- package/dist/structure/utils/index.js.map +1 -0
- package/dist/structure/utils/permission-checker.d.ts +57 -0
- package/dist/structure/utils/permission-checker.d.ts.map +1 -0
- package/dist/structure/utils/permission-checker.js +286 -0
- package/dist/structure/utils/permission-checker.js.map +1 -0
- package/dist/structure/utils/query-builder.d.ts +82 -0
- package/dist/structure/utils/query-builder.d.ts.map +1 -0
- package/dist/structure/utils/query-builder.js +489 -0
- package/dist/structure/utils/query-builder.js.map +1 -0
- package/dist/structure/utils/structure-merger.d.ts +91 -0
- package/dist/structure/utils/structure-merger.d.ts.map +1 -0
- package/dist/structure/utils/structure-merger.js +445 -0
- package/dist/structure/utils/structure-merger.js.map +1 -0
- package/dist/structure/validation/schemas.d.ts +3107 -0
- package/dist/structure/validation/schemas.d.ts.map +1 -0
- package/dist/structure/validation/schemas.js +436 -0
- package/dist/structure/validation/schemas.js.map +1 -0
- package/dist/types/api-requests.d.ts +334 -0
- package/dist/types/api-requests.d.ts.map +1 -0
- package/dist/types/api-requests.js +8 -0
- package/dist/types/api-requests.js.map +1 -0
- package/dist/types/auth.d.ts +394 -0
- package/dist/types/auth.d.ts.map +1 -0
- package/dist/types/auth.js +8 -0
- package/dist/types/auth.js.map +1 -0
- package/dist/types/document.d.ts +49 -0
- package/dist/types/document.d.ts.map +1 -0
- package/dist/types/document.js +6 -0
- package/dist/types/document.js.map +1 -0
- package/dist/types/field-system.d.ts +197 -0
- package/dist/types/field-system.d.ts.map +1 -0
- package/dist/types/field-system.js +8 -0
- package/dist/types/field-system.js.map +1 -0
- package/dist/types/fields.d.ts +17 -0
- package/dist/types/fields.d.ts.map +1 -0
- package/dist/types/fields.js +21 -0
- package/dist/types/fields.js.map +1 -0
- package/dist/types/http.d.ts +162 -0
- package/dist/types/http.d.ts.map +1 -0
- package/dist/types/http.js +8 -0
- package/dist/types/http.js.map +1 -0
- package/dist/types/i18n.d.ts +56 -0
- package/dist/types/i18n.d.ts.map +1 -0
- package/dist/types/i18n.js +25 -0
- package/dist/types/i18n.js.map +1 -0
- package/dist/types/index.d.ts +22 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +10 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/mail.d.ts +224 -0
- package/dist/types/mail.d.ts.map +1 -0
- package/dist/types/mail.js +8 -0
- package/dist/types/mail.js.map +1 -0
- package/dist/types/media.d.ts +56 -0
- package/dist/types/media.d.ts.map +1 -0
- package/dist/types/media.js +6 -0
- package/dist/types/media.js.map +1 -0
- package/dist/types/oauth2.d.ts +287 -0
- package/dist/types/oauth2.d.ts.map +1 -0
- package/dist/types/oauth2.js +43 -0
- package/dist/types/oauth2.js.map +1 -0
- package/dist/types/passkey.d.ts +217 -0
- package/dist/types/passkey.d.ts.map +1 -0
- package/dist/types/passkey.js +8 -0
- package/dist/types/passkey.js.map +1 -0
- package/dist/types/slug.d.ts +99 -0
- package/dist/types/slug.d.ts.map +1 -0
- package/dist/types/slug.js +8 -0
- package/dist/types/slug.js.map +1 -0
- package/package.json +183 -0
|
@@ -0,0 +1,1143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MFA (Multi-Factor Authentication) Routes
|
|
3
|
+
*
|
|
4
|
+
* API routes for MFA setup, verification, and management.
|
|
5
|
+
*/
|
|
6
|
+
import { InvalidInputError, createLogger, } from '../../core/index.js';
|
|
7
|
+
const logger = createLogger('routes', 'MFA');
|
|
8
|
+
// =============================================================================
|
|
9
|
+
// HELPER FUNCTIONS
|
|
10
|
+
// =============================================================================
|
|
11
|
+
/**
|
|
12
|
+
* Get user ID from either the setup token header or authenticated user.
|
|
13
|
+
* Used for MFA setup routes that need to work during the login flow.
|
|
14
|
+
*/
|
|
15
|
+
async function getUserIdFromSetupTokenOrAuth(req, core) {
|
|
16
|
+
// First check for setup token in header
|
|
17
|
+
const setupTokenHeader = req.headers['x-mfa-setup-token'];
|
|
18
|
+
const setupToken = Array.isArray(setupTokenHeader) ? setupTokenHeader[0] : setupTokenHeader;
|
|
19
|
+
if (setupToken) {
|
|
20
|
+
const payload = await core.verifyMFAToken(setupToken);
|
|
21
|
+
if (payload && payload.type === 'mfa_setup' && payload.userId) {
|
|
22
|
+
return { userId: payload.userId, fromSetupToken: true };
|
|
23
|
+
}
|
|
24
|
+
return null; // Invalid setup token
|
|
25
|
+
}
|
|
26
|
+
// Fall back to authenticated user
|
|
27
|
+
if (req.user?.id) {
|
|
28
|
+
return { userId: req.user.id, fromSetupToken: false };
|
|
29
|
+
}
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
// =============================================================================
|
|
33
|
+
// ROUTE HANDLERS - MFA VERIFICATION
|
|
34
|
+
// =============================================================================
|
|
35
|
+
/**
|
|
36
|
+
* POST /auth/mfa/verify
|
|
37
|
+
* Verify MFA code after login (using mfaToken)
|
|
38
|
+
*/
|
|
39
|
+
export async function verifyMFA(req, core) {
|
|
40
|
+
try {
|
|
41
|
+
const body = req.body;
|
|
42
|
+
if (!body.mfaToken) {
|
|
43
|
+
throw new InvalidInputError('MFA token is required', 'mfaToken');
|
|
44
|
+
}
|
|
45
|
+
if (!body.code) {
|
|
46
|
+
throw new InvalidInputError('Verification code is required', 'code');
|
|
47
|
+
}
|
|
48
|
+
if (!body.method) {
|
|
49
|
+
throw new InvalidInputError('MFA method is required', 'method');
|
|
50
|
+
}
|
|
51
|
+
// Verify the MFA token first to get user info
|
|
52
|
+
const tokenPayload = await core.verifyMFAToken(body.mfaToken);
|
|
53
|
+
if (!tokenPayload || tokenPayload.type !== 'mfa_pending') {
|
|
54
|
+
return {
|
|
55
|
+
status: 401,
|
|
56
|
+
headers: {},
|
|
57
|
+
body: {
|
|
58
|
+
success: false,
|
|
59
|
+
error: {
|
|
60
|
+
message: 'Invalid or expired MFA token',
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
// Verify the MFA code
|
|
66
|
+
const isValid = await core.verifyMFACode(tokenPayload.userId, body.code, body.method);
|
|
67
|
+
if (!isValid) {
|
|
68
|
+
logger.warn('MFA verification failed', {
|
|
69
|
+
userId: tokenPayload.userId,
|
|
70
|
+
method: body.method,
|
|
71
|
+
});
|
|
72
|
+
return {
|
|
73
|
+
status: 401,
|
|
74
|
+
headers: {},
|
|
75
|
+
body: {
|
|
76
|
+
success: false,
|
|
77
|
+
error: {
|
|
78
|
+
message: 'Invalid verification code',
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
// Complete MFA authentication and issue full tokens
|
|
84
|
+
const result = await core.completeMFAAuthentication(body.mfaToken, {
|
|
85
|
+
rememberMe: body.rememberMe,
|
|
86
|
+
trustDevice: body.trustDevice,
|
|
87
|
+
deviceId: body.deviceId,
|
|
88
|
+
deviceName: body.deviceName,
|
|
89
|
+
});
|
|
90
|
+
if (!result) {
|
|
91
|
+
return {
|
|
92
|
+
status: 401,
|
|
93
|
+
headers: {},
|
|
94
|
+
body: {
|
|
95
|
+
success: false,
|
|
96
|
+
error: {
|
|
97
|
+
message: 'Failed to complete authentication',
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
logger.info('MFA verification successful', {
|
|
103
|
+
userId: tokenPayload.userId,
|
|
104
|
+
method: body.method,
|
|
105
|
+
deviceTrusted: body.trustDevice,
|
|
106
|
+
});
|
|
107
|
+
// Return without password hash
|
|
108
|
+
const { passwordHash, ...safeUser } = result.user;
|
|
109
|
+
return {
|
|
110
|
+
status: 200,
|
|
111
|
+
headers: {},
|
|
112
|
+
body: {
|
|
113
|
+
success: true,
|
|
114
|
+
token: result.token,
|
|
115
|
+
refreshToken: result.refreshToken,
|
|
116
|
+
user: safeUser,
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
logger.error('MFA verification error', error);
|
|
122
|
+
if (error instanceof InvalidInputError) {
|
|
123
|
+
return {
|
|
124
|
+
status: 400,
|
|
125
|
+
headers: {},
|
|
126
|
+
body: {
|
|
127
|
+
success: false,
|
|
128
|
+
error: {
|
|
129
|
+
message: error.message,
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
return {
|
|
135
|
+
status: 500,
|
|
136
|
+
headers: {},
|
|
137
|
+
body: {
|
|
138
|
+
success: false,
|
|
139
|
+
error: {
|
|
140
|
+
message: 'Failed to verify MFA code',
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* POST /auth/mfa/verify-backup
|
|
148
|
+
* Verify using backup code
|
|
149
|
+
*/
|
|
150
|
+
export async function verifyMFABackup(req, core) {
|
|
151
|
+
try {
|
|
152
|
+
const body = req.body;
|
|
153
|
+
if (!body.mfaToken) {
|
|
154
|
+
throw new InvalidInputError('MFA token is required', 'mfaToken');
|
|
155
|
+
}
|
|
156
|
+
if (!body.code) {
|
|
157
|
+
throw new InvalidInputError('Backup code is required', 'code');
|
|
158
|
+
}
|
|
159
|
+
// Verify the MFA token first
|
|
160
|
+
const tokenPayload = await core.verifyMFAToken(body.mfaToken);
|
|
161
|
+
if (!tokenPayload || tokenPayload.type !== 'mfa_pending') {
|
|
162
|
+
return {
|
|
163
|
+
status: 401,
|
|
164
|
+
headers: {},
|
|
165
|
+
body: {
|
|
166
|
+
success: false,
|
|
167
|
+
error: {
|
|
168
|
+
message: 'Invalid or expired MFA token',
|
|
169
|
+
},
|
|
170
|
+
},
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
// Verify backup code
|
|
174
|
+
const result = await core.verifyBackupCode(tokenPayload.userId, body.code);
|
|
175
|
+
if (!result.valid) {
|
|
176
|
+
logger.warn('Backup code verification failed', {
|
|
177
|
+
userId: tokenPayload.userId,
|
|
178
|
+
});
|
|
179
|
+
return {
|
|
180
|
+
status: 401,
|
|
181
|
+
headers: {},
|
|
182
|
+
body: {
|
|
183
|
+
success: false,
|
|
184
|
+
error: {
|
|
185
|
+
message: 'Invalid backup code',
|
|
186
|
+
},
|
|
187
|
+
},
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
// Complete authentication
|
|
191
|
+
const authResult = await core.completeMFAAuthentication(body.mfaToken, {
|
|
192
|
+
rememberMe: body.rememberMe,
|
|
193
|
+
trustDevice: body.trustDevice,
|
|
194
|
+
deviceId: body.deviceId,
|
|
195
|
+
deviceName: body.deviceName,
|
|
196
|
+
});
|
|
197
|
+
if (!authResult) {
|
|
198
|
+
return {
|
|
199
|
+
status: 401,
|
|
200
|
+
headers: {},
|
|
201
|
+
body: {
|
|
202
|
+
success: false,
|
|
203
|
+
error: {
|
|
204
|
+
message: 'Failed to complete authentication',
|
|
205
|
+
},
|
|
206
|
+
},
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
logger.info('Backup code verification successful', {
|
|
210
|
+
userId: tokenPayload.userId,
|
|
211
|
+
remainingCodes: result.remainingCodes.length,
|
|
212
|
+
});
|
|
213
|
+
const { passwordHash, ...safeUser } = authResult.user;
|
|
214
|
+
return {
|
|
215
|
+
status: 200,
|
|
216
|
+
headers: {},
|
|
217
|
+
body: {
|
|
218
|
+
success: true,
|
|
219
|
+
token: authResult.token,
|
|
220
|
+
refreshToken: authResult.refreshToken,
|
|
221
|
+
user: safeUser,
|
|
222
|
+
warning: result.remainingCodes.length < 3
|
|
223
|
+
? `Only ${result.remainingCodes.length} backup codes remaining. Consider regenerating.`
|
|
224
|
+
: undefined,
|
|
225
|
+
},
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
catch (error) {
|
|
229
|
+
logger.error('Backup code verification error', error);
|
|
230
|
+
if (error instanceof InvalidInputError) {
|
|
231
|
+
return {
|
|
232
|
+
status: 400,
|
|
233
|
+
headers: {},
|
|
234
|
+
body: {
|
|
235
|
+
success: false,
|
|
236
|
+
error: {
|
|
237
|
+
message: error.message,
|
|
238
|
+
},
|
|
239
|
+
},
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
return {
|
|
243
|
+
status: 500,
|
|
244
|
+
headers: {},
|
|
245
|
+
body: {
|
|
246
|
+
success: false,
|
|
247
|
+
error: {
|
|
248
|
+
message: 'Failed to verify backup code',
|
|
249
|
+
},
|
|
250
|
+
},
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
// =============================================================================
|
|
255
|
+
// ROUTE HANDLERS - MFA SETUP (TOTP)
|
|
256
|
+
// =============================================================================
|
|
257
|
+
/**
|
|
258
|
+
* POST /auth/mfa/setup/totp
|
|
259
|
+
* Initialize TOTP setup (get QR code)
|
|
260
|
+
* Accepts either normal auth or X-MFA-Setup-Token header for login flow
|
|
261
|
+
*/
|
|
262
|
+
export async function initTOTPSetup(req, core) {
|
|
263
|
+
try {
|
|
264
|
+
// Get user ID from setup token or authenticated user
|
|
265
|
+
const userInfo = await getUserIdFromSetupTokenOrAuth(req, core);
|
|
266
|
+
if (!userInfo) {
|
|
267
|
+
return {
|
|
268
|
+
status: 401,
|
|
269
|
+
headers: {},
|
|
270
|
+
body: {
|
|
271
|
+
success: false,
|
|
272
|
+
error: {
|
|
273
|
+
message: 'Authentication required (provide auth token or X-MFA-Setup-Token header)',
|
|
274
|
+
},
|
|
275
|
+
},
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
const result = await core.initializeTOTPSetup(userInfo.userId);
|
|
279
|
+
if (!result) {
|
|
280
|
+
return {
|
|
281
|
+
status: 500,
|
|
282
|
+
headers: {},
|
|
283
|
+
body: {
|
|
284
|
+
success: false,
|
|
285
|
+
error: {
|
|
286
|
+
message: 'Failed to initialize TOTP setup',
|
|
287
|
+
},
|
|
288
|
+
},
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
logger.info('TOTP setup initialized', { userId: userInfo.userId, fromSetupToken: userInfo.fromSetupToken });
|
|
292
|
+
return {
|
|
293
|
+
status: 200,
|
|
294
|
+
headers: {},
|
|
295
|
+
body: {
|
|
296
|
+
success: true,
|
|
297
|
+
data: {
|
|
298
|
+
qrCode: result.qrCodeDataUrl,
|
|
299
|
+
manualEntryKey: result.manualEntryKey,
|
|
300
|
+
uri: result.uri,
|
|
301
|
+
},
|
|
302
|
+
},
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
catch (error) {
|
|
306
|
+
logger.error('TOTP setup initialization error', error);
|
|
307
|
+
return {
|
|
308
|
+
status: 500,
|
|
309
|
+
headers: {},
|
|
310
|
+
body: {
|
|
311
|
+
success: false,
|
|
312
|
+
error: {
|
|
313
|
+
message: 'Failed to initialize TOTP setup',
|
|
314
|
+
},
|
|
315
|
+
},
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* POST /auth/mfa/setup/totp/verify
|
|
321
|
+
* Verify TOTP code and enable
|
|
322
|
+
* Accepts either normal auth or X-MFA-Setup-Token header for login flow
|
|
323
|
+
*/
|
|
324
|
+
export async function verifyTOTPSetup(req, core) {
|
|
325
|
+
try {
|
|
326
|
+
// Get user ID from setup token or authenticated user
|
|
327
|
+
const userInfo = await getUserIdFromSetupTokenOrAuth(req, core);
|
|
328
|
+
if (!userInfo) {
|
|
329
|
+
return {
|
|
330
|
+
status: 401,
|
|
331
|
+
headers: {},
|
|
332
|
+
body: {
|
|
333
|
+
success: false,
|
|
334
|
+
error: {
|
|
335
|
+
message: 'Authentication required (provide auth token or X-MFA-Setup-Token header)',
|
|
336
|
+
},
|
|
337
|
+
},
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
const body = req.body;
|
|
341
|
+
if (!body.code) {
|
|
342
|
+
throw new InvalidInputError('Verification code is required', 'code');
|
|
343
|
+
}
|
|
344
|
+
const result = await core.verifyAndEnableTOTP(userInfo.userId, body.code);
|
|
345
|
+
if (!result.enabled) {
|
|
346
|
+
return {
|
|
347
|
+
status: 400,
|
|
348
|
+
headers: {},
|
|
349
|
+
body: {
|
|
350
|
+
success: false,
|
|
351
|
+
error: {
|
|
352
|
+
message: 'Invalid verification code',
|
|
353
|
+
},
|
|
354
|
+
},
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
logger.info('TOTP enabled', { userId: userInfo.userId, fromSetupToken: userInfo.fromSetupToken });
|
|
358
|
+
// If this was from a setup token (during login flow), issue full auth tokens
|
|
359
|
+
if (userInfo.fromSetupToken) {
|
|
360
|
+
const setupTokenHeader = req.headers['x-mfa-setup-token'];
|
|
361
|
+
const setupToken = Array.isArray(setupTokenHeader) ? setupTokenHeader[0] : setupTokenHeader;
|
|
362
|
+
const authResult = await core.completeMFASetupAndLogin(setupToken);
|
|
363
|
+
if (authResult) {
|
|
364
|
+
const { passwordHash, ...safeUser } = authResult.user;
|
|
365
|
+
return {
|
|
366
|
+
status: 200,
|
|
367
|
+
headers: {},
|
|
368
|
+
body: {
|
|
369
|
+
success: true,
|
|
370
|
+
message: 'TOTP authentication enabled',
|
|
371
|
+
backupCodes: result.backupCodes,
|
|
372
|
+
token: authResult.token,
|
|
373
|
+
refreshToken: authResult.refreshToken,
|
|
374
|
+
user: safeUser,
|
|
375
|
+
expiresAt: authResult.expiresAt,
|
|
376
|
+
},
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
// Normal flow (user already authenticated)
|
|
381
|
+
return {
|
|
382
|
+
status: 200,
|
|
383
|
+
headers: {},
|
|
384
|
+
body: {
|
|
385
|
+
success: true,
|
|
386
|
+
message: 'TOTP authentication enabled',
|
|
387
|
+
backupCodes: result.backupCodes,
|
|
388
|
+
},
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
catch (error) {
|
|
392
|
+
logger.error('TOTP verification error', error);
|
|
393
|
+
if (error instanceof InvalidInputError) {
|
|
394
|
+
return {
|
|
395
|
+
status: 400,
|
|
396
|
+
headers: {},
|
|
397
|
+
body: {
|
|
398
|
+
success: false,
|
|
399
|
+
error: {
|
|
400
|
+
message: error.message,
|
|
401
|
+
},
|
|
402
|
+
},
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
return {
|
|
406
|
+
status: 500,
|
|
407
|
+
headers: {},
|
|
408
|
+
body: {
|
|
409
|
+
success: false,
|
|
410
|
+
error: {
|
|
411
|
+
message: 'Failed to verify TOTP setup',
|
|
412
|
+
},
|
|
413
|
+
},
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
// =============================================================================
|
|
418
|
+
// ROUTE HANDLERS - MFA SETUP (EMAIL OTP)
|
|
419
|
+
// =============================================================================
|
|
420
|
+
/**
|
|
421
|
+
* POST /auth/mfa/setup/email
|
|
422
|
+
* Initialize Email OTP setup (sends verification code)
|
|
423
|
+
* Accepts either normal auth or X-MFA-Setup-Token header for login flow
|
|
424
|
+
*/
|
|
425
|
+
export async function initEmailOTPSetup(req, core) {
|
|
426
|
+
try {
|
|
427
|
+
// Get user ID from setup token or authenticated user
|
|
428
|
+
const userInfo = await getUserIdFromSetupTokenOrAuth(req, core);
|
|
429
|
+
if (!userInfo) {
|
|
430
|
+
return {
|
|
431
|
+
status: 401,
|
|
432
|
+
headers: {},
|
|
433
|
+
body: {
|
|
434
|
+
success: false,
|
|
435
|
+
error: {
|
|
436
|
+
message: 'Authentication required (provide auth token or X-MFA-Setup-Token header)',
|
|
437
|
+
},
|
|
438
|
+
},
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
const result = await core.initializeEmailOTPSetup(userInfo.userId);
|
|
442
|
+
if (!result) {
|
|
443
|
+
return {
|
|
444
|
+
status: 500,
|
|
445
|
+
headers: {},
|
|
446
|
+
body: {
|
|
447
|
+
success: false,
|
|
448
|
+
error: {
|
|
449
|
+
message: 'Failed to initialize Email OTP setup',
|
|
450
|
+
},
|
|
451
|
+
},
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
logger.info('Email OTP setup initialized', { userId: userInfo.userId, fromSetupToken: userInfo.fromSetupToken });
|
|
455
|
+
return {
|
|
456
|
+
status: 200,
|
|
457
|
+
headers: {},
|
|
458
|
+
body: {
|
|
459
|
+
success: true,
|
|
460
|
+
message: 'Verification code sent to your email',
|
|
461
|
+
expiresIn: result.expiresIn,
|
|
462
|
+
},
|
|
463
|
+
};
|
|
464
|
+
}
|
|
465
|
+
catch (error) {
|
|
466
|
+
logger.error('Email OTP setup initialization error', error);
|
|
467
|
+
return {
|
|
468
|
+
status: 500,
|
|
469
|
+
headers: {},
|
|
470
|
+
body: {
|
|
471
|
+
success: false,
|
|
472
|
+
error: {
|
|
473
|
+
message: 'Failed to initialize Email OTP setup',
|
|
474
|
+
},
|
|
475
|
+
},
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
480
|
+
* POST /auth/mfa/setup/email/verify
|
|
481
|
+
* Verify Email OTP code and enable
|
|
482
|
+
* Accepts either normal auth or X-MFA-Setup-Token header for login flow
|
|
483
|
+
*/
|
|
484
|
+
export async function verifyEmailOTPSetup(req, core) {
|
|
485
|
+
try {
|
|
486
|
+
// Get user ID from setup token or authenticated user
|
|
487
|
+
const userInfo = await getUserIdFromSetupTokenOrAuth(req, core);
|
|
488
|
+
if (!userInfo) {
|
|
489
|
+
return {
|
|
490
|
+
status: 401,
|
|
491
|
+
headers: {},
|
|
492
|
+
body: {
|
|
493
|
+
success: false,
|
|
494
|
+
error: {
|
|
495
|
+
message: 'Authentication required (provide auth token or X-MFA-Setup-Token header)',
|
|
496
|
+
},
|
|
497
|
+
},
|
|
498
|
+
};
|
|
499
|
+
}
|
|
500
|
+
const body = req.body;
|
|
501
|
+
if (!body.code) {
|
|
502
|
+
throw new InvalidInputError('Verification code is required', 'code');
|
|
503
|
+
}
|
|
504
|
+
const result = await core.verifyAndEnableEmailOTP(userInfo.userId, body.code);
|
|
505
|
+
if (!result.enabled) {
|
|
506
|
+
return {
|
|
507
|
+
status: 400,
|
|
508
|
+
headers: {},
|
|
509
|
+
body: {
|
|
510
|
+
success: false,
|
|
511
|
+
error: {
|
|
512
|
+
message: 'Failed to enable Email OTP',
|
|
513
|
+
},
|
|
514
|
+
},
|
|
515
|
+
};
|
|
516
|
+
}
|
|
517
|
+
logger.info('Email OTP enabled', { userId: userInfo.userId, fromSetupToken: userInfo.fromSetupToken });
|
|
518
|
+
// If this was from a setup token (during login flow), issue full auth tokens
|
|
519
|
+
if (userInfo.fromSetupToken) {
|
|
520
|
+
const setupTokenHeader = req.headers['x-mfa-setup-token'];
|
|
521
|
+
const setupToken = Array.isArray(setupTokenHeader) ? setupTokenHeader[0] : setupTokenHeader;
|
|
522
|
+
const authResult = await core.completeMFASetupAndLogin(setupToken);
|
|
523
|
+
if (authResult) {
|
|
524
|
+
const { passwordHash, ...safeUser } = authResult.user;
|
|
525
|
+
return {
|
|
526
|
+
status: 200,
|
|
527
|
+
headers: {},
|
|
528
|
+
body: {
|
|
529
|
+
success: true,
|
|
530
|
+
message: 'Email OTP authentication enabled',
|
|
531
|
+
backupCodes: result.backupCodes,
|
|
532
|
+
token: authResult.token,
|
|
533
|
+
refreshToken: authResult.refreshToken,
|
|
534
|
+
user: safeUser,
|
|
535
|
+
expiresAt: authResult.expiresAt,
|
|
536
|
+
},
|
|
537
|
+
};
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
// Normal flow (user already authenticated)
|
|
541
|
+
return {
|
|
542
|
+
status: 200,
|
|
543
|
+
headers: {},
|
|
544
|
+
body: {
|
|
545
|
+
success: true,
|
|
546
|
+
message: 'Email OTP authentication enabled',
|
|
547
|
+
// Only return backup codes if they were newly generated
|
|
548
|
+
backupCodes: result.backupCodes,
|
|
549
|
+
},
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
catch (error) {
|
|
553
|
+
logger.error('Email OTP verification error', error);
|
|
554
|
+
if (error instanceof InvalidInputError) {
|
|
555
|
+
return {
|
|
556
|
+
status: 400,
|
|
557
|
+
headers: {},
|
|
558
|
+
body: {
|
|
559
|
+
success: false,
|
|
560
|
+
error: {
|
|
561
|
+
message: error.message,
|
|
562
|
+
},
|
|
563
|
+
},
|
|
564
|
+
};
|
|
565
|
+
}
|
|
566
|
+
return {
|
|
567
|
+
status: 500,
|
|
568
|
+
headers: {},
|
|
569
|
+
body: {
|
|
570
|
+
success: false,
|
|
571
|
+
error: {
|
|
572
|
+
message: 'Failed to verify Email OTP setup',
|
|
573
|
+
},
|
|
574
|
+
},
|
|
575
|
+
};
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
// =============================================================================
|
|
579
|
+
// ROUTE HANDLERS - MFA MANAGEMENT
|
|
580
|
+
// =============================================================================
|
|
581
|
+
/**
|
|
582
|
+
* POST /auth/mfa/disable
|
|
583
|
+
* Disable an MFA method
|
|
584
|
+
*/
|
|
585
|
+
export async function disableMFA(req, core) {
|
|
586
|
+
try {
|
|
587
|
+
if (!req.user) {
|
|
588
|
+
return {
|
|
589
|
+
status: 401,
|
|
590
|
+
headers: {},
|
|
591
|
+
body: {
|
|
592
|
+
success: false,
|
|
593
|
+
error: {
|
|
594
|
+
message: 'Authentication required',
|
|
595
|
+
},
|
|
596
|
+
},
|
|
597
|
+
};
|
|
598
|
+
}
|
|
599
|
+
const body = req.body;
|
|
600
|
+
if (!body.method) {
|
|
601
|
+
throw new InvalidInputError('MFA method is required', 'method');
|
|
602
|
+
}
|
|
603
|
+
if (!body.password) {
|
|
604
|
+
throw new InvalidInputError('Password is required to disable MFA', 'password');
|
|
605
|
+
}
|
|
606
|
+
// disableMFAMethod handles password verification and org policy validation internally
|
|
607
|
+
await core.disableMFAMethod(req.user.id, body.method, body.password);
|
|
608
|
+
logger.info('MFA method disabled', {
|
|
609
|
+
userId: req.user.id,
|
|
610
|
+
method: body.method,
|
|
611
|
+
});
|
|
612
|
+
return {
|
|
613
|
+
status: 200,
|
|
614
|
+
headers: {},
|
|
615
|
+
body: {
|
|
616
|
+
success: true,
|
|
617
|
+
message: `${body.method.toUpperCase()} authentication disabled`,
|
|
618
|
+
},
|
|
619
|
+
};
|
|
620
|
+
}
|
|
621
|
+
catch (error) {
|
|
622
|
+
logger.error('MFA disable error', error);
|
|
623
|
+
if (error instanceof InvalidInputError) {
|
|
624
|
+
return {
|
|
625
|
+
status: 400,
|
|
626
|
+
headers: {},
|
|
627
|
+
body: {
|
|
628
|
+
success: false,
|
|
629
|
+
error: {
|
|
630
|
+
message: error.message,
|
|
631
|
+
},
|
|
632
|
+
},
|
|
633
|
+
};
|
|
634
|
+
}
|
|
635
|
+
return {
|
|
636
|
+
status: 500,
|
|
637
|
+
headers: {},
|
|
638
|
+
body: {
|
|
639
|
+
success: false,
|
|
640
|
+
error: {
|
|
641
|
+
message: 'Failed to disable MFA',
|
|
642
|
+
},
|
|
643
|
+
},
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
/**
|
|
648
|
+
* POST /auth/mfa/disable-all
|
|
649
|
+
* Disable all MFA for user (removes all methods and backup codes)
|
|
650
|
+
*/
|
|
651
|
+
export async function disableAllMFA(req, core) {
|
|
652
|
+
try {
|
|
653
|
+
if (!req.user) {
|
|
654
|
+
return {
|
|
655
|
+
status: 401,
|
|
656
|
+
headers: {},
|
|
657
|
+
body: {
|
|
658
|
+
success: false,
|
|
659
|
+
error: {
|
|
660
|
+
message: 'Authentication required',
|
|
661
|
+
},
|
|
662
|
+
},
|
|
663
|
+
};
|
|
664
|
+
}
|
|
665
|
+
const body = req.body;
|
|
666
|
+
if (!body.password) {
|
|
667
|
+
throw new InvalidInputError('Password is required to disable MFA', 'password');
|
|
668
|
+
}
|
|
669
|
+
await core.disableAllMFA(req.user.id, body.password);
|
|
670
|
+
logger.info('All MFA disabled', { userId: req.user.id });
|
|
671
|
+
return {
|
|
672
|
+
status: 200,
|
|
673
|
+
headers: {},
|
|
674
|
+
body: {
|
|
675
|
+
success: true,
|
|
676
|
+
message: 'All MFA has been disabled',
|
|
677
|
+
},
|
|
678
|
+
};
|
|
679
|
+
}
|
|
680
|
+
catch (error) {
|
|
681
|
+
logger.error('Disable all MFA error', error);
|
|
682
|
+
if (error instanceof InvalidInputError) {
|
|
683
|
+
return {
|
|
684
|
+
status: 400,
|
|
685
|
+
headers: {},
|
|
686
|
+
body: {
|
|
687
|
+
success: false,
|
|
688
|
+
error: {
|
|
689
|
+
message: error.message,
|
|
690
|
+
},
|
|
691
|
+
},
|
|
692
|
+
};
|
|
693
|
+
}
|
|
694
|
+
return {
|
|
695
|
+
status: 500,
|
|
696
|
+
headers: {},
|
|
697
|
+
body: {
|
|
698
|
+
success: false,
|
|
699
|
+
error: {
|
|
700
|
+
message: 'Failed to disable MFA',
|
|
701
|
+
},
|
|
702
|
+
},
|
|
703
|
+
};
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
/**
|
|
707
|
+
* POST /auth/mfa/backup-codes/regenerate
|
|
708
|
+
* Regenerate backup codes
|
|
709
|
+
*/
|
|
710
|
+
export async function regenerateBackupCodes(req, core) {
|
|
711
|
+
try {
|
|
712
|
+
if (!req.user) {
|
|
713
|
+
return {
|
|
714
|
+
status: 401,
|
|
715
|
+
headers: {},
|
|
716
|
+
body: {
|
|
717
|
+
success: false,
|
|
718
|
+
error: {
|
|
719
|
+
message: 'Authentication required',
|
|
720
|
+
},
|
|
721
|
+
},
|
|
722
|
+
};
|
|
723
|
+
}
|
|
724
|
+
const body = req.body;
|
|
725
|
+
if (!body.password) {
|
|
726
|
+
throw new InvalidInputError('Password is required', 'password');
|
|
727
|
+
}
|
|
728
|
+
// Engine method handles password verification internally
|
|
729
|
+
const backupCodes = await core.regenerateBackupCodes(req.user.id, body.password);
|
|
730
|
+
if (!backupCodes || backupCodes.length === 0) {
|
|
731
|
+
return {
|
|
732
|
+
status: 400,
|
|
733
|
+
headers: {},
|
|
734
|
+
body: {
|
|
735
|
+
success: false,
|
|
736
|
+
error: {
|
|
737
|
+
message: 'Failed to regenerate backup codes. Ensure MFA is enabled.',
|
|
738
|
+
},
|
|
739
|
+
},
|
|
740
|
+
};
|
|
741
|
+
}
|
|
742
|
+
logger.info('Backup codes regenerated', { userId: req.user.id });
|
|
743
|
+
return {
|
|
744
|
+
status: 200,
|
|
745
|
+
headers: {},
|
|
746
|
+
body: {
|
|
747
|
+
success: true,
|
|
748
|
+
backupCodes,
|
|
749
|
+
message: 'Backup codes regenerated. Previous codes are now invalid.',
|
|
750
|
+
},
|
|
751
|
+
};
|
|
752
|
+
}
|
|
753
|
+
catch (error) {
|
|
754
|
+
logger.error('Backup code regeneration error', error);
|
|
755
|
+
if (error instanceof InvalidInputError) {
|
|
756
|
+
return {
|
|
757
|
+
status: 400,
|
|
758
|
+
headers: {},
|
|
759
|
+
body: {
|
|
760
|
+
success: false,
|
|
761
|
+
error: {
|
|
762
|
+
message: error.message,
|
|
763
|
+
},
|
|
764
|
+
},
|
|
765
|
+
};
|
|
766
|
+
}
|
|
767
|
+
return {
|
|
768
|
+
status: 500,
|
|
769
|
+
headers: {},
|
|
770
|
+
body: {
|
|
771
|
+
success: false,
|
|
772
|
+
error: {
|
|
773
|
+
message: 'Failed to regenerate backup codes',
|
|
774
|
+
},
|
|
775
|
+
},
|
|
776
|
+
};
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
/**
|
|
780
|
+
* GET /auth/mfa/status
|
|
781
|
+
* Get user's MFA status
|
|
782
|
+
*/
|
|
783
|
+
export async function getMFAStatus(req, core) {
|
|
784
|
+
try {
|
|
785
|
+
if (!req.user) {
|
|
786
|
+
return {
|
|
787
|
+
status: 401,
|
|
788
|
+
headers: {},
|
|
789
|
+
body: {
|
|
790
|
+
success: false,
|
|
791
|
+
error: {
|
|
792
|
+
message: 'Authentication required',
|
|
793
|
+
},
|
|
794
|
+
},
|
|
795
|
+
};
|
|
796
|
+
}
|
|
797
|
+
const status = await core.getMFAStatus(req.user.id);
|
|
798
|
+
return {
|
|
799
|
+
status: 200,
|
|
800
|
+
headers: {},
|
|
801
|
+
body: {
|
|
802
|
+
success: true,
|
|
803
|
+
data: status,
|
|
804
|
+
},
|
|
805
|
+
};
|
|
806
|
+
}
|
|
807
|
+
catch (error) {
|
|
808
|
+
logger.error('MFA status error', error);
|
|
809
|
+
return {
|
|
810
|
+
status: 500,
|
|
811
|
+
headers: {},
|
|
812
|
+
body: {
|
|
813
|
+
success: false,
|
|
814
|
+
error: {
|
|
815
|
+
message: 'Failed to get MFA status',
|
|
816
|
+
},
|
|
817
|
+
},
|
|
818
|
+
};
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
// =============================================================================
|
|
822
|
+
// ROUTE HANDLERS - TRUSTED DEVICES
|
|
823
|
+
// =============================================================================
|
|
824
|
+
/**
|
|
825
|
+
* GET /auth/mfa/trusted-devices
|
|
826
|
+
* List user's trusted devices
|
|
827
|
+
*/
|
|
828
|
+
export async function getTrustedDevices(req, core) {
|
|
829
|
+
try {
|
|
830
|
+
if (!req.user) {
|
|
831
|
+
return {
|
|
832
|
+
status: 401,
|
|
833
|
+
headers: {},
|
|
834
|
+
body: {
|
|
835
|
+
success: false,
|
|
836
|
+
error: {
|
|
837
|
+
message: 'Authentication required',
|
|
838
|
+
},
|
|
839
|
+
},
|
|
840
|
+
};
|
|
841
|
+
}
|
|
842
|
+
const devices = await core.getTrustedDevices(req.user.id);
|
|
843
|
+
return {
|
|
844
|
+
status: 200,
|
|
845
|
+
headers: {},
|
|
846
|
+
body: {
|
|
847
|
+
success: true,
|
|
848
|
+
data: {
|
|
849
|
+
devices,
|
|
850
|
+
},
|
|
851
|
+
},
|
|
852
|
+
};
|
|
853
|
+
}
|
|
854
|
+
catch (error) {
|
|
855
|
+
logger.error('Get trusted devices error', error);
|
|
856
|
+
return {
|
|
857
|
+
status: 500,
|
|
858
|
+
headers: {},
|
|
859
|
+
body: {
|
|
860
|
+
success: false,
|
|
861
|
+
error: {
|
|
862
|
+
message: 'Failed to get trusted devices',
|
|
863
|
+
},
|
|
864
|
+
},
|
|
865
|
+
};
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
/**
|
|
869
|
+
* DELETE /auth/mfa/trusted-devices/:deviceId
|
|
870
|
+
* Revoke a trusted device
|
|
871
|
+
*/
|
|
872
|
+
export async function revokeTrustedDevice(req, core) {
|
|
873
|
+
try {
|
|
874
|
+
if (!req.user) {
|
|
875
|
+
return {
|
|
876
|
+
status: 401,
|
|
877
|
+
headers: {},
|
|
878
|
+
body: {
|
|
879
|
+
success: false,
|
|
880
|
+
error: {
|
|
881
|
+
message: 'Authentication required',
|
|
882
|
+
},
|
|
883
|
+
},
|
|
884
|
+
};
|
|
885
|
+
}
|
|
886
|
+
const deviceId = req.params.deviceId;
|
|
887
|
+
if (!deviceId) {
|
|
888
|
+
throw new InvalidInputError('Device ID is required', 'deviceId');
|
|
889
|
+
}
|
|
890
|
+
// Method throws if user not found
|
|
891
|
+
await core.revokeTrustedDevice(req.user.id, deviceId);
|
|
892
|
+
logger.info('Trusted device revoked', {
|
|
893
|
+
userId: req.user.id,
|
|
894
|
+
deviceId,
|
|
895
|
+
});
|
|
896
|
+
return {
|
|
897
|
+
status: 200,
|
|
898
|
+
headers: {},
|
|
899
|
+
body: {
|
|
900
|
+
success: true,
|
|
901
|
+
message: 'Device trust revoked',
|
|
902
|
+
},
|
|
903
|
+
};
|
|
904
|
+
}
|
|
905
|
+
catch (error) {
|
|
906
|
+
logger.error('Revoke trusted device error', error);
|
|
907
|
+
if (error instanceof InvalidInputError) {
|
|
908
|
+
return {
|
|
909
|
+
status: 400,
|
|
910
|
+
headers: {},
|
|
911
|
+
body: {
|
|
912
|
+
success: false,
|
|
913
|
+
error: {
|
|
914
|
+
message: error.message,
|
|
915
|
+
},
|
|
916
|
+
},
|
|
917
|
+
};
|
|
918
|
+
}
|
|
919
|
+
return {
|
|
920
|
+
status: 500,
|
|
921
|
+
headers: {},
|
|
922
|
+
body: {
|
|
923
|
+
success: false,
|
|
924
|
+
error: {
|
|
925
|
+
message: 'Failed to revoke device trust',
|
|
926
|
+
},
|
|
927
|
+
},
|
|
928
|
+
};
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
/**
|
|
932
|
+
* DELETE /auth/mfa/trusted-devices
|
|
933
|
+
* Revoke all trusted devices
|
|
934
|
+
*/
|
|
935
|
+
export async function revokeAllTrustedDevices(req, core) {
|
|
936
|
+
try {
|
|
937
|
+
if (!req.user) {
|
|
938
|
+
return {
|
|
939
|
+
status: 401,
|
|
940
|
+
headers: {},
|
|
941
|
+
body: {
|
|
942
|
+
success: false,
|
|
943
|
+
error: {
|
|
944
|
+
message: 'Authentication required',
|
|
945
|
+
},
|
|
946
|
+
},
|
|
947
|
+
};
|
|
948
|
+
}
|
|
949
|
+
await core.revokeAllTrustedDevices(req.user.id);
|
|
950
|
+
logger.info('All trusted devices revoked', { userId: req.user.id });
|
|
951
|
+
return {
|
|
952
|
+
status: 200,
|
|
953
|
+
headers: {},
|
|
954
|
+
body: {
|
|
955
|
+
success: true,
|
|
956
|
+
message: 'All device trusts revoked',
|
|
957
|
+
},
|
|
958
|
+
};
|
|
959
|
+
}
|
|
960
|
+
catch (error) {
|
|
961
|
+
logger.error('Revoke all trusted devices error', error);
|
|
962
|
+
return {
|
|
963
|
+
status: 500,
|
|
964
|
+
headers: {},
|
|
965
|
+
body: {
|
|
966
|
+
success: false,
|
|
967
|
+
error: {
|
|
968
|
+
message: 'Failed to revoke all device trusts',
|
|
969
|
+
},
|
|
970
|
+
},
|
|
971
|
+
};
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
// =============================================================================
|
|
975
|
+
// ROUTE HANDLERS - ADMIN MFA MANAGEMENT
|
|
976
|
+
// =============================================================================
|
|
977
|
+
/**
|
|
978
|
+
* POST /admin/users/:userId/mfa/reset
|
|
979
|
+
* Admin: Reset a user's MFA configuration
|
|
980
|
+
*/
|
|
981
|
+
export async function adminResetUserMFA(req, core) {
|
|
982
|
+
try {
|
|
983
|
+
if (!req.user) {
|
|
984
|
+
return {
|
|
985
|
+
status: 401,
|
|
986
|
+
headers: {},
|
|
987
|
+
body: {
|
|
988
|
+
success: false,
|
|
989
|
+
error: {
|
|
990
|
+
message: 'Authentication required',
|
|
991
|
+
},
|
|
992
|
+
},
|
|
993
|
+
};
|
|
994
|
+
}
|
|
995
|
+
// Check admin permission
|
|
996
|
+
if (req.user.role !== 'admin') {
|
|
997
|
+
return {
|
|
998
|
+
status: 403,
|
|
999
|
+
headers: {},
|
|
1000
|
+
body: {
|
|
1001
|
+
success: false,
|
|
1002
|
+
error: {
|
|
1003
|
+
message: 'Admin permission required',
|
|
1004
|
+
},
|
|
1005
|
+
},
|
|
1006
|
+
};
|
|
1007
|
+
}
|
|
1008
|
+
const userId = req.params.userId;
|
|
1009
|
+
const body = req.body;
|
|
1010
|
+
if (!userId) {
|
|
1011
|
+
throw new InvalidInputError('User ID is required', 'userId');
|
|
1012
|
+
}
|
|
1013
|
+
// Prevent admin from resetting their own MFA via this endpoint
|
|
1014
|
+
if (userId === req.user.id) {
|
|
1015
|
+
return {
|
|
1016
|
+
status: 400,
|
|
1017
|
+
headers: {},
|
|
1018
|
+
body: {
|
|
1019
|
+
success: false,
|
|
1020
|
+
error: {
|
|
1021
|
+
message: 'Cannot reset your own MFA via admin endpoint. Use the regular MFA settings.',
|
|
1022
|
+
},
|
|
1023
|
+
},
|
|
1024
|
+
};
|
|
1025
|
+
}
|
|
1026
|
+
// Engine method takes (adminUserId, targetUserId)
|
|
1027
|
+
await core.adminResetUserMFA(req.user.id, userId);
|
|
1028
|
+
logger.info('Admin reset user MFA', {
|
|
1029
|
+
targetUserId: userId,
|
|
1030
|
+
adminId: req.user.id,
|
|
1031
|
+
reason: body.reason,
|
|
1032
|
+
});
|
|
1033
|
+
return {
|
|
1034
|
+
status: 200,
|
|
1035
|
+
headers: {},
|
|
1036
|
+
body: {
|
|
1037
|
+
success: true,
|
|
1038
|
+
message: 'User MFA configuration has been reset',
|
|
1039
|
+
},
|
|
1040
|
+
};
|
|
1041
|
+
}
|
|
1042
|
+
catch (error) {
|
|
1043
|
+
logger.error('Admin MFA reset error', error);
|
|
1044
|
+
if (error instanceof InvalidInputError) {
|
|
1045
|
+
return {
|
|
1046
|
+
status: 400,
|
|
1047
|
+
headers: {},
|
|
1048
|
+
body: {
|
|
1049
|
+
success: false,
|
|
1050
|
+
error: {
|
|
1051
|
+
message: error.message,
|
|
1052
|
+
},
|
|
1053
|
+
},
|
|
1054
|
+
};
|
|
1055
|
+
}
|
|
1056
|
+
return {
|
|
1057
|
+
status: 500,
|
|
1058
|
+
headers: {},
|
|
1059
|
+
body: {
|
|
1060
|
+
success: false,
|
|
1061
|
+
error: {
|
|
1062
|
+
message: 'Failed to reset user MFA',
|
|
1063
|
+
},
|
|
1064
|
+
},
|
|
1065
|
+
};
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
/**
|
|
1069
|
+
* POST /auth/mfa/send-code
|
|
1070
|
+
* Send MFA code via email (for email OTP method during login)
|
|
1071
|
+
*/
|
|
1072
|
+
export async function sendMFACode(req, core) {
|
|
1073
|
+
try {
|
|
1074
|
+
const body = req.body;
|
|
1075
|
+
if (!body.mfaToken) {
|
|
1076
|
+
throw new InvalidInputError('MFA token is required', 'mfaToken');
|
|
1077
|
+
}
|
|
1078
|
+
// Verify the MFA token
|
|
1079
|
+
const tokenPayload = await core.verifyMFAToken(body.mfaToken);
|
|
1080
|
+
if (!tokenPayload || tokenPayload.type !== 'mfa_pending') {
|
|
1081
|
+
return {
|
|
1082
|
+
status: 401,
|
|
1083
|
+
headers: {},
|
|
1084
|
+
body: {
|
|
1085
|
+
success: false,
|
|
1086
|
+
error: {
|
|
1087
|
+
message: 'Invalid or expired MFA token',
|
|
1088
|
+
},
|
|
1089
|
+
},
|
|
1090
|
+
};
|
|
1091
|
+
}
|
|
1092
|
+
// Send OTP email
|
|
1093
|
+
const result = await core.sendMFAEmailOTP(tokenPayload.userId);
|
|
1094
|
+
if (!result) {
|
|
1095
|
+
return {
|
|
1096
|
+
status: 500,
|
|
1097
|
+
headers: {},
|
|
1098
|
+
body: {
|
|
1099
|
+
success: false,
|
|
1100
|
+
error: {
|
|
1101
|
+
message: 'Failed to send verification code',
|
|
1102
|
+
},
|
|
1103
|
+
},
|
|
1104
|
+
};
|
|
1105
|
+
}
|
|
1106
|
+
logger.info('MFA email code sent', { userId: tokenPayload.userId });
|
|
1107
|
+
return {
|
|
1108
|
+
status: 200,
|
|
1109
|
+
headers: {},
|
|
1110
|
+
body: {
|
|
1111
|
+
success: true,
|
|
1112
|
+
message: 'Verification code sent to your email',
|
|
1113
|
+
expiresIn: result.expiresIn,
|
|
1114
|
+
},
|
|
1115
|
+
};
|
|
1116
|
+
}
|
|
1117
|
+
catch (error) {
|
|
1118
|
+
logger.error('Send MFA code error', error);
|
|
1119
|
+
if (error instanceof InvalidInputError) {
|
|
1120
|
+
return {
|
|
1121
|
+
status: 400,
|
|
1122
|
+
headers: {},
|
|
1123
|
+
body: {
|
|
1124
|
+
success: false,
|
|
1125
|
+
error: {
|
|
1126
|
+
message: error.message,
|
|
1127
|
+
},
|
|
1128
|
+
},
|
|
1129
|
+
};
|
|
1130
|
+
}
|
|
1131
|
+
return {
|
|
1132
|
+
status: 500,
|
|
1133
|
+
headers: {},
|
|
1134
|
+
body: {
|
|
1135
|
+
success: false,
|
|
1136
|
+
error: {
|
|
1137
|
+
message: 'Failed to send verification code',
|
|
1138
|
+
},
|
|
1139
|
+
},
|
|
1140
|
+
};
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
//# sourceMappingURL=mfa.js.map
|