@solidxai/core 0.1.4 → 0.1.5-beta.1
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/.claude/settings.local.json +8 -0
- package/dist/constants/error-messages.d.ts +1 -0
- package/dist/constants/error-messages.d.ts.map +1 -1
- package/dist/constants/error-messages.js +1 -0
- package/dist/constants/error-messages.js.map +1 -1
- package/dist/constants.d.ts +3 -3
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +12 -12
- package/dist/constants.js.map +1 -1
- package/dist/controllers/otp-authentication.controller.d.ts +1 -4
- package/dist/controllers/otp-authentication.controller.d.ts.map +1 -1
- package/dist/controllers/otp-authentication.controller.js +1 -1
- package/dist/controllers/role-metadata.controller.d.ts +1 -0
- package/dist/controllers/role-metadata.controller.d.ts.map +1 -1
- package/dist/controllers/role-metadata.controller.js +15 -0
- package/dist/controllers/role-metadata.controller.js.map +1 -1
- package/dist/controllers/view-metadata.controller.d.ts +1 -0
- package/dist/controllers/view-metadata.controller.d.ts.map +1 -1
- package/dist/dtos/create-email-template.dto.d.ts.map +1 -1
- package/dist/dtos/create-email-template.dto.js.map +1 -1
- package/dist/dtos/create-list-of-values.dto.d.ts.map +1 -1
- package/dist/dtos/create-list-of-values.dto.js.map +1 -1
- package/dist/dtos/create-menu-item-metadata.dto.d.ts.map +1 -1
- package/dist/dtos/create-menu-item-metadata.dto.js.map +1 -1
- package/dist/dtos/create-role-metadata.dto.d.ts.map +1 -1
- package/dist/dtos/create-role-metadata.dto.js.map +1 -1
- package/dist/dtos/create-saved-filters.dto.d.ts +1 -0
- package/dist/dtos/create-saved-filters.dto.d.ts.map +1 -1
- package/dist/dtos/create-saved-filters.dto.js +8 -1
- package/dist/dtos/create-saved-filters.dto.js.map +1 -1
- package/dist/dtos/create-scheduled-job.dto.d.ts.map +1 -1
- package/dist/dtos/create-scheduled-job.dto.js.map +1 -1
- package/dist/dtos/create-security-rule.dto.d.ts.map +1 -1
- package/dist/dtos/create-security-rule.dto.js.map +1 -1
- package/dist/dtos/create-sms-template.dto.d.ts.map +1 -1
- package/dist/dtos/create-sms-template.dto.js.map +1 -1
- package/dist/dtos/create-view-metadata.dto.d.ts.map +1 -1
- package/dist/dtos/create-view-metadata.dto.js.map +1 -1
- package/dist/dtos/otp-sign-in.dto.d.ts +1 -1
- package/dist/dtos/otp-sign-in.dto.d.ts.map +1 -1
- package/dist/dtos/otp-sign-in.dto.js +2 -2
- package/dist/dtos/otp-sign-in.dto.js.map +1 -1
- package/dist/dtos/otp-sign-up.dto.d.ts +2 -2
- package/dist/dtos/otp-sign-up.dto.d.ts.map +1 -1
- package/dist/dtos/otp-sign-up.dto.js +2 -2
- package/dist/dtos/otp-sign-up.dto.js.map +1 -1
- package/dist/dtos/resolve-s3-url.dto.d.ts +2 -5
- package/dist/dtos/resolve-s3-url.dto.d.ts.map +1 -1
- package/dist/dtos/resolve-s3-url.dto.js +1 -13
- package/dist/dtos/resolve-s3-url.dto.js.map +1 -1
- package/dist/dtos/sign-up.dto.d.ts.map +1 -1
- package/dist/dtos/sign-up.dto.js.map +1 -1
- package/dist/dtos/update-email-template.dto.d.ts.map +1 -1
- package/dist/dtos/update-email-template.dto.js.map +1 -1
- package/dist/dtos/update-list-of-values.dto.d.ts.map +1 -1
- package/dist/dtos/update-list-of-values.dto.js.map +1 -1
- package/dist/dtos/update-menu-item-metadata.dto.d.ts.map +1 -1
- package/dist/dtos/update-menu-item-metadata.dto.js.map +1 -1
- package/dist/dtos/update-saved-filters.dto.d.ts +1 -0
- package/dist/dtos/update-saved-filters.dto.d.ts.map +1 -1
- package/dist/dtos/update-saved-filters.dto.js +10 -1
- package/dist/dtos/update-saved-filters.dto.js.map +1 -1
- package/dist/dtos/update-scheduled-job.dto.d.ts.map +1 -1
- package/dist/dtos/update-scheduled-job.dto.js.map +1 -1
- package/dist/dtos/update-security-rule.dto.d.ts.map +1 -1
- package/dist/dtos/update-security-rule.dto.js.map +1 -1
- package/dist/dtos/update-sms-template.dto.d.ts.map +1 -1
- package/dist/dtos/update-sms-template.dto.js.map +1 -1
- package/dist/dtos/update-view-metadata.dto.d.ts.map +1 -1
- package/dist/dtos/update-view-metadata.dto.js.map +1 -1
- package/dist/entities/chatter-message-details.entity.d.ts.map +1 -1
- package/dist/entities/chatter-message-details.entity.js +1 -0
- package/dist/entities/chatter-message-details.entity.js.map +1 -1
- package/dist/entities/chatter-message.entity.d.ts.map +1 -1
- package/dist/entities/chatter-message.entity.js +1 -0
- package/dist/entities/chatter-message.entity.js.map +1 -1
- package/dist/entities/common.entity.js +4 -4
- package/dist/entities/common.entity.js.map +1 -1
- package/dist/entities/legacy-common.entity.js +4 -4
- package/dist/entities/legacy-common.entity.js.map +1 -1
- package/dist/entities/saved-filters.entity.d.ts +1 -0
- package/dist/entities/saved-filters.entity.d.ts.map +1 -1
- package/dist/entities/saved-filters.entity.js +6 -1
- package/dist/entities/saved-filters.entity.js.map +1 -1
- package/dist/entities/user.entity.d.ts +1 -0
- package/dist/entities/user.entity.d.ts.map +1 -1
- package/dist/entities/user.entity.js +6 -1
- package/dist/entities/user.entity.js.map +1 -1
- package/dist/helpers/field-crud-managers/ManyToManyRelationFieldCrudManager.d.ts +2 -0
- package/dist/helpers/field-crud-managers/ManyToManyRelationFieldCrudManager.d.ts.map +1 -1
- package/dist/helpers/field-crud-managers/ManyToManyRelationFieldCrudManager.js +33 -23
- package/dist/helpers/field-crud-managers/ManyToManyRelationFieldCrudManager.js.map +1 -1
- package/dist/helpers/field-crud-managers/OneToManyRelationFieldCrudManager.d.ts +3 -0
- package/dist/helpers/field-crud-managers/OneToManyRelationFieldCrudManager.d.ts.map +1 -1
- package/dist/helpers/field-crud-managers/OneToManyRelationFieldCrudManager.js +36 -23
- package/dist/helpers/field-crud-managers/OneToManyRelationFieldCrudManager.js.map +1 -1
- package/dist/helpers/security.helper.js +1 -0
- package/dist/helpers/security.helper.js.map +1 -1
- package/dist/index.d.ts +0 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -4
- package/dist/index.js.map +1 -1
- package/dist/repository/solid-base.repository.d.ts +10 -1
- package/dist/repository/solid-base.repository.d.ts.map +1 -1
- package/dist/repository/solid-base.repository.js +109 -0
- package/dist/repository/solid-base.repository.js.map +1 -1
- package/dist/seeders/module-metadata-seeder.service.d.ts +2 -0
- package/dist/seeders/module-metadata-seeder.service.d.ts.map +1 -1
- package/dist/seeders/module-metadata-seeder.service.js +142 -72
- package/dist/seeders/module-metadata-seeder.service.js.map +1 -1
- package/dist/seeders/permission-metadata-seeder.service.d.ts +1 -1
- package/dist/seeders/permission-metadata-seeder.service.d.ts.map +1 -1
- package/dist/seeders/permission-metadata-seeder.service.js +1 -1
- package/dist/seeders/permission-metadata-seeder.service.js.map +1 -1
- package/dist/seeders/seed-data/solid-core-metadata.json +23 -25
- package/dist/services/authentication.service.d.ts +22 -8
- package/dist/services/authentication.service.d.ts.map +1 -1
- package/dist/services/authentication.service.js +230 -214
- package/dist/services/authentication.service.js.map +1 -1
- package/dist/services/crud-helper.service.d.ts +4 -0
- package/dist/services/crud-helper.service.d.ts.map +1 -1
- package/dist/services/crud-helper.service.js +66 -32
- package/dist/services/crud-helper.service.js.map +1 -1
- package/dist/services/crud.service.d.ts.map +1 -1
- package/dist/services/crud.service.js +7 -4
- package/dist/services/crud.service.js.map +1 -1
- package/dist/services/field-metadata.service.d.ts.map +1 -1
- package/dist/services/field-metadata.service.js.map +1 -1
- package/dist/services/file/disk-file.service.d.ts +0 -2
- package/dist/services/file/disk-file.service.d.ts.map +1 -1
- package/dist/services/file/disk-file.service.js +7 -16
- package/dist/services/file/disk-file.service.js.map +1 -1
- package/dist/services/file/index.d.ts +1 -0
- package/dist/services/file/index.d.ts.map +1 -1
- package/dist/services/file/index.js +1 -0
- package/dist/services/file/index.js.map +1 -1
- package/dist/services/file/storage-path-builder.d.ts +17 -0
- package/dist/services/file/storage-path-builder.d.ts.map +1 -0
- package/dist/{seeders/sms-template-seeder.service.js → services/file/storage-path-builder.js} +45 -35
- package/dist/services/file/storage-path-builder.js.map +1 -0
- package/dist/services/media.service.d.ts +1 -1
- package/dist/services/media.service.d.ts.map +1 -1
- package/dist/services/media.service.js +45 -6
- package/dist/services/media.service.js.map +1 -1
- package/dist/services/mediaStorageProviders/file-s3-storage-provider.js.map +1 -1
- package/dist/services/mediaStorageProviders/file-storage-provider.d.ts.map +1 -1
- package/dist/services/mediaStorageProviders/file-storage-provider.js +46 -7
- package/dist/services/mediaStorageProviders/file-storage-provider.js.map +1 -1
- package/dist/services/module-metadata.service.d.ts +4 -6
- package/dist/services/module-metadata.service.d.ts.map +1 -1
- package/dist/services/module-metadata.service.js +16 -14
- package/dist/services/module-metadata.service.js.map +1 -1
- package/dist/services/setting.service.d.ts +3 -2
- package/dist/services/setting.service.d.ts.map +1 -1
- package/dist/services/setting.service.js +7 -4
- package/dist/services/setting.service.js.map +1 -1
- package/dist/services/settings/default-settings-provider.service.d.ts +24 -2
- package/dist/services/settings/default-settings-provider.service.d.ts.map +1 -1
- package/dist/services/settings/default-settings-provider.service.js +8 -6
- package/dist/services/settings/default-settings-provider.service.js.map +1 -1
- package/dist/services/view-metadata.service.d.ts +1 -0
- package/dist/services/view-metadata.service.d.ts.map +1 -1
- package/dist/services/view-metadata.service.js +25 -1
- package/dist/services/view-metadata.service.js.map +1 -1
- package/dist/solid-core.module.d.ts +3 -1
- package/dist/solid-core.module.d.ts.map +1 -1
- package/dist/solid-core.module.js +45 -9
- package/dist/solid-core.module.js.map +1 -1
- package/dist/testing/adapters/ui/playwright-adapter.d.ts.map +1 -1
- package/dist/testing/adapters/ui/playwright-adapter.js +35 -2
- package/dist/testing/adapters/ui/playwright-adapter.js.map +1 -1
- package/dist/transformers/typeorm/local-date-time-transformer.d.ts +4 -3
- package/dist/transformers/typeorm/local-date-time-transformer.d.ts.map +1 -1
- package/dist/transformers/typeorm/local-date-time-transformer.js +20 -2
- package/dist/transformers/typeorm/local-date-time-transformer.js.map +1 -1
- package/package.json +8 -2
- package/src/constants/error-messages.ts +1 -0
- package/src/constants.ts +3 -3
- package/src/controllers/role-metadata.controller.ts +26 -18
- package/src/dtos/create-email-template.dto.ts +7 -0
- package/src/dtos/create-list-of-values.dto.ts +7 -0
- package/src/dtos/create-menu-item-metadata.dto.ts +12 -1
- package/src/dtos/create-role-metadata.dto.ts +9 -0
- package/src/dtos/create-saved-filters.dto.ts +7 -0
- package/src/dtos/create-scheduled-job.dto.ts +14 -0
- package/src/dtos/create-security-rule.dto.ts +6 -0
- package/src/dtos/create-sms-template.dto.ts +6 -0
- package/src/dtos/create-view-metadata.dto.ts +11 -0
- package/src/dtos/otp-sign-in.dto.ts +3 -3
- package/src/dtos/otp-sign-up.dto.ts +3 -3
- package/src/dtos/resolve-s3-url.dto.ts +2 -12
- package/src/dtos/sign-up.dto.ts +0 -2
- package/src/dtos/update-email-template.dto.ts +6 -0
- package/src/dtos/update-list-of-values.dto.ts +8 -0
- package/src/dtos/update-menu-item-metadata.dto.ts +12 -0
- package/src/dtos/update-saved-filters.dto.ts +5 -1
- package/src/dtos/update-scheduled-job.dto.ts +15 -0
- package/src/dtos/update-security-rule.dto.ts +7 -0
- package/src/dtos/update-sms-template.dto.ts +32 -32
- package/src/dtos/update-view-metadata.dto.ts +12 -0
- package/src/entities/chatter-message-details.entity.ts +1 -0
- package/src/entities/chatter-message.entity.ts +1 -0
- package/src/entities/common.entity.ts +5 -5
- package/src/entities/legacy-common.entity.ts +5 -5
- package/src/entities/saved-filters.entity.ts +3 -0
- package/src/entities/user.entity.ts +4 -1
- package/src/helpers/field-crud-managers/ManyToManyRelationFieldCrudManager.ts +43 -32
- package/src/helpers/field-crud-managers/OneToManyRelationFieldCrudManager.ts +45 -33
- package/src/helpers/security.helper.ts +1 -1
- package/src/index.ts +0 -4
- package/src/repository/solid-base.repository.ts +172 -1
- package/src/seeders/module-metadata-seeder.service.ts +189 -127
- package/src/seeders/permission-metadata-seeder.service.ts +1 -4
- package/src/seeders/seed-data/solid-core-metadata.json +30 -32
- package/src/services/authentication.service.ts +273 -269
- package/src/services/crud-helper.service.ts +79 -36
- package/src/services/crud.service.ts +9 -4
- package/src/services/field-metadata.service.ts +0 -71
- package/src/services/file/disk-file.service.ts +8 -18
- package/src/services/file/index.ts +1 -0
- package/src/services/file/storage-path-builder.ts +56 -0
- package/src/services/media.service.ts +13 -7
- package/src/services/mediaStorageProviders/file-s3-storage-provider.ts +1 -1
- package/src/services/mediaStorageProviders/file-storage-provider.ts +13 -8
- package/src/services/module-metadata.service.ts +18 -15
- package/src/services/setting.service.ts +5 -3
- package/src/services/settings/default-settings-provider.service.ts +5 -3
- package/src/services/view-metadata.service.ts +29 -1
- package/src/solid-core.module.ts +16 -12
- package/src/testing/adapters/ui/playwright-adapter.ts +1 -1
- package/src/transformers/typeorm/local-date-time-transformer.ts +21 -3
- package/dist/passport-strategies/local.strategy.d.ts +0 -15
- package/dist/passport-strategies/local.strategy.d.ts.map +0 -1
- package/dist/passport-strategies/local.strategy.js +0 -44
- package/dist/passport-strategies/local.strategy.js.map +0 -1
- package/dist/seeders/email-template-seeder.service.d.ts +0 -10
- package/dist/seeders/email-template-seeder.service.d.ts.map +0 -1
- package/dist/seeders/email-template-seeder.service.js +0 -84
- package/dist/seeders/email-template-seeder.service.js.map +0 -1
- package/dist/seeders/sms-template-seeder.service.d.ts +0 -10
- package/dist/seeders/sms-template-seeder.service.d.ts.map +0 -1
- package/dist/seeders/sms-template-seeder.service.js.map +0 -1
- package/dist/seeders/user-seeder.service.d.ts +0 -10
- package/dist/seeders/user-seeder.service.d.ts.map +0 -1
- package/dist/seeders/user-seeder.service.js +0 -44
- package/dist/seeders/user-seeder.service.js.map +0 -1
- package/src/passport-strategies/local.strategy.ts +0 -28
- package/src/seeders/email-template-seeder.service.ts +0 -49
- package/src/seeders/sms-template-seeder.service.ts +0 -50
- package/src/seeders/user-seeder.service.ts +0 -33
- package/src/workflow.readme.md +0 -25
|
@@ -3,14 +3,13 @@ import type { SolidCoreSetting } from "src/services/settings/default-settings-pr
|
|
|
3
3
|
import {
|
|
4
4
|
BadRequestException,
|
|
5
5
|
ConflictException,
|
|
6
|
-
|
|
6
|
+
ForbiddenException,
|
|
7
7
|
Injectable,
|
|
8
8
|
InternalServerErrorException,
|
|
9
9
|
Logger,
|
|
10
10
|
NotFoundException,
|
|
11
11
|
UnauthorizedException,
|
|
12
12
|
} from '@nestjs/common';
|
|
13
|
-
import { ConfigType } from '@nestjs/config';
|
|
14
13
|
import { EventEmitter2 } from '@nestjs/event-emitter';
|
|
15
14
|
import { JwtService } from '@nestjs/jwt';
|
|
16
15
|
import { InjectDataSource } from '@nestjs/typeorm';
|
|
@@ -21,13 +20,12 @@ import { SUCCESS_MESSAGES } from 'src/constants/success-messages';
|
|
|
21
20
|
import { CreateUserDto } from 'src/dtos/create-user.dto';
|
|
22
21
|
import { MailFactory } from 'src/factories/mail.factory';
|
|
23
22
|
import { UserRepository } from 'src/repository/user.repository';
|
|
24
|
-
import { Msg91OTPService } from 'src/services/sms/Msg91OTPService';
|
|
25
23
|
import { DataSource, Repository } from 'typeorm';
|
|
26
24
|
import { v4 as uuidv4 } from 'uuid';
|
|
27
25
|
import {
|
|
28
26
|
ForgotPasswordSendVerificationTokenOn,
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
PasswordlessLoginValidateWhatSources,
|
|
28
|
+
PasswordlessRegistrationValidateWhatSources
|
|
31
29
|
} from "../constants";
|
|
32
30
|
import { ChangePasswordDto } from "../dtos/change-password.dto";
|
|
33
31
|
import { ConfirmForgotPasswordDto } from '../dtos/confirm-forgot-password.dto';
|
|
@@ -124,33 +122,22 @@ export class AuthenticationService {
|
|
|
124
122
|
});
|
|
125
123
|
}
|
|
126
124
|
|
|
127
|
-
async
|
|
128
|
-
|
|
129
|
-
const user = await this.resolveUser(signInDto.username, signInDto.email);
|
|
130
|
-
|
|
131
|
-
if (!user) {
|
|
132
|
-
throw new UnauthorizedException(ERROR_MESSAGES.INVALID_CREDENTIALS);
|
|
133
|
-
}
|
|
125
|
+
private async validateUserForPasswordLogin(user: User, password: string): Promise<void> {
|
|
134
126
|
if (!user.active) {
|
|
135
127
|
throw new UnauthorizedException(ERROR_MESSAGES.USER_NOT_ACTIVE);
|
|
136
128
|
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
user.password,
|
|
140
|
-
user.passwordSchemeVersion
|
|
141
|
-
);
|
|
129
|
+
this.checkAccountBlocked(user);
|
|
130
|
+
const isEqual = await this.hashingService.compare(password, user.password, user.passwordSchemeVersion);
|
|
142
131
|
if (!isEqual) {
|
|
132
|
+
await this.incrementFailedAttempts(user);
|
|
143
133
|
throw new UnauthorizedException(ERROR_MESSAGES.INVALID_CREDENTIALS);
|
|
144
134
|
}
|
|
135
|
+
}
|
|
145
136
|
|
|
146
|
-
|
|
147
|
-
// Now we check if the password needs to be rehashed based on the current hashing scheme and version.
|
|
137
|
+
private async rehashPasswordIfRequired(user: User, password: string): Promise<void> {
|
|
148
138
|
if (this.hashingService.needsRehash(user.password, user.passwordSchemeVersion)) {
|
|
149
|
-
|
|
150
|
-
return rehashedUser;
|
|
139
|
+
await this.updatePasswordDetails(user, password);
|
|
151
140
|
}
|
|
152
|
-
|
|
153
|
-
return user;
|
|
154
141
|
}
|
|
155
142
|
|
|
156
143
|
async signUp(signUpDto: SignUpDto, activeUser: ActiveUserData = null): Promise<User> {
|
|
@@ -375,60 +362,67 @@ export class AuthenticationService {
|
|
|
375
362
|
}
|
|
376
363
|
|
|
377
364
|
async otpInitiateRegistration(signUpDto: OTPSignUpDto) {
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
}
|
|
383
|
-
// Validate if either mobile or email is present.
|
|
384
|
-
if (isEmpty(signUpDto.mobile) && isEmpty(signUpDto.email)) {
|
|
385
|
-
throw new BadRequestException(ERROR_MESSAGES.REGISTRATION_REQUIRES_CONTACT);
|
|
386
|
-
}
|
|
387
|
-
if (signUpDto.validationSources.includes(TransactionalRegistrationValidationSource.EMAIL) && isEmpty(signUpDto.email)) {
|
|
388
|
-
throw new BadRequestException(ERROR_MESSAGES.EMAIL_REQUIRED_FOR_VALIDATION);
|
|
389
|
-
}
|
|
390
|
-
if (signUpDto.validationSources.includes(TransactionalRegistrationValidationSource.MOBILE) && isEmpty(signUpDto.mobile)) {
|
|
391
|
-
throw new BadRequestException(ERROR_MESSAGES.MOBILE_REQUIRED_FOR_VALIDATION);
|
|
392
|
-
}
|
|
365
|
+
const isPasswordlessRegistrationEnabled = await this.isPasswordlessRegistrationEnabled();
|
|
366
|
+
if (!isPasswordlessRegistrationEnabled) {
|
|
367
|
+
throw new BadRequestException(ERROR_MESSAGES.PASSWORDLESS_REGISTRATION_DISABLED);
|
|
368
|
+
}
|
|
393
369
|
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
where: [
|
|
397
|
-
{ email: signUpDto.email, },
|
|
398
|
-
{ mobile: signUpDto.mobile, },
|
|
399
|
-
{ username: signUpDto.username, }
|
|
400
|
-
]
|
|
401
|
-
});
|
|
402
|
-
if (isNotEmpty(existingUser) && existingUser.active) {
|
|
403
|
-
throw new ConflictException(ERROR_MESSAGES.USER_ALREADY_EXISTS);
|
|
404
|
-
}
|
|
405
|
-
let passwordlessRegistrationValidateWhat = this.settingService.getConfigValue<SolidCoreSetting>('passwordlessRegistrationValidateWhat');
|
|
406
|
-
if (!Array.isArray(passwordlessRegistrationValidateWhat)) {
|
|
407
|
-
passwordlessRegistrationValidateWhat = [passwordlessRegistrationValidateWhat];
|
|
408
|
-
}
|
|
409
|
-
const finalRegistrationVerificationSources = this.calculateVerificationSources(passwordlessRegistrationValidateWhat, signUpDto);
|
|
410
|
-
let user = existingUser
|
|
411
|
-
if (isEmpty(user)) {
|
|
412
|
-
user = this.createUser(signUpDto);
|
|
413
|
-
await this.populateVerificationTokens(finalRegistrationVerificationSources, user);
|
|
414
|
-
await this.userRepository.save(user);
|
|
415
|
-
await this.userService.addRoleToUser(user.username, this.settingService.getConfigValue<SolidCoreSetting>('defaultRole'));
|
|
416
|
-
}
|
|
417
|
-
else {
|
|
418
|
-
await this.populateVerificationTokens(finalRegistrationVerificationSources, user);
|
|
419
|
-
await this.userRepository.save(user);
|
|
420
|
-
}
|
|
370
|
+
const validationSource = this.resolvePasswordlessValidationSource();
|
|
371
|
+
this.validateOtpRegistrationInput(signUpDto, validationSource);
|
|
421
372
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
373
|
+
const existingUser = await this.findExistingRegistrationUser(signUpDto);
|
|
374
|
+
if (isNotEmpty(existingUser) && existingUser.active) {
|
|
375
|
+
throw new ConflictException(ERROR_MESSAGES.USER_ALREADY_EXISTS);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
try {
|
|
379
|
+
const user = await this.upsertUserWithRegistrationVerificationTokens(existingUser, signUpDto, validationSource);
|
|
380
|
+
await this.notifyUserOnOtpInitiateRegistration(user, validationSource);
|
|
425
381
|
} catch (err) {
|
|
426
|
-
|
|
427
|
-
if (err.code === pgUniqueViolationErrorCode) {
|
|
382
|
+
if (err.code === '23505') {
|
|
428
383
|
throw new ConflictException(ERROR_MESSAGES.USER_ALREADY_EXISTS);
|
|
429
384
|
}
|
|
430
385
|
throw err;
|
|
431
386
|
}
|
|
387
|
+
|
|
388
|
+
return { message: SUCCESS_MESSAGES.OTP_SENT_SUCCESS_REGISTRATION };
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
private validateOtpRegistrationInput(signUpDto: OTPSignUpDto, validationSource: string): void {
|
|
392
|
+
if (validationSource === PasswordlessRegistrationValidateWhatSources.EMAIL && isEmpty(signUpDto.email)) {
|
|
393
|
+
throw new BadRequestException(ERROR_MESSAGES.EMAIL_REQUIRED_FOR_VALIDATION);
|
|
394
|
+
}
|
|
395
|
+
if (validationSource === PasswordlessRegistrationValidateWhatSources.MOBILE && isEmpty(signUpDto.mobile)) {
|
|
396
|
+
throw new BadRequestException(ERROR_MESSAGES.MOBILE_REQUIRED_FOR_VALIDATION);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
private async findExistingRegistrationUser(signUpDto: OTPSignUpDto): Promise<User> {
|
|
401
|
+
return this.userRepository.findOne({ //TODO Perhaps we should use the user service instead of the repository directly.
|
|
402
|
+
where: [
|
|
403
|
+
{ email: signUpDto.email },
|
|
404
|
+
{ mobile: signUpDto.mobile },
|
|
405
|
+
{ username: signUpDto.username },
|
|
406
|
+
]
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
private resolvePasswordlessValidationSource(): string {
|
|
411
|
+
return this.settingService.getConfigValue<SolidCoreSetting>('passwordlessRegistrationValidateWhat');
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
private async upsertUserWithRegistrationVerificationTokens(existingUser: User, signUpDto: OTPSignUpDto, validationSource: string): Promise<User> {
|
|
415
|
+
let user = existingUser;
|
|
416
|
+
if (isEmpty(user)) {
|
|
417
|
+
user = this.createUser(signUpDto);
|
|
418
|
+
await this.assignRegistrationOtp(validationSource, user);
|
|
419
|
+
await this.userRepository.save(user);
|
|
420
|
+
await this.userService.addRoleToUser(user.username, this.settingService.getConfigValue<SolidCoreSetting>('defaultRole'));
|
|
421
|
+
} else {
|
|
422
|
+
await this.assignRegistrationOtp(validationSource, user);
|
|
423
|
+
await this.userRepository.save(user);
|
|
424
|
+
}
|
|
425
|
+
return user;
|
|
432
426
|
}
|
|
433
427
|
|
|
434
428
|
// Create a new user entity.
|
|
@@ -442,36 +436,25 @@ export class AuthenticationService {
|
|
|
442
436
|
return user;
|
|
443
437
|
}
|
|
444
438
|
|
|
445
|
-
private calculateVerificationSources(configuredRegistrationValidationSources: string[], signUpDto: OTPSignUpDto): string[] {
|
|
446
|
-
const finalRegistrationValidationSources = configuredRegistrationValidationSources.filter((source) => source !== RegistrationValidationSource.TRANSACTIONAL);
|
|
447
|
-
if (configuredRegistrationValidationSources.includes(RegistrationValidationSource.TRANSACTIONAL)) {
|
|
448
|
-
finalRegistrationValidationSources.push(...signUpDto.validationSources); // Add the validation sources provided by the user.
|
|
449
|
-
}
|
|
450
|
-
return finalRegistrationValidationSources;
|
|
451
|
-
}
|
|
452
|
-
|
|
453
439
|
// Generate the validation tokens for the user i.e (system configured + user provided)
|
|
454
|
-
private async
|
|
455
|
-
if (
|
|
440
|
+
private async assignRegistrationOtp(passwordlessRegistrationValidateWhat: string, user: User) {
|
|
441
|
+
if (!passwordlessRegistrationValidateWhat) {
|
|
456
442
|
throw new BadRequestException(ERROR_MESSAGES.VALIDATION_SOURCE_REQUIRED);
|
|
457
443
|
}
|
|
458
|
-
|
|
444
|
+
const autoLoginUserOnRegistration = this.settingService.getConfigValue<SolidCoreSetting>('autoLoginUserOnRegistration');
|
|
445
|
+
if (passwordlessRegistrationValidateWhat === PasswordlessRegistrationValidateWhatSources.EMAIL) {
|
|
459
446
|
const { token, expiresAt } = await this.otp();
|
|
460
447
|
user.emailVerificationTokenOnRegistration = token;
|
|
461
448
|
user.emailVerificationTokenOnRegistrationExpiresAt = expiresAt;
|
|
462
|
-
const autoLoginUserOnRegistration = this.settingService.getConfigValue<SolidCoreSetting>('autoLoginUserOnRegistration');
|
|
463
|
-
|
|
464
449
|
if (autoLoginUserOnRegistration) {
|
|
465
450
|
user.emailVerificationTokenOnLogin = token;
|
|
466
451
|
user.emailVerificationTokenOnLoginExpiresAt = expiresAt;
|
|
467
452
|
}
|
|
468
453
|
}
|
|
469
|
-
if (
|
|
454
|
+
if (passwordlessRegistrationValidateWhat === PasswordlessRegistrationValidateWhatSources.MOBILE) {
|
|
470
455
|
const { token, expiresAt } = await this.otp();
|
|
471
456
|
user.mobileVerificationTokenOnRegistration = token;
|
|
472
457
|
user.mobileVerificationTokenOnRegistrationExpiresAt = expiresAt;
|
|
473
|
-
|
|
474
|
-
const autoLoginUserOnRegistration = this.settingService.getConfigValue<SolidCoreSetting>('autoLoginUserOnRegistration');
|
|
475
458
|
if (autoLoginUserOnRegistration) {
|
|
476
459
|
user.mobileVerificationTokenOnLogin = token;
|
|
477
460
|
user.mobileVerificationTokenOnLoginExpiresAt = expiresAt;
|
|
@@ -479,13 +462,13 @@ export class AuthenticationService {
|
|
|
479
462
|
}
|
|
480
463
|
}
|
|
481
464
|
|
|
482
|
-
private async notifyUserOnOtpInitiateRegistration(user: User,
|
|
465
|
+
private async notifyUserOnOtpInitiateRegistration(user: User, registrationValidationSource: string) {
|
|
483
466
|
const companyLogo = await this.getCompanyLogo();
|
|
484
467
|
const dummyOtp = this.settingService.getConfigValue<SolidCoreSetting>('dummyOtp');
|
|
485
468
|
|
|
486
469
|
if (dummyOtp)
|
|
487
470
|
return; // Do nothing if dummy otp is configured.
|
|
488
|
-
if (
|
|
471
|
+
if (registrationValidationSource === PasswordlessLoginValidateWhatSources.EMAIL) {
|
|
489
472
|
const mailService = this.mailServiceFactory.getMailService();
|
|
490
473
|
mailService.sendEmailUsingTemplate(
|
|
491
474
|
user.email,
|
|
@@ -505,7 +488,7 @@ export class AuthenticationService {
|
|
|
505
488
|
user.id
|
|
506
489
|
);
|
|
507
490
|
}
|
|
508
|
-
if (
|
|
491
|
+
if (registrationValidationSource === PasswordlessLoginValidateWhatSources.MOBILE) {
|
|
509
492
|
const smsService = this.smsFactory.getSmsService();
|
|
510
493
|
smsService.sendSMSUsingTemplate(
|
|
511
494
|
user.mobile,
|
|
@@ -526,58 +509,68 @@ export class AuthenticationService {
|
|
|
526
509
|
|
|
527
510
|
async otpConfirmRegistration(confirmSignUpDto: OTPConfirmOTPDto) {
|
|
528
511
|
const isPasswordlessRegistrationEnabled = await this.isPasswordlessRegistrationEnabled();
|
|
529
|
-
|
|
530
512
|
if (!isPasswordlessRegistrationEnabled) {
|
|
531
513
|
throw new BadRequestException(ERROR_MESSAGES.PASSWORDLESS_REGISTRATION_DISABLED);
|
|
532
514
|
}
|
|
533
515
|
|
|
534
|
-
|
|
535
|
-
if (
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
516
|
+
const { type, identifier, otp } = confirmSignUpDto;
|
|
517
|
+
if (type !== PasswordlessRegistrationValidateWhatSources.EMAIL &&
|
|
518
|
+
type !== PasswordlessRegistrationValidateWhatSources.MOBILE) {
|
|
519
|
+
throw new BadRequestException(ERROR_MESSAGES.INVALID_VERIFICATION_TYPE);
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
const user = await this.findUserByRegistrationIdentifier(type, identifier);
|
|
523
|
+
this.validateRegistrationOtp(user, otp, type);
|
|
524
|
+
this.clearRegistrationOtp(user, type);
|
|
525
|
+
user.active = this.settingService.getConfigValue<SolidCoreSetting>('activateUserOnRegistration') &&
|
|
526
|
+
await this.areAllPasswordlessRegistrationValidationSourcesVerified(user);
|
|
527
|
+
|
|
528
|
+
const savedUser: User = await this.userRepository.save(user);
|
|
529
|
+
this.triggerRegistrationEvent(savedUser);
|
|
530
|
+
return { active: savedUser.active, message: `User registration verified for ${type}` };
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
private async findUserByRegistrationIdentifier(
|
|
534
|
+
type: PasswordlessRegistrationValidateWhatSources,
|
|
535
|
+
identifier: string,
|
|
536
|
+
): Promise<User> {
|
|
537
|
+
const where = type === PasswordlessRegistrationValidateWhatSources.EMAIL
|
|
538
|
+
? { email: identifier }
|
|
539
|
+
: { mobile: identifier };
|
|
540
|
+
const user = await this.userRepository.findOne({ where });
|
|
541
|
+
if (!user) {
|
|
542
|
+
throw new UnauthorizedException(ERROR_MESSAGES.USER_NOT_FOUND);
|
|
543
|
+
}
|
|
544
|
+
return user;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
private validateRegistrationOtp(
|
|
548
|
+
user: User,
|
|
549
|
+
otp: string,
|
|
550
|
+
type: PasswordlessRegistrationValidateWhatSources,
|
|
551
|
+
): void {
|
|
552
|
+
const isEmail = type === PasswordlessRegistrationValidateWhatSources.EMAIL;
|
|
553
|
+
const token = isEmail ? user.emailVerificationTokenOnRegistration : user.mobileVerificationTokenOnRegistration;
|
|
554
|
+
const expiresAt = isEmail ? user.emailVerificationTokenOnRegistrationExpiresAt : user.mobileVerificationTokenOnRegistrationExpiresAt;
|
|
555
|
+
|
|
556
|
+
if (token !== otp) {
|
|
557
|
+
throw new UnauthorizedException(ERROR_MESSAGES.INVALID_OTP);
|
|
558
|
+
}
|
|
559
|
+
if (expiresAt < new Date()) {
|
|
560
|
+
throw new UnauthorizedException(ERROR_MESSAGES.OTP_EXPIRED);
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
private clearRegistrationOtp(user: User, type: PasswordlessRegistrationValidateWhatSources): void {
|
|
565
|
+
if (type === PasswordlessRegistrationValidateWhatSources.EMAIL) {
|
|
550
566
|
user.emailVerifiedOnRegistrationAt = new Date();
|
|
551
567
|
user.emailVerificationTokenOnRegistration = null;
|
|
552
568
|
user.emailVerificationTokenOnRegistrationExpiresAt = null;
|
|
553
|
-
|
|
554
|
-
const savedUser: User = await this.userRepository.save(user);
|
|
555
|
-
this.triggerRegistrationEvent(savedUser);
|
|
556
|
-
return { active: savedUser.active, message: `User registration verified for ${confirmSignUpDto.type}` }
|
|
557
|
-
} else if (confirmSignUpDto.type === RegistrationValidationSource.MOBILE) {
|
|
558
|
-
const user = await this.userRepository.findOne({
|
|
559
|
-
where: {
|
|
560
|
-
mobile: confirmSignUpDto.identifier,
|
|
561
|
-
}
|
|
562
|
-
});
|
|
563
|
-
if (!user) {
|
|
564
|
-
throw new UnauthorizedException(ERROR_MESSAGES.USER_NOT_FOUND);
|
|
565
|
-
}
|
|
566
|
-
if (user.mobileVerificationTokenOnRegistration !== confirmSignUpDto.otp) {
|
|
567
|
-
throw new UnauthorizedException(ERROR_MESSAGES.INVALID_OTP);
|
|
568
|
-
}
|
|
569
|
-
if (user.mobileVerificationTokenOnRegistrationExpiresAt < new Date()) {
|
|
570
|
-
throw new UnauthorizedException(ERROR_MESSAGES.INVALID_OTP);
|
|
571
|
-
}
|
|
569
|
+
} else {
|
|
572
570
|
user.mobileVerifiedOnRegistrationAt = new Date();
|
|
573
571
|
user.mobileVerificationTokenOnRegistration = null;
|
|
574
572
|
user.mobileVerificationTokenOnRegistrationExpiresAt = null;
|
|
575
|
-
user.active = this.settingService.getConfigValue<SolidCoreSetting>('activateUserOnRegistration') && await this.areRegistrationValidationSourcesVerified(user);
|
|
576
|
-
const savedUser: User = await this.userRepository.save(user);
|
|
577
|
-
this.triggerRegistrationEvent(savedUser);
|
|
578
|
-
return { active: savedUser.active, message: `User registration verified for ${confirmSignUpDto.type}` }
|
|
579
573
|
}
|
|
580
|
-
throw new BadRequestException(ERROR_MESSAGES.INVALID_VERIFICATION_TYPE);
|
|
581
574
|
}
|
|
582
575
|
|
|
583
576
|
private triggerRegistrationEvent(savedUser: User) {
|
|
@@ -586,16 +579,14 @@ export class AuthenticationService {
|
|
|
586
579
|
this.eventEmitter.emit(EventType.USER_REGISTERED, event);
|
|
587
580
|
}
|
|
588
581
|
|
|
589
|
-
async
|
|
590
|
-
const
|
|
591
|
-
|
|
592
|
-
const registrationValidationSources = passwordlessRegistrationValidateWhat;
|
|
593
|
-
if (registrationValidationSources.includes(RegistrationValidationSource.EMAIL)) {
|
|
582
|
+
private async areAllPasswordlessRegistrationValidationSourcesVerified(user: User): Promise<boolean> {
|
|
583
|
+
const registrationValidationSource = this.resolvePasswordlessValidationSource();
|
|
584
|
+
if (registrationValidationSource === PasswordlessLoginValidateWhatSources.EMAIL) {
|
|
594
585
|
if (!user.emailVerifiedOnRegistrationAt) {
|
|
595
586
|
return false;
|
|
596
587
|
}
|
|
597
588
|
}
|
|
598
|
-
if (
|
|
589
|
+
if (registrationValidationSource === PasswordlessLoginValidateWhatSources.MOBILE) {
|
|
599
590
|
if (!user.mobileVerifiedOnRegistrationAt) {
|
|
600
591
|
return false;
|
|
601
592
|
}
|
|
@@ -615,9 +606,14 @@ export class AuthenticationService {
|
|
|
615
606
|
}
|
|
616
607
|
|
|
617
608
|
async signIn(signInDto: SignInDto) {
|
|
618
|
-
const user = await this.
|
|
609
|
+
const user = await this.resolveUser(signInDto.username, signInDto.email);
|
|
610
|
+
if (!user) {
|
|
611
|
+
throw new UnauthorizedException(ERROR_MESSAGES.INVALID_CREDENTIALS);
|
|
612
|
+
}
|
|
613
|
+
await this.validateUserForPasswordLogin(user, signInDto.password);
|
|
614
|
+
await this.rehashPasswordIfRequired(user, signInDto.password);
|
|
615
|
+
await this.resetFailedAttempts(user);
|
|
619
616
|
|
|
620
|
-
// TODO: Unset the password etc...
|
|
621
617
|
const tokens = await this.generateTokens(user);
|
|
622
618
|
|
|
623
619
|
await this.userActivityHistoryService.logEvent('login', user);
|
|
@@ -665,78 +661,79 @@ export class AuthenticationService {
|
|
|
665
661
|
throw new BadRequestException(ERROR_MESSAGES.PASSWORDLESS_REGISTRATION_DISABLED);
|
|
666
662
|
}
|
|
667
663
|
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
// });
|
|
675
|
-
const user = await this.userRepository.findOne({
|
|
676
|
-
where: [
|
|
677
|
-
{ username: signInDto.identifier },
|
|
678
|
-
{ email: signInDto.identifier },
|
|
679
|
-
]
|
|
680
|
-
});
|
|
664
|
+
const type = this.resolveLoginType(signInDto);
|
|
665
|
+
const user = await this.findUserForLogin(type, signInDto.identifier);
|
|
666
|
+
await this.assignLoginOtp(user, type);
|
|
667
|
+
this.notifyUserOnOtpInititateLogin(user, type);
|
|
668
|
+
return this.buildLoginOtpResponse(user, type);
|
|
669
|
+
}
|
|
681
670
|
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
}
|
|
685
|
-
if (!user.active) {
|
|
686
|
-
throw new UnauthorizedException(ERROR_MESSAGES.USER_INACTIVE);
|
|
687
|
-
}
|
|
688
|
-
const { token, expiresAt } = await this.otp();
|
|
689
|
-
user.emailVerificationTokenOnLogin = token;
|
|
690
|
-
user.emailVerificationTokenOnLoginExpiresAt = expiresAt;
|
|
691
|
-
await this.userRepository.save(user);
|
|
692
|
-
this.notifyUserOnOtpInititateLogin(user, RegistrationValidationSource.EMAIL);
|
|
693
|
-
return {
|
|
694
|
-
message: SUCCESS_MESSAGES.OTP_SENT_SUCCESS_LOGIN,
|
|
695
|
-
user: {
|
|
696
|
-
email: this.maskEmail(user.email)
|
|
697
|
-
}
|
|
698
|
-
};
|
|
699
|
-
} else if (signInDto.type === RegistrationValidationSource.MOBILE) {
|
|
700
|
-
// const user = await this.userRepository.findOne({
|
|
701
|
-
// where: {
|
|
702
|
-
// mobile: signInDto.identifier,
|
|
703
|
-
// }
|
|
704
|
-
// });
|
|
705
|
-
const user = await this.userRepository.findOne({
|
|
706
|
-
where: [
|
|
707
|
-
{ username: signInDto.identifier },
|
|
708
|
-
{ mobile: signInDto.identifier },
|
|
709
|
-
]
|
|
710
|
-
});
|
|
671
|
+
private resolveLoginType(signInDto: OTPSignInDto): PasswordlessLoginValidateWhatSources {
|
|
672
|
+
const setting = this.settingService.getConfigValue<SolidCoreSetting>('passwordlessLoginValidateWhat') as PasswordlessLoginValidateWhatSources;
|
|
711
673
|
|
|
712
|
-
|
|
713
|
-
|
|
674
|
+
if (setting === PasswordlessLoginValidateWhatSources.SELECTABLE) {
|
|
675
|
+
if (signInDto.type !== PasswordlessLoginValidateWhatSources.EMAIL &&
|
|
676
|
+
signInDto.type !== PasswordlessLoginValidateWhatSources.MOBILE) {
|
|
677
|
+
throw new BadRequestException(ERROR_MESSAGES.INVALID_VERIFICATION_TYPE);
|
|
714
678
|
}
|
|
679
|
+
return signInDto.type as PasswordlessLoginValidateWhatSources;
|
|
680
|
+
}
|
|
715
681
|
|
|
716
|
-
|
|
682
|
+
if (setting === PasswordlessLoginValidateWhatSources.EMAIL ||
|
|
683
|
+
setting === PasswordlessLoginValidateWhatSources.MOBILE) {
|
|
684
|
+
return setting;
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
throw new BadRequestException(ERROR_MESSAGES.INVALID_VERIFICATION_TYPE);
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
private async findUserForLogin(
|
|
691
|
+
type: PasswordlessLoginValidateWhatSources,
|
|
692
|
+
identifier: string,
|
|
693
|
+
options: { withRoles?: boolean } = {},
|
|
694
|
+
): Promise<User> {
|
|
695
|
+
const typeWhere = type === PasswordlessLoginValidateWhatSources.EMAIL
|
|
696
|
+
? { email: identifier }
|
|
697
|
+
: { mobile: identifier };
|
|
698
|
+
const user = await this.userRepository.findOne({
|
|
699
|
+
where: [{ username: identifier }, typeWhere],
|
|
700
|
+
...(options.withRoles ? { relations: { roles: true } } : {}),
|
|
701
|
+
});
|
|
702
|
+
if (!user) {
|
|
703
|
+
throw new UnauthorizedException(ERROR_MESSAGES.USER_NOT_FOUND);
|
|
704
|
+
}
|
|
705
|
+
if (!user.active) {
|
|
706
|
+
throw new UnauthorizedException(ERROR_MESSAGES.USER_INACTIVE);
|
|
707
|
+
}
|
|
708
|
+
return user;
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
private async assignLoginOtp(user: User, type: PasswordlessLoginValidateWhatSources): Promise<void> {
|
|
712
|
+
const { token, expiresAt } = await this.otp();
|
|
713
|
+
if (type === PasswordlessLoginValidateWhatSources.EMAIL) {
|
|
714
|
+
user.emailVerificationTokenOnLogin = token;
|
|
715
|
+
user.emailVerificationTokenOnLoginExpiresAt = expiresAt;
|
|
716
|
+
} else {
|
|
717
717
|
user.mobileVerificationTokenOnLogin = token;
|
|
718
718
|
user.mobileVerificationTokenOnLoginExpiresAt = expiresAt;
|
|
719
|
-
await this.userRepository.save(user);
|
|
720
|
-
this.notifyUserOnOtpInititateLogin(user, RegistrationValidationSource.MOBILE);
|
|
721
|
-
return {
|
|
722
|
-
message: SUCCESS_MESSAGES.OTP_SENT_SUCCESS_LOGIN,
|
|
723
|
-
user: {
|
|
724
|
-
mobile: this.maskMobile(user.mobile)
|
|
725
|
-
}
|
|
726
|
-
};
|
|
727
|
-
}
|
|
728
|
-
else {
|
|
729
|
-
throw new BadRequestException(ERROR_MESSAGES.INVALID_VERIFICATION_TYPE);
|
|
730
719
|
}
|
|
720
|
+
await this.userRepository.save(user);
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
private buildLoginOtpResponse(user: User, type: PasswordlessLoginValidateWhatSources) {
|
|
724
|
+
const maskedIdentifier = type === PasswordlessLoginValidateWhatSources.EMAIL
|
|
725
|
+
? { email: this.maskEmail(user.email) }
|
|
726
|
+
: { mobile: this.maskMobile(user.mobile) };
|
|
727
|
+
return { message: SUCCESS_MESSAGES.OTP_SENT_SUCCESS_LOGIN, user: maskedIdentifier };
|
|
731
728
|
}
|
|
732
729
|
|
|
733
|
-
private async notifyUserOnOtpInititateLogin(user: User, loginType:
|
|
730
|
+
private async notifyUserOnOtpInititateLogin(user: User, loginType: PasswordlessLoginValidateWhatSources) {
|
|
734
731
|
const companyLogo = await this.getCompanyLogo();
|
|
735
732
|
const dummyOtp = this.settingService.getConfigValue<SolidCoreSetting>('dummyOtp');
|
|
736
733
|
|
|
737
734
|
if (dummyOtp)
|
|
738
735
|
return; // Do nothing if dummy otp is configured.
|
|
739
|
-
if (loginType ===
|
|
736
|
+
if (loginType === PasswordlessLoginValidateWhatSources.EMAIL) {
|
|
740
737
|
const mailService = this.mailServiceFactory.getMailService();
|
|
741
738
|
mailService.sendEmailUsingTemplate(
|
|
742
739
|
user.email,
|
|
@@ -756,7 +753,7 @@ export class AuthenticationService {
|
|
|
756
753
|
user.id
|
|
757
754
|
);
|
|
758
755
|
}
|
|
759
|
-
if (loginType ===
|
|
756
|
+
if (loginType === PasswordlessLoginValidateWhatSources.MOBILE) {
|
|
760
757
|
const smsService = this.smsFactory.getSmsService();
|
|
761
758
|
smsService.sendSMSUsingTemplate(
|
|
762
759
|
user.mobile,
|
|
@@ -780,81 +777,62 @@ export class AuthenticationService {
|
|
|
780
777
|
if (!isPasswordlessRegistrationEnabled) {
|
|
781
778
|
throw new BadRequestException(ERROR_MESSAGES.PASSWORDLESS_REGISTRATION_DISABLED);
|
|
782
779
|
}
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
780
|
+
|
|
781
|
+
const { type, identifier, otp } = confirmSignInDto;
|
|
782
|
+
if (type !== PasswordlessLoginValidateWhatSources.EMAIL &&
|
|
783
|
+
type !== PasswordlessLoginValidateWhatSources.MOBILE) {
|
|
784
|
+
throw new BadRequestException(ERROR_MESSAGES.INVALID_VERIFICATION_TYPE);
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
const user = await this.findUserForLogin(type, identifier, { withRoles: true });
|
|
788
|
+
this.checkAccountBlocked(user);
|
|
789
|
+
try {
|
|
790
|
+
this.validateLoginOtp(user, otp, type);
|
|
791
|
+
} catch (e) {
|
|
792
|
+
await this.incrementFailedAttempts(user);
|
|
793
|
+
throw e;
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
// we do not need to clear the otp when dummy otp is configured...
|
|
797
|
+
const dummyOtp = this.settingService.getConfigValue<SolidCoreSetting>('dummyOtp');
|
|
798
|
+
if (!dummyOtp)
|
|
799
|
+
this.clearLoginOtp(user, type);
|
|
800
|
+
|
|
801
|
+
user.failedLoginAttempts = 0;
|
|
802
|
+
await this.userRepository.save(user);
|
|
803
|
+
return this.buildLoginTokenResponse(user);
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
private validateLoginOtp(user: User, otp: string, type: PasswordlessLoginValidateWhatSources): void {
|
|
807
|
+
const isEmail = type === PasswordlessLoginValidateWhatSources.EMAIL;
|
|
808
|
+
const token = isEmail ? user.emailVerificationTokenOnLogin : user.mobileVerificationTokenOnLogin;
|
|
809
|
+
const expiresAt = isEmail ? user.emailVerificationTokenOnLoginExpiresAt : user.mobileVerificationTokenOnLoginExpiresAt;
|
|
810
|
+
|
|
811
|
+
if (token !== otp) {
|
|
812
|
+
throw new UnauthorizedException(ERROR_MESSAGES.INVALID_OTP);
|
|
813
|
+
}
|
|
814
|
+
if (expiresAt < new Date()) {
|
|
815
|
+
throw new UnauthorizedException(ERROR_MESSAGES.OTP_EXPIRED);
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
private clearLoginOtp(user: User, type: PasswordlessLoginValidateWhatSources): void {
|
|
820
|
+
if (type === PasswordlessLoginValidateWhatSources.EMAIL) {
|
|
811
821
|
user.emailVerifiedOnLoginAt = new Date();
|
|
812
822
|
user.emailVerificationTokenOnLogin = null;
|
|
813
823
|
user.emailVerificationTokenOnLoginExpiresAt = null;
|
|
814
|
-
|
|
815
|
-
const { accessToken, refreshToken } = await this.generateTokens(user);
|
|
816
|
-
const { id, username, email, mobile, lastLoginProvider } = user;
|
|
817
|
-
const roles = user.roles.map((role) => role.name);
|
|
818
|
-
return { accessToken, refreshToken, user: { id, username, email, mobile, lastLoginProvider, roles } };
|
|
819
|
-
} else if (confirmSignInDto.type === RegistrationValidationSource.MOBILE) {
|
|
820
|
-
// const user = await this.userRepository.findOne({
|
|
821
|
-
// where: {
|
|
822
|
-
// mobile: confirmSignInDto.identifier,
|
|
823
|
-
// },
|
|
824
|
-
// relations: ['roles']
|
|
825
|
-
// });
|
|
826
|
-
const user = await this.userRepository.findOne({
|
|
827
|
-
where: [
|
|
828
|
-
{ username: confirmSignInDto.identifier },
|
|
829
|
-
{ mobile: confirmSignInDto.identifier },
|
|
830
|
-
],
|
|
831
|
-
relations: {
|
|
832
|
-
roles: true
|
|
833
|
-
}
|
|
834
|
-
});
|
|
835
|
-
if (!user) {
|
|
836
|
-
throw new UnauthorizedException(ERROR_MESSAGES.USER_NOT_ACTIVE);
|
|
837
|
-
}
|
|
838
|
-
if (!user.active) {
|
|
839
|
-
throw new UnauthorizedException(ERROR_MESSAGES.USER_INACTIVE);
|
|
840
|
-
}
|
|
841
|
-
if (user.mobileVerificationTokenOnLogin !== confirmSignInDto.otp) {
|
|
842
|
-
throw new UnauthorizedException(ERROR_MESSAGES.INVALID_OTP);
|
|
843
|
-
}
|
|
844
|
-
if (user.mobileVerificationTokenOnLoginExpiresAt < new Date()) {
|
|
845
|
-
throw new UnauthorizedException(ERROR_MESSAGES.INVALID_OTP);
|
|
846
|
-
}
|
|
824
|
+
} else {
|
|
847
825
|
user.mobileVerifiedOnLoginAt = new Date();
|
|
848
826
|
user.mobileVerificationTokenOnLogin = null;
|
|
849
827
|
user.mobileVerificationTokenOnLoginExpiresAt = null;
|
|
850
|
-
await this.userRepository.save(user);
|
|
851
|
-
const { accessToken, refreshToken } = await this.generateTokens(user);
|
|
852
|
-
const { id, username, email, mobile, lastLoginProvider } = user;
|
|
853
|
-
const roles = user.roles.map((role) => role.name);
|
|
854
|
-
return { accessToken, refreshToken, user: { id, username, email, mobile, lastLoginProvider, roles } };
|
|
855
|
-
|
|
856
828
|
}
|
|
857
|
-
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
private async buildLoginTokenResponse(user: User) {
|
|
832
|
+
const { accessToken, refreshToken } = await this.generateTokens(user);
|
|
833
|
+
const { id, username, email, mobile, lastLoginProvider } = user;
|
|
834
|
+
const roles = user.roles.map((role) => role.name);
|
|
835
|
+
return { accessToken, refreshToken, user: { id, username, email, mobile, lastLoginProvider, roles } };
|
|
858
836
|
}
|
|
859
837
|
|
|
860
838
|
async changePassword(changePasswordDto: ChangePasswordDto, activeUser: ActiveUserData) {
|
|
@@ -1251,11 +1229,19 @@ export class AuthenticationService {
|
|
|
1251
1229
|
}
|
|
1252
1230
|
});
|
|
1253
1231
|
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1232
|
+
if (!user) {
|
|
1233
|
+
throw new UnauthorizedException(ERROR_MESSAGES.USER_NOT_FOUND);
|
|
1234
|
+
}
|
|
1235
|
+
this.checkAccountBlocked(user);
|
|
1236
|
+
|
|
1237
|
+
try {
|
|
1238
|
+
await this.validateUserUsingGoogle(user);
|
|
1239
|
+
} catch (e) {
|
|
1240
|
+
await this.incrementFailedAttempts(user);
|
|
1241
|
+
throw e;
|
|
1242
|
+
}
|
|
1257
1243
|
|
|
1258
|
-
|
|
1244
|
+
await this.resetFailedAttempts(user);
|
|
1259
1245
|
const tokens = await this.generateTokens(user);
|
|
1260
1246
|
return {
|
|
1261
1247
|
user: {
|
|
@@ -1275,6 +1261,24 @@ export class AuthenticationService {
|
|
|
1275
1261
|
return this.settingService.getConfigValue<SolidCoreSetting>('passwordLessAuth');
|
|
1276
1262
|
}
|
|
1277
1263
|
|
|
1264
|
+
private checkAccountBlocked(user: User): void {
|
|
1265
|
+
const maxFailedAttempts = this.settingService.getConfigValue<SolidCoreSetting>('maxFailedLoginAttempts') as number;
|
|
1266
|
+
if (maxFailedAttempts > 0 && user.failedLoginAttempts >= maxFailedAttempts) {
|
|
1267
|
+
throw new ForbiddenException(ERROR_MESSAGES.ACCOUNT_BLOCKED);
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
private async incrementFailedAttempts(user: User): Promise<void> {
|
|
1272
|
+
user.failedLoginAttempts += 1;
|
|
1273
|
+
await this.userRepository.save(user);
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
private async resetFailedAttempts(user: User): Promise<void> {
|
|
1277
|
+
if (user.failedLoginAttempts === 0) return;
|
|
1278
|
+
user.failedLoginAttempts = 0;
|
|
1279
|
+
await this.userRepository.save(user);
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1278
1282
|
//FIXME - Pending implementation
|
|
1279
1283
|
// async logout() {
|
|
1280
1284
|
// // const user = this.request.user; //TODO: // Access the user from the execution context
|