@techelusive/nest-auth 0.0.0-stage → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +64 -0
- package/LICENSE +21 -0
- package/README.md +204 -2
- package/api/openapi.json +4399 -0
- package/api/techelusive-authentication.postman_collection.json +3178 -0
- package/dist/auth-options.d.ts +156 -0
- package/dist/auth-options.js +3 -0
- package/dist/auth-options.js.map +1 -0
- package/dist/auth-options.validation.d.ts +9 -0
- package/dist/auth-options.validation.js +74 -0
- package/dist/auth-options.validation.js.map +1 -0
- package/dist/auth.module.d.ts +38 -0
- package/dist/auth.module.js +173 -0
- package/dist/auth.module.js.map +1 -0
- package/dist/common/auth-error.d.ts +16 -0
- package/dist/common/auth-error.js +23 -0
- package/dist/common/auth-error.js.map +1 -0
- package/dist/common/auth-exception.filter.d.ts +24 -0
- package/dist/common/auth-exception.filter.js +66 -0
- package/dist/common/auth-exception.filter.js.map +1 -0
- package/dist/common/crypto/jwt.d.ts +89 -0
- package/dist/common/crypto/jwt.js +144 -0
- package/dist/common/crypto/jwt.js.map +1 -0
- package/dist/common/crypto/random-secrets.d.ts +78 -0
- package/dist/common/crypto/random-secrets.js +113 -0
- package/dist/common/crypto/random-secrets.js.map +1 -0
- package/dist/common/crypto/remote-public-keys.d.ts +49 -0
- package/dist/common/crypto/remote-public-keys.js +105 -0
- package/dist/common/crypto/remote-public-keys.js.map +1 -0
- package/dist/common/crypto/totp.d.ts +68 -0
- package/dist/common/crypto/totp.js +148 -0
- package/dist/common/crypto/totp.js.map +1 -0
- package/dist/common/encryption.service.d.ts +36 -0
- package/dist/common/encryption.service.js +111 -0
- package/dist/common/encryption.service.js.map +1 -0
- package/dist/common/identifiers.d.ts +48 -0
- package/dist/common/identifiers.js +89 -0
- package/dist/common/identifiers.js.map +1 -0
- package/dist/common/network-safety.d.ts +19 -0
- package/dist/common/network-safety.js +72 -0
- package/dist/common/network-safety.js.map +1 -0
- package/dist/common/rate-limiter.service.d.ts +63 -0
- package/dist/common/rate-limiter.service.js +158 -0
- package/dist/common/rate-limiter.service.js.map +1 -0
- package/dist/common/refresh-token-cookie.d.ts +78 -0
- package/dist/common/refresh-token-cookie.js +185 -0
- package/dist/common/refresh-token-cookie.js.map +1 -0
- package/dist/common/request-context.d.ts +14 -0
- package/dist/common/request-context.js +22 -0
- package/dist/common/request-context.js.map +1 -0
- package/dist/common/strict-validation.pipe.d.ts +8 -0
- package/dist/common/strict-validation.pipe.js +16 -0
- package/dist/common/strict-validation.pipe.js.map +1 -0
- package/dist/common/time.d.ts +15 -0
- package/dist/common/time.js +34 -0
- package/dist/common/time.js.map +1 -0
- package/dist/constants.d.ts +1326 -0
- package/dist/constants.js +1142 -0
- package/dist/constants.js.map +1 -0
- package/dist/database/create-auth-tables.migration.d.ts +26 -0
- package/dist/database/create-auth-tables.migration.js +291 -0
- package/dist/database/create-auth-tables.migration.js.map +1 -0
- package/dist/database/email-change-and-retention.migration.d.ts +25 -0
- package/dist/database/email-change-and-retention.migration.js +54 -0
- package/dist/database/email-change-and-retention.migration.js.map +1 -0
- package/dist/database/entities/api-key.entity.d.ts +17 -0
- package/dist/database/entities/api-key.entity.js +80 -0
- package/dist/database/entities/api-key.entity.js.map +1 -0
- package/dist/database/entities/audit-log.entity.d.ts +13 -0
- package/dist/database/entities/audit-log.entity.js +64 -0
- package/dist/database/entities/audit-log.entity.js.map +1 -0
- package/dist/database/entities/backup-code.entity.d.ts +9 -0
- package/dist/database/entities/backup-code.entity.js +52 -0
- package/dist/database/entities/backup-code.entity.js.map +1 -0
- package/dist/database/entities/index.d.ts +43 -0
- package/dist/database/entities/index.js +82 -0
- package/dist/database/entities/index.js.map +1 -0
- package/dist/database/entities/json-object.d.ts +12 -0
- package/dist/database/entities/json-object.js +3 -0
- package/dist/database/entities/json-object.js.map +1 -0
- package/dist/database/entities/link-token.entity.d.ts +15 -0
- package/dist/database/entities/link-token.entity.js +64 -0
- package/dist/database/entities/link-token.entity.js.map +1 -0
- package/dist/database/entities/one-time-code.entity.d.ts +16 -0
- package/dist/database/entities/one-time-code.entity.js +69 -0
- package/dist/database/entities/one-time-code.entity.js.map +1 -0
- package/dist/database/entities/organization-invitation.entity.d.ts +14 -0
- package/dist/database/entities/organization-invitation.entity.js +73 -0
- package/dist/database/entities/organization-invitation.entity.js.map +1 -0
- package/dist/database/entities/organization-member.entity.d.ts +9 -0
- package/dist/database/entities/organization-member.entity.js +48 -0
- package/dist/database/entities/organization-member.entity.js.map +1 -0
- package/dist/database/entities/organization.entity.d.ts +12 -0
- package/dist/database/entities/organization.entity.js +58 -0
- package/dist/database/entities/organization.entity.js.map +1 -0
- package/dist/database/entities/passkey.entity.d.ts +19 -0
- package/dist/database/entities/passkey.entity.js +78 -0
- package/dist/database/entities/passkey.entity.js.map +1 -0
- package/dist/database/entities/password.entity.d.ts +6 -0
- package/dist/database/entities/password.entity.js +36 -0
- package/dist/database/entities/password.entity.js.map +1 -0
- package/dist/database/entities/refresh-token.entity.d.ts +9 -0
- package/dist/database/entities/refresh-token.entity.js +53 -0
- package/dist/database/entities/refresh-token.entity.js.map +1 -0
- package/dist/database/entities/session.entity.d.ts +20 -0
- package/dist/database/entities/session.entity.js +87 -0
- package/dist/database/entities/session.entity.js.map +1 -0
- package/dist/database/entities/settings.entity.d.ts +7 -0
- package/dist/database/entities/settings.entity.js +41 -0
- package/dist/database/entities/settings.entity.js.map +1 -0
- package/dist/database/entities/sign-in-challenge.entity.d.ts +23 -0
- package/dist/database/entities/sign-in-challenge.entity.js +80 -0
- package/dist/database/entities/sign-in-challenge.entity.js.map +1 -0
- package/dist/database/entities/signing-key.entity.d.ts +11 -0
- package/dist/database/entities/signing-key.entity.js +54 -0
- package/dist/database/entities/signing-key.entity.js.map +1 -0
- package/dist/database/entities/social-account.entity.d.ts +13 -0
- package/dist/database/entities/social-account.entity.js +54 -0
- package/dist/database/entities/social-account.entity.js.map +1 -0
- package/dist/database/entities/two-factor-secret.entity.d.ts +10 -0
- package/dist/database/entities/two-factor-secret.entity.js +48 -0
- package/dist/database/entities/two-factor-secret.entity.js.map +1 -0
- package/dist/database/entities/user.entity.d.ts +34 -0
- package/dist/database/entities/user.entity.js +129 -0
- package/dist/database/entities/user.entity.js.map +1 -0
- package/dist/database/entities/webhook-delivery.entity.d.ts +17 -0
- package/dist/database/entities/webhook-delivery.entity.js +79 -0
- package/dist/database/entities/webhook-delivery.entity.js.map +1 -0
- package/dist/database/entities/webhook.entity.d.ts +12 -0
- package/dist/database/entities/webhook.entity.js +58 -0
- package/dist/database/entities/webhook.entity.js.map +1 -0
- package/dist/database/typeorm-row.d.ts +21 -0
- package/dist/database/typeorm-row.js +29 -0
- package/dist/database/typeorm-row.js.map +1 -0
- package/dist/features/account/account.controller.d.ts +259 -0
- package/dist/features/account/account.controller.js +372 -0
- package/dist/features/account/account.controller.js.map +1 -0
- package/dist/features/account/account.dto.d.ts +16 -0
- package/dist/features/account/account.dto.js +61 -0
- package/dist/features/account/account.dto.js.map +1 -0
- package/dist/features/account/data-export.service.d.ts +85 -0
- package/dist/features/account/data-export.service.js +125 -0
- package/dist/features/account/data-export.service.js.map +1 -0
- package/dist/features/account/health.service.d.ts +30 -0
- package/dist/features/account/health.service.js +59 -0
- package/dist/features/account/health.service.js.map +1 -0
- package/dist/features/admin/admin.controller.d.ts +382 -0
- package/dist/features/admin/admin.controller.js +623 -0
- package/dist/features/admin/admin.controller.js.map +1 -0
- package/dist/features/admin/admin.dto.d.ts +61 -0
- package/dist/features/admin/admin.dto.js +253 -0
- package/dist/features/admin/admin.dto.js.map +1 -0
- package/dist/features/admin/admin.service.d.ts +142 -0
- package/dist/features/admin/admin.service.js +227 -0
- package/dist/features/admin/admin.service.js.map +1 -0
- package/dist/features/api-keys/api-keys.service.d.ts +67 -0
- package/dist/features/api-keys/api-keys.service.js +144 -0
- package/dist/features/api-keys/api-keys.service.js.map +1 -0
- package/dist/features/email-change/email-change.controller.d.ts +38 -0
- package/dist/features/email-change/email-change.controller.js +85 -0
- package/dist/features/email-change/email-change.controller.js.map +1 -0
- package/dist/features/email-change/email-change.dto.d.ts +10 -0
- package/dist/features/email-change/email-change.dto.js +43 -0
- package/dist/features/email-change/email-change.dto.js.map +1 -0
- package/dist/features/email-change/email-change.service.d.ts +74 -0
- package/dist/features/email-change/email-change.service.js +179 -0
- package/dist/features/email-change/email-change.service.js.map +1 -0
- package/dist/features/email-links/email-verification.service.d.ts +46 -0
- package/dist/features/email-links/email-verification.service.js +94 -0
- package/dist/features/email-links/email-verification.service.js.map +1 -0
- package/dist/features/email-links/link-tokens.service.d.ts +44 -0
- package/dist/features/email-links/link-tokens.service.js +122 -0
- package/dist/features/email-links/link-tokens.service.js.map +1 -0
- package/dist/features/events/auth-events.service.d.ts +52 -0
- package/dist/features/events/auth-events.service.js +117 -0
- package/dist/features/events/auth-events.service.js.map +1 -0
- package/dist/features/maintenance/data-retention.service.d.ts +60 -0
- package/dist/features/maintenance/data-retention.service.js +182 -0
- package/dist/features/maintenance/data-retention.service.js.map +1 -0
- package/dist/features/notifications/notifications.service.d.ts +40 -0
- package/dist/features/notifications/notifications.service.js +94 -0
- package/dist/features/notifications/notifications.service.js.map +1 -0
- package/dist/features/one-time-codes/one-time-codes.service.d.ts +40 -0
- package/dist/features/one-time-codes/one-time-codes.service.js +102 -0
- package/dist/features/one-time-codes/one-time-codes.service.js.map +1 -0
- package/dist/features/organizations/organizations.controller.d.ts +220 -0
- package/dist/features/organizations/organizations.controller.js +337 -0
- package/dist/features/organizations/organizations.controller.js.map +1 -0
- package/dist/features/organizations/organizations.dto.d.ts +31 -0
- package/dist/features/organizations/organizations.dto.js +100 -0
- package/dist/features/organizations/organizations.dto.js.map +1 -0
- package/dist/features/organizations/organizations.service.d.ts +274 -0
- package/dist/features/organizations/organizations.service.js +481 -0
- package/dist/features/organizations/organizations.service.js.map +1 -0
- package/dist/features/passkeys/passkeys.controller.d.ts +92 -0
- package/dist/features/passkeys/passkeys.controller.js +183 -0
- package/dist/features/passkeys/passkeys.controller.js.map +1 -0
- package/dist/features/passkeys/passkeys.dto.d.ts +13 -0
- package/dist/features/passkeys/passkeys.dto.js +50 -0
- package/dist/features/passkeys/passkeys.dto.js.map +1 -0
- package/dist/features/passkeys/passkeys.service.d.ts +159 -0
- package/dist/features/passkeys/passkeys.service.js +350 -0
- package/dist/features/passkeys/passkeys.service.js.map +1 -0
- package/dist/features/passwordless/passwordless-auth.service.d.ts +152 -0
- package/dist/features/passwordless/passwordless-auth.service.js +304 -0
- package/dist/features/passwordless/passwordless-auth.service.js.map +1 -0
- package/dist/features/passwordless/passwordless.controller.d.ts +75 -0
- package/dist/features/passwordless/passwordless.controller.js +163 -0
- package/dist/features/passwordless/passwordless.controller.js.map +1 -0
- package/dist/features/passwordless/passwordless.dto.d.ts +29 -0
- package/dist/features/passwordless/passwordless.dto.js +101 -0
- package/dist/features/passwordless/passwordless.dto.js.map +1 -0
- package/dist/features/passwords/password-auth.service.d.ts +154 -0
- package/dist/features/passwords/password-auth.service.js +317 -0
- package/dist/features/passwords/password-auth.service.js.map +1 -0
- package/dist/features/passwords/password-policy.service.d.ts +63 -0
- package/dist/features/passwords/password-policy.service.js +175 -0
- package/dist/features/passwords/password-policy.service.js.map +1 -0
- package/dist/features/passwords/password.controller.d.ts +95 -0
- package/dist/features/passwords/password.controller.js +193 -0
- package/dist/features/passwords/password.controller.js.map +1 -0
- package/dist/features/passwords/password.dto.d.ts +36 -0
- package/dist/features/passwords/password.dto.js +140 -0
- package/dist/features/passwords/password.dto.js.map +1 -0
- package/dist/features/sessions/session.service.d.ts +132 -0
- package/dist/features/sessions/session.service.js +308 -0
- package/dist/features/sessions/session.service.js.map +1 -0
- package/dist/features/sessions/session.types.d.ts +34 -0
- package/dist/features/sessions/session.types.js +3 -0
- package/dist/features/sessions/session.types.js.map +1 -0
- package/dist/features/settings/settings.defaults.d.ts +2 -0
- package/dist/features/settings/settings.defaults.js +7 -0
- package/dist/features/settings/settings.defaults.js.map +1 -0
- package/dist/features/settings/settings.service.d.ts +44 -0
- package/dist/features/settings/settings.service.js +112 -0
- package/dist/features/settings/settings.service.js.map +1 -0
- package/dist/features/settings/settings.types.d.ts +133 -0
- package/dist/features/settings/settings.types.js +3 -0
- package/dist/features/settings/settings.types.js.map +1 -0
- package/dist/features/settings/settings.validation.d.ts +33 -0
- package/dist/features/settings/settings.validation.js +614 -0
- package/dist/features/settings/settings.validation.js.map +1 -0
- package/dist/features/sign-in/sign-in-challenges.service.d.ts +54 -0
- package/dist/features/sign-in/sign-in-challenges.service.js +104 -0
- package/dist/features/sign-in/sign-in-challenges.service.js.map +1 -0
- package/dist/features/sign-in/sign-in.service.d.ts +127 -0
- package/dist/features/sign-in/sign-in.service.js +248 -0
- package/dist/features/sign-in/sign-in.service.js.map +1 -0
- package/dist/features/sign-in/sign-in.types.d.ts +22 -0
- package/dist/features/sign-in/sign-in.types.js +3 -0
- package/dist/features/sign-in/sign-in.types.js.map +1 -0
- package/dist/features/signing-keys/jwks.controller.d.ts +17 -0
- package/dist/features/signing-keys/jwks.controller.js +51 -0
- package/dist/features/signing-keys/jwks.controller.js.map +1 -0
- package/dist/features/signing-keys/signing-keys.service.d.ts +67 -0
- package/dist/features/signing-keys/signing-keys.service.js +142 -0
- package/dist/features/signing-keys/signing-keys.service.js.map +1 -0
- package/dist/features/social/social-auth.service.d.ts +100 -0
- package/dist/features/social/social-auth.service.js +232 -0
- package/dist/features/social/social-auth.service.js.map +1 -0
- package/dist/features/social/social-providers.service.d.ts +97 -0
- package/dist/features/social/social-providers.service.js +214 -0
- package/dist/features/social/social-providers.service.js.map +1 -0
- package/dist/features/social/social.controller.d.ts +54 -0
- package/dist/features/social/social.controller.js +124 -0
- package/dist/features/social/social.controller.js.map +1 -0
- package/dist/features/social/social.dto.d.ts +11 -0
- package/dist/features/social/social.dto.js +38 -0
- package/dist/features/social/social.dto.js.map +1 -0
- package/dist/features/two-factor/two-factor.controller.d.ts +84 -0
- package/dist/features/two-factor/two-factor.controller.js +174 -0
- package/dist/features/two-factor/two-factor.controller.js.map +1 -0
- package/dist/features/two-factor/two-factor.dto.d.ts +10 -0
- package/dist/features/two-factor/two-factor.dto.js +37 -0
- package/dist/features/two-factor/two-factor.dto.js.map +1 -0
- package/dist/features/two-factor/two-factor.service.d.ts +171 -0
- package/dist/features/two-factor/two-factor.service.js +348 -0
- package/dist/features/two-factor/two-factor.service.js.map +1 -0
- package/dist/features/users/sign-in-methods.service.d.ts +40 -0
- package/dist/features/users/sign-in-methods.service.js +81 -0
- package/dist/features/users/sign-in-methods.service.js.map +1 -0
- package/dist/features/users/sign-up-rules.d.ts +12 -0
- package/dist/features/users/sign-up-rules.js +29 -0
- package/dist/features/users/sign-up-rules.js.map +1 -0
- package/dist/features/users/users.service.d.ts +246 -0
- package/dist/features/users/users.service.js +509 -0
- package/dist/features/users/users.service.js.map +1 -0
- package/dist/features/users/users.types.d.ts +24 -0
- package/dist/features/users/users.types.js +3 -0
- package/dist/features/users/users.types.js.map +1 -0
- package/dist/features/webhooks/webhook-delivery.worker.d.ts +55 -0
- package/dist/features/webhooks/webhook-delivery.worker.js +175 -0
- package/dist/features/webhooks/webhook-delivery.worker.js.map +1 -0
- package/dist/features/webhooks/webhook-signature.d.ts +28 -0
- package/dist/features/webhooks/webhook-signature.js +85 -0
- package/dist/features/webhooks/webhook-signature.js.map +1 -0
- package/dist/features/webhooks/webhooks.service.d.ts +144 -0
- package/dist/features/webhooks/webhooks.service.js +238 -0
- package/dist/features/webhooks/webhooks.service.js.map +1 -0
- package/dist/guards/auth.decorators.d.ts +65 -0
- package/dist/guards/auth.decorators.js +68 -0
- package/dist/guards/auth.decorators.js.map +1 -0
- package/dist/guards/auth.guard.d.ts +47 -0
- package/dist/guards/auth.guard.js +132 -0
- package/dist/guards/auth.guard.js.map +1 -0
- package/dist/index.d.ts +38 -0
- package/dist/index.js +95 -0
- package/dist/index.js.map +1 -0
- package/dist/integrations/console-email-sender.d.ts +15 -0
- package/dist/integrations/console-email-sender.js +26 -0
- package/dist/integrations/console-email-sender.js.map +1 -0
- package/dist/integrations/msg91-sms-sender.d.ts +28 -0
- package/dist/integrations/msg91-sms-sender.js +46 -0
- package/dist/integrations/msg91-sms-sender.js.map +1 -0
- package/docs/README.md +39 -0
- package/docs/admin-api-keys-webhooks.md +147 -0
- package/docs/api-reference.md +175 -0
- package/docs/architecture.md +119 -0
- package/docs/client-integration.md +192 -0
- package/docs/code-walkthrough.md +273 -0
- package/docs/configuration.md +229 -0
- package/docs/endpoints/README.md +151 -0
- package/docs/endpoints/account.md +363 -0
- package/docs/endpoints/admin.md +422 -0
- package/docs/endpoints/email-password.md +261 -0
- package/docs/endpoints/organizations.md +192 -0
- package/docs/endpoints/passkeys.md +195 -0
- package/docs/endpoints/passwordless.md +214 -0
- package/docs/endpoints/social.md +116 -0
- package/docs/endpoints/two-factor.md +146 -0
- package/docs/error-codes.md +100 -0
- package/docs/events.md +83 -0
- package/docs/getting-started.md +139 -0
- package/docs/manual-testing.md +215 -0
- package/docs/organizations.md +86 -0
- package/docs/production-checklist.md +60 -0
- package/docs/protecting-routes.md +147 -0
- package/docs/security.md +61 -0
- package/docs/sign-in-flows.md +294 -0
- package/package.json +92 -4
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.TwoFactorService = void 0;
|
|
16
|
+
const common_1 = require("@nestjs/common");
|
|
17
|
+
const typeorm_1 = require("typeorm");
|
|
18
|
+
const auth_error_1 = require("../../common/auth-error.js");
|
|
19
|
+
const random_secrets_1 = require("../../common/crypto/random-secrets.js");
|
|
20
|
+
const totp_1 = require("../../common/crypto/totp.js");
|
|
21
|
+
const encryption_service_1 = require("../../common/encryption.service.js");
|
|
22
|
+
const rate_limiter_service_1 = require("../../common/rate-limiter.service.js");
|
|
23
|
+
const constants_1 = require("../../constants.js");
|
|
24
|
+
const entities_1 = require("../../database/entities/index.js");
|
|
25
|
+
const auth_events_service_1 = require("../events/auth-events.service.js");
|
|
26
|
+
const session_service_1 = require("../sessions/session.service.js");
|
|
27
|
+
const settings_service_1 = require("../settings/settings.service.js");
|
|
28
|
+
const sign_in_challenges_service_1 = require("../sign-in/sign-in-challenges.service.js");
|
|
29
|
+
const sign_in_service_1 = require("../sign-in/sign-in.service.js");
|
|
30
|
+
const users_service_1 = require("../users/users.service.js");
|
|
31
|
+
/**
|
|
32
|
+
* Two-factor authentication with an authenticator app (Google Authenticator, Microsoft
|
|
33
|
+
* Authenticator, 1Password, Authy...), plus single-use backup codes for a lost phone.
|
|
34
|
+
*
|
|
35
|
+
* Turning it on: POST /auth/2fa/setup -> secret + QR-code URL
|
|
36
|
+
* POST /auth/2fa/enable -> first code from the app; returns backup codes (shown once)
|
|
37
|
+
* Signing in: sign-in returns { status: "two_factor_required", challengeToken }
|
|
38
|
+
* POST /auth/2fa/verify -> challengeToken + code (or a backup code) -> tokens
|
|
39
|
+
*/
|
|
40
|
+
let TwoFactorService = class TwoFactorService {
|
|
41
|
+
options;
|
|
42
|
+
settingsService;
|
|
43
|
+
encryption;
|
|
44
|
+
users;
|
|
45
|
+
sessions;
|
|
46
|
+
signIn;
|
|
47
|
+
challenges;
|
|
48
|
+
rateLimiter;
|
|
49
|
+
events;
|
|
50
|
+
database;
|
|
51
|
+
constructor(options, settingsService, encryption, users, sessions, signIn, challenges, rateLimiter, events) {
|
|
52
|
+
this.options = options;
|
|
53
|
+
this.settingsService = settingsService;
|
|
54
|
+
this.encryption = encryption;
|
|
55
|
+
this.users = users;
|
|
56
|
+
this.sessions = sessions;
|
|
57
|
+
this.signIn = signIn;
|
|
58
|
+
this.challenges = challenges;
|
|
59
|
+
this.rateLimiter = rateLimiter;
|
|
60
|
+
this.events = events;
|
|
61
|
+
this.database = options.dataSource;
|
|
62
|
+
}
|
|
63
|
+
// -------------------------------------------------------------------------------------------
|
|
64
|
+
// Turning 2FA on and off
|
|
65
|
+
// -------------------------------------------------------------------------------------------
|
|
66
|
+
/**
|
|
67
|
+
* Starts setup: creates a new secret. Show `authenticatorAppUrl` as a QR code (and `secret` for
|
|
68
|
+
* typing by hand). Nothing changes for the user until they confirm with a code (`confirmSetup`).
|
|
69
|
+
* Calling it again replaces the unconfirmed secret.
|
|
70
|
+
*
|
|
71
|
+
* @param userId - The signed-in user.
|
|
72
|
+
* @returns The plain secret and the otpauth:// URL for the QR code.
|
|
73
|
+
* @throws AuthError FEATURE_DISABLED when 2FA is turned off in settings.
|
|
74
|
+
* @throws AuthError TWO_FACTOR_ALREADY_ENABLED when the user already has 2FA on.
|
|
75
|
+
*/
|
|
76
|
+
async startSetup(userId) {
|
|
77
|
+
await this.assertTwoFactorAllowed();
|
|
78
|
+
const user = await this.users.getUserOrThrow(userId);
|
|
79
|
+
if (user.isTwoFactorEnabled)
|
|
80
|
+
throw new auth_error_1.AuthError(constants_1.AUTH_ERRORS.twoFactorAlreadyEnabled);
|
|
81
|
+
const secret = (0, totp_1.createTotpSecret)();
|
|
82
|
+
await this.database.getRepository(entities_1.TwoFactorSecretEntity).upsert({
|
|
83
|
+
userId,
|
|
84
|
+
encryptedSecret: await this.encryption.encrypt(secret, this.encryptionContext(userId)),
|
|
85
|
+
confirmedAt: null,
|
|
86
|
+
lastUsedTimeStep: null,
|
|
87
|
+
}, ['userId']);
|
|
88
|
+
const accountName = user.email ?? user.phoneNumber ?? user.id;
|
|
89
|
+
const issuerName = this.options.appName ?? constants_1.TOTP_DEFAULT_ISSUER_NAME;
|
|
90
|
+
return { success: true, secret, authenticatorAppUrl: (0, totp_1.buildAuthenticatorAppUrl)(secret, accountName, issuerName) };
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Finishes setup: the user types the first code from their app. Turns 2FA on, signs out other
|
|
94
|
+
* devices, and returns backup codes. Show them once and ask the user to save them.
|
|
95
|
+
*
|
|
96
|
+
* @param userId - The signed-in user.
|
|
97
|
+
* @param currentSessionId - This device's session, which stays signed in.
|
|
98
|
+
* @param code - The 6-digit code from the authenticator app.
|
|
99
|
+
* @param request - Who is asking (for the audit log).
|
|
100
|
+
* @returns The plain backup codes, formatted like "k7m2-x9p4".
|
|
101
|
+
* @throws AuthError TWO_FACTOR_SETUP_NOT_STARTED when `startSetup` wasn't called first.
|
|
102
|
+
* @throws AuthError INVALID_OTP when the code is wrong.
|
|
103
|
+
* @throws AuthError TOO_MANY_REQUESTS after too many attempts.
|
|
104
|
+
*/
|
|
105
|
+
async confirmSetup(userId, currentSessionId, code, request) {
|
|
106
|
+
await this.assertTwoFactorAllowed();
|
|
107
|
+
await this.rateLimiter.consume(constants_1.RATE_LIMIT_KEY.twoFactorPerUser(userId), constants_1.RATE_LIMITS.twoFactorPerUser);
|
|
108
|
+
const user = await this.users.getUserOrThrow(userId);
|
|
109
|
+
if (user.isTwoFactorEnabled)
|
|
110
|
+
throw new auth_error_1.AuthError(constants_1.AUTH_ERRORS.twoFactorAlreadyEnabled);
|
|
111
|
+
const storedSecret = await this.database.getRepository(entities_1.TwoFactorSecretEntity).findOne({ where: { userId, confirmedAt: (0, typeorm_1.IsNull)() } });
|
|
112
|
+
if (!storedSecret)
|
|
113
|
+
throw new auth_error_1.AuthError(constants_1.AUTH_ERRORS.twoFactorSetupNotStarted);
|
|
114
|
+
if (!(await this.checkAuthenticatorCode(storedSecret, code)))
|
|
115
|
+
throw new auth_error_1.AuthError(constants_1.AUTH_ERRORS.invalidTwoFactorCode);
|
|
116
|
+
await this.database.getRepository(entities_1.TwoFactorSecretEntity).update({ userId }, { confirmedAt: new Date() });
|
|
117
|
+
await this.database.getRepository(entities_1.UserEntity).update({ id: userId }, { isTwoFactorEnabled: true });
|
|
118
|
+
const backupCodes = await this.replaceBackupCodes(userId);
|
|
119
|
+
await this.sessions.revokeAllSessions(userId, constants_1.SESSION_END_REASON.twoFactorEnabled, currentSessionId);
|
|
120
|
+
await this.events.record({ name: constants_1.AUTH_EVENTS.twoFactorEnabled, userId, request });
|
|
121
|
+
return { success: true, backupCodes };
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Turns 2FA off. Requires a current authenticator code or a backup code.
|
|
125
|
+
*
|
|
126
|
+
* @param userId - The signed-in user.
|
|
127
|
+
* @param code - An authenticator code or a backup code.
|
|
128
|
+
* @param request - Who is asking (for the audit log).
|
|
129
|
+
* @returns `{ success: true }`.
|
|
130
|
+
* @throws AuthError TWO_FACTOR_NOT_ENABLED when 2FA is already off.
|
|
131
|
+
* @throws AuthError INVALID_OTP when the code is wrong.
|
|
132
|
+
*/
|
|
133
|
+
async disable(userId, code, request) {
|
|
134
|
+
await this.rateLimiter.consume(constants_1.RATE_LIMIT_KEY.twoFactorPerUser(userId), constants_1.RATE_LIMITS.twoFactorPerUser);
|
|
135
|
+
const user = await this.users.getUserOrThrow(userId);
|
|
136
|
+
if (!user.isTwoFactorEnabled)
|
|
137
|
+
throw new auth_error_1.AuthError(constants_1.AUTH_ERRORS.twoFactorNotEnabled);
|
|
138
|
+
if (!(await this.checkAnyCode(userId, code)))
|
|
139
|
+
throw new auth_error_1.AuthError(constants_1.AUTH_ERRORS.invalidTwoFactorCode);
|
|
140
|
+
await this.removeTwoFactor(userId);
|
|
141
|
+
await this.events.record({ name: constants_1.AUTH_EVENTS.twoFactorDisabled, userId, request });
|
|
142
|
+
return { success: true };
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Replaces all backup codes (e.g. the user lost the list). The old codes stop working.
|
|
146
|
+
*
|
|
147
|
+
* @param userId - The signed-in user.
|
|
148
|
+
* @param code - An authenticator code or a backup code.
|
|
149
|
+
* @returns The new plain backup codes.
|
|
150
|
+
* @throws AuthError TWO_FACTOR_NOT_ENABLED when 2FA is off.
|
|
151
|
+
* @throws AuthError INVALID_OTP when the code is wrong.
|
|
152
|
+
*/
|
|
153
|
+
async regenerateBackupCodes(userId, code) {
|
|
154
|
+
await this.rateLimiter.consume(constants_1.RATE_LIMIT_KEY.twoFactorPerUser(userId), constants_1.RATE_LIMITS.twoFactorPerUser);
|
|
155
|
+
const user = await this.users.getUserOrThrow(userId);
|
|
156
|
+
if (!user.isTwoFactorEnabled)
|
|
157
|
+
throw new auth_error_1.AuthError(constants_1.AUTH_ERRORS.twoFactorNotEnabled);
|
|
158
|
+
if (!(await this.checkAnyCode(userId, code)))
|
|
159
|
+
throw new auth_error_1.AuthError(constants_1.AUTH_ERRORS.invalidTwoFactorCode);
|
|
160
|
+
return { success: true, backupCodes: await this.replaceBackupCodes(userId) };
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Counts the unused backup codes (show a warning when it gets low).
|
|
164
|
+
*
|
|
165
|
+
* @param userId - The user to check.
|
|
166
|
+
* @returns How many backup codes are left.
|
|
167
|
+
*/
|
|
168
|
+
async countRemainingBackupCodes(userId) {
|
|
169
|
+
return this.database.getRepository(entities_1.BackupCodeEntity).count({ where: { userId, usedAt: (0, typeorm_1.IsNull)() } });
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Admin: turns 2FA off for a user who lost their phone and backup codes.
|
|
173
|
+
* Only call this after checking who the user is some other way.
|
|
174
|
+
*
|
|
175
|
+
* @param userId - The user whose 2FA is removed.
|
|
176
|
+
* @param adminUserId - The admin doing it (recorded in the audit log).
|
|
177
|
+
* @param request - Who is asking (for the audit log).
|
|
178
|
+
* @returns `{ success: true }`.
|
|
179
|
+
* @throws AuthError NOT_FOUND when the user doesn't exist.
|
|
180
|
+
*/
|
|
181
|
+
async resetForUser(userId, adminUserId, request) {
|
|
182
|
+
await this.users.getUserOrThrow(userId);
|
|
183
|
+
await this.removeTwoFactor(userId);
|
|
184
|
+
await this.events.record({
|
|
185
|
+
name: constants_1.AUTH_EVENTS.twoFactorDisabled,
|
|
186
|
+
userId,
|
|
187
|
+
performedBy: constants_1.PERFORMED_BY.admin(adminUserId),
|
|
188
|
+
request,
|
|
189
|
+
details: { resetByAdmin: true },
|
|
190
|
+
});
|
|
191
|
+
return { success: true };
|
|
192
|
+
}
|
|
193
|
+
// -------------------------------------------------------------------------------------------
|
|
194
|
+
// The second step of sign-in
|
|
195
|
+
// -------------------------------------------------------------------------------------------
|
|
196
|
+
/**
|
|
197
|
+
* The second step of sign-in: checks the code for a challenge token returned by the first step.
|
|
198
|
+
*
|
|
199
|
+
* @param challengeToken - The "ch_..." token from `{ status: "two_factor_required" }`.
|
|
200
|
+
* @param code - An authenticator code or a backup code.
|
|
201
|
+
* @param request - Who is asking (for rate limits, sessions and the audit log).
|
|
202
|
+
* @returns The signed-in result with access and refresh tokens.
|
|
203
|
+
* @throws AuthError INVALID_TOKEN when the challenge is unknown, used, expired or burnt by wrong codes.
|
|
204
|
+
* @throws AuthError INVALID_OTP when the code is wrong.
|
|
205
|
+
*/
|
|
206
|
+
async verifySignInChallenge(challengeToken, code, request) {
|
|
207
|
+
const settings = await this.settingsService.getSettings();
|
|
208
|
+
const challenge = await this.challenges.findOpenChallenge(challengeToken, constants_1.CHALLENGE_PURPOSE.twoFactor);
|
|
209
|
+
await this.rateLimiter.consume(constants_1.RATE_LIMIT_KEY.twoFactorPerUser(challenge.userId), constants_1.RATE_LIMITS.twoFactorPerUser);
|
|
210
|
+
if (!(await this.checkAnyCode(challenge.userId, code))) {
|
|
211
|
+
await this.challenges.recordFailedAttempt(challenge.challengeId);
|
|
212
|
+
await this.signIn.recordFailedSignIn(challenge.userId, challenge.signInMethod, constants_1.SIGN_IN_FAILURE_REASON.wrongTwoFactorCode, request);
|
|
213
|
+
throw new auth_error_1.AuthError(constants_1.AUTH_ERRORS.invalidTwoFactorCode);
|
|
214
|
+
}
|
|
215
|
+
if (!(await this.challenges.markChallengeUsed(challenge.challengeId)))
|
|
216
|
+
throw new auth_error_1.AuthError(constants_1.AUTH_ERRORS.invalidOrExpiredLink);
|
|
217
|
+
const user = await this.users.getUserOrThrow(challenge.userId);
|
|
218
|
+
return this.signIn.finishTwoFactorSignIn(user, challenge.signInMethod, request, settings);
|
|
219
|
+
}
|
|
220
|
+
// -------------------------------------------------------------------------------------------
|
|
221
|
+
/**
|
|
222
|
+
* @throws AuthError FEATURE_DISABLED when 2FA is turned off in settings.
|
|
223
|
+
*/
|
|
224
|
+
async assertTwoFactorAllowed() {
|
|
225
|
+
const settings = await this.settingsService.getSettings();
|
|
226
|
+
if (!settings.twoFactor.enabled)
|
|
227
|
+
throw new auth_error_1.AuthError(constants_1.AUTH_ERRORS.featureDisabled);
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Accepts a 6-digit authenticator code, or a backup code (which is then used up).
|
|
231
|
+
*
|
|
232
|
+
* @param userId - The user the code belongs to.
|
|
233
|
+
* @param code - What the user typed.
|
|
234
|
+
* @returns True when the code was correct.
|
|
235
|
+
*/
|
|
236
|
+
async checkAnyCode(userId, code) {
|
|
237
|
+
if (typeof code !== 'string')
|
|
238
|
+
return false;
|
|
239
|
+
const storedSecret = await this.database
|
|
240
|
+
.getRepository(entities_1.TwoFactorSecretEntity)
|
|
241
|
+
.findOne({ where: { userId } });
|
|
242
|
+
if (storedSecret?.confirmedAt && (await this.checkAuthenticatorCode(storedSecret, code)))
|
|
243
|
+
return true;
|
|
244
|
+
return this.useBackupCode(userId, code);
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Checks an authenticator code and remembers its time step, so the same code can't be used twice.
|
|
248
|
+
* The UPDATE only succeeds if no newer code was accepted meanwhile (two requests at once).
|
|
249
|
+
*
|
|
250
|
+
* @param storedSecret - The user's row with the encrypted secret.
|
|
251
|
+
* @param code - What the user typed.
|
|
252
|
+
* @returns True when the code was correct and not used before.
|
|
253
|
+
*/
|
|
254
|
+
async checkAuthenticatorCode(storedSecret, code) {
|
|
255
|
+
if (typeof code !== 'string')
|
|
256
|
+
return false;
|
|
257
|
+
const secret = await this.encryption.decrypt(storedSecret.encryptedSecret, this.encryptionContext(storedSecret.userId));
|
|
258
|
+
const matchedTimeStep = (0, totp_1.verifyTotpCode)(secret, code, storedSecret.lastUsedTimeStep);
|
|
259
|
+
if (matchedTimeStep === null)
|
|
260
|
+
return false;
|
|
261
|
+
const result = await this.database
|
|
262
|
+
.getRepository(entities_1.TwoFactorSecretEntity)
|
|
263
|
+
.createQueryBuilder()
|
|
264
|
+
.update(entities_1.TwoFactorSecretEntity)
|
|
265
|
+
.set({ lastUsedTimeStep: matchedTimeStep })
|
|
266
|
+
.where('user_id = :userId AND (last_used_time_step IS NULL OR last_used_time_step < :matchedTimeStep)', {
|
|
267
|
+
userId: storedSecret.userId,
|
|
268
|
+
matchedTimeStep,
|
|
269
|
+
})
|
|
270
|
+
.execute();
|
|
271
|
+
return (result.affected ?? 0) > 0;
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Checks a backup code and marks it used, so it works only once.
|
|
275
|
+
*
|
|
276
|
+
* @param userId - The user the code belongs to.
|
|
277
|
+
* @param typedCode - What the user typed; dashes, spaces and capitals are ignored.
|
|
278
|
+
* @returns True when the code matched an unused backup code (and this request was the one to use it).
|
|
279
|
+
*/
|
|
280
|
+
async useBackupCode(userId, typedCode) {
|
|
281
|
+
const normalizedCode = (0, random_secrets_1.normalizeBackupCode)(typedCode);
|
|
282
|
+
const unusedCodes = await this.database.getRepository(entities_1.BackupCodeEntity).find({ where: { userId, usedAt: (0, typeorm_1.IsNull)() } });
|
|
283
|
+
// Check every code (no early exit), so the time taken doesn't hint at which one matched.
|
|
284
|
+
let matchingCode;
|
|
285
|
+
for (const storedCode of unusedCodes) {
|
|
286
|
+
if ((0, random_secrets_1.constantTimeEquals)((0, random_secrets_1.hashShortCode)(normalizedCode, storedCode.salt), storedCode.codeHash))
|
|
287
|
+
matchingCode = storedCode;
|
|
288
|
+
}
|
|
289
|
+
if (!matchingCode)
|
|
290
|
+
return false;
|
|
291
|
+
const result = await this.database
|
|
292
|
+
.getRepository(entities_1.BackupCodeEntity)
|
|
293
|
+
.update({ id: matchingCode.id, usedAt: (0, typeorm_1.IsNull)() }, { usedAt: new Date() });
|
|
294
|
+
return (result.affected ?? 0) > 0;
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Deletes old backup codes and creates new ones. Only their hashes are stored.
|
|
298
|
+
*
|
|
299
|
+
* @param userId - The user to create codes for.
|
|
300
|
+
* @returns The plain codes, formatted like "k7m2-x9p4", to show the user once.
|
|
301
|
+
*/
|
|
302
|
+
async replaceBackupCodes(userId) {
|
|
303
|
+
const plainCodes = Array.from({ length: constants_1.BACKUP_CODE_COUNT }, () => (0, random_secrets_1.createBackupCode)());
|
|
304
|
+
await this.database.transaction(async (transaction) => {
|
|
305
|
+
await transaction.delete(entities_1.BackupCodeEntity, { userId });
|
|
306
|
+
await transaction.insert(entities_1.BackupCodeEntity, plainCodes.map((code) => {
|
|
307
|
+
const salt = (0, random_secrets_1.createSalt)();
|
|
308
|
+
return { userId, salt, codeHash: (0, random_secrets_1.hashShortCode)(code, salt) };
|
|
309
|
+
}));
|
|
310
|
+
});
|
|
311
|
+
return plainCodes.map((code) => [code.slice(0, constants_1.BACKUP_CODE_DISPLAY_GROUP_LENGTH), code.slice(constants_1.BACKUP_CODE_DISPLAY_GROUP_LENGTH)].join(constants_1.BACKUP_CODE_DISPLAY_SEPARATOR));
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Deletes the secret and backup codes and turns 2FA off, all in one transaction.
|
|
315
|
+
*
|
|
316
|
+
* @param userId - The user to turn 2FA off for.
|
|
317
|
+
*/
|
|
318
|
+
async removeTwoFactor(userId) {
|
|
319
|
+
await this.database.transaction(async (transaction) => {
|
|
320
|
+
await transaction.delete(entities_1.TwoFactorSecretEntity, { userId });
|
|
321
|
+
await transaction.delete(entities_1.BackupCodeEntity, { userId });
|
|
322
|
+
await transaction.update(entities_1.UserEntity, { id: userId }, { isTwoFactorEnabled: false });
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Binds the encrypted secret to its user, so a secret copied to another user's row won't decrypt.
|
|
327
|
+
*
|
|
328
|
+
* @param userId - The secret's owner.
|
|
329
|
+
* @returns The context for `EncryptionService`.
|
|
330
|
+
*/
|
|
331
|
+
encryptionContext(userId) {
|
|
332
|
+
return { purpose: constants_1.ENCRYPTION_PURPOSE.totpSecret, userId };
|
|
333
|
+
}
|
|
334
|
+
};
|
|
335
|
+
exports.TwoFactorService = TwoFactorService;
|
|
336
|
+
exports.TwoFactorService = TwoFactorService = __decorate([
|
|
337
|
+
(0, common_1.Injectable)(),
|
|
338
|
+
__param(0, (0, common_1.Inject)(constants_1.AUTH_OPTIONS_TOKEN)),
|
|
339
|
+
__metadata("design:paramtypes", [Object, settings_service_1.SettingsService,
|
|
340
|
+
encryption_service_1.EncryptionService,
|
|
341
|
+
users_service_1.UsersService,
|
|
342
|
+
session_service_1.SessionService,
|
|
343
|
+
sign_in_service_1.SignInService,
|
|
344
|
+
sign_in_challenges_service_1.SignInChallengesService,
|
|
345
|
+
rate_limiter_service_1.RateLimiterService,
|
|
346
|
+
auth_events_service_1.AuthEventsService])
|
|
347
|
+
], TwoFactorService);
|
|
348
|
+
//# sourceMappingURL=two-factor.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"two-factor.service.js","sourceRoot":"","sources":["../../../src/features/two-factor/two-factor.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { AuthOptions } from '../../auth-options.js';
|
|
2
|
+
import { type RemovableSignInMethod } from '../../constants.js';
|
|
3
|
+
import { UserEntity } from '../../database/entities/index.js';
|
|
4
|
+
import { AuthSettings } from '../settings/settings.types.js';
|
|
5
|
+
/** Every way a user can currently sign in. Shown on a "Sign-in methods" screen. */
|
|
6
|
+
export interface UserSignInMethods {
|
|
7
|
+
hasPassword: boolean;
|
|
8
|
+
/** Can sign in with an email code or magic link (verified email + one of those turned on). */
|
|
9
|
+
canUseEmailCode: boolean;
|
|
10
|
+
/** Can sign in with an SMS code (verified phone + phone OTP turned on). */
|
|
11
|
+
canUsePhoneCode: boolean;
|
|
12
|
+
socialAccounts: {
|
|
13
|
+
provider: string;
|
|
14
|
+
email: string | null;
|
|
15
|
+
linkedAt: string;
|
|
16
|
+
}[];
|
|
17
|
+
passkeyCount: number;
|
|
18
|
+
}
|
|
19
|
+
/** Answers "how can this user sign in?" and stops them from removing the last way. */
|
|
20
|
+
export declare class SignInMethodsService {
|
|
21
|
+
private readonly database;
|
|
22
|
+
constructor(options: AuthOptions);
|
|
23
|
+
/**
|
|
24
|
+
* Lists every way the user can currently sign in. Methods turned off in settings don't count.
|
|
25
|
+
*
|
|
26
|
+
* @param user - The user.
|
|
27
|
+
* @param settings - The current settings.
|
|
28
|
+
* @returns Password, email/phone codes, linked social accounts and passkey count.
|
|
29
|
+
*/
|
|
30
|
+
listSignInMethods(user: UserEntity, settings: AuthSettings): Promise<UserSignInMethods>;
|
|
31
|
+
/**
|
|
32
|
+
* Stops the user from removing their last way to sign in.
|
|
33
|
+
*
|
|
34
|
+
* @param user - The user.
|
|
35
|
+
* @param settings - The current settings.
|
|
36
|
+
* @param kind - What is about to be removed: "password", "social" or "passkey" (one item).
|
|
37
|
+
* @throws AuthError LAST_SIGN_IN_METHOD if removing it would leave no way to sign in.
|
|
38
|
+
*/
|
|
39
|
+
assertCanRemove(user: UserEntity, settings: AuthSettings, kind: RemovableSignInMethod): Promise<void>;
|
|
40
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.SignInMethodsService = void 0;
|
|
16
|
+
const common_1 = require("@nestjs/common");
|
|
17
|
+
const auth_error_1 = require("../../common/auth-error.js");
|
|
18
|
+
const constants_1 = require("../../constants.js");
|
|
19
|
+
const entities_1 = require("../../database/entities/index.js");
|
|
20
|
+
/** Answers "how can this user sign in?" and stops them from removing the last way. */
|
|
21
|
+
let SignInMethodsService = class SignInMethodsService {
|
|
22
|
+
database;
|
|
23
|
+
constructor(options) {
|
|
24
|
+
this.database = options.dataSource;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Lists every way the user can currently sign in. Methods turned off in settings don't count.
|
|
28
|
+
*
|
|
29
|
+
* @param user - The user.
|
|
30
|
+
* @param settings - The current settings.
|
|
31
|
+
* @returns Password, email/phone codes, linked social accounts and passkey count.
|
|
32
|
+
*/
|
|
33
|
+
async listSignInMethods(user, settings) {
|
|
34
|
+
const [hasPassword, socialAccounts, passkeyCount] = await Promise.all([
|
|
35
|
+
this.database.getRepository(entities_1.PasswordEntity).exists({ where: { userId: user.id } }),
|
|
36
|
+
this.database.getRepository(entities_1.SocialAccountEntity).find({ where: { userId: user.id }, order: { createdAt: constants_1.SORT_ORDER.oldestFirst } }),
|
|
37
|
+
this.database.getRepository(entities_1.PasskeyEntity).count({ where: { userId: user.id } }),
|
|
38
|
+
]);
|
|
39
|
+
return {
|
|
40
|
+
hasPassword: hasPassword && settings.password.enabled,
|
|
41
|
+
canUseEmailCode: !!user.email && user.isEmailVerified && (settings.emailOtp.enabled || settings.magicLink.enabled),
|
|
42
|
+
canUsePhoneCode: !!user.phoneNumber && user.isPhoneVerified && settings.phoneOtp.enabled,
|
|
43
|
+
socialAccounts: socialAccounts.map((account) => ({
|
|
44
|
+
provider: account.provider,
|
|
45
|
+
email: account.email,
|
|
46
|
+
linkedAt: account.createdAt.toISOString(),
|
|
47
|
+
})),
|
|
48
|
+
passkeyCount: settings.passkeys.enabled ? passkeyCount : 0,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Stops the user from removing their last way to sign in.
|
|
53
|
+
*
|
|
54
|
+
* @param user - The user.
|
|
55
|
+
* @param settings - The current settings.
|
|
56
|
+
* @param kind - What is about to be removed: "password", "social" or "passkey" (one item).
|
|
57
|
+
* @throws AuthError LAST_SIGN_IN_METHOD if removing it would leave no way to sign in.
|
|
58
|
+
*/
|
|
59
|
+
async assertCanRemove(user, settings, kind) {
|
|
60
|
+
const methods = await this.listSignInMethods(user, settings);
|
|
61
|
+
const totalMethods = Number(methods.hasPassword) +
|
|
62
|
+
Number(methods.canUseEmailCode) +
|
|
63
|
+
Number(methods.canUsePhoneCode) +
|
|
64
|
+
methods.socialAccounts.length +
|
|
65
|
+
methods.passkeyCount;
|
|
66
|
+
const removedCount = (kind === constants_1.REMOVABLE_SIGN_IN_METHOD.password && methods.hasPassword) ||
|
|
67
|
+
(kind === constants_1.REMOVABLE_SIGN_IN_METHOD.social && methods.socialAccounts.length > 0) ||
|
|
68
|
+
(kind === constants_1.REMOVABLE_SIGN_IN_METHOD.passkey && methods.passkeyCount > 0)
|
|
69
|
+
? 1
|
|
70
|
+
: 0;
|
|
71
|
+
if (totalMethods - removedCount < 1)
|
|
72
|
+
throw new auth_error_1.AuthError(constants_1.AUTH_ERRORS.cannotRemoveLastSignInMethod);
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
exports.SignInMethodsService = SignInMethodsService;
|
|
76
|
+
exports.SignInMethodsService = SignInMethodsService = __decorate([
|
|
77
|
+
(0, common_1.Injectable)(),
|
|
78
|
+
__param(0, (0, common_1.Inject)(constants_1.AUTH_OPTIONS_TOKEN)),
|
|
79
|
+
__metadata("design:paramtypes", [Object])
|
|
80
|
+
], SignInMethodsService);
|
|
81
|
+
//# sourceMappingURL=sign-in-methods.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sign-in-methods.service.js","sourceRoot":"","sources":["../../../src/features/users/sign-in-methods.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { SignUpDetailsInput } from '../passwordless/passwordless.dto.js';
|
|
2
|
+
import { AuthSettings } from '../settings/settings.types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Checks the details a new account needs under the current settings (names, consent).
|
|
5
|
+
* Used by every sign-in method that can create an account (codes, links, social, passkeys).
|
|
6
|
+
*
|
|
7
|
+
* @param details - The names and consents from the request.
|
|
8
|
+
* @param settings - The current settings (registration rules).
|
|
9
|
+
* @throws AuthError VALIDATION_ERROR with `missingSignUpDetails` listing exactly what is missing,
|
|
10
|
+
* so the app can show the right form.
|
|
11
|
+
*/
|
|
12
|
+
export declare function assertSignUpDetailsComplete(details: SignUpDetailsInput, settings: AuthSettings): void;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.assertSignUpDetailsComplete = assertSignUpDetailsComplete;
|
|
4
|
+
const auth_error_1 = require("../../common/auth-error.js");
|
|
5
|
+
const constants_1 = require("../../constants.js");
|
|
6
|
+
/**
|
|
7
|
+
* Checks the details a new account needs under the current settings (names, consent).
|
|
8
|
+
* Used by every sign-in method that can create an account (codes, links, social, passkeys).
|
|
9
|
+
*
|
|
10
|
+
* @param details - The names and consents from the request.
|
|
11
|
+
* @param settings - The current settings (registration rules).
|
|
12
|
+
* @throws AuthError VALIDATION_ERROR with `missingSignUpDetails` listing exactly what is missing,
|
|
13
|
+
* so the app can show the right form.
|
|
14
|
+
*/
|
|
15
|
+
function assertSignUpDetailsComplete(details, settings) {
|
|
16
|
+
const rules = settings.registration;
|
|
17
|
+
const missingSignUpDetails = [];
|
|
18
|
+
if (rules.requireFirstAndLastName && !details.firstName?.trim())
|
|
19
|
+
missingSignUpDetails.push(constants_1.SIGN_UP_DETAIL.firstName);
|
|
20
|
+
if (rules.requireFirstAndLastName && !details.lastName?.trim())
|
|
21
|
+
missingSignUpDetails.push(constants_1.SIGN_UP_DETAIL.lastName);
|
|
22
|
+
if (rules.requireTermsAcceptance && details.acceptTerms !== true)
|
|
23
|
+
missingSignUpDetails.push(constants_1.SIGN_UP_DETAIL.acceptTerms);
|
|
24
|
+
if (rules.requirePrivacyPolicyAcceptance && details.acceptPrivacyPolicy !== true)
|
|
25
|
+
missingSignUpDetails.push(constants_1.SIGN_UP_DETAIL.acceptPrivacyPolicy);
|
|
26
|
+
if (missingSignUpDetails.length > 0)
|
|
27
|
+
throw new auth_error_1.AuthError(constants_1.AUTH_ERRORS.validationFailed, { missingSignUpDetails });
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=sign-up-rules.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sign-up-rules.js","sourceRoot":"","sources":["../../../src/features/users/sign-up-rules.ts"],"names":[],"mappings":";;AAcA,kEAQC;AAtBD,oDAAgD;AAGhD,2CAA0D;AAE1D;;;;;;;;GAQG;AACH,SAAgB,2BAA2B,CAAC,OAA2B,EAAE,QAAsB;IAC7F,MAAM,KAAK,GAAG,QAAQ,CAAC,YAAY,CAAC;IACpC,MAAM,oBAAoB,GAAa,EAAE,CAAC;IAC1C,IAAI,KAAK,CAAC,uBAAuB,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE;QAAE,oBAAoB,CAAC,IAAI,CAAC,0BAAc,CAAC,SAAS,CAAC,CAAC;IACrH,IAAI,KAAK,CAAC,uBAAuB,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,IAAI,EAAE;QAAE,oBAAoB,CAAC,IAAI,CAAC,0BAAc,CAAC,QAAQ,CAAC,CAAC;IACnH,IAAI,KAAK,CAAC,sBAAsB,IAAI,OAAO,CAAC,WAAW,KAAK,IAAI;QAAE,oBAAoB,CAAC,IAAI,CAAC,0BAAc,CAAC,WAAW,CAAC,CAAC;IACxH,IAAI,KAAK,CAAC,8BAA8B,IAAI,OAAO,CAAC,mBAAmB,KAAK,IAAI;QAAE,oBAAoB,CAAC,IAAI,CAAC,0BAAc,CAAC,mBAAmB,CAAC,CAAC;IAChJ,IAAI,oBAAoB,CAAC,MAAM,GAAG,CAAC;QAAE,MAAM,IAAI,sBAAS,CAAC,uBAAW,CAAC,gBAAgB,EAAE,EAAE,oBAAoB,EAAE,CAAC,CAAC;AACnH,CAAC"}
|