@lastshotlabs/bunshot 0.0.27 → 0.1.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/.oclif.manifest.json +39 -0
- package/README.md +8282 -2147
- package/dist/cli/commands/init.js +690 -0
- package/dist/cli/index.js +6 -0
- package/dist/cli.js +4 -4
- package/dist/packages/bunshot-admin/src/index.d.ts +15 -0
- package/dist/packages/bunshot-admin/src/index.js +11 -0
- package/dist/packages/bunshot-admin/src/lib/resourceTypes.d.ts +8 -0
- package/dist/packages/bunshot-admin/src/lib/resourceTypes.js +33 -0
- package/dist/packages/bunshot-admin/src/lib/typedRoute.d.ts +14 -0
- package/dist/packages/bunshot-admin/src/lib/typedRoute.js +17 -0
- package/dist/packages/bunshot-admin/src/plugin.d.ts +4 -0
- package/dist/packages/bunshot-admin/src/plugin.js +46 -0
- package/dist/packages/bunshot-admin/src/providers/auth0Access.d.ts +6 -0
- package/dist/packages/bunshot-admin/src/providers/auth0Access.js +32 -0
- package/dist/packages/bunshot-admin/src/routes/admin.d.ts +10 -0
- package/dist/packages/bunshot-admin/src/routes/admin.js +923 -0
- package/dist/packages/bunshot-admin/src/routes/mail.d.ts +6 -0
- package/dist/packages/bunshot-admin/src/routes/mail.js +114 -0
- package/dist/packages/bunshot-admin/src/routes/permissions.d.ts +8 -0
- package/dist/packages/bunshot-admin/src/routes/permissions.js +315 -0
- package/dist/packages/bunshot-admin/src/types/config.d.ts +16 -0
- package/dist/packages/bunshot-admin/src/types/config.js +37 -0
- package/dist/packages/bunshot-admin/src/types/env.d.ts +14 -0
- package/dist/packages/bunshot-admin/src/types/provider.d.ts +1 -0
- package/dist/packages/bunshot-admin/src/types/provider.js +4 -0
- package/dist/packages/bunshot-auth/src/adapters/memoryAuth.d.ts +66 -0
- package/dist/packages/bunshot-auth/src/adapters/memoryAuth.js +1063 -0
- package/dist/packages/bunshot-auth/src/adapters/mongoAuth.d.ts +2 -0
- package/dist/packages/bunshot-auth/src/adapters/mongoAuth.js +536 -0
- package/dist/packages/bunshot-auth/src/adapters/sqliteAuth.d.ts +88 -0
- package/dist/packages/bunshot-auth/src/adapters/sqliteAuth.js +1366 -0
- package/dist/packages/bunshot-auth/src/admin/bunshotAccess.d.ts +2 -0
- package/dist/packages/bunshot-auth/src/admin/bunshotAccess.js +23 -0
- package/dist/packages/bunshot-auth/src/admin/bunshotUsers.d.ts +5 -0
- package/dist/packages/bunshot-auth/src/admin/bunshotUsers.js +131 -0
- package/dist/packages/bunshot-auth/src/bootstrap.d.ts +38 -0
- package/dist/packages/bunshot-auth/src/bootstrap.js +384 -0
- package/dist/packages/bunshot-auth/src/config/appConfig.d.ts +3 -0
- package/dist/packages/bunshot-auth/src/config/appConfig.js +4 -0
- package/dist/packages/bunshot-auth/src/config/authConfig.d.ts +478 -0
- package/dist/packages/bunshot-auth/src/config/authConfig.js +46 -0
- package/dist/packages/bunshot-auth/src/config/configLock.d.ts +2 -0
- package/dist/packages/bunshot-auth/src/config/configLock.js +10 -0
- package/dist/packages/bunshot-auth/src/index.d.ts +25 -0
- package/dist/packages/bunshot-auth/src/index.js +23 -0
- package/dist/packages/bunshot-auth/src/infra/mongo.d.ts +15 -0
- package/dist/packages/bunshot-auth/src/infra/mongo.js +44 -0
- package/dist/packages/bunshot-auth/src/infra/queue.d.ts +14 -0
- package/dist/packages/bunshot-auth/src/infra/queue.js +27 -0
- package/dist/packages/bunshot-auth/src/infra/redis.d.ts +5 -0
- package/dist/packages/bunshot-auth/src/infra/redis.js +15 -0
- package/dist/packages/bunshot-auth/src/infra/signing.d.ts +7 -0
- package/dist/packages/bunshot-auth/src/infra/signing.js +8 -0
- package/dist/packages/bunshot-auth/src/lib/accountLockout.d.ts +34 -0
- package/dist/packages/bunshot-auth/src/lib/accountLockout.js +244 -0
- package/dist/packages/bunshot-auth/src/lib/adapterTiers.d.ts +1 -0
- package/dist/packages/bunshot-auth/src/lib/adapterTiers.js +1 -0
- package/dist/packages/bunshot-auth/src/lib/authAdapter.d.ts +1 -0
- package/dist/packages/bunshot-auth/src/lib/authAdapter.js +1 -0
- package/dist/packages/bunshot-auth/src/lib/authContext.d.ts +15 -0
- package/dist/packages/bunshot-auth/src/lib/authContext.js +1 -0
- package/dist/packages/bunshot-auth/src/lib/authEventBus.d.ts +4 -0
- package/dist/packages/bunshot-auth/src/lib/authEventBus.js +15 -0
- package/dist/packages/bunshot-auth/src/lib/authRateLimit.d.ts +28 -0
- package/dist/packages/bunshot-auth/src/lib/authRateLimit.js +205 -0
- package/dist/{lib → packages/bunshot-auth/src/lib}/breachedPassword.d.ts +8 -2
- package/dist/{lib → packages/bunshot-auth/src/lib}/breachedPassword.js +22 -9
- package/dist/packages/bunshot-auth/src/lib/cache.d.ts +12 -0
- package/dist/packages/bunshot-auth/src/lib/cache.js +120 -0
- package/dist/packages/bunshot-auth/src/lib/clientIp.d.ts +4 -0
- package/dist/{lib → packages/bunshot-auth/src/lib}/clientIp.js +14 -7
- package/dist/packages/bunshot-auth/src/lib/cookieOptions.d.ts +27 -0
- package/dist/packages/bunshot-auth/src/lib/cookieOptions.js +33 -0
- package/dist/packages/bunshot-auth/src/lib/credentialStuffing.d.ts +40 -0
- package/dist/packages/bunshot-auth/src/lib/credentialStuffing.js +211 -0
- package/dist/packages/bunshot-auth/src/lib/deletionCancelToken.d.ts +19 -0
- package/dist/packages/bunshot-auth/src/lib/deletionCancelToken.js +148 -0
- package/dist/packages/bunshot-auth/src/lib/emailTemplates.d.ts +23 -0
- package/dist/packages/bunshot-auth/src/lib/emailTemplates.js +265 -0
- package/dist/packages/bunshot-auth/src/lib/emailVerification.d.ts +30 -0
- package/dist/packages/bunshot-auth/src/lib/emailVerification.js +200 -0
- package/dist/packages/bunshot-auth/src/lib/env.d.ts +1 -0
- package/dist/packages/bunshot-auth/src/lib/env.js +3 -0
- package/dist/packages/bunshot-auth/src/lib/fingerprint.js +36 -0
- package/dist/{lib → packages/bunshot-auth/src/lib}/groups.d.ts +15 -16
- package/dist/{lib → packages/bunshot-auth/src/lib}/groups.js +22 -34
- package/dist/packages/bunshot-auth/src/lib/jwks.d.ts +28 -0
- package/dist/packages/bunshot-auth/src/lib/jwks.js +79 -0
- package/dist/packages/bunshot-auth/src/lib/jwt.d.ts +12 -0
- package/dist/packages/bunshot-auth/src/lib/jwt.js +86 -0
- package/dist/{lib → packages/bunshot-auth/src/lib}/logger.js +3 -3
- package/dist/{lib → packages/bunshot-auth/src/lib}/m2m.d.ts +5 -4
- package/dist/{lib → packages/bunshot-auth/src/lib}/m2m.js +6 -10
- package/dist/packages/bunshot-auth/src/lib/magicLink.d.ts +13 -0
- package/dist/packages/bunshot-auth/src/lib/magicLink.js +145 -0
- package/dist/packages/bunshot-auth/src/lib/mfaChallenge.d.ts +60 -0
- package/dist/packages/bunshot-auth/src/lib/mfaChallenge.js +419 -0
- package/dist/packages/bunshot-auth/src/lib/oauth.d.ts +82 -0
- package/dist/packages/bunshot-auth/src/lib/oauth.js +177 -0
- package/dist/packages/bunshot-auth/src/lib/oauthCode.d.ts +19 -0
- package/dist/packages/bunshot-auth/src/lib/oauthCode.js +182 -0
- package/dist/packages/bunshot-auth/src/lib/oauthReauth.d.ts +19 -0
- package/dist/packages/bunshot-auth/src/lib/oauthReauth.js +255 -0
- package/dist/packages/bunshot-auth/src/lib/organization.d.ts +66 -0
- package/dist/packages/bunshot-auth/src/lib/organization.js +225 -0
- package/dist/packages/bunshot-auth/src/lib/passwordHistory.d.ts +12 -0
- package/dist/packages/bunshot-auth/src/lib/passwordHistory.js +31 -0
- package/dist/packages/bunshot-auth/src/lib/resetPassword.d.ts +20 -0
- package/dist/packages/bunshot-auth/src/lib/resetPassword.js +148 -0
- package/dist/packages/bunshot-auth/src/lib/roles.d.ts +9 -0
- package/dist/packages/bunshot-auth/src/lib/roles.js +93 -0
- package/dist/packages/bunshot-auth/src/lib/saml.d.ts +29 -0
- package/dist/packages/bunshot-auth/src/lib/saml.js +73 -0
- package/dist/packages/bunshot-auth/src/lib/samlRequestId.d.ts +13 -0
- package/dist/packages/bunshot-auth/src/lib/samlRequestId.js +129 -0
- package/dist/{lib → packages/bunshot-auth/src/lib}/scim.d.ts +7 -7
- package/dist/{lib → packages/bunshot-auth/src/lib}/scim.js +15 -13
- package/dist/packages/bunshot-auth/src/lib/securityEventWiring.d.ts +22 -0
- package/dist/packages/bunshot-auth/src/lib/securityEventWiring.js +65 -0
- package/dist/packages/bunshot-auth/src/lib/session.d.ts +45 -0
- package/dist/packages/bunshot-auth/src/lib/session.js +1211 -0
- package/dist/packages/bunshot-auth/src/lib/storeInfra.d.ts +26 -0
- package/dist/packages/bunshot-auth/src/lib/storeInfra.js +18 -0
- package/dist/{lib → packages/bunshot-auth/src/lib}/suspension.d.ts +3 -2
- package/dist/{lib → packages/bunshot-auth/src/lib}/suspension.js +2 -5
- package/dist/packages/bunshot-auth/src/lib/validateAdapter.d.ts +16 -0
- package/dist/packages/bunshot-auth/src/lib/validateAdapter.js +161 -0
- package/dist/packages/bunshot-auth/src/middleware/bearerAuth.d.ts +13 -0
- package/dist/packages/bunshot-auth/src/middleware/bearerAuth.js +58 -0
- package/dist/{middleware → packages/bunshot-auth/src/middleware}/csrf.d.ts +5 -4
- package/dist/packages/bunshot-auth/src/middleware/csrf.js +138 -0
- package/dist/packages/bunshot-auth/src/middleware/identify.d.ts +4 -0
- package/dist/packages/bunshot-auth/src/middleware/identify.js +124 -0
- package/dist/{middleware → packages/bunshot-auth/src/middleware}/requireMfaSetup.d.ts +2 -2
- package/dist/{middleware → packages/bunshot-auth/src/middleware}/requireMfaSetup.js +10 -8
- package/dist/{middleware → packages/bunshot-auth/src/middleware}/requireRole.d.ts +2 -2
- package/dist/{middleware → packages/bunshot-auth/src/middleware}/requireRole.js +20 -16
- package/dist/{middleware → packages/bunshot-auth/src/middleware}/requireScope.d.ts +2 -2
- package/dist/{middleware → packages/bunshot-auth/src/middleware}/requireScope.js +6 -6
- package/dist/{middleware → packages/bunshot-auth/src/middleware}/requireStepUp.d.ts +2 -2
- package/dist/{middleware → packages/bunshot-auth/src/middleware}/requireStepUp.js +8 -7
- package/dist/{middleware → packages/bunshot-auth/src/middleware}/requireVerifiedEmail.d.ts +2 -2
- package/dist/{middleware → packages/bunshot-auth/src/middleware}/requireVerifiedEmail.js +7 -6
- package/dist/packages/bunshot-auth/src/middleware/scimAuth.d.ts +8 -0
- package/dist/packages/bunshot-auth/src/middleware/scimAuth.js +29 -0
- package/dist/packages/bunshot-auth/src/middleware/userAuth.d.ts +3 -0
- package/dist/packages/bunshot-auth/src/middleware/userAuth.js +6 -0
- package/dist/{models → packages/bunshot-auth/src/models}/AuthUser.d.ts +12 -8
- package/dist/packages/bunshot-auth/src/models/AuthUser.js +53 -0
- package/dist/packages/bunshot-auth/src/models/Group.d.ts +19 -0
- package/dist/packages/bunshot-auth/src/models/Group.js +22 -0
- package/dist/{models → packages/bunshot-auth/src/models}/GroupMembership.d.ts +6 -8
- package/dist/packages/bunshot-auth/src/models/GroupMembership.js +19 -0
- package/dist/{models → packages/bunshot-auth/src/models}/M2MClient.d.ts +1 -1
- package/dist/{models → packages/bunshot-auth/src/models}/M2MClient.js +5 -5
- package/dist/packages/bunshot-auth/src/models/TenantRole.d.ts +13 -0
- package/dist/packages/bunshot-auth/src/models/TenantRole.js +17 -0
- package/dist/packages/bunshot-auth/src/plugin.d.ts +4 -0
- package/dist/packages/bunshot-auth/src/plugin.js +277 -0
- package/dist/packages/bunshot-auth/src/routes/auth.d.ts +15 -0
- package/dist/packages/bunshot-auth/src/routes/auth.js +1624 -0
- package/dist/packages/bunshot-auth/src/routes/groups.d.ts +4 -0
- package/dist/packages/bunshot-auth/src/routes/groups.js +481 -0
- package/dist/packages/bunshot-auth/src/routes/m2m.d.ts +2 -0
- package/dist/packages/bunshot-auth/src/routes/m2m.js +145 -0
- package/dist/packages/bunshot-auth/src/routes/mfa.d.ts +6 -0
- package/dist/packages/bunshot-auth/src/routes/mfa.js +991 -0
- package/dist/packages/bunshot-auth/src/routes/oauth.d.ts +3 -0
- package/dist/packages/bunshot-auth/src/routes/oauth.js +1727 -0
- package/dist/packages/bunshot-auth/src/routes/oidc.d.ts +2 -0
- package/dist/packages/bunshot-auth/src/routes/oidc.js +84 -0
- package/dist/packages/bunshot-auth/src/routes/organizations.d.ts +3 -0
- package/dist/packages/bunshot-auth/src/routes/organizations.js +741 -0
- package/dist/packages/bunshot-auth/src/routes/passkey.d.ts +2 -0
- package/dist/packages/bunshot-auth/src/routes/passkey.js +199 -0
- package/dist/packages/bunshot-auth/src/routes/saml.d.ts +2 -0
- package/dist/packages/bunshot-auth/src/routes/saml.js +226 -0
- package/dist/packages/bunshot-auth/src/routes/scim.d.ts +3 -0
- package/dist/packages/bunshot-auth/src/routes/scim.js +588 -0
- package/dist/packages/bunshot-auth/src/runtime.d.ts +52 -0
- package/dist/packages/bunshot-auth/src/runtime.js +11 -0
- package/dist/{schemas → packages/bunshot-auth/src/schemas}/auth.d.ts +4 -5
- package/dist/packages/bunshot-auth/src/schemas/auth.js +24 -0
- package/dist/packages/bunshot-auth/src/schemas/error.d.ts +10 -0
- package/dist/packages/bunshot-auth/src/schemas/error.js +10 -0
- package/dist/packages/bunshot-auth/src/schemas/success.d.ts +10 -0
- package/dist/packages/bunshot-auth/src/schemas/success.js +10 -0
- package/dist/packages/bunshot-auth/src/services/auth.d.ts +39 -0
- package/dist/packages/bunshot-auth/src/services/auth.js +378 -0
- package/dist/{services → packages/bunshot-auth/src/services}/mfa.d.ts +41 -17
- package/dist/{services → packages/bunshot-auth/src/services}/mfa.js +259 -183
- package/dist/packages/bunshot-auth/src/testing.d.ts +31 -0
- package/dist/packages/bunshot-auth/src/testing.js +23 -0
- package/dist/packages/bunshot-auth/src/types/adapter.d.ts +1 -0
- package/dist/packages/bunshot-auth/src/types/adapter.js +1 -0
- package/dist/packages/bunshot-auth/src/types/config.d.ts +152 -0
- package/dist/packages/bunshot-auth/src/types/config.js +179 -0
- package/dist/{routes → packages/bunshot-auth/src/types}/groups.d.ts +2 -3
- package/dist/packages/bunshot-auth/src/types/groups.js +1 -0
- package/dist/packages/bunshot-auth/src/types/oauthCode.d.ts +6 -0
- package/dist/packages/bunshot-auth/src/types/oauthCode.js +1 -0
- package/dist/packages/bunshot-auth/src/types/oauthReauth.d.ts +13 -0
- package/dist/packages/bunshot-auth/src/types/oauthReauth.js +1 -0
- package/dist/packages/bunshot-auth/src/types/redis.d.ts +1 -0
- package/dist/packages/bunshot-auth/src/types/redis.js +1 -0
- package/dist/packages/bunshot-auth/src/types/saml.d.ts +10 -0
- package/dist/packages/bunshot-auth/src/types/saml.js +1 -0
- package/dist/packages/bunshot-auth/src/types/session.d.ts +18 -0
- package/dist/packages/bunshot-auth/src/types/session.js +1 -0
- package/dist/packages/bunshot-auth/src/types/store.d.ts +1 -0
- package/dist/packages/bunshot-auth/src/types/store.js +1 -0
- package/dist/packages/bunshot-core/src/adminProvider.d.ts +95 -0
- package/dist/packages/bunshot-core/src/adminProvider.js +1 -0
- package/dist/packages/bunshot-core/src/auditLog.d.ts +34 -0
- package/dist/packages/bunshot-core/src/auditLog.js +1 -0
- package/dist/packages/bunshot-core/src/auth-adapter.d.ts +227 -0
- package/dist/packages/bunshot-core/src/auth-adapter.js +4 -0
- package/dist/packages/bunshot-core/src/authVariables.d.ts +14 -0
- package/dist/packages/bunshot-core/src/authVariables.js +4 -0
- package/dist/packages/bunshot-core/src/cache.d.ts +12 -0
- package/dist/packages/bunshot-core/src/cache.js +21 -0
- package/dist/{lib → packages/bunshot-core/src}/captcha.d.ts +1 -10
- package/dist/packages/bunshot-core/src/captcha.js +1 -0
- package/dist/packages/bunshot-core/src/clearRegistry.d.ts +6 -0
- package/dist/packages/bunshot-core/src/clearRegistry.js +17 -0
- package/dist/packages/bunshot-core/src/clientIp.d.ts +3 -0
- package/dist/packages/bunshot-core/src/clientIp.js +45 -0
- package/dist/packages/bunshot-core/src/configLock.d.ts +4 -0
- package/dist/packages/bunshot-core/src/configLock.js +7 -0
- package/dist/packages/bunshot-core/src/configValidation.d.ts +22 -0
- package/dist/packages/bunshot-core/src/configValidation.js +39 -0
- package/dist/packages/bunshot-core/src/constants.js +10 -0
- package/dist/packages/bunshot-core/src/context/bunshotContext.d.ts +232 -0
- package/dist/packages/bunshot-core/src/context/bunshotContext.js +1 -0
- package/dist/packages/bunshot-core/src/context/contextAccess.d.ts +3 -0
- package/dist/packages/bunshot-core/src/context/contextAccess.js +16 -0
- package/dist/packages/bunshot-core/src/context/contextStore.d.ts +16 -0
- package/dist/packages/bunshot-core/src/context/contextStore.js +31 -0
- package/dist/packages/bunshot-core/src/context/frameworkConfig.d.ts +38 -0
- package/dist/packages/bunshot-core/src/context/frameworkConfig.js +1 -0
- package/dist/packages/bunshot-core/src/context/index.d.ts +4 -0
- package/dist/packages/bunshot-core/src/context/index.js +2 -0
- package/dist/packages/bunshot-core/src/context.d.ts +40 -0
- package/dist/packages/bunshot-core/src/context.js +35 -0
- package/dist/packages/bunshot-core/src/coreContracts.d.ts +47 -0
- package/dist/packages/bunshot-core/src/coreContracts.js +1 -0
- package/dist/packages/bunshot-core/src/coreRegistrar.d.ts +6 -0
- package/dist/packages/bunshot-core/src/coreRegistrar.js +42 -0
- package/dist/{lib → packages/bunshot-core/src}/createRoute.d.ts +4 -30
- package/dist/{lib → packages/bunshot-core/src}/createRoute.js +39 -88
- package/dist/packages/bunshot-core/src/cronRegistry.d.ts +11 -0
- package/dist/packages/bunshot-core/src/cronRegistry.js +1 -0
- package/dist/packages/bunshot-core/src/crypto.d.ts +43 -0
- package/dist/packages/bunshot-core/src/crypto.js +74 -0
- package/dist/packages/bunshot-core/src/csrf.d.ts +8 -0
- package/dist/packages/bunshot-core/src/csrf.js +1 -0
- package/dist/packages/bunshot-core/src/defaults/defaultFingerprint.d.ts +7 -0
- package/dist/packages/bunshot-core/src/defaults/defaultFingerprint.js +19 -0
- package/dist/packages/bunshot-core/src/defaults/memoryCacheAdapter.d.ts +6 -0
- package/dist/packages/bunshot-core/src/defaults/memoryCacheAdapter.js +40 -0
- package/dist/packages/bunshot-core/src/defaults/memoryRateLimit.d.ts +6 -0
- package/dist/packages/bunshot-core/src/defaults/memoryRateLimit.js +24 -0
- package/dist/packages/bunshot-core/src/emailTemplates.d.ts +5 -0
- package/dist/packages/bunshot-core/src/emailTemplates.js +10 -0
- package/dist/{lib/HttpError.d.ts → packages/bunshot-core/src/errors.d.ts} +4 -1
- package/dist/{lib/HttpError.js → packages/bunshot-core/src/errors.js} +7 -1
- package/dist/packages/bunshot-core/src/eventBus.d.ts +270 -0
- package/dist/packages/bunshot-core/src/eventBus.js +143 -0
- package/dist/packages/bunshot-core/src/idempotency.d.ts +18 -0
- package/dist/packages/bunshot-core/src/idempotency.js +1 -0
- package/dist/packages/bunshot-core/src/index.d.ts +60 -0
- package/dist/packages/bunshot-core/src/index.js +34 -0
- package/dist/packages/bunshot-core/src/mail.d.ts +14 -0
- package/dist/packages/bunshot-core/src/mail.js +8 -0
- package/dist/packages/bunshot-core/src/memoryEviction.d.ts +24 -0
- package/dist/packages/bunshot-core/src/memoryEviction.js +52 -0
- package/dist/packages/bunshot-core/src/pagination.d.ts +45 -0
- package/dist/packages/bunshot-core/src/pagination.js +61 -0
- package/dist/packages/bunshot-core/src/permissions.d.ts +64 -0
- package/dist/packages/bunshot-core/src/permissions.js +27 -0
- package/dist/packages/bunshot-core/src/plugin.d.ts +44 -0
- package/dist/packages/bunshot-core/src/plugin.js +1 -0
- package/dist/packages/bunshot-core/src/rateLimit.d.ts +5 -0
- package/dist/packages/bunshot-core/src/rateLimit.js +18 -0
- package/dist/packages/bunshot-core/src/redis.d.ts +21 -0
- package/dist/packages/bunshot-core/src/redis.js +1 -0
- package/dist/packages/bunshot-core/src/routeAuth.d.ts +5 -0
- package/dist/packages/bunshot-core/src/routeAuth.js +11 -0
- package/dist/packages/bunshot-core/src/routeOverrides.d.ts +24 -0
- package/dist/packages/bunshot-core/src/routeOverrides.js +25 -0
- package/dist/packages/bunshot-core/src/routerAdapter.d.ts +6 -0
- package/dist/packages/bunshot-core/src/routerAdapter.js +56 -0
- package/dist/packages/bunshot-core/src/secrets.d.ts +48 -0
- package/dist/packages/bunshot-core/src/secrets.js +8 -0
- package/dist/packages/bunshot-core/src/signing.d.ts +41 -0
- package/dist/packages/bunshot-core/src/signing.js +1 -0
- package/dist/packages/bunshot-core/src/sse.d.ts +36 -0
- package/dist/packages/bunshot-core/src/sse.js +1 -0
- package/dist/packages/bunshot-core/src/storageAdapter.js +1 -0
- package/dist/packages/bunshot-core/src/storeInfra.d.ts +44 -0
- package/dist/packages/bunshot-core/src/storeInfra.js +18 -0
- package/dist/packages/bunshot-core/src/storeType.d.ts +7 -0
- package/dist/packages/bunshot-core/src/storeType.js +1 -0
- package/dist/packages/bunshot-core/src/testing.d.ts +1 -0
- package/dist/packages/bunshot-core/src/testing.js +1 -0
- package/dist/packages/bunshot-core/src/uploadRegistry.d.ts +23 -0
- package/dist/packages/bunshot-core/src/uploadRegistry.js +4 -0
- package/dist/packages/bunshot-core/src/userResolver.d.ts +5 -0
- package/dist/packages/bunshot-core/src/userResolver.js +14 -0
- package/dist/packages/bunshot-core/src/wsMessages.d.ts +42 -0
- package/dist/packages/bunshot-core/src/wsMessages.js +4 -0
- package/dist/packages/bunshot-permissions/src/adapters/memory.d.ts +7 -0
- package/dist/packages/bunshot-permissions/src/adapters/memory.js +73 -0
- package/dist/packages/bunshot-permissions/src/index.d.ts +10 -0
- package/dist/packages/bunshot-permissions/src/index.js +5 -0
- package/dist/packages/bunshot-permissions/src/lib/bootstrap.d.ts +7 -0
- package/dist/packages/bunshot-permissions/src/lib/bootstrap.js +12 -0
- package/dist/packages/bunshot-permissions/src/lib/evaluator.d.ts +10 -0
- package/dist/packages/bunshot-permissions/src/lib/evaluator.js +165 -0
- package/dist/packages/bunshot-permissions/src/lib/registry.d.ts +2 -0
- package/dist/packages/bunshot-permissions/src/lib/registry.js +31 -0
- package/dist/packages/bunshot-permissions/src/lib/validation.d.ts +1 -0
- package/dist/packages/bunshot-permissions/src/lib/validation.js +1 -0
- package/dist/packages/bunshot-permissions/src/types/adapter.d.ts +1 -0
- package/dist/packages/bunshot-permissions/src/types/adapter.js +1 -0
- package/dist/packages/bunshot-permissions/src/types/evaluator.d.ts +1 -0
- package/dist/packages/bunshot-permissions/src/types/evaluator.js +1 -0
- package/dist/packages/bunshot-permissions/src/types/models.d.ts +1 -0
- package/dist/packages/bunshot-permissions/src/types/models.js +1 -0
- package/dist/packages/bunshot-permissions/src/types/registry.d.ts +1 -0
- package/dist/packages/bunshot-permissions/src/types/registry.js +1 -0
- package/dist/packages/bunshot-postgres/src/adapter.d.ts +6 -0
- package/dist/packages/bunshot-postgres/src/adapter.js +794 -0
- package/dist/packages/bunshot-postgres/src/connection.d.ts +15 -0
- package/dist/packages/bunshot-postgres/src/connection.js +16 -0
- package/dist/packages/bunshot-postgres/src/index.d.ts +4 -0
- package/dist/packages/bunshot-postgres/src/index.js +2 -0
- package/dist/packages/bunshot-postgres/src/schema.d.ts +997 -0
- package/dist/packages/bunshot-postgres/src/schema.js +105 -0
- package/dist/src/app.d.ts +230 -0
- package/dist/src/app.js +182 -0
- package/dist/src/cli/commands/init.d.ts +10 -0
- package/dist/src/cli/commands/init.js +709 -0
- package/dist/src/cli/index.d.ts +1 -0
- package/dist/src/cli/index.js +3 -0
- package/dist/src/entrypoints/mongo.d.ts +6 -0
- package/dist/src/entrypoints/mongo.js +4 -0
- package/dist/src/entrypoints/queue.d.ts +2 -0
- package/dist/src/entrypoints/queue.js +1 -0
- package/dist/src/entrypoints/redis.d.ts +1 -0
- package/dist/src/entrypoints/redis.js +1 -0
- package/dist/{adapters → src/framework/adapters}/localStorage.d.ts +1 -1
- package/dist/{adapters → src/framework/adapters}/localStorage.js +10 -10
- package/dist/src/framework/adapters/memoryStorage.d.ts +2 -0
- package/dist/src/framework/adapters/memoryStorage.js +45 -0
- package/dist/{adapters → src/framework/adapters}/s3Storage.d.ts +1 -1
- package/dist/{adapters → src/framework/adapters}/s3Storage.js +12 -12
- package/dist/src/framework/admin/bunshotAccess.d.ts +2 -0
- package/dist/src/framework/admin/bunshotAccess.js +23 -0
- package/dist/src/framework/admin/bunshotUsers.d.ts +2 -0
- package/dist/src/framework/admin/bunshotUsers.js +103 -0
- package/dist/src/framework/admin/index.d.ts +7 -0
- package/dist/src/framework/admin/index.js +21 -0
- package/dist/src/framework/boundaryAdapters/cacheFactories.d.ts +13 -0
- package/dist/src/framework/boundaryAdapters/cacheFactories.js +86 -0
- package/dist/src/framework/boundaryAdapters/index.d.ts +2 -0
- package/dist/src/framework/boundaryAdapters/index.js +1 -0
- package/dist/src/framework/boundaryAdapters.d.ts +17 -0
- package/dist/src/framework/boundaryAdapters.js +62 -0
- package/dist/src/framework/buildContext.d.ts +33 -0
- package/dist/src/framework/buildContext.js +119 -0
- package/dist/src/framework/config/schema.d.ts +447 -0
- package/dist/src/framework/config/schema.js +528 -0
- package/dist/src/framework/createInfrastructure.d.ts +76 -0
- package/dist/src/framework/createInfrastructure.js +221 -0
- package/dist/src/framework/lib/auditLog.d.ts +23 -0
- package/dist/src/framework/lib/auditLog.js +416 -0
- package/dist/src/framework/lib/captcha.d.ts +11 -0
- package/dist/{lib → src/framework/lib}/captcha.js +13 -10
- package/dist/{lib → src/framework/lib}/createDtoMapper.js +4 -4
- package/dist/src/framework/lib/createRoute.d.ts +1 -0
- package/dist/src/framework/lib/createRoute.js +2 -0
- package/dist/{lib → src/framework/lib}/idempotency.d.ts +2 -6
- package/dist/src/framework/lib/idempotency.js +74 -0
- package/dist/src/framework/lib/logger.d.ts +3 -0
- package/dist/src/framework/lib/logger.js +14 -0
- package/dist/src/framework/lib/metrics.d.ts +34 -0
- package/dist/{lib → src/framework/lib}/metrics.js +49 -57
- package/dist/src/framework/lib/pagination.d.ts +42 -0
- package/dist/src/framework/lib/pagination.js +51 -0
- package/dist/src/framework/lib/redisTransport.d.ts +38 -0
- package/dist/src/framework/lib/redisTransport.js +107 -0
- package/dist/src/framework/lib/resolveUserId.d.ts +2 -0
- package/dist/src/framework/lib/resolveUserId.js +5 -0
- package/dist/src/framework/lib/sseCollision.d.ts +6 -0
- package/dist/src/framework/lib/sseCollision.js +26 -0
- package/dist/src/framework/lib/storageAdapter.d.ts +1 -0
- package/dist/src/framework/lib/storageAdapter.js +1 -0
- package/dist/{lib → src/framework/lib}/stripUnreferencedSchemas.js +4 -4
- package/dist/src/framework/lib/tenant.d.ts +21 -0
- package/dist/src/framework/lib/tenant.js +70 -0
- package/dist/{lib → src/framework/lib}/upload.d.ts +11 -10
- package/dist/src/framework/lib/upload.js +132 -0
- package/dist/src/framework/lib/uploadRegistry.d.ts +23 -0
- package/dist/src/framework/lib/uploadRegistry.js +34 -0
- package/dist/{lib → src/framework/lib}/validate.d.ts +1 -1
- package/dist/{lib → src/framework/lib}/validate.js +2 -2
- package/dist/src/framework/lib/ws.d.ts +19 -0
- package/dist/src/framework/lib/ws.js +130 -0
- package/dist/src/framework/lib/wsHeartbeat.d.ts +12 -0
- package/dist/src/framework/lib/wsHeartbeat.js +53 -0
- package/dist/src/framework/lib/wsMessages.d.ts +25 -0
- package/dist/src/framework/lib/wsMessages.js +45 -0
- package/dist/src/framework/lib/wsNamespace.d.ts +17 -0
- package/dist/src/framework/lib/wsNamespace.js +19 -0
- package/dist/src/framework/lib/wsPresence.d.ts +17 -0
- package/dist/src/framework/lib/wsPresence.js +84 -0
- package/dist/src/framework/lib/wsTransport.d.ts +38 -0
- package/dist/src/framework/lib/wsTransport.js +9 -0
- package/dist/{lib → src/framework/lib}/zodToMongoose.d.ts +1 -1
- package/dist/{lib → src/framework/lib}/zodToMongoose.js +11 -11
- package/dist/{middleware → src/framework/middleware}/auditLog.d.ts +4 -3
- package/dist/src/framework/middleware/auditLog.js +42 -0
- package/dist/{middleware → src/framework/middleware}/botProtection.d.ts +2 -2
- package/dist/{middleware → src/framework/middleware}/botProtection.js +8 -9
- package/dist/src/framework/middleware/cacheResponse.d.ts +35 -0
- package/dist/src/framework/middleware/cacheResponse.js +126 -0
- package/dist/{middleware → src/framework/middleware}/captcha.d.ts +2 -3
- package/dist/src/framework/middleware/captcha.js +37 -0
- package/dist/{middleware → src/framework/middleware}/errorHandler.d.ts +1 -1
- package/dist/{middleware → src/framework/middleware}/errorHandler.js +2 -2
- package/dist/src/framework/middleware/index.js +1 -0
- package/dist/{middleware → src/framework/middleware}/logger.d.ts +1 -1
- package/dist/src/framework/middleware/metrics.d.ts +12 -0
- package/dist/src/framework/middleware/metrics.js +26 -0
- package/dist/{middleware → src/framework/middleware}/rateLimit.d.ts +2 -2
- package/dist/src/framework/middleware/rateLimit.js +22 -0
- package/dist/src/framework/middleware/requestId.d.ts +3 -0
- package/dist/{middleware → src/framework/middleware}/requestId.js +2 -2
- package/dist/{middleware → src/framework/middleware}/requestLogger.d.ts +3 -3
- package/dist/{middleware → src/framework/middleware}/requestLogger.js +17 -12
- package/dist/{middleware → src/framework/middleware}/requestSigning.d.ts +2 -2
- package/dist/{middleware → src/framework/middleware}/requestSigning.js +18 -20
- package/dist/src/framework/middleware/tenant.d.ts +14 -0
- package/dist/{middleware → src/framework/middleware}/tenant.js +31 -27
- package/dist/src/framework/middleware/upload.d.ts +5 -0
- package/dist/{middleware → src/framework/middleware}/upload.js +4 -4
- package/dist/{middleware → src/framework/middleware}/webhookAuth.d.ts +3 -3
- package/dist/{middleware → src/framework/middleware}/webhookAuth.js +11 -12
- package/dist/src/framework/models/AuditLog.d.ts +21 -0
- package/dist/src/framework/models/AuditLog.js +31 -0
- package/dist/src/framework/mountMiddleware.d.ts +91 -0
- package/dist/src/framework/mountMiddleware.js +128 -0
- package/dist/src/framework/mountOptionalEndpoints.d.ts +103 -0
- package/dist/src/framework/mountOptionalEndpoints.js +64 -0
- package/dist/src/framework/mountRoutes.d.ts +21 -0
- package/dist/src/framework/mountRoutes.js +144 -0
- package/dist/src/framework/persistence/cronRegistry.d.ts +28 -0
- package/dist/src/framework/persistence/cronRegistry.js +139 -0
- package/dist/src/framework/persistence/idempotency.d.ts +26 -0
- package/dist/src/framework/persistence/idempotency.js +178 -0
- package/dist/src/framework/persistence/index.d.ts +6 -0
- package/dist/src/framework/persistence/index.js +8 -0
- package/dist/src/framework/persistence/storeInfra.d.ts +9 -0
- package/dist/src/framework/persistence/storeInfra.js +1 -0
- package/dist/src/framework/persistence/uploadRegistry.d.ts +35 -0
- package/dist/src/framework/persistence/uploadRegistry.js +235 -0
- package/dist/src/framework/persistence/wsMessages.d.ts +22 -0
- package/dist/src/framework/persistence/wsMessages.js +296 -0
- package/dist/src/framework/preloadSchemas.d.ts +24 -0
- package/dist/src/framework/preloadSchemas.js +42 -0
- package/dist/src/framework/registerBoundaryAdapters.d.ts +23 -0
- package/dist/src/framework/registerBoundaryAdapters.js +46 -0
- package/dist/src/framework/routes/admin.d.ts +9 -0
- package/dist/src/framework/routes/admin.js +361 -0
- package/dist/src/framework/routes/health.d.ts +1 -0
- package/dist/src/framework/routes/health.js +21 -0
- package/dist/src/framework/routes/home.d.ts +1 -0
- package/dist/src/framework/routes/home.js +18 -0
- package/dist/src/framework/routes/jobs.d.ts +3 -0
- package/dist/{routes → src/framework/routes}/jobs.js +128 -103
- package/dist/src/framework/routes/metrics.d.ts +10 -0
- package/dist/src/framework/routes/metrics.js +57 -0
- package/dist/{routes → src/framework/routes}/uploads.d.ts +3 -3
- package/dist/src/framework/routes/uploads.js +262 -0
- package/dist/src/framework/runPluginLifecycle.d.ts +27 -0
- package/dist/src/framework/runPluginLifecycle.js +121 -0
- package/dist/src/framework/secrets/frameworkSecretSchema.d.ts +58 -0
- package/dist/src/framework/secrets/frameworkSecretSchema.js +20 -0
- package/dist/src/framework/secrets/index.d.ts +9 -0
- package/dist/src/framework/secrets/index.js +7 -0
- package/dist/src/framework/secrets/providers/envProvider.d.ts +15 -0
- package/dist/src/framework/secrets/providers/envProvider.js +18 -0
- package/dist/src/framework/secrets/providers/fileProvider.d.ts +8 -0
- package/dist/src/framework/secrets/providers/fileProvider.js +82 -0
- package/dist/src/framework/secrets/providers/ssmProvider.d.ts +20 -0
- package/dist/src/framework/secrets/providers/ssmProvider.js +127 -0
- package/dist/src/framework/secrets/resolveSecretBundle.d.ts +53 -0
- package/dist/src/framework/secrets/resolveSecretBundle.js +84 -0
- package/dist/src/framework/secrets/resolveSecrets.d.ts +18 -0
- package/dist/src/framework/secrets/resolveSecrets.js +34 -0
- package/dist/src/framework/sse/index.d.ts +21 -0
- package/dist/src/framework/sse/index.js +109 -0
- package/dist/src/framework/ws/index.d.ts +11 -0
- package/dist/src/framework/ws/index.js +8 -0
- package/dist/src/index.d.ts +87 -0
- package/dist/src/index.js +58 -0
- package/dist/src/lib/appConfig.d.ts +7 -0
- package/dist/src/lib/appConfig.js +27 -0
- package/dist/src/lib/appMeta.d.ts +7 -0
- package/dist/src/lib/appMeta.js +3 -0
- package/dist/src/lib/authConfig.d.ts +532 -0
- package/dist/{lib/appConfig.js → src/lib/authConfig.js} +75 -17
- package/dist/{lib → src/lib}/context.d.ts +6 -12
- package/dist/{lib → src/lib}/context.js +5 -5
- package/dist/src/lib/logger.d.ts +1 -0
- package/dist/src/lib/logger.js +1 -0
- package/dist/src/lib/mongo.d.ts +58 -0
- package/dist/src/lib/mongo.js +96 -0
- package/dist/src/lib/queue.d.ts +72 -0
- package/dist/src/lib/queue.js +152 -0
- package/dist/src/lib/redis.d.ts +28 -0
- package/dist/src/lib/redis.js +72 -0
- package/dist/{lib → src/lib}/signing.d.ts +2 -2
- package/dist/src/lib/signing.js +210 -0
- package/dist/src/lib/signingConfig.d.ts +40 -0
- package/dist/src/lib/signingConfig.js +28 -0
- package/dist/src/server.d.ts +146 -0
- package/dist/src/server.js +469 -0
- package/dist/src/shared/lib/HttpError.d.ts +1 -0
- package/dist/src/shared/lib/HttpError.js +2 -0
- package/dist/src/shared/lib/constants.d.ts +10 -0
- package/dist/src/shared/lib/crypto.d.ts +43 -0
- package/dist/src/shared/lib/crypto.js +74 -0
- package/dist/src/shared/lib/signing.d.ts +52 -0
- package/dist/{lib → src/shared/lib}/signing.js +35 -8
- package/dist/src/testing.d.ts +34 -0
- package/dist/src/testing.js +93 -0
- package/package.json +100 -26
- package/dist/adapters/memoryAuth.d.ts +0 -52
- package/dist/adapters/memoryAuth.js +0 -749
- package/dist/adapters/memoryStorage.d.ts +0 -3
- package/dist/adapters/memoryStorage.js +0 -44
- package/dist/adapters/mongoAuth.d.ts +0 -2
- package/dist/adapters/mongoAuth.js +0 -403
- package/dist/adapters/sqliteAuth.d.ts +0 -72
- package/dist/adapters/sqliteAuth.js +0 -858
- package/dist/app.d.ts +0 -559
- package/dist/app.js +0 -651
- package/dist/entrypoints/mongo.d.ts +0 -5
- package/dist/entrypoints/mongo.js +0 -4
- package/dist/entrypoints/queue.d.ts +0 -2
- package/dist/entrypoints/queue.js +0 -1
- package/dist/entrypoints/redis.d.ts +0 -1
- package/dist/entrypoints/redis.js +0 -1
- package/dist/index.d.ts +0 -117
- package/dist/index.js +0 -88
- package/dist/lib/appConfig.d.ts +0 -275
- package/dist/lib/auditLog.d.ts +0 -58
- package/dist/lib/auditLog.js +0 -218
- package/dist/lib/authAdapter.d.ts +0 -246
- package/dist/lib/authAdapter.js +0 -7
- package/dist/lib/authRateLimit.d.ts +0 -13
- package/dist/lib/authRateLimit.js +0 -117
- package/dist/lib/clientIp.d.ts +0 -14
- package/dist/lib/credentialStuffing.d.ts +0 -31
- package/dist/lib/credentialStuffing.js +0 -77
- package/dist/lib/crypto.d.ts +0 -11
- package/dist/lib/crypto.js +0 -22
- package/dist/lib/deletionCancelToken.d.ts +0 -12
- package/dist/lib/deletionCancelToken.js +0 -88
- package/dist/lib/emailVerification.d.ts +0 -19
- package/dist/lib/emailVerification.js +0 -129
- package/dist/lib/fingerprint.js +0 -36
- package/dist/lib/idempotency.js +0 -182
- package/dist/lib/jwks.d.ts +0 -25
- package/dist/lib/jwks.js +0 -51
- package/dist/lib/jwt.d.ts +0 -15
- package/dist/lib/jwt.js +0 -111
- package/dist/lib/metrics.d.ts +0 -14
- package/dist/lib/mfaChallenge.d.ts +0 -55
- package/dist/lib/mfaChallenge.js +0 -398
- package/dist/lib/mongo.d.ts +0 -39
- package/dist/lib/mongo.js +0 -124
- package/dist/lib/oauth.d.ts +0 -40
- package/dist/lib/oauth.js +0 -101
- package/dist/lib/oauthCode.d.ts +0 -15
- package/dist/lib/oauthCode.js +0 -95
- package/dist/lib/pagination.d.ts +0 -119
- package/dist/lib/pagination.js +0 -166
- package/dist/lib/queue.d.ts +0 -37
- package/dist/lib/queue.js +0 -117
- package/dist/lib/redis.d.ts +0 -9
- package/dist/lib/redis.js +0 -61
- package/dist/lib/resetPassword.d.ts +0 -12
- package/dist/lib/resetPassword.js +0 -93
- package/dist/lib/roles.d.ts +0 -7
- package/dist/lib/roles.js +0 -49
- package/dist/lib/saml.d.ts +0 -25
- package/dist/lib/saml.js +0 -64
- package/dist/lib/securityEvents.d.ts +0 -28
- package/dist/lib/securityEvents.js +0 -26
- package/dist/lib/session.d.ts +0 -49
- package/dist/lib/session.js +0 -597
- package/dist/lib/tenant.d.ts +0 -15
- package/dist/lib/tenant.js +0 -65
- package/dist/lib/upload.js +0 -112
- package/dist/lib/uploadRegistry.d.ts +0 -18
- package/dist/lib/uploadRegistry.js +0 -83
- package/dist/lib/ws.d.ts +0 -22
- package/dist/lib/ws.js +0 -96
- package/dist/lib/wsHeartbeat.d.ts +0 -12
- package/dist/lib/wsHeartbeat.js +0 -57
- package/dist/lib/wsMessages.d.ts +0 -40
- package/dist/lib/wsMessages.js +0 -330
- package/dist/lib/wsPresence.d.ts +0 -25
- package/dist/lib/wsPresence.js +0 -99
- package/dist/middleware/auditLog.js +0 -39
- package/dist/middleware/bearerAuth.d.ts +0 -2
- package/dist/middleware/bearerAuth.js +0 -11
- package/dist/middleware/cacheResponse.d.ts +0 -15
- package/dist/middleware/cacheResponse.js +0 -178
- package/dist/middleware/captcha.js +0 -36
- package/dist/middleware/csrf.js +0 -129
- package/dist/middleware/identify.d.ts +0 -3
- package/dist/middleware/identify.js +0 -122
- package/dist/middleware/index.js +0 -1
- package/dist/middleware/metrics.d.ts +0 -9
- package/dist/middleware/metrics.js +0 -26
- package/dist/middleware/rateLimit.js +0 -22
- package/dist/middleware/requestId.d.ts +0 -3
- package/dist/middleware/scimAuth.d.ts +0 -8
- package/dist/middleware/scimAuth.js +0 -29
- package/dist/middleware/tenant.d.ts +0 -5
- package/dist/middleware/upload.d.ts +0 -5
- package/dist/middleware/userAuth.d.ts +0 -3
- package/dist/middleware/userAuth.js +0 -6
- package/dist/models/AuditLog.d.ts +0 -30
- package/dist/models/AuditLog.js +0 -39
- package/dist/models/AuthUser.js +0 -55
- package/dist/models/Group.d.ts +0 -21
- package/dist/models/Group.js +0 -28
- package/dist/models/GroupMembership.js +0 -25
- package/dist/models/TenantRole.d.ts +0 -15
- package/dist/models/TenantRole.js +0 -23
- package/dist/routes/auth.d.ts +0 -12
- package/dist/routes/auth.js +0 -744
- package/dist/routes/groups.js +0 -346
- package/dist/routes/health.d.ts +0 -1
- package/dist/routes/health.js +0 -22
- package/dist/routes/home.d.ts +0 -1
- package/dist/routes/home.js +0 -16
- package/dist/routes/jobs.d.ts +0 -2
- package/dist/routes/m2m.d.ts +0 -2
- package/dist/routes/m2m.js +0 -72
- package/dist/routes/metrics.d.ts +0 -8
- package/dist/routes/metrics.js +0 -55
- package/dist/routes/mfa.d.ts +0 -5
- package/dist/routes/mfa.js +0 -628
- package/dist/routes/oauth.d.ts +0 -2
- package/dist/routes/oauth.js +0 -520
- package/dist/routes/oidc.d.ts +0 -2
- package/dist/routes/oidc.js +0 -29
- package/dist/routes/passkey.d.ts +0 -1
- package/dist/routes/passkey.js +0 -157
- package/dist/routes/saml.d.ts +0 -2
- package/dist/routes/saml.js +0 -86
- package/dist/routes/scim.d.ts +0 -2
- package/dist/routes/scim.js +0 -255
- package/dist/routes/uploads.js +0 -227
- package/dist/schemas/auth.js +0 -30
- package/dist/server.d.ts +0 -57
- package/dist/server.js +0 -112
- package/dist/services/auth.d.ts +0 -29
- package/dist/services/auth.js +0 -238
- package/dist/ws/index.d.ts +0 -10
- package/dist/ws/index.js +0 -39
- package/docs/sections/adding-middleware/full.md +0 -35
- package/docs/sections/adding-models/full.md +0 -125
- package/docs/sections/adding-models/overview.md +0 -13
- package/docs/sections/adding-routes/full.md +0 -182
- package/docs/sections/adding-routes/overview.md +0 -23
- package/docs/sections/auth-flow/full.md +0 -790
- package/docs/sections/auth-flow/overview.md +0 -10
- package/docs/sections/auth-security-examples/full.md +0 -388
- package/docs/sections/authentication/full.md +0 -130
- package/docs/sections/authentication/overview.md +0 -5
- package/docs/sections/cli/full.md +0 -42
- package/docs/sections/configuration/full.md +0 -172
- package/docs/sections/configuration/overview.md +0 -18
- package/docs/sections/configuration-example/full.md +0 -117
- package/docs/sections/configuration-example/overview.md +0 -30
- package/docs/sections/documentation/full.md +0 -171
- package/docs/sections/environment-variables/full.md +0 -55
- package/docs/sections/exports/full.md +0 -123
- package/docs/sections/extending-context/full.md +0 -59
- package/docs/sections/header.md +0 -3
- package/docs/sections/installation/full.md +0 -6
- package/docs/sections/jobs/full.md +0 -140
- package/docs/sections/jobs/overview.md +0 -15
- package/docs/sections/logging/full.md +0 -83
- package/docs/sections/metrics/full.md +0 -131
- package/docs/sections/mongodb-connections/full.md +0 -45
- package/docs/sections/mongodb-connections/overview.md +0 -7
- package/docs/sections/multi-tenancy/full.md +0 -66
- package/docs/sections/multi-tenancy/overview.md +0 -15
- package/docs/sections/oauth/full.md +0 -189
- package/docs/sections/oauth/overview.md +0 -16
- package/docs/sections/package-development/full.md +0 -7
- package/docs/sections/pagination/full.md +0 -93
- package/docs/sections/passkey-login/full.md +0 -90
- package/docs/sections/passkey-login/overview.md +0 -1
- package/docs/sections/peer-dependencies/full.md +0 -47
- package/docs/sections/quick-start/full.md +0 -43
- package/docs/sections/response-caching/full.md +0 -117
- package/docs/sections/response-caching/overview.md +0 -13
- package/docs/sections/roles/full.md +0 -225
- package/docs/sections/roles/overview.md +0 -14
- package/docs/sections/running-without-redis/full.md +0 -16
- package/docs/sections/running-without-redis-or-mongodb/full.md +0 -60
- package/docs/sections/signing/full.md +0 -203
- package/docs/sections/stack/full.md +0 -10
- package/docs/sections/uploads/full.md +0 -208
- package/docs/sections/versioning/full.md +0 -85
- package/docs/sections/webhook-auth/full.md +0 -100
- package/docs/sections/websocket/full.md +0 -196
- package/docs/sections/websocket/overview.md +0 -5
- package/docs/sections/websocket-rooms/full.md +0 -102
- package/docs/sections/websocket-rooms/overview.md +0 -5
- /package/dist/{lib/storageAdapter.js → packages/bunshot-admin/src/types/env.js} +0 -0
- /package/dist/{lib → packages/bunshot-auth/src/lib}/fingerprint.d.ts +0 -0
- /package/dist/{lib → packages/bunshot-auth/src/lib}/logger.d.ts +0 -0
- /package/dist/{lib → packages/bunshot-core/src}/constants.d.ts +0 -0
- /package/dist/{lib → packages/bunshot-core/src}/storageAdapter.d.ts +0 -0
- /package/dist/{lib → src/framework/lib}/createDtoMapper.d.ts +0 -0
- /package/dist/{lib → src/framework/lib}/stripUnreferencedSchemas.d.ts +0 -0
- /package/dist/{middleware → src/framework/middleware}/cors.d.ts +0 -0
- /package/dist/{middleware → src/framework/middleware}/cors.js +0 -0
- /package/dist/{middleware → src/framework/middleware}/index.d.ts +0 -0
- /package/dist/{middleware → src/framework/middleware}/logger.js +0 -0
- /package/dist/{lib → src/shared/lib}/constants.js +0 -0
|
@@ -0,0 +1,991 @@
|
|
|
1
|
+
import { getAuthCookieOptions } from '../lib/cookieOptions';
|
|
2
|
+
import { isProd } from '../lib/env';
|
|
3
|
+
import { consumeMfaChallenge, replaceMfaChallengeOtp } from '../lib/mfaChallenge';
|
|
4
|
+
import { refreshCsrfToken } from '../middleware/csrf';
|
|
5
|
+
import { userAuth } from '../middleware/userAuth';
|
|
6
|
+
import { ErrorResponse } from '../schemas/error';
|
|
7
|
+
import { SuccessResponse } from '../schemas/success';
|
|
8
|
+
import * as AuthService from '../services/auth';
|
|
9
|
+
import { emitLoginSuccess } from '../services/auth';
|
|
10
|
+
import * as MfaService from '../services/mfa';
|
|
11
|
+
import { setCookie } from 'hono/cookie';
|
|
12
|
+
import { z } from 'zod';
|
|
13
|
+
import { createRoute, withSecurity } from '../../../bunshot-core/src/index.js';
|
|
14
|
+
import { COOKIE_REFRESH_TOKEN, COOKIE_TOKEN, HttpError, createRouter, } from '../../../bunshot-core/src/index.js';
|
|
15
|
+
import { getClientIp } from '../../../bunshot-core/src/index.js';
|
|
16
|
+
const hookCtx = (c) => ({
|
|
17
|
+
ip: getClientIp(c) !== 'unknown' ? getClientIp(c) : undefined,
|
|
18
|
+
userAgent: c.req.header('user-agent') ?? undefined,
|
|
19
|
+
requestId: c.get('requestId'),
|
|
20
|
+
});
|
|
21
|
+
const tags = ['MFA'];
|
|
22
|
+
const RecoveryCodesSuccessResponse = SuccessResponse.extend({
|
|
23
|
+
recoveryCodes: z.array(z.string()).describe('One-time recovery codes. Store these securely.'),
|
|
24
|
+
});
|
|
25
|
+
const SetupTokenSuccessResponse = SuccessResponse.extend({
|
|
26
|
+
setupToken: z.string().describe('Setup challenge token for the follow-up verification request.'),
|
|
27
|
+
});
|
|
28
|
+
const WebAuthnRegistrationSuccessResponse = SuccessResponse.extend({
|
|
29
|
+
credentialId: z.string(),
|
|
30
|
+
recoveryCodes: z
|
|
31
|
+
.array(z.string())
|
|
32
|
+
.nullable()
|
|
33
|
+
.describe('Recovery codes returned when WebAuthn registration completes.'),
|
|
34
|
+
});
|
|
35
|
+
export const createMfaRouter = ({ rateLimit } = {}, runtime) => {
|
|
36
|
+
const { adapter, eventBus } = runtime;
|
|
37
|
+
const getConfig = () => runtime.config;
|
|
38
|
+
const cookieOptions = (maxAge) => getAuthCookieOptions(isProd(), runtime.config, maxAge);
|
|
39
|
+
const router = createRouter();
|
|
40
|
+
// Resolve MFA rate limits with defaults
|
|
41
|
+
const mfaVerifyOpts = {
|
|
42
|
+
windowMs: rateLimit?.mfaVerify?.windowMs ?? 15 * 60 * 1000,
|
|
43
|
+
max: rateLimit?.mfaVerify?.max ?? 10,
|
|
44
|
+
};
|
|
45
|
+
const mfaEmailOtpInitiateOpts = {
|
|
46
|
+
windowMs: rateLimit?.mfaEmailOtpInitiate?.windowMs ?? 15 * 60 * 1000,
|
|
47
|
+
max: rateLimit?.mfaEmailOtpInitiate?.max ?? 3,
|
|
48
|
+
};
|
|
49
|
+
const mfaResendOpts = {
|
|
50
|
+
windowMs: rateLimit?.mfaResend?.windowMs ?? 60 * 1000,
|
|
51
|
+
max: rateLimit?.mfaResend?.max ?? 5,
|
|
52
|
+
};
|
|
53
|
+
const mfaDisableOpts = {
|
|
54
|
+
windowMs: rateLimit?.mfaDisable?.windowMs ?? 15 * 60 * 1000,
|
|
55
|
+
max: rateLimit?.mfaDisable?.max ?? 5,
|
|
56
|
+
};
|
|
57
|
+
// All MFA setup/management routes require auth
|
|
58
|
+
router.use('/auth/mfa/setup', userAuth);
|
|
59
|
+
router.use('/auth/mfa/verify-setup', userAuth);
|
|
60
|
+
router.use('/auth/mfa', userAuth);
|
|
61
|
+
router.use('/auth/mfa/recovery-codes', userAuth);
|
|
62
|
+
router.use('/auth/mfa/email-otp/enable', userAuth);
|
|
63
|
+
router.use('/auth/mfa/email-otp/verify-setup', userAuth);
|
|
64
|
+
router.use('/auth/mfa/email-otp', userAuth);
|
|
65
|
+
router.use('/auth/mfa/methods', userAuth);
|
|
66
|
+
// ─── Setup ────────────────────────────────────────────────────────────────
|
|
67
|
+
router.openapi(withSecurity(createRoute({
|
|
68
|
+
method: 'post',
|
|
69
|
+
path: '/auth/mfa/setup',
|
|
70
|
+
summary: 'Initiate MFA setup',
|
|
71
|
+
description: 'Generates a TOTP secret and returns the otpauth URI for QR code scanning. The user must confirm setup by verifying a code via POST /auth/mfa/verify-setup.',
|
|
72
|
+
tags,
|
|
73
|
+
responses: {
|
|
74
|
+
200: {
|
|
75
|
+
content: {
|
|
76
|
+
'application/json': {
|
|
77
|
+
schema: z.object({
|
|
78
|
+
secret: z.string().describe('Base32-encoded TOTP secret.'),
|
|
79
|
+
uri: z.string().describe('otpauth:// URI for QR code generation.'),
|
|
80
|
+
}),
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
description: 'TOTP secret generated. Scan the QR code with an authenticator app.',
|
|
84
|
+
},
|
|
85
|
+
401: {
|
|
86
|
+
content: { 'application/json': { schema: ErrorResponse } },
|
|
87
|
+
description: 'No valid session.',
|
|
88
|
+
},
|
|
89
|
+
429: {
|
|
90
|
+
content: { 'application/json': { schema: ErrorResponse } },
|
|
91
|
+
description: 'Too many MFA setup attempts. Try again later.',
|
|
92
|
+
},
|
|
93
|
+
501: {
|
|
94
|
+
content: { 'application/json': { schema: ErrorResponse } },
|
|
95
|
+
description: 'Auth adapter does not support MFA.',
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
}), { cookieAuth: [] }, { userToken: [] }), async (c) => {
|
|
99
|
+
const userId = c.get('authUserId');
|
|
100
|
+
if (await runtime.rateLimit.trackAttempt(`mfa-setup:${userId}`, {
|
|
101
|
+
windowMs: 15 * 60 * 1000,
|
|
102
|
+
max: 5,
|
|
103
|
+
})) {
|
|
104
|
+
return c.json({ error: 'Too many MFA setup attempts. Try again later.' }, 429);
|
|
105
|
+
}
|
|
106
|
+
const result = await MfaService.setupMfa(userId, runtime);
|
|
107
|
+
return c.json(result, 200);
|
|
108
|
+
});
|
|
109
|
+
// ─── Verify Setup ─────────────────────────────────────────────────────────
|
|
110
|
+
router.openapi(withSecurity(createRoute({
|
|
111
|
+
method: 'post',
|
|
112
|
+
path: '/auth/mfa/verify-setup',
|
|
113
|
+
summary: 'Confirm MFA setup',
|
|
114
|
+
description: 'Verifies a TOTP code from the authenticator app and enables MFA. Returns one-time recovery codes that should be stored securely. If email OTP was previously enabled, recovery codes are regenerated.',
|
|
115
|
+
tags,
|
|
116
|
+
request: {
|
|
117
|
+
body: {
|
|
118
|
+
content: {
|
|
119
|
+
'application/json': {
|
|
120
|
+
schema: z.object({
|
|
121
|
+
code: z
|
|
122
|
+
.string()
|
|
123
|
+
.length(6)
|
|
124
|
+
.describe('6-digit TOTP code from the authenticator app.'),
|
|
125
|
+
}),
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
responses: {
|
|
131
|
+
200: {
|
|
132
|
+
content: {
|
|
133
|
+
'application/json': {
|
|
134
|
+
schema: RecoveryCodesSuccessResponse.extend({
|
|
135
|
+
recoveryCodes: z
|
|
136
|
+
.array(z.string())
|
|
137
|
+
.describe('One-time recovery codes. Store these securely — they cannot be shown again.'),
|
|
138
|
+
}),
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
description: 'MFA enabled successfully.',
|
|
142
|
+
},
|
|
143
|
+
400: {
|
|
144
|
+
content: { 'application/json': { schema: ErrorResponse } },
|
|
145
|
+
description: 'MFA setup not initiated.',
|
|
146
|
+
},
|
|
147
|
+
401: {
|
|
148
|
+
content: { 'application/json': { schema: ErrorResponse } },
|
|
149
|
+
description: 'Invalid TOTP code or no valid session.',
|
|
150
|
+
},
|
|
151
|
+
501: {
|
|
152
|
+
content: { 'application/json': { schema: ErrorResponse } },
|
|
153
|
+
description: 'Auth adapter does not support MFA.',
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
}), { cookieAuth: [] }, { userToken: [] }), async (c) => {
|
|
157
|
+
const userId = c.get('authUserId');
|
|
158
|
+
const { code } = c.req.valid('json');
|
|
159
|
+
const recoveryCodes = await MfaService.verifySetup(userId, code, runtime);
|
|
160
|
+
eventBus.emit('security.auth.mfa.setup', { userId: c.get('authUserId') ?? undefined });
|
|
161
|
+
eventBus.emit('auth:mfa.enabled', { userId, method: 'totp' });
|
|
162
|
+
return c.json({ ok: true, recoveryCodes }, 200);
|
|
163
|
+
});
|
|
164
|
+
// ─── Verify (complete login after password) ───────────────────────────────
|
|
165
|
+
const MfaLoginResponse = z
|
|
166
|
+
.object({
|
|
167
|
+
token: z.string().describe('JWT session token.'),
|
|
168
|
+
userId: z.string().describe('Unique user ID.'),
|
|
169
|
+
refreshToken: z.string().optional().describe('Refresh token (when configured).'),
|
|
170
|
+
})
|
|
171
|
+
.openapi('MfaLoginResponse');
|
|
172
|
+
router.openapi(createRoute({
|
|
173
|
+
method: 'post',
|
|
174
|
+
path: '/auth/mfa/verify',
|
|
175
|
+
summary: 'Complete MFA login',
|
|
176
|
+
description: "Completes login by verifying a TOTP code, email OTP code, recovery code, or WebAuthn assertion after password authentication. Requires the mfaToken returned from the login endpoint. Optionally specify 'method' to target a specific verification method.",
|
|
177
|
+
tags,
|
|
178
|
+
request: {
|
|
179
|
+
body: {
|
|
180
|
+
content: {
|
|
181
|
+
'application/json': {
|
|
182
|
+
schema: z.object({
|
|
183
|
+
mfaToken: z.string().describe('MFA challenge token from the login response.'),
|
|
184
|
+
code: z
|
|
185
|
+
.string()
|
|
186
|
+
.optional()
|
|
187
|
+
.describe('6-digit TOTP/email OTP code or 8-character recovery code. Required unless using WebAuthn.'),
|
|
188
|
+
method: z
|
|
189
|
+
.enum(['totp', 'emailOtp', 'webauthn'])
|
|
190
|
+
.optional()
|
|
191
|
+
.describe('Specify which MFA method to verify. If omitted, methods are tried automatically.'),
|
|
192
|
+
webauthnResponse: z
|
|
193
|
+
.record(z.string(), z.unknown())
|
|
194
|
+
.optional()
|
|
195
|
+
.describe('WebAuthn authentication response from navigator.credentials.get(). Pass the entire response object.'),
|
|
196
|
+
}),
|
|
197
|
+
},
|
|
198
|
+
},
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
responses: {
|
|
202
|
+
200: {
|
|
203
|
+
content: { 'application/json': { schema: MfaLoginResponse } },
|
|
204
|
+
description: 'MFA verified. Session created.',
|
|
205
|
+
},
|
|
206
|
+
401: {
|
|
207
|
+
content: { 'application/json': { schema: ErrorResponse } },
|
|
208
|
+
description: 'Invalid or expired MFA token, or invalid code.',
|
|
209
|
+
},
|
|
210
|
+
429: {
|
|
211
|
+
content: { 'application/json': { schema: ErrorResponse } },
|
|
212
|
+
description: 'Too many MFA verification attempts. Try again later.',
|
|
213
|
+
},
|
|
214
|
+
},
|
|
215
|
+
}), async (c) => {
|
|
216
|
+
const ip = getClientIp(c);
|
|
217
|
+
if (await runtime.rateLimit.trackAttempt(`mfa-verify:${ip}`, mfaVerifyOpts)) {
|
|
218
|
+
return c.json({ error: 'Too many MFA verification attempts. Try again later.' }, 429);
|
|
219
|
+
}
|
|
220
|
+
const { mfaToken, code, method, webauthnResponse } = c.req.valid('json');
|
|
221
|
+
if (!code && !webauthnResponse) {
|
|
222
|
+
return c.json({ error: "Either 'code' or 'webauthnResponse' is required" }, 401);
|
|
223
|
+
}
|
|
224
|
+
const challenge = await consumeMfaChallenge(runtime.repos.mfaChallenge, mfaToken);
|
|
225
|
+
if (!challenge)
|
|
226
|
+
return c.json({ error: 'Invalid or expired MFA token' }, 401);
|
|
227
|
+
const { userId, emailOtpHash, webauthnChallenge } = challenge;
|
|
228
|
+
let valid = false;
|
|
229
|
+
if (method === 'webauthn' || (!method && webauthnResponse)) {
|
|
230
|
+
// WebAuthn verification
|
|
231
|
+
if (webauthnResponse && webauthnChallenge) {
|
|
232
|
+
valid = await MfaService.verifyWebAuthn(userId, webauthnResponse, webauthnChallenge, runtime);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
else if (method === 'totp') {
|
|
236
|
+
// Only try TOTP
|
|
237
|
+
if (code)
|
|
238
|
+
valid = await MfaService.verifyTotp(userId, code, runtime);
|
|
239
|
+
}
|
|
240
|
+
else if (method === 'emailOtp') {
|
|
241
|
+
// Only try email OTP
|
|
242
|
+
if (code && emailOtpHash)
|
|
243
|
+
valid = MfaService.verifyEmailOtp(emailOtpHash, code);
|
|
244
|
+
}
|
|
245
|
+
else if (code) {
|
|
246
|
+
// Auto-detect: use emailOtpHash presence to pick order
|
|
247
|
+
if (emailOtpHash) {
|
|
248
|
+
// Email OTP first, then TOTP, then recovery
|
|
249
|
+
valid = MfaService.verifyEmailOtp(emailOtpHash, code);
|
|
250
|
+
if (!valid)
|
|
251
|
+
valid = await MfaService.verifyTotp(userId, code, runtime);
|
|
252
|
+
}
|
|
253
|
+
else {
|
|
254
|
+
// TOTP first
|
|
255
|
+
valid = await MfaService.verifyTotp(userId, code, runtime);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
// Always try recovery code as fallback (code-based only)
|
|
259
|
+
if (!valid && code) {
|
|
260
|
+
valid = await MfaService.verifyRecoveryCode(userId, code, runtime);
|
|
261
|
+
}
|
|
262
|
+
if (!valid) {
|
|
263
|
+
eventBus.emit('security.auth.mfa.verify.failure', {});
|
|
264
|
+
return c.json({ error: 'Invalid MFA code' }, 401);
|
|
265
|
+
}
|
|
266
|
+
// Create session — reuse the service helper for refresh token support
|
|
267
|
+
const result = await AuthService.createSessionForUser(userId, runtime, {
|
|
268
|
+
ipAddress: getClientIp(c),
|
|
269
|
+
userAgent: c.req.header('user-agent') ?? undefined,
|
|
270
|
+
}, hookCtx(c));
|
|
271
|
+
// Mark MFA as verified on the new session so step-up is satisfied immediately.
|
|
272
|
+
// If this fails, clean up the orphaned session so the client can retry cleanly.
|
|
273
|
+
try {
|
|
274
|
+
await runtime.repos.session.setMfaVerifiedAt(result.sessionId);
|
|
275
|
+
}
|
|
276
|
+
catch (err) {
|
|
277
|
+
await runtime.repos.session.deleteSession(result.sessionId, runtime.config).catch(() => { }); // best-effort cleanup
|
|
278
|
+
throw err;
|
|
279
|
+
}
|
|
280
|
+
const rtConfig = getConfig().refreshToken;
|
|
281
|
+
setCookie(c, COOKIE_TOKEN, result.token, cookieOptions(rtConfig ? (rtConfig.accessTokenExpiry ?? 900) : undefined));
|
|
282
|
+
if (result.refreshToken) {
|
|
283
|
+
setCookie(c, COOKIE_REFRESH_TOKEN, result.refreshToken, cookieOptions(rtConfig?.refreshTokenExpiry ?? 2_592_000));
|
|
284
|
+
}
|
|
285
|
+
if (getConfig().csrfEnabled)
|
|
286
|
+
refreshCsrfToken(c);
|
|
287
|
+
eventBus.emit('security.auth.mfa.verify.success', {});
|
|
288
|
+
emitLoginSuccess(userId, result.sessionId, runtime);
|
|
289
|
+
return c.json({ token: result.token, userId, refreshToken: result.refreshToken }, 200);
|
|
290
|
+
});
|
|
291
|
+
// ─── Disable MFA ──────────────────────────────────────────────────────────
|
|
292
|
+
const disableMfaSchema = z.object({
|
|
293
|
+
method: z
|
|
294
|
+
.enum(['totp', 'emailOtp', 'webauthn', 'password', 'recovery'])
|
|
295
|
+
.optional()
|
|
296
|
+
.describe('Verification method. Inferred from provided credentials when omitted (code→totp, password→password).'),
|
|
297
|
+
code: z.string().optional().describe('TOTP code, email OTP code, or recovery code.'),
|
|
298
|
+
password: z.string().optional().describe("Account password (for method: 'password')."),
|
|
299
|
+
reauthToken: z
|
|
300
|
+
.string()
|
|
301
|
+
.optional()
|
|
302
|
+
.describe('Reauth challenge token (for emailOtp and webauthn methods).'),
|
|
303
|
+
webauthnResponse: z
|
|
304
|
+
.record(z.string(), z.unknown())
|
|
305
|
+
.optional()
|
|
306
|
+
.describe('WebAuthn authentication response.'),
|
|
307
|
+
});
|
|
308
|
+
router.openapi(withSecurity(createRoute({
|
|
309
|
+
method: 'delete',
|
|
310
|
+
path: '/auth/mfa',
|
|
311
|
+
summary: 'Disable MFA',
|
|
312
|
+
description: 'Disables MFA for the authenticated user. Requires identity verification via TOTP, email OTP, WebAuthn, password, or recovery code.',
|
|
313
|
+
tags,
|
|
314
|
+
request: {
|
|
315
|
+
body: {
|
|
316
|
+
content: {
|
|
317
|
+
'application/json': {
|
|
318
|
+
schema: disableMfaSchema,
|
|
319
|
+
},
|
|
320
|
+
},
|
|
321
|
+
},
|
|
322
|
+
},
|
|
323
|
+
responses: {
|
|
324
|
+
200: {
|
|
325
|
+
content: { 'application/json': { schema: SuccessResponse } },
|
|
326
|
+
description: 'MFA disabled.',
|
|
327
|
+
},
|
|
328
|
+
400: {
|
|
329
|
+
content: { 'application/json': { schema: ErrorResponse } },
|
|
330
|
+
description: 'Missing required verification.',
|
|
331
|
+
},
|
|
332
|
+
401: {
|
|
333
|
+
content: { 'application/json': { schema: ErrorResponse } },
|
|
334
|
+
description: 'Invalid verification or no valid session.',
|
|
335
|
+
},
|
|
336
|
+
429: {
|
|
337
|
+
content: { 'application/json': { schema: ErrorResponse } },
|
|
338
|
+
description: 'Too many MFA disable attempts. Try again later.',
|
|
339
|
+
},
|
|
340
|
+
501: {
|
|
341
|
+
content: { 'application/json': { schema: ErrorResponse } },
|
|
342
|
+
description: 'Auth adapter does not support MFA.',
|
|
343
|
+
},
|
|
344
|
+
},
|
|
345
|
+
}), { cookieAuth: [] }, { userToken: [] }), async (c) => {
|
|
346
|
+
const userId = c.get('authUserId');
|
|
347
|
+
const sessionId = c.get('sessionId');
|
|
348
|
+
const body = c.req.valid('json');
|
|
349
|
+
if (await runtime.rateLimit.trackAttempt(`mfa-disable:${userId}`, mfaDisableOpts)) {
|
|
350
|
+
eventBus.emit('security.rate_limit.exceeded', { meta: { path: c.req.path } });
|
|
351
|
+
return c.json({ error: 'Too many MFA disable attempts. Try again later.' }, 429);
|
|
352
|
+
}
|
|
353
|
+
// Infer method from provided credentials when not specified
|
|
354
|
+
const method = body.method ?? (body.password ? 'password' : body.code ? 'totp' : undefined);
|
|
355
|
+
if (!method) {
|
|
356
|
+
throw new HttpError(400, 'Verification is required to disable MFA. Provide method and credentials.');
|
|
357
|
+
}
|
|
358
|
+
const valid = await MfaService.verifyAnyFactor(userId, sessionId, runtime, {
|
|
359
|
+
method,
|
|
360
|
+
code: body.code,
|
|
361
|
+
password: body.password,
|
|
362
|
+
reauthToken: body.reauthToken,
|
|
363
|
+
webauthnResponse: body.webauthnResponse,
|
|
364
|
+
});
|
|
365
|
+
if (!valid) {
|
|
366
|
+
throw new HttpError(401, 'Invalid verification');
|
|
367
|
+
}
|
|
368
|
+
if (!adapter.setMfaEnabled || !adapter.setMfaSecret || !adapter.setRecoveryCodes) {
|
|
369
|
+
throw new HttpError(501, 'Auth adapter does not support MFA');
|
|
370
|
+
}
|
|
371
|
+
await adapter.setMfaEnabled(userId, false);
|
|
372
|
+
await adapter.setMfaSecret(userId, null);
|
|
373
|
+
await adapter.setRecoveryCodes(userId, []);
|
|
374
|
+
if (adapter.setMfaMethods) {
|
|
375
|
+
await adapter.setMfaMethods(userId, []);
|
|
376
|
+
}
|
|
377
|
+
eventBus.emit('auth:mfa.disabled', { userId });
|
|
378
|
+
return c.json({ ok: true }, 200);
|
|
379
|
+
});
|
|
380
|
+
// ─── Regenerate Recovery Codes ────────────────────────────────────────────
|
|
381
|
+
router.openapi(withSecurity(createRoute({
|
|
382
|
+
method: 'post',
|
|
383
|
+
path: '/auth/mfa/recovery-codes',
|
|
384
|
+
summary: 'Regenerate recovery codes',
|
|
385
|
+
description: 'Generates new recovery codes, invalidating all previous ones. Requires a valid TOTP code to confirm.',
|
|
386
|
+
tags,
|
|
387
|
+
request: {
|
|
388
|
+
body: {
|
|
389
|
+
content: {
|
|
390
|
+
'application/json': {
|
|
391
|
+
schema: z.object({
|
|
392
|
+
code: z.string().length(6).describe('6-digit TOTP code to confirm regeneration.'),
|
|
393
|
+
}),
|
|
394
|
+
},
|
|
395
|
+
},
|
|
396
|
+
},
|
|
397
|
+
},
|
|
398
|
+
responses: {
|
|
399
|
+
200: {
|
|
400
|
+
content: {
|
|
401
|
+
'application/json': {
|
|
402
|
+
schema: z.object({
|
|
403
|
+
recoveryCodes: z.array(z.string()).describe('New one-time recovery codes.'),
|
|
404
|
+
}),
|
|
405
|
+
},
|
|
406
|
+
},
|
|
407
|
+
description: 'New recovery codes generated.',
|
|
408
|
+
},
|
|
409
|
+
401: {
|
|
410
|
+
content: { 'application/json': { schema: ErrorResponse } },
|
|
411
|
+
description: 'Invalid TOTP code or no valid session.',
|
|
412
|
+
},
|
|
413
|
+
501: {
|
|
414
|
+
content: { 'application/json': { schema: ErrorResponse } },
|
|
415
|
+
description: 'Auth adapter does not support MFA.',
|
|
416
|
+
},
|
|
417
|
+
},
|
|
418
|
+
}), { cookieAuth: [] }, { userToken: [] }), async (c) => {
|
|
419
|
+
const userId = c.get('authUserId');
|
|
420
|
+
const { code } = c.req.valid('json');
|
|
421
|
+
const recoveryCodes = await MfaService.regenerateRecoveryCodes(userId, code, runtime);
|
|
422
|
+
return c.json({ recoveryCodes }, 200);
|
|
423
|
+
});
|
|
424
|
+
// ─── Email OTP: Enable (initiate) ────────────────────────────────────────
|
|
425
|
+
router.openapi(withSecurity(createRoute({
|
|
426
|
+
method: 'post',
|
|
427
|
+
path: '/auth/mfa/email-otp/enable',
|
|
428
|
+
summary: 'Initiate email OTP setup',
|
|
429
|
+
description: "Sends a verification code to the user's email to confirm email OTP setup. Confirm via POST /auth/mfa/email-otp/verify-setup.",
|
|
430
|
+
tags,
|
|
431
|
+
responses: {
|
|
432
|
+
200: {
|
|
433
|
+
content: {
|
|
434
|
+
'application/json': {
|
|
435
|
+
schema: SetupTokenSuccessResponse,
|
|
436
|
+
},
|
|
437
|
+
},
|
|
438
|
+
description: 'Verification code sent to email.',
|
|
439
|
+
},
|
|
440
|
+
400: {
|
|
441
|
+
content: { 'application/json': { schema: ErrorResponse } },
|
|
442
|
+
description: 'No email address on account.',
|
|
443
|
+
},
|
|
444
|
+
401: {
|
|
445
|
+
content: { 'application/json': { schema: ErrorResponse } },
|
|
446
|
+
description: 'No valid session.',
|
|
447
|
+
},
|
|
448
|
+
429: {
|
|
449
|
+
content: { 'application/json': { schema: ErrorResponse } },
|
|
450
|
+
description: 'Too many initiation attempts. Try again later.',
|
|
451
|
+
},
|
|
452
|
+
501: {
|
|
453
|
+
content: { 'application/json': { schema: ErrorResponse } },
|
|
454
|
+
description: 'Email OTP is not configured.',
|
|
455
|
+
},
|
|
456
|
+
},
|
|
457
|
+
}), { cookieAuth: [] }, { userToken: [] }), async (c) => {
|
|
458
|
+
const userId = c.get('authUserId');
|
|
459
|
+
if (await runtime.rateLimit.trackAttempt(`mfa-email-otp-initiate:${userId}`, mfaEmailOtpInitiateOpts)) {
|
|
460
|
+
return c.json({ error: 'Too many initiation attempts. Try again later.' }, 429);
|
|
461
|
+
}
|
|
462
|
+
const setupToken = await MfaService.initiateEmailOtp(userId, runtime);
|
|
463
|
+
return c.json({ ok: true, setupToken }, 200);
|
|
464
|
+
});
|
|
465
|
+
// ─── Email OTP: Verify Setup ─────────────────────────────────────────────
|
|
466
|
+
router.openapi(withSecurity(createRoute({
|
|
467
|
+
method: 'post',
|
|
468
|
+
path: '/auth/mfa/email-otp/verify-setup',
|
|
469
|
+
summary: 'Confirm email OTP setup',
|
|
470
|
+
description: 'Verifies the code sent during email OTP initiation and enables email OTP as an MFA method. Returns recovery codes (new or regenerated if another MFA method was already active).',
|
|
471
|
+
tags,
|
|
472
|
+
request: {
|
|
473
|
+
body: {
|
|
474
|
+
content: {
|
|
475
|
+
'application/json': {
|
|
476
|
+
schema: z.object({
|
|
477
|
+
setupToken: z
|
|
478
|
+
.string()
|
|
479
|
+
.describe('Setup challenge token from POST /auth/mfa/email-otp/enable.'),
|
|
480
|
+
code: z.string().describe('Verification code sent to email.'),
|
|
481
|
+
}),
|
|
482
|
+
},
|
|
483
|
+
},
|
|
484
|
+
},
|
|
485
|
+
},
|
|
486
|
+
responses: {
|
|
487
|
+
200: {
|
|
488
|
+
content: {
|
|
489
|
+
'application/json': {
|
|
490
|
+
schema: SuccessResponse.extend({
|
|
491
|
+
recoveryCodes: z
|
|
492
|
+
.array(z.string())
|
|
493
|
+
.optional()
|
|
494
|
+
.describe('Recovery codes (always returned when email OTP is enabled).'),
|
|
495
|
+
}),
|
|
496
|
+
},
|
|
497
|
+
},
|
|
498
|
+
description: 'Email OTP enabled.',
|
|
499
|
+
},
|
|
500
|
+
401: {
|
|
501
|
+
content: { 'application/json': { schema: ErrorResponse } },
|
|
502
|
+
description: 'Invalid setup token or code.',
|
|
503
|
+
},
|
|
504
|
+
501: {
|
|
505
|
+
content: { 'application/json': { schema: ErrorResponse } },
|
|
506
|
+
description: 'Auth adapter does not support MFA.',
|
|
507
|
+
},
|
|
508
|
+
},
|
|
509
|
+
}), { cookieAuth: [] }, { userToken: [] }), async (c) => {
|
|
510
|
+
const userId = c.get('authUserId');
|
|
511
|
+
const { setupToken, code } = c.req.valid('json');
|
|
512
|
+
const recoveryCodes = await MfaService.confirmEmailOtp(userId, setupToken, code, runtime);
|
|
513
|
+
eventBus.emit('security.auth.mfa.setup', { userId });
|
|
514
|
+
eventBus.emit('auth:mfa.enabled', { userId, method: 'email-otp' });
|
|
515
|
+
return c.json({ ok: true, recoveryCodes: recoveryCodes ?? undefined }, 200);
|
|
516
|
+
});
|
|
517
|
+
// ─── Email OTP: Disable ──────────────────────────────────────────────────
|
|
518
|
+
router.openapi(withSecurity(createRoute({
|
|
519
|
+
method: 'delete',
|
|
520
|
+
path: '/auth/mfa/email-otp',
|
|
521
|
+
summary: 'Disable email OTP',
|
|
522
|
+
description: 'Disables email OTP for the authenticated user. Requires identity verification via TOTP, email OTP, WebAuthn, password, or recovery code.',
|
|
523
|
+
tags,
|
|
524
|
+
request: {
|
|
525
|
+
body: {
|
|
526
|
+
content: {
|
|
527
|
+
'application/json': {
|
|
528
|
+
schema: disableMfaSchema,
|
|
529
|
+
},
|
|
530
|
+
},
|
|
531
|
+
},
|
|
532
|
+
},
|
|
533
|
+
responses: {
|
|
534
|
+
200: {
|
|
535
|
+
content: { 'application/json': { schema: SuccessResponse } },
|
|
536
|
+
description: 'Email OTP disabled.',
|
|
537
|
+
},
|
|
538
|
+
400: {
|
|
539
|
+
content: { 'application/json': { schema: ErrorResponse } },
|
|
540
|
+
description: 'Missing required verification.',
|
|
541
|
+
},
|
|
542
|
+
401: {
|
|
543
|
+
content: { 'application/json': { schema: ErrorResponse } },
|
|
544
|
+
description: 'Invalid credentials or no valid session.',
|
|
545
|
+
},
|
|
546
|
+
501: {
|
|
547
|
+
content: { 'application/json': { schema: ErrorResponse } },
|
|
548
|
+
description: 'Auth adapter does not support MFA.',
|
|
549
|
+
},
|
|
550
|
+
},
|
|
551
|
+
}), { cookieAuth: [] }, { userToken: [] }), async (c) => {
|
|
552
|
+
const userId = c.get('authUserId');
|
|
553
|
+
const sessionId = c.get('sessionId');
|
|
554
|
+
const body = c.req.valid('json');
|
|
555
|
+
const method = body.method ?? (body.password ? 'password' : body.code ? 'totp' : undefined);
|
|
556
|
+
if (!method) {
|
|
557
|
+
throw new HttpError(400, 'Verification is required. Provide method and credentials.');
|
|
558
|
+
}
|
|
559
|
+
const valid = await MfaService.verifyAnyFactor(userId, sessionId, runtime, {
|
|
560
|
+
method,
|
|
561
|
+
code: body.code,
|
|
562
|
+
password: body.password,
|
|
563
|
+
reauthToken: body.reauthToken,
|
|
564
|
+
webauthnResponse: body.webauthnResponse,
|
|
565
|
+
});
|
|
566
|
+
if (!valid) {
|
|
567
|
+
throw new HttpError(401, 'Invalid verification');
|
|
568
|
+
}
|
|
569
|
+
// Remove "emailOtp" from methods
|
|
570
|
+
if (!adapter.setMfaEnabled)
|
|
571
|
+
return c.json({ error: 'Auth adapter does not support MFA' }, 501);
|
|
572
|
+
const methods = adapter.getMfaMethods ? await adapter.getMfaMethods(userId) : [];
|
|
573
|
+
if (adapter.setMfaMethods) {
|
|
574
|
+
const updated = methods.filter(m => m !== 'emailOtp');
|
|
575
|
+
await adapter.setMfaMethods(userId, updated);
|
|
576
|
+
if (updated.length === 0) {
|
|
577
|
+
await adapter.setMfaEnabled(userId, false);
|
|
578
|
+
if (adapter.setRecoveryCodes)
|
|
579
|
+
await adapter.setRecoveryCodes(userId, []);
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
eventBus.emit('auth:mfa.disabled', { userId, method: 'email-otp' });
|
|
583
|
+
return c.json({ ok: true }, 200);
|
|
584
|
+
});
|
|
585
|
+
// ─── Resend Email OTP ────────────────────────────────────────────────────
|
|
586
|
+
router.openapi(createRoute({
|
|
587
|
+
method: 'post',
|
|
588
|
+
path: '/auth/mfa/resend',
|
|
589
|
+
summary: 'Resend email OTP code',
|
|
590
|
+
description: 'Generates and sends a new email OTP code for the given MFA challenge. Rate-limited to 3 resends per challenge. Does not extend the challenge beyond 3x the original TTL.',
|
|
591
|
+
tags,
|
|
592
|
+
request: {
|
|
593
|
+
body: {
|
|
594
|
+
content: {
|
|
595
|
+
'application/json': {
|
|
596
|
+
schema: z.object({
|
|
597
|
+
mfaToken: z.string().describe('MFA challenge token from the login response.'),
|
|
598
|
+
}),
|
|
599
|
+
},
|
|
600
|
+
},
|
|
601
|
+
},
|
|
602
|
+
},
|
|
603
|
+
responses: {
|
|
604
|
+
200: {
|
|
605
|
+
content: { 'application/json': { schema: SuccessResponse } },
|
|
606
|
+
description: 'Code sent.',
|
|
607
|
+
},
|
|
608
|
+
400: {
|
|
609
|
+
content: { 'application/json': { schema: ErrorResponse } },
|
|
610
|
+
description: 'Email OTP not configured.',
|
|
611
|
+
},
|
|
612
|
+
401: {
|
|
613
|
+
content: { 'application/json': { schema: ErrorResponse } },
|
|
614
|
+
description: 'Invalid or expired MFA token.',
|
|
615
|
+
},
|
|
616
|
+
429: {
|
|
617
|
+
content: { 'application/json': { schema: ErrorResponse } },
|
|
618
|
+
description: 'Maximum resends reached.',
|
|
619
|
+
},
|
|
620
|
+
},
|
|
621
|
+
}), async (c) => {
|
|
622
|
+
const ip = getClientIp(c);
|
|
623
|
+
if (await runtime.rateLimit.trackAttempt(`mfa-resend:${ip}`, mfaResendOpts)) {
|
|
624
|
+
return c.json({ error: 'Too many resend attempts. Try again later.' }, 429);
|
|
625
|
+
}
|
|
626
|
+
const { mfaToken } = c.req.valid('json');
|
|
627
|
+
const emailOtpConfig = getConfig().mfa?.emailOtp ?? null;
|
|
628
|
+
if (!emailOtpConfig)
|
|
629
|
+
return c.json({ error: 'Email OTP is not configured' }, 400);
|
|
630
|
+
const { code, hash } = MfaService.generateEmailOtpCode(runtime);
|
|
631
|
+
const result = await replaceMfaChallengeOtp(runtime.repos.mfaChallenge, mfaToken, hash, runtime.config);
|
|
632
|
+
if (!result)
|
|
633
|
+
return c.json({ error: 'Invalid/expired MFA token or maximum resends reached' }, 401);
|
|
634
|
+
// Get user email and send
|
|
635
|
+
const user = adapter.getUser ? await adapter.getUser(result.userId) : null;
|
|
636
|
+
if (user?.email) {
|
|
637
|
+
eventBus.emit('auth:delivery.email_otp', { email: user.email, code });
|
|
638
|
+
}
|
|
639
|
+
return c.json({ ok: true }, 200);
|
|
640
|
+
});
|
|
641
|
+
// ─── Get MFA Methods ────────────────────────────────────────────────────
|
|
642
|
+
router.openapi(withSecurity(createRoute({
|
|
643
|
+
method: 'get',
|
|
644
|
+
path: '/auth/mfa/methods',
|
|
645
|
+
summary: 'Get enabled MFA methods',
|
|
646
|
+
description: 'Returns the MFA methods currently enabled for the authenticated user.',
|
|
647
|
+
tags,
|
|
648
|
+
responses: {
|
|
649
|
+
200: {
|
|
650
|
+
content: {
|
|
651
|
+
'application/json': {
|
|
652
|
+
schema: z.object({
|
|
653
|
+
methods: z
|
|
654
|
+
.array(z.string())
|
|
655
|
+
.describe("Enabled MFA methods (e.g., 'totp', 'emailOtp')."),
|
|
656
|
+
}),
|
|
657
|
+
},
|
|
658
|
+
},
|
|
659
|
+
description: 'Enabled MFA methods.',
|
|
660
|
+
},
|
|
661
|
+
401: {
|
|
662
|
+
content: { 'application/json': { schema: ErrorResponse } },
|
|
663
|
+
description: 'No valid session.',
|
|
664
|
+
},
|
|
665
|
+
},
|
|
666
|
+
}), { cookieAuth: [] }, { userToken: [] }), async (c) => {
|
|
667
|
+
const userId = c.get('authUserId');
|
|
668
|
+
const methods = await MfaService.getMfaMethods(userId, runtime);
|
|
669
|
+
return c.json({ methods }, 200);
|
|
670
|
+
});
|
|
671
|
+
// ─── WebAuthn / Security Keys ─────────────────────────────────────────────
|
|
672
|
+
if (getConfig().mfa?.webauthn) {
|
|
673
|
+
// Eager dependency check — fail fast at server start
|
|
674
|
+
MfaService.assertWebAuthnDependency().catch(err => {
|
|
675
|
+
throw err;
|
|
676
|
+
});
|
|
677
|
+
router.use('/auth/mfa/webauthn/*', userAuth);
|
|
678
|
+
// Register options
|
|
679
|
+
router.openapi(withSecurity(createRoute({
|
|
680
|
+
method: 'post',
|
|
681
|
+
path: '/auth/mfa/webauthn/register-options',
|
|
682
|
+
summary: 'Generate WebAuthn registration options',
|
|
683
|
+
description: 'Generates registration options for the client to pass to navigator.credentials.create(). Returns a registrationToken to confirm registration.',
|
|
684
|
+
tags,
|
|
685
|
+
responses: {
|
|
686
|
+
200: {
|
|
687
|
+
content: {
|
|
688
|
+
'application/json': {
|
|
689
|
+
schema: z.object({
|
|
690
|
+
options: z
|
|
691
|
+
.record(z.string(), z.unknown())
|
|
692
|
+
.describe('PublicKeyCredentialCreationOptions — pass directly to navigator.credentials.create().'),
|
|
693
|
+
registrationToken: z
|
|
694
|
+
.string()
|
|
695
|
+
.describe('Token to pass back when completing registration.'),
|
|
696
|
+
}),
|
|
697
|
+
},
|
|
698
|
+
},
|
|
699
|
+
description: 'Registration options generated.',
|
|
700
|
+
},
|
|
701
|
+
401: {
|
|
702
|
+
content: { 'application/json': { schema: ErrorResponse } },
|
|
703
|
+
description: 'No valid session.',
|
|
704
|
+
},
|
|
705
|
+
501: {
|
|
706
|
+
content: { 'application/json': { schema: ErrorResponse } },
|
|
707
|
+
description: 'WebAuthn not configured or adapter does not support it.',
|
|
708
|
+
},
|
|
709
|
+
},
|
|
710
|
+
}), { cookieAuth: [] }, { userToken: [] }), async (c) => {
|
|
711
|
+
const userId = c.get('authUserId');
|
|
712
|
+
const result = await MfaService.initiateWebAuthnRegistration(userId, runtime);
|
|
713
|
+
return c.json(result, 200);
|
|
714
|
+
});
|
|
715
|
+
// Complete registration
|
|
716
|
+
router.openapi(withSecurity(createRoute({
|
|
717
|
+
method: 'post',
|
|
718
|
+
path: '/auth/mfa/webauthn/register',
|
|
719
|
+
summary: 'Complete WebAuthn registration',
|
|
720
|
+
description: 'Verifies the attestation response from navigator.credentials.create() and stores the credential. Returns recovery codes.',
|
|
721
|
+
tags,
|
|
722
|
+
request: {
|
|
723
|
+
body: {
|
|
724
|
+
content: {
|
|
725
|
+
'application/json': {
|
|
726
|
+
schema: z.object({
|
|
727
|
+
registrationToken: z
|
|
728
|
+
.string()
|
|
729
|
+
.describe('Token from POST /auth/mfa/webauthn/register-options.'),
|
|
730
|
+
attestationResponse: z
|
|
731
|
+
.record(z.string(), z.unknown())
|
|
732
|
+
.describe('Full response from navigator.credentials.create().'),
|
|
733
|
+
name: z
|
|
734
|
+
.string()
|
|
735
|
+
.optional()
|
|
736
|
+
.describe("User-friendly name for the key (e.g. 'YubiKey 5')."),
|
|
737
|
+
}),
|
|
738
|
+
},
|
|
739
|
+
},
|
|
740
|
+
},
|
|
741
|
+
},
|
|
742
|
+
responses: {
|
|
743
|
+
200: {
|
|
744
|
+
content: {
|
|
745
|
+
'application/json': {
|
|
746
|
+
schema: WebAuthnRegistrationSuccessResponse,
|
|
747
|
+
},
|
|
748
|
+
},
|
|
749
|
+
description: 'Security key registered.',
|
|
750
|
+
},
|
|
751
|
+
401: {
|
|
752
|
+
content: { 'application/json': { schema: ErrorResponse } },
|
|
753
|
+
description: 'Invalid registration token or verification failed.',
|
|
754
|
+
},
|
|
755
|
+
409: {
|
|
756
|
+
content: { 'application/json': { schema: ErrorResponse } },
|
|
757
|
+
description: 'Security key already registered to another account.',
|
|
758
|
+
},
|
|
759
|
+
501: {
|
|
760
|
+
content: { 'application/json': { schema: ErrorResponse } },
|
|
761
|
+
description: 'WebAuthn not configured or adapter does not support it.',
|
|
762
|
+
},
|
|
763
|
+
},
|
|
764
|
+
}), { cookieAuth: [] }, { userToken: [] }), async (c) => {
|
|
765
|
+
const userId = c.get('authUserId');
|
|
766
|
+
const { registrationToken, attestationResponse, name } = c.req.valid('json');
|
|
767
|
+
const result = await MfaService.completeWebAuthnRegistration(userId, registrationToken, attestationResponse, runtime, name);
|
|
768
|
+
eventBus.emit('security.auth.mfa.setup', { userId });
|
|
769
|
+
eventBus.emit('auth:mfa.enabled', { userId, method: 'webauthn' });
|
|
770
|
+
return c.json({ ok: true, ...result }, 200);
|
|
771
|
+
});
|
|
772
|
+
// List credentials
|
|
773
|
+
router.openapi(withSecurity(createRoute({
|
|
774
|
+
method: 'get',
|
|
775
|
+
path: '/auth/mfa/webauthn/credentials',
|
|
776
|
+
summary: 'List WebAuthn credentials',
|
|
777
|
+
description: 'Returns the security keys registered for the authenticated user. Does not include private key data.',
|
|
778
|
+
tags,
|
|
779
|
+
responses: {
|
|
780
|
+
200: {
|
|
781
|
+
content: {
|
|
782
|
+
'application/json': {
|
|
783
|
+
schema: z.object({
|
|
784
|
+
credentials: z.array(z.object({
|
|
785
|
+
credentialId: z.string(),
|
|
786
|
+
name: z.string().optional(),
|
|
787
|
+
createdAt: z.number(),
|
|
788
|
+
transports: z.array(z.string()).optional(),
|
|
789
|
+
})),
|
|
790
|
+
}),
|
|
791
|
+
},
|
|
792
|
+
},
|
|
793
|
+
description: 'List of registered security keys.',
|
|
794
|
+
},
|
|
795
|
+
401: {
|
|
796
|
+
content: { 'application/json': { schema: ErrorResponse } },
|
|
797
|
+
description: 'No valid session.',
|
|
798
|
+
},
|
|
799
|
+
},
|
|
800
|
+
}), { cookieAuth: [] }, { userToken: [] }), async (c) => {
|
|
801
|
+
const userId = c.get('authUserId');
|
|
802
|
+
const creds = adapter.getWebAuthnCredentials
|
|
803
|
+
? await adapter.getWebAuthnCredentials(userId)
|
|
804
|
+
: [];
|
|
805
|
+
return c.json({
|
|
806
|
+
credentials: creds.map(cr => ({
|
|
807
|
+
credentialId: cr.credentialId,
|
|
808
|
+
name: cr.name,
|
|
809
|
+
createdAt: cr.createdAt,
|
|
810
|
+
transports: cr.transports,
|
|
811
|
+
})),
|
|
812
|
+
}, 200);
|
|
813
|
+
});
|
|
814
|
+
// Remove a single credential
|
|
815
|
+
router.openapi(withSecurity(createRoute({
|
|
816
|
+
method: 'delete',
|
|
817
|
+
path: '/auth/mfa/webauthn/credentials/{credentialId}',
|
|
818
|
+
summary: 'Remove a WebAuthn credential',
|
|
819
|
+
description: 'Removes a single security key. Identity verification is only required when removing the last MFA credential.',
|
|
820
|
+
tags,
|
|
821
|
+
request: {
|
|
822
|
+
params: z.object({ credentialId: z.string() }),
|
|
823
|
+
body: {
|
|
824
|
+
content: {
|
|
825
|
+
'application/json': {
|
|
826
|
+
schema: z.object({
|
|
827
|
+
method: z
|
|
828
|
+
.enum(['totp', 'emailOtp', 'webauthn', 'password', 'recovery'])
|
|
829
|
+
.optional()
|
|
830
|
+
.describe('Verification method (required when removing the last MFA credential).'),
|
|
831
|
+
code: z
|
|
832
|
+
.string()
|
|
833
|
+
.optional()
|
|
834
|
+
.describe('TOTP code, email OTP code, or recovery code.'),
|
|
835
|
+
password: z
|
|
836
|
+
.string()
|
|
837
|
+
.optional()
|
|
838
|
+
.describe("Account password (for method: 'password')."),
|
|
839
|
+
reauthToken: z
|
|
840
|
+
.string()
|
|
841
|
+
.optional()
|
|
842
|
+
.describe('Reauth challenge token (for emailOtp and webauthn methods).'),
|
|
843
|
+
webauthnResponse: z
|
|
844
|
+
.record(z.string(), z.unknown())
|
|
845
|
+
.optional()
|
|
846
|
+
.describe('WebAuthn authentication response.'),
|
|
847
|
+
}),
|
|
848
|
+
},
|
|
849
|
+
},
|
|
850
|
+
},
|
|
851
|
+
},
|
|
852
|
+
responses: {
|
|
853
|
+
200: {
|
|
854
|
+
content: { 'application/json': { schema: SuccessResponse } },
|
|
855
|
+
description: 'Credential removed.',
|
|
856
|
+
},
|
|
857
|
+
400: {
|
|
858
|
+
content: { 'application/json': { schema: ErrorResponse } },
|
|
859
|
+
description: 'Missing required verification.',
|
|
860
|
+
},
|
|
861
|
+
401: {
|
|
862
|
+
content: { 'application/json': { schema: ErrorResponse } },
|
|
863
|
+
description: 'Invalid credentials or no valid session.',
|
|
864
|
+
},
|
|
865
|
+
404: {
|
|
866
|
+
content: { 'application/json': { schema: ErrorResponse } },
|
|
867
|
+
description: 'Credential not found.',
|
|
868
|
+
},
|
|
869
|
+
501: {
|
|
870
|
+
content: { 'application/json': { schema: ErrorResponse } },
|
|
871
|
+
description: 'Adapter does not support WebAuthn.',
|
|
872
|
+
},
|
|
873
|
+
},
|
|
874
|
+
}), { cookieAuth: [] }, { userToken: [] }), async (c) => {
|
|
875
|
+
const userId = c.get('authUserId');
|
|
876
|
+
const sessionId = c.get('sessionId');
|
|
877
|
+
const { credentialId } = c.req.valid('param');
|
|
878
|
+
const { method, code, password, reauthToken, webauthnResponse } = c.req.valid('json');
|
|
879
|
+
// If method is provided, pre-verify before delegating
|
|
880
|
+
if (method) {
|
|
881
|
+
const valid = await MfaService.verifyAnyFactor(userId, sessionId, runtime, {
|
|
882
|
+
method,
|
|
883
|
+
code,
|
|
884
|
+
password,
|
|
885
|
+
reauthToken,
|
|
886
|
+
webauthnResponse: webauthnResponse,
|
|
887
|
+
});
|
|
888
|
+
if (!valid)
|
|
889
|
+
return c.json({ error: 'Invalid credentials' }, 401);
|
|
890
|
+
// Delegate without re-verifying (pass empty params to skip internal verifyIdentity)
|
|
891
|
+
if (!adapter.getWebAuthnCredentials || !adapter.removeWebAuthnCredential) {
|
|
892
|
+
return c.json({ error: 'Adapter does not support WebAuthn' }, 501);
|
|
893
|
+
}
|
|
894
|
+
const credentials = await adapter.getWebAuthnCredentials(userId);
|
|
895
|
+
if (!credentials.some(cr => cr.credentialId === credentialId)) {
|
|
896
|
+
return c.json({ error: 'Credential not found' }, 404);
|
|
897
|
+
}
|
|
898
|
+
await adapter.removeWebAuthnCredential(userId, credentialId);
|
|
899
|
+
const remaining = credentials.filter(cr => cr.credentialId !== credentialId);
|
|
900
|
+
if (remaining.length === 0 && adapter.setMfaMethods) {
|
|
901
|
+
const methods = adapter.getMfaMethods ? await adapter.getMfaMethods(userId) : [];
|
|
902
|
+
const updated = methods.filter(m => m !== 'webauthn');
|
|
903
|
+
await adapter.setMfaMethods(userId, updated);
|
|
904
|
+
if (updated.length === 0 && adapter.setMfaEnabled) {
|
|
905
|
+
await adapter.setMfaEnabled(userId, false);
|
|
906
|
+
if (adapter.setRecoveryCodes)
|
|
907
|
+
await adapter.setRecoveryCodes(userId, []);
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
else {
|
|
912
|
+
// No verification needed (not removing last credential)
|
|
913
|
+
await MfaService.removeWebAuthnCredential(userId, credentialId, { code, password }, runtime);
|
|
914
|
+
}
|
|
915
|
+
return c.json({ ok: true }, 200);
|
|
916
|
+
});
|
|
917
|
+
// Disable WebAuthn entirely
|
|
918
|
+
router.openapi(withSecurity(createRoute({
|
|
919
|
+
method: 'delete',
|
|
920
|
+
path: '/auth/mfa/webauthn',
|
|
921
|
+
summary: 'Disable WebAuthn MFA',
|
|
922
|
+
description: 'Removes all WebAuthn credentials and disables WebAuthn as an MFA method. Requires identity verification via TOTP, email OTP, WebAuthn, password, or recovery code.',
|
|
923
|
+
tags,
|
|
924
|
+
request: {
|
|
925
|
+
body: {
|
|
926
|
+
content: {
|
|
927
|
+
'application/json': {
|
|
928
|
+
schema: disableMfaSchema,
|
|
929
|
+
},
|
|
930
|
+
},
|
|
931
|
+
},
|
|
932
|
+
},
|
|
933
|
+
responses: {
|
|
934
|
+
200: {
|
|
935
|
+
content: { 'application/json': { schema: SuccessResponse } },
|
|
936
|
+
description: 'WebAuthn disabled.',
|
|
937
|
+
},
|
|
938
|
+
400: {
|
|
939
|
+
content: { 'application/json': { schema: ErrorResponse } },
|
|
940
|
+
description: 'Missing required verification.',
|
|
941
|
+
},
|
|
942
|
+
401: {
|
|
943
|
+
content: { 'application/json': { schema: ErrorResponse } },
|
|
944
|
+
description: 'Invalid credentials or no valid session.',
|
|
945
|
+
},
|
|
946
|
+
501: {
|
|
947
|
+
content: { 'application/json': { schema: ErrorResponse } },
|
|
948
|
+
description: 'Adapter does not support WebAuthn.',
|
|
949
|
+
},
|
|
950
|
+
},
|
|
951
|
+
}), { cookieAuth: [] }, { userToken: [] }), async (c) => {
|
|
952
|
+
const userId = c.get('authUserId');
|
|
953
|
+
const sessionId = c.get('sessionId');
|
|
954
|
+
const body = c.req.valid('json');
|
|
955
|
+
const method = body.method ?? (body.password ? 'password' : body.code ? 'totp' : undefined);
|
|
956
|
+
if (!method) {
|
|
957
|
+
throw new HttpError(400, 'Verification is required. Provide method and credentials.');
|
|
958
|
+
}
|
|
959
|
+
const valid = await MfaService.verifyAnyFactor(userId, sessionId, runtime, {
|
|
960
|
+
method,
|
|
961
|
+
code: body.code,
|
|
962
|
+
password: body.password,
|
|
963
|
+
reauthToken: body.reauthToken,
|
|
964
|
+
webauthnResponse: body.webauthnResponse,
|
|
965
|
+
});
|
|
966
|
+
if (!valid) {
|
|
967
|
+
throw new HttpError(401, 'Invalid verification');
|
|
968
|
+
}
|
|
969
|
+
if (!adapter.getWebAuthnCredentials || !adapter.removeWebAuthnCredential) {
|
|
970
|
+
throw new HttpError(501, 'Auth adapter does not support WebAuthn');
|
|
971
|
+
}
|
|
972
|
+
const credentials = await adapter.getWebAuthnCredentials(userId);
|
|
973
|
+
for (const cred of credentials) {
|
|
974
|
+
await adapter.removeWebAuthnCredential(userId, cred.credentialId);
|
|
975
|
+
}
|
|
976
|
+
// Remove "webauthn" from methods
|
|
977
|
+
if (adapter.getMfaMethods && adapter.setMfaMethods) {
|
|
978
|
+
const methods = await adapter.getMfaMethods(userId);
|
|
979
|
+
const updated = methods.filter(m => m !== 'webauthn');
|
|
980
|
+
await adapter.setMfaMethods(userId, updated);
|
|
981
|
+
if (updated.length === 0 && adapter.setMfaEnabled) {
|
|
982
|
+
await adapter.setMfaEnabled(userId, false);
|
|
983
|
+
if (adapter.setRecoveryCodes)
|
|
984
|
+
await adapter.setRecoveryCodes(userId, []);
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
return c.json({ ok: true }, 200);
|
|
988
|
+
});
|
|
989
|
+
}
|
|
990
|
+
return router;
|
|
991
|
+
};
|