@nauth-toolkit/core 0.1.0 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +90 -0
- package/README.md +30 -0
- package/package.json +7 -2
- package/jest.config.js +0 -15
- package/jest.setup.ts +0 -6
- package/src/adapters/database-columns.ts +0 -165
- package/src/adapters/express.adapter.ts +0 -385
- package/src/adapters/fastify.adapter.ts +0 -416
- package/src/adapters/index.ts +0 -16
- package/src/adapters/storage.factory.ts +0 -143
- package/src/bootstrap.ts +0 -374
- package/src/dto/auth-challenge.dto.ts +0 -231
- package/src/dto/auth-response.dto.ts +0 -253
- package/src/dto/challenge-response.dto.ts +0 -234
- package/src/dto/change-password-request.dto.ts +0 -50
- package/src/dto/change-password-response.dto.ts +0 -29
- package/src/dto/change-password.dto.ts +0 -57
- package/src/dto/error-response.dto.ts +0 -136
- package/src/dto/get-available-methods.dto.ts +0 -55
- package/src/dto/get-challenge-data-response.dto.ts +0 -28
- package/src/dto/get-challenge-data.dto.ts +0 -69
- package/src/dto/get-client-info.dto.ts +0 -104
- package/src/dto/get-device-token-response.dto.ts +0 -25
- package/src/dto/get-events-by-type.dto.ts +0 -76
- package/src/dto/get-ip-address-response.dto.ts +0 -24
- package/src/dto/get-mfa-status.dto.ts +0 -94
- package/src/dto/get-risk-assessment-history.dto.ts +0 -39
- package/src/dto/get-session-id-response.dto.ts +0 -25
- package/src/dto/get-setup-data-response.dto.ts +0 -31
- package/src/dto/get-setup-data.dto.ts +0 -75
- package/src/dto/get-suspicious-activity.dto.ts +0 -42
- package/src/dto/get-user-agent-response.dto.ts +0 -23
- package/src/dto/get-user-auth-history.dto.ts +0 -95
- package/src/dto/get-user-by-email.dto.ts +0 -61
- package/src/dto/get-user-by-id.dto.ts +0 -46
- package/src/dto/get-user-devices.dto.ts +0 -53
- package/src/dto/get-user-response.dto.ts +0 -17
- package/src/dto/has-provider.dto.ts +0 -56
- package/src/dto/index.ts +0 -57
- package/src/dto/is-trusted-device-response.dto.ts +0 -34
- package/src/dto/list-providers-response.dto.ts +0 -23
- package/src/dto/login.dto.ts +0 -95
- package/src/dto/logout-all-response.dto.ts +0 -24
- package/src/dto/logout-all.dto.ts +0 -65
- package/src/dto/logout-response.dto.ts +0 -25
- package/src/dto/logout.dto.ts +0 -64
- package/src/dto/refresh-token.dto.ts +0 -36
- package/src/dto/remove-devices.dto.ts +0 -85
- package/src/dto/resend-code-response.dto.ts +0 -32
- package/src/dto/resend-code.dto.ts +0 -51
- package/src/dto/reset-password.dto.ts +0 -115
- package/src/dto/respond-challenge.dto.ts +0 -272
- package/src/dto/set-mfa-exemption.dto.ts +0 -112
- package/src/dto/set-must-change-password-response.dto.ts +0 -27
- package/src/dto/set-must-change-password.dto.ts +0 -46
- package/src/dto/set-preferred-method.dto.ts +0 -80
- package/src/dto/setup-mfa.dto.ts +0 -98
- package/src/dto/signup.dto.ts +0 -174
- package/src/dto/social-auth.dto.ts +0 -422
- package/src/dto/trust-device-response.dto.ts +0 -30
- package/src/dto/trust-device.dto.ts +0 -9
- package/src/dto/update-user-attributes-request.dto.ts +0 -51
- package/src/dto/user-response.dto.ts +0 -138
- package/src/dto/user-update.dto.ts +0 -222
- package/src/dto/verify-email.dto.ts +0 -313
- package/src/dto/verify-mfa-code.dto.ts +0 -103
- package/src/dto/verify-phone-by-sub.dto.ts +0 -78
- package/src/dto/verify-phone.dto.ts +0 -245
- package/src/entities/auth-audit.entity.ts +0 -232
- package/src/entities/challenge-session.entity.ts +0 -116
- package/src/entities/index.ts +0 -29
- package/src/entities/login-attempt.entity.ts +0 -64
- package/src/entities/mfa-device.entity.ts +0 -151
- package/src/entities/rate-limit.entity.ts +0 -44
- package/src/entities/session.entity.ts +0 -180
- package/src/entities/social-account.entity.ts +0 -96
- package/src/entities/storage-lock.entity.ts +0 -39
- package/src/entities/trusted-device.entity.ts +0 -112
- package/src/entities/user.entity.ts +0 -243
- package/src/entities/verification-token.entity.ts +0 -141
- package/src/enums/auth-audit-event-type.enum.ts +0 -360
- package/src/enums/error-codes.enum.ts +0 -420
- package/src/enums/mfa-method.enum.ts +0 -97
- package/src/enums/risk-factor.enum.ts +0 -111
- package/src/exceptions/nauth.exception.ts +0 -231
- package/src/handlers/auth.handler.ts +0 -260
- package/src/handlers/client-info.handler.ts +0 -101
- package/src/handlers/csrf.handler.ts +0 -156
- package/src/handlers/token-delivery.handler.ts +0 -118
- package/src/index.ts +0 -118
- package/src/interfaces/client-info.interface.ts +0 -85
- package/src/interfaces/config.interface.ts +0 -2135
- package/src/interfaces/entities.interface.ts +0 -226
- package/src/interfaces/index.ts +0 -15
- package/src/interfaces/logger.interface.ts +0 -283
- package/src/interfaces/mfa-provider.interface.ts +0 -154
- package/src/interfaces/oauth.interface.ts +0 -148
- package/src/interfaces/provider.interface.ts +0 -47
- package/src/interfaces/social-auth-provider.interface.ts +0 -131
- package/src/interfaces/storage-adapter.interface.ts +0 -82
- package/src/interfaces/template.interface.ts +0 -510
- package/src/interfaces/token-verifier.interface.ts +0 -110
- package/src/internal.ts +0 -178
- package/src/platform/interfaces.ts +0 -299
- package/src/schemas/auth-config.schema.ts +0 -646
- package/src/services/adaptive-mfa-decision.service.spec.ts +0 -1058
- package/src/services/adaptive-mfa-decision.service.ts +0 -457
- package/src/services/auth-audit.service.spec.ts +0 -675
- package/src/services/auth-audit.service.ts +0 -558
- package/src/services/auth-challenge-helper.service.spec.ts +0 -3227
- package/src/services/auth-challenge-helper.service.ts +0 -825
- package/src/services/auth-flow-context-builder.service.ts +0 -520
- package/src/services/auth-flow-rules.ts +0 -202
- package/src/services/auth-flow-state-definitions.ts +0 -190
- package/src/services/auth-flow-state-machine.service.ts +0 -207
- package/src/services/auth-flow-state-machine.types.ts +0 -316
- package/src/services/auth.service.spec.ts +0 -4195
- package/src/services/auth.service.ts +0 -3727
- package/src/services/challenge.service.spec.ts +0 -1363
- package/src/services/challenge.service.ts +0 -696
- package/src/services/client-info.service.spec.ts +0 -572
- package/src/services/client-info.service.ts +0 -374
- package/src/services/csrf.service.ts +0 -54
- package/src/services/email-verification.service.spec.ts +0 -1229
- package/src/services/email-verification.service.ts +0 -578
- package/src/services/geo-location.service.spec.ts +0 -603
- package/src/services/geo-location.service.ts +0 -599
- package/src/services/index.ts +0 -13
- package/src/services/jwt.service.spec.ts +0 -882
- package/src/services/jwt.service.ts +0 -621
- package/src/services/mfa-base.service.spec.ts +0 -246
- package/src/services/mfa-base.service.ts +0 -611
- package/src/services/mfa.service.spec.ts +0 -693
- package/src/services/mfa.service.ts +0 -960
- package/src/services/password.service.spec.ts +0 -166
- package/src/services/password.service.ts +0 -309
- package/src/services/phone-verification.service.spec.ts +0 -1120
- package/src/services/phone-verification.service.ts +0 -751
- package/src/services/risk-detection.service.spec.ts +0 -1292
- package/src/services/risk-detection.service.ts +0 -1012
- package/src/services/risk-scoring.service.spec.ts +0 -204
- package/src/services/risk-scoring.service.ts +0 -131
- package/src/services/session.service.spec.ts +0 -1293
- package/src/services/session.service.ts +0 -803
- package/src/services/social-account.service.spec.ts +0 -725
- package/src/services/social-auth-base.service.spec.ts +0 -418
- package/src/services/social-auth-base.service.ts +0 -581
- package/src/services/social-auth.service.spec.ts +0 -238
- package/src/services/social-auth.service.ts +0 -436
- package/src/services/social-provider-registry.service.spec.ts +0 -238
- package/src/services/social-provider-registry.service.ts +0 -122
- package/src/services/trusted-device.service.spec.ts +0 -505
- package/src/services/trusted-device.service.ts +0 -339
- package/src/storage/account-lockout-storage.service.spec.ts +0 -310
- package/src/storage/account-lockout-storage.service.ts +0 -89
- package/src/storage/index.ts +0 -3
- package/src/storage/memory-storage.adapter.ts +0 -443
- package/src/storage/rate-limit-storage.service.spec.ts +0 -247
- package/src/storage/rate-limit-storage.service.ts +0 -38
- package/src/templates/html-template.engine.spec.ts +0 -161
- package/src/templates/html-template.engine.ts +0 -688
- package/src/templates/index.ts +0 -7
- package/src/utils/common-passwords.spec.ts +0 -230
- package/src/utils/common-passwords.ts +0 -170
- package/src/utils/context-storage.ts +0 -188
- package/src/utils/cookie-names.util.ts +0 -67
- package/src/utils/cookies.util.ts +0 -94
- package/src/utils/index.ts +0 -12
- package/src/utils/ip-extractor.spec.ts +0 -330
- package/src/utils/ip-extractor.ts +0 -220
- package/src/utils/nauth-logger.spec.ts +0 -388
- package/src/utils/nauth-logger.ts +0 -215
- package/src/utils/pii-redactor.spec.ts +0 -130
- package/src/utils/pii-redactor.ts +0 -288
- package/src/utils/setup/get-repositories.ts +0 -140
- package/src/utils/setup/init-services.ts +0 -422
- package/src/utils/setup/init-social.ts +0 -189
- package/src/utils/setup/init-storage.ts +0 -94
- package/src/utils/setup/register-mfa.ts +0 -165
- package/src/utils/setup/run-nauth-migrations.ts +0 -61
- package/src/utils/token-delivery-policy.ts +0 -38
- package/src/validators/template.validator.ts +0 -219
- package/tsconfig.json +0 -37
- package/tsconfig.lint.json +0 -6
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
import { IUser } from '../interfaces/entities.interface';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* User Response DTO
|
|
5
|
-
*
|
|
6
|
-
* Sanitized user object for API responses.
|
|
7
|
-
* Excludes all sensitive and internal fields.
|
|
8
|
-
*
|
|
9
|
-
* Security:
|
|
10
|
-
* - Never exposes password hash
|
|
11
|
-
* - Never exposes MFA secrets
|
|
12
|
-
* - Never exposes internal tracking fields
|
|
13
|
-
* - Exposes 'sub' (external UUID) instead of internal 'id'
|
|
14
|
-
*
|
|
15
|
-
* No validators needed - this is generated internally by the library via fromEntity().
|
|
16
|
-
*
|
|
17
|
-
* @example
|
|
18
|
-
* ```typescript
|
|
19
|
-
* const user = await userRepository.findOne({ where: { sub } });
|
|
20
|
-
* return UserResponseDto.fromEntity(user);
|
|
21
|
-
* ```
|
|
22
|
-
*/
|
|
23
|
-
export class UserResponseDto {
|
|
24
|
-
/**
|
|
25
|
-
* External user identifier (UUID v4)
|
|
26
|
-
* This is the 'sub' (subject) field from JWT tokens
|
|
27
|
-
*/
|
|
28
|
-
sub!: string;
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* User's email address
|
|
32
|
-
*/
|
|
33
|
-
email!: string;
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* User's username (optional)
|
|
37
|
-
*/
|
|
38
|
-
username?: string | null;
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* User's first name (optional)
|
|
42
|
-
*/
|
|
43
|
-
firstName?: string | null;
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* User's last name (optional)
|
|
47
|
-
*/
|
|
48
|
-
lastName?: string | null;
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* User's phone number (optional)
|
|
52
|
-
* E.164 format validated in service layer if present
|
|
53
|
-
*/
|
|
54
|
-
phone?: string | null;
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Email verification status
|
|
58
|
-
*/
|
|
59
|
-
isEmailVerified!: boolean;
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Phone verification status
|
|
63
|
-
*/
|
|
64
|
-
isPhoneVerified!: boolean;
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Account active status
|
|
68
|
-
*/
|
|
69
|
-
isActive!: boolean;
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* MFA enabled status
|
|
73
|
-
*/
|
|
74
|
-
mfaEnabled!: boolean;
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Array of social providers linked to this account
|
|
78
|
-
*
|
|
79
|
-
* Examples: ['google', 'apple', 'facebook']
|
|
80
|
-
* null/undefined means no social auth, only password-based
|
|
81
|
-
*/
|
|
82
|
-
socialProviders?: string[] | null;
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Whether this user has a password set
|
|
86
|
-
* Used to determine if user can use password-based authentication
|
|
87
|
-
* or is a pure social signup (no password, only social auth)
|
|
88
|
-
*/
|
|
89
|
-
hasPasswordHash!: boolean;
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Account creation timestamp
|
|
93
|
-
*/
|
|
94
|
-
createdAt!: Date;
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Last account update timestamp
|
|
98
|
-
*/
|
|
99
|
-
updatedAt!: Date;
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Convert User entity to safe response DTO
|
|
103
|
-
*
|
|
104
|
-
* @param user - User entity from database
|
|
105
|
-
* @returns Sanitized user object with external identifier (sub)
|
|
106
|
-
*/
|
|
107
|
-
static fromEntity(user: IUser): UserResponseDto {
|
|
108
|
-
const dto = new UserResponseDto();
|
|
109
|
-
|
|
110
|
-
// Essential fields only
|
|
111
|
-
dto.sub = user.sub; // External UUID identifier
|
|
112
|
-
dto.email = user.email;
|
|
113
|
-
dto.username = user.username;
|
|
114
|
-
dto.firstName = user.firstName;
|
|
115
|
-
dto.lastName = user.lastName;
|
|
116
|
-
dto.phone = user.phone;
|
|
117
|
-
dto.isEmailVerified = user.isEmailVerified;
|
|
118
|
-
dto.isPhoneVerified = user.isPhoneVerified;
|
|
119
|
-
dto.isActive = user.isActive;
|
|
120
|
-
dto.mfaEnabled = user.mfaEnabled;
|
|
121
|
-
dto.socialProviders = user.socialProviders;
|
|
122
|
-
dto.hasPasswordHash = !!user.passwordHash; // Check if password exists
|
|
123
|
-
dto.createdAt = user.createdAt;
|
|
124
|
-
dto.updatedAt = user.updatedAt;
|
|
125
|
-
|
|
126
|
-
return dto;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* Convert array of User entities to safe response DTOs
|
|
131
|
-
*
|
|
132
|
-
* @param users - Array of User entities
|
|
133
|
-
* @returns Array of sanitized user objects
|
|
134
|
-
*/
|
|
135
|
-
static fromEntities(users: IUser[]): UserResponseDto[] {
|
|
136
|
-
return users.map((user) => UserResponseDto.fromEntity(user));
|
|
137
|
-
}
|
|
138
|
-
}
|
|
@@ -1,222 +0,0 @@
|
|
|
1
|
-
import { IsEmail, IsString, MinLength, MaxLength, IsOptional, Matches, IsBoolean, IsEnum } from 'class-validator';
|
|
2
|
-
import { Transform } from 'class-transformer';
|
|
3
|
-
import { MFADeviceMethod, MFAMethod } from '../enums/mfa-method.enum';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* DTO for updating user attributes
|
|
7
|
-
*
|
|
8
|
-
* Security:
|
|
9
|
-
* - All fields validated against DB constraints
|
|
10
|
-
* - Input sanitization applied automatically
|
|
11
|
-
* - Email uniqueness checked in service layer
|
|
12
|
-
* - Phone uniqueness checked in service layer
|
|
13
|
-
* - Username uniqueness checked in service layer
|
|
14
|
-
*
|
|
15
|
-
* @example
|
|
16
|
-
* ```typescript
|
|
17
|
-
* const updateData: UserUpdateDTO = {
|
|
18
|
-
* firstName: 'John',
|
|
19
|
-
* lastName: 'Doe',
|
|
20
|
-
* email: 'john.doe@example.com',
|
|
21
|
-
* phone: '+61444567890'
|
|
22
|
-
* };
|
|
23
|
-
* ```
|
|
24
|
-
*/
|
|
25
|
-
export class UserUpdateDTO {
|
|
26
|
-
/**
|
|
27
|
-
* Optional username update
|
|
28
|
-
*
|
|
29
|
-
* Validation:
|
|
30
|
-
* - 3-50 characters
|
|
31
|
-
* - Alphanumeric, underscores, and hyphens only
|
|
32
|
-
* - Max 255 characters (DB limit)
|
|
33
|
-
* - Uniqueness checked in service layer
|
|
34
|
-
*
|
|
35
|
-
* Sanitization:
|
|
36
|
-
* - Trimmed
|
|
37
|
-
* - Case preserved (username can be case-sensitive per config)
|
|
38
|
-
*/
|
|
39
|
-
@IsOptional()
|
|
40
|
-
@IsString({ message: 'Username must be a string' })
|
|
41
|
-
@MinLength(3, { message: 'Username must be at least 3 characters' })
|
|
42
|
-
@MaxLength(255, { message: 'Username must not exceed 255 characters' })
|
|
43
|
-
@Matches(/^[a-zA-Z0-9_-]+$/, {
|
|
44
|
-
message: 'Username can only contain letters, numbers, underscores, and hyphens',
|
|
45
|
-
})
|
|
46
|
-
@Transform(({ value }) => {
|
|
47
|
-
if (typeof value === 'string') {
|
|
48
|
-
return value.trim();
|
|
49
|
-
}
|
|
50
|
-
return value;
|
|
51
|
-
})
|
|
52
|
-
username?: string;
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Optional first name update
|
|
56
|
-
*
|
|
57
|
-
* Validation:
|
|
58
|
-
* - 1-100 characters
|
|
59
|
-
* - Letters, spaces, hyphens, and apostrophes only
|
|
60
|
-
* - Max 100 characters (DB limit)
|
|
61
|
-
*
|
|
62
|
-
* Sanitization:
|
|
63
|
-
* - Trimmed
|
|
64
|
-
* - Title case preserved
|
|
65
|
-
*/
|
|
66
|
-
@IsOptional()
|
|
67
|
-
@IsString({ message: 'First name must be a string' })
|
|
68
|
-
@MinLength(1, { message: 'First name must be at least 1 character' })
|
|
69
|
-
@MaxLength(100, { message: 'First name must not exceed 100 characters' })
|
|
70
|
-
@Matches(/^[a-zA-Z\s\-']+$/, {
|
|
71
|
-
message: 'First name can only contain letters, spaces, hyphens, and apostrophes',
|
|
72
|
-
})
|
|
73
|
-
@Transform(({ value }) => {
|
|
74
|
-
if (typeof value === 'string') {
|
|
75
|
-
return value.trim();
|
|
76
|
-
}
|
|
77
|
-
return value;
|
|
78
|
-
})
|
|
79
|
-
firstName?: string;
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Optional last name update
|
|
83
|
-
*
|
|
84
|
-
* Validation:
|
|
85
|
-
* - 1-100 characters
|
|
86
|
-
* - Letters, spaces, hyphens, and apostrophes only
|
|
87
|
-
* - Max 100 characters (DB limit)
|
|
88
|
-
*
|
|
89
|
-
* Sanitization:
|
|
90
|
-
* - Trimmed
|
|
91
|
-
* - Title case preserved
|
|
92
|
-
*/
|
|
93
|
-
@IsOptional()
|
|
94
|
-
@IsString({ message: 'Last name must be a string' })
|
|
95
|
-
@MinLength(1, { message: 'Last name must be at least 1 character' })
|
|
96
|
-
@MaxLength(100, { message: 'Last name must not exceed 100 characters' })
|
|
97
|
-
@Matches(/^[a-zA-Z\s\-']+$/, {
|
|
98
|
-
message: 'Last name can only contain letters, spaces, hyphens, and apostrophes',
|
|
99
|
-
})
|
|
100
|
-
@Transform(({ value }) => {
|
|
101
|
-
if (typeof value === 'string') {
|
|
102
|
-
return value.trim();
|
|
103
|
-
}
|
|
104
|
-
return value;
|
|
105
|
-
})
|
|
106
|
-
lastName?: string;
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Optional email address update
|
|
110
|
-
*
|
|
111
|
-
* Validation:
|
|
112
|
-
* - Valid email format (RFC 5322)
|
|
113
|
-
* - Max 255 characters (matches DB limit)
|
|
114
|
-
* - Uniqueness checked in service layer
|
|
115
|
-
*
|
|
116
|
-
* Sanitization:
|
|
117
|
-
* - Trimmed and lowercased
|
|
118
|
-
*/
|
|
119
|
-
@IsOptional()
|
|
120
|
-
@IsEmail({}, { message: 'Invalid email format' })
|
|
121
|
-
@MaxLength(255, { message: 'Email must not exceed 255 characters' })
|
|
122
|
-
@Transform(({ value }) => {
|
|
123
|
-
if (typeof value === 'string') {
|
|
124
|
-
return value.trim().toLowerCase();
|
|
125
|
-
}
|
|
126
|
-
return value;
|
|
127
|
-
})
|
|
128
|
-
email?: string;
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* Optional phone number update
|
|
132
|
-
*
|
|
133
|
-
* Validation:
|
|
134
|
-
* - E.164 format (international standard)
|
|
135
|
-
* - MUST start with + (required for security)
|
|
136
|
-
* - Max 20 characters (DB limit)
|
|
137
|
-
* - Uniqueness checked in service layer
|
|
138
|
-
*
|
|
139
|
-
* Sanitization:
|
|
140
|
-
* - Whitespace removed
|
|
141
|
-
* - Only digits and leading + preserved
|
|
142
|
-
*
|
|
143
|
-
* Security:
|
|
144
|
-
* - Strict E.164 validation prevents SQL injection
|
|
145
|
-
* - Max length prevents oversized inputs
|
|
146
|
-
*/
|
|
147
|
-
@IsOptional()
|
|
148
|
-
@IsString({ message: 'Phone must be a string' })
|
|
149
|
-
@MaxLength(20, { message: 'Phone must not exceed 20 characters' })
|
|
150
|
-
@Matches(/^\+[1-9]\d{1,14}$/, {
|
|
151
|
-
message: 'Phone must be in E.164 format with + prefix (e.g., +14155552671)',
|
|
152
|
-
})
|
|
153
|
-
@Transform(({ value }) => {
|
|
154
|
-
if (typeof value === 'string') {
|
|
155
|
-
// Remove all whitespace and keep only digits and +
|
|
156
|
-
return value.replace(/\s/g, '');
|
|
157
|
-
}
|
|
158
|
-
return value;
|
|
159
|
-
})
|
|
160
|
-
phone?: string;
|
|
161
|
-
|
|
162
|
-
/**
|
|
163
|
-
* Optional metadata update (custom fields)
|
|
164
|
-
*
|
|
165
|
-
* Security:
|
|
166
|
-
* - Validated in service layer if used
|
|
167
|
-
* - Max depth/size limits should be enforced
|
|
168
|
-
* - Existing metadata merged with new values
|
|
169
|
-
*/
|
|
170
|
-
@IsOptional()
|
|
171
|
-
metadata?: Record<string, unknown>;
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* Optional preferred MFA method
|
|
175
|
-
*
|
|
176
|
-
* Sets the user's preferred MFA method for authentication.
|
|
177
|
-
* Must be one of the MFA device methods the user has configured.
|
|
178
|
-
*
|
|
179
|
-
* Validation:
|
|
180
|
-
* - Must be one of: totp, sms, email, passkey
|
|
181
|
-
* - Max 50 characters (matches typical method name length)
|
|
182
|
-
*
|
|
183
|
-
* @example
|
|
184
|
-
* ```typescript
|
|
185
|
-
* await authService.updateUserAttributes(userId, {
|
|
186
|
-
* preferredMfaMethod: 'totp'
|
|
187
|
-
* });
|
|
188
|
-
* ```
|
|
189
|
-
*/
|
|
190
|
-
@IsOptional()
|
|
191
|
-
@IsEnum([MFAMethod.TOTP, MFAMethod.SMS, MFAMethod.EMAIL, MFAMethod.PASSKEY], {
|
|
192
|
-
message: 'Preferred MFA method must be one of: totp, sms, email, passkey',
|
|
193
|
-
})
|
|
194
|
-
@MaxLength(50, { message: 'Preferred MFA method must not exceed 50 characters' })
|
|
195
|
-
preferredMfaMethod?: MFADeviceMethod;
|
|
196
|
-
|
|
197
|
-
/**
|
|
198
|
-
* Optional flag to retain verification status when updating email/phone
|
|
199
|
-
*
|
|
200
|
-
* When true:
|
|
201
|
-
* - Email verification status is preserved when email is updated
|
|
202
|
-
* - Phone verification status is preserved when phone is updated
|
|
203
|
-
* - Useful when verification was done externally or outside nauth-toolkit
|
|
204
|
-
*
|
|
205
|
-
* When false or undefined (default):
|
|
206
|
-
* - Email verification is reset to false when email is updated
|
|
207
|
-
* - Phone verification is reset to false when phone is updated
|
|
208
|
-
* - User must re-verify the new email/phone
|
|
209
|
-
*
|
|
210
|
-
* @example
|
|
211
|
-
* ```typescript
|
|
212
|
-
* // Update email but keep verification status (external verification)
|
|
213
|
-
* await authService.updateUserAttributes(userId, {
|
|
214
|
-
* email: 'new@example.com',
|
|
215
|
-
* retainVerification: true
|
|
216
|
-
* });
|
|
217
|
-
* ```
|
|
218
|
-
*/
|
|
219
|
-
@IsOptional()
|
|
220
|
-
@IsBoolean({ message: 'retainVerification must be a boolean' })
|
|
221
|
-
retainVerification?: boolean;
|
|
222
|
-
}
|
|
@@ -1,313 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
IsEmail,
|
|
3
|
-
IsString,
|
|
4
|
-
IsNumberString,
|
|
5
|
-
IsUrl,
|
|
6
|
-
MaxLength,
|
|
7
|
-
Length,
|
|
8
|
-
Matches,
|
|
9
|
-
IsBoolean,
|
|
10
|
-
IsOptional,
|
|
11
|
-
IsUUID,
|
|
12
|
-
IsInt,
|
|
13
|
-
Min,
|
|
14
|
-
} from 'class-validator';
|
|
15
|
-
import { Transform } from 'class-transformer';
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* DTO for verifying email with code (6-digit OTP)
|
|
19
|
-
*
|
|
20
|
-
* Security:
|
|
21
|
-
* - Email must be valid format and match DB limits
|
|
22
|
-
* - Code must be exactly 6 digits (no more, no less)
|
|
23
|
-
* - All fields are required (no optional fields to prevent attacks)
|
|
24
|
-
* - Input sanitization applied automatically
|
|
25
|
-
*/
|
|
26
|
-
export class VerifyEmailWithCodeDTO {
|
|
27
|
-
/**
|
|
28
|
-
* User's email address
|
|
29
|
-
* Must match the email used during signup
|
|
30
|
-
*
|
|
31
|
-
* Validation:
|
|
32
|
-
* - Valid email format (RFC 5322)
|
|
33
|
-
* - Max 255 characters (matches DB column limit)
|
|
34
|
-
* - Automatically trimmed and lowercased
|
|
35
|
-
*
|
|
36
|
-
* Sanitization:
|
|
37
|
-
* - Removes leading/trailing whitespace
|
|
38
|
-
* - Converts to lowercase for case-insensitive matching
|
|
39
|
-
*/
|
|
40
|
-
@IsEmail({}, { message: 'Invalid email format' })
|
|
41
|
-
@MaxLength(255, { message: 'Email must not exceed 255 characters' })
|
|
42
|
-
@Transform(({ value }) => {
|
|
43
|
-
if (typeof value === 'string') {
|
|
44
|
-
return value.trim().toLowerCase();
|
|
45
|
-
}
|
|
46
|
-
return value;
|
|
47
|
-
})
|
|
48
|
-
email!: string;
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* 6-digit verification code from email
|
|
52
|
-
*
|
|
53
|
-
* Validation:
|
|
54
|
-
* - Must be numeric string (digits only)
|
|
55
|
-
* - Exactly 6 characters long
|
|
56
|
-
* - Fixed length prevents timing attacks
|
|
57
|
-
*
|
|
58
|
-
* Sanitization:
|
|
59
|
-
* - Removes all whitespace (users might copy "123 456")
|
|
60
|
-
* - Removes non-digit characters
|
|
61
|
-
*/
|
|
62
|
-
@IsNumberString({}, { message: 'Verification code must contain only digits' })
|
|
63
|
-
@MaxLength(6, { message: 'Verification code must be exactly 6 digits' })
|
|
64
|
-
@Transform(({ value }) => {
|
|
65
|
-
if (typeof value === 'string') {
|
|
66
|
-
// Remove all whitespace and non-digit characters, then validate length
|
|
67
|
-
const cleaned = value.replace(/\D/g, '');
|
|
68
|
-
return cleaned.length === 6 ? cleaned : value; // Return original if not 6 digits (let validator catch it)
|
|
69
|
-
}
|
|
70
|
-
return value;
|
|
71
|
-
})
|
|
72
|
-
code!: string;
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Challenge session ID (internal use)
|
|
76
|
-
* Optional - used internally to link verification to specific challenge session.
|
|
77
|
-
* Provides security by ensuring codes are only valid for the session they were created for.
|
|
78
|
-
*
|
|
79
|
-
* Validation:
|
|
80
|
-
* - Must be a positive integer if provided
|
|
81
|
-
* - Optional (for backward compatibility and direct verification flows)
|
|
82
|
-
*/
|
|
83
|
-
@IsOptional()
|
|
84
|
-
@IsInt({ message: 'challengeSessionId must be an integer' })
|
|
85
|
-
@Min(1, { message: 'challengeSessionId must be a positive integer' })
|
|
86
|
-
challengeSessionId?: number;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* DTO for verifying email with URL token
|
|
91
|
-
*
|
|
92
|
-
* Security:
|
|
93
|
-
* - Token must be valid hex format
|
|
94
|
-
* - Exact length enforced (64 chars = 32 bytes SHA-256 hash)
|
|
95
|
-
* - No SQL injection or XSS possible
|
|
96
|
-
* - Input sanitization prevents malformed tokens
|
|
97
|
-
*/
|
|
98
|
-
export class VerifyEmailWithTokenDTO {
|
|
99
|
-
/**
|
|
100
|
-
* Verification token from email link
|
|
101
|
-
*
|
|
102
|
-
* Validation:
|
|
103
|
-
* - Exactly 64 hexadecimal characters (SHA-256 hash output)
|
|
104
|
-
* - Only 0-9 and a-f characters allowed
|
|
105
|
-
* - Case-insensitive
|
|
106
|
-
*
|
|
107
|
-
* Sanitization:
|
|
108
|
-
* - Removes whitespace
|
|
109
|
-
* - Converts to lowercase for consistent hashing
|
|
110
|
-
*/
|
|
111
|
-
@IsString({ message: 'Token must be a string' })
|
|
112
|
-
@Length(64, 64, { message: 'Invalid token format' })
|
|
113
|
-
@Matches(/^[a-f0-9]{64}$/i, {
|
|
114
|
-
message: 'Token must be a valid hexadecimal string',
|
|
115
|
-
})
|
|
116
|
-
@Transform(({ value }) => {
|
|
117
|
-
if (typeof value === 'string') {
|
|
118
|
-
return value.trim().toLowerCase();
|
|
119
|
-
}
|
|
120
|
-
return value;
|
|
121
|
-
})
|
|
122
|
-
token!: string;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* DTO for sending a verification email
|
|
127
|
-
*
|
|
128
|
-
* Security:
|
|
129
|
-
* - User sub validated as UUID v4
|
|
130
|
-
* - BaseURL validated as max length
|
|
131
|
-
* - Skip flag is boolean (prevents injection)
|
|
132
|
-
*/
|
|
133
|
-
export class SendVerificationEmailDTO {
|
|
134
|
-
/**
|
|
135
|
-
* User identifier (UUID v4)
|
|
136
|
-
*
|
|
137
|
-
* Validation:
|
|
138
|
-
* - Must be valid UUID v4 format
|
|
139
|
-
*
|
|
140
|
-
* Sanitization:
|
|
141
|
-
* - Trimmed and lowercased
|
|
142
|
-
*/
|
|
143
|
-
@IsUUID('4', { message: 'User ID must be a valid UUID v4 format' })
|
|
144
|
-
@Transform(({ value }) => {
|
|
145
|
-
if (typeof value === 'string') {
|
|
146
|
-
return value.trim().toLowerCase();
|
|
147
|
-
}
|
|
148
|
-
return value;
|
|
149
|
-
})
|
|
150
|
-
sub!: string;
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* Base URL for verification link (optional)
|
|
154
|
-
*
|
|
155
|
-
* Validation:
|
|
156
|
-
* - Must be valid URL format (http:// or https://)
|
|
157
|
-
* - Max 2048 characters (typical URL length limit)
|
|
158
|
-
* - Optional field
|
|
159
|
-
*
|
|
160
|
-
* Sanitization:
|
|
161
|
-
* - Trimmed
|
|
162
|
-
*/
|
|
163
|
-
@IsOptional()
|
|
164
|
-
@IsUrl(
|
|
165
|
-
{ require_protocol: true, protocols: ['http', 'https'] },
|
|
166
|
-
{ message: 'Base URL must be a valid URL with http:// or https://' },
|
|
167
|
-
)
|
|
168
|
-
@MaxLength(2048, { message: 'Base URL must not exceed 2048 characters' })
|
|
169
|
-
@Transform(({ value }) => {
|
|
170
|
-
if (typeof value === 'string') {
|
|
171
|
-
return value.trim();
|
|
172
|
-
}
|
|
173
|
-
return value;
|
|
174
|
-
})
|
|
175
|
-
baseUrl?: string;
|
|
176
|
-
|
|
177
|
-
/**
|
|
178
|
-
* Skip the "already verified" check
|
|
179
|
-
* Used for MFA contexts where codes are needed even if email is verified
|
|
180
|
-
*
|
|
181
|
-
* Validation:
|
|
182
|
-
* - Must be boolean
|
|
183
|
-
* - Optional (defaults to false)
|
|
184
|
-
*/
|
|
185
|
-
@IsOptional()
|
|
186
|
-
@IsBoolean({ message: 'skipAlreadyVerifiedCheck must be a boolean' })
|
|
187
|
-
skipAlreadyVerifiedCheck?: boolean;
|
|
188
|
-
|
|
189
|
-
/**
|
|
190
|
-
* Challenge session ID to link this verification token to
|
|
191
|
-
* Optional - for linking verification tokens to specific challenge sessions.
|
|
192
|
-
* Provides security by preventing old tokens from being used with new sessions.
|
|
193
|
-
*
|
|
194
|
-
* Validation:
|
|
195
|
-
* - Must be a positive integer
|
|
196
|
-
* - Optional (for backward compatibility and non-challenge flows like password reset)
|
|
197
|
-
*/
|
|
198
|
-
@IsOptional()
|
|
199
|
-
@IsInt({ message: 'challengeSessionId must be an integer' })
|
|
200
|
-
@Min(1, { message: 'challengeSessionId must be a positive integer' })
|
|
201
|
-
challengeSessionId?: number;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
/**
|
|
205
|
-
* Response DTO for sendVerificationEmail
|
|
206
|
-
*/
|
|
207
|
-
export class SendVerificationEmailResponseDTO {
|
|
208
|
-
/**
|
|
209
|
-
* Verification token ID (internal integer)
|
|
210
|
-
*/
|
|
211
|
-
tokenId!: number;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
/**
|
|
215
|
-
* DTO for requesting a verification email resend
|
|
216
|
-
*
|
|
217
|
-
* Supports both overload patterns:
|
|
218
|
-
* 1. Resend by user sub (string)
|
|
219
|
-
* 2. Resend by email address (object with email property)
|
|
220
|
-
*
|
|
221
|
-
* Security:
|
|
222
|
-
* - Either sub or email must be provided (conditional validation)
|
|
223
|
-
* - Rate limiting applied in service layer
|
|
224
|
-
* - Input sanitization prevents abuse
|
|
225
|
-
*/
|
|
226
|
-
export class ResendVerificationEmailDTO {
|
|
227
|
-
/**
|
|
228
|
-
* User identifier (UUID v4) - optional if email provided
|
|
229
|
-
*
|
|
230
|
-
* Validation:
|
|
231
|
-
* - Must be valid UUID v4 format if provided
|
|
232
|
-
* - Required if email is not provided
|
|
233
|
-
*
|
|
234
|
-
* Sanitization:
|
|
235
|
-
* - Trimmed and lowercased
|
|
236
|
-
*/
|
|
237
|
-
@IsOptional()
|
|
238
|
-
@IsUUID('4', { message: 'User ID must be a valid UUID v4 format' })
|
|
239
|
-
@Transform(({ value }) => {
|
|
240
|
-
if (typeof value === 'string') {
|
|
241
|
-
return value.trim().toLowerCase();
|
|
242
|
-
}
|
|
243
|
-
return value;
|
|
244
|
-
})
|
|
245
|
-
sub?: string;
|
|
246
|
-
|
|
247
|
-
/**
|
|
248
|
-
* User's email address - optional if sub provided
|
|
249
|
-
*
|
|
250
|
-
* Validation:
|
|
251
|
-
* - Valid email format if provided
|
|
252
|
-
* - Max 255 characters (DB limit)
|
|
253
|
-
* - Required if sub is not provided
|
|
254
|
-
*
|
|
255
|
-
* Sanitization:
|
|
256
|
-
* - Trimmed and lowercased
|
|
257
|
-
*/
|
|
258
|
-
@IsOptional()
|
|
259
|
-
@IsEmail({}, { message: 'Invalid email format' })
|
|
260
|
-
@MaxLength(255, { message: 'Email must not exceed 255 characters' })
|
|
261
|
-
@Transform(({ value }) => {
|
|
262
|
-
if (typeof value === 'string') {
|
|
263
|
-
return value.trim().toLowerCase();
|
|
264
|
-
}
|
|
265
|
-
return value;
|
|
266
|
-
})
|
|
267
|
-
email?: string;
|
|
268
|
-
|
|
269
|
-
/**
|
|
270
|
-
* Base URL for verification link (optional)
|
|
271
|
-
*
|
|
272
|
-
* Validation:
|
|
273
|
-
* - Must be valid URL format (http:// or https://)
|
|
274
|
-
* - Max 2048 characters
|
|
275
|
-
* - Optional field
|
|
276
|
-
*
|
|
277
|
-
* Sanitization:
|
|
278
|
-
* - Trimmed
|
|
279
|
-
*/
|
|
280
|
-
@IsOptional()
|
|
281
|
-
@IsUrl(
|
|
282
|
-
{ require_protocol: true, protocols: ['http', 'https'] },
|
|
283
|
-
{ message: 'Base URL must be a valid URL with http:// or https://' },
|
|
284
|
-
)
|
|
285
|
-
@MaxLength(2048, { message: 'Base URL must not exceed 2048 characters' })
|
|
286
|
-
@Transform(({ value }) => {
|
|
287
|
-
if (typeof value === 'string') {
|
|
288
|
-
return value.trim();
|
|
289
|
-
}
|
|
290
|
-
return value;
|
|
291
|
-
})
|
|
292
|
-
baseUrl?: string;
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
/**
|
|
296
|
-
* Response DTO for resendVerificationEmail
|
|
297
|
-
*/
|
|
298
|
-
export class ResendVerificationEmailResponseDTO {
|
|
299
|
-
/**
|
|
300
|
-
* Verification token ID (internal integer)
|
|
301
|
-
*/
|
|
302
|
-
tokenId!: number;
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
/**
|
|
306
|
-
* Response DTO for verifyEmailWithCode and verifyEmailWithToken
|
|
307
|
-
*/
|
|
308
|
-
export class VerifyEmailResponseDTO {
|
|
309
|
-
/**
|
|
310
|
-
* Success message
|
|
311
|
-
*/
|
|
312
|
-
message!: string;
|
|
313
|
-
}
|