diff --git a/api/src/modules/auth/auth.module.ts b/api/src/modules/auth/auth.module.ts index 5e13f1e2..840e7eff 100644 --- a/api/src/modules/auth/auth.module.ts +++ b/api/src/modules/auth/auth.module.ts @@ -1,5 +1,4 @@ import { Module } from '@nestjs/common'; -import { PasswordRecoveryService } from '@api/modules/auth/services/password-recovery.service'; import { AuthMailer } from '@api/modules/auth/services/auth.mailer'; import { NotificationsModule } from '@api/modules/notifications/notifications.module'; import { AuthenticationController } from '@api/modules/auth/authentication.controller'; @@ -9,11 +8,7 @@ import { RequestPasswordRecoveryHandler } from '@api/modules/auth/commands/reque @Module({ imports: [AuthenticationModule, NotificationsModule], controllers: [AuthenticationController], - providers: [ - PasswordRecoveryService, - AuthMailer, - RequestPasswordRecoveryHandler, - ], + providers: [AuthMailer, RequestPasswordRecoveryHandler], exports: [AuthenticationModule, AuthMailer], }) export class AuthModule {} diff --git a/api/src/modules/auth/authentication.controller.ts b/api/src/modules/auth/authentication.controller.ts index edd5d546..1b48b38f 100644 --- a/api/src/modules/auth/authentication.controller.ts +++ b/api/src/modules/auth/authentication.controller.ts @@ -10,14 +10,12 @@ import { User } from '@shared/entities/users/user.entity'; import { LocalAuthGuard } from '@api/modules/auth/guards/local-auth.guard'; import { GetUser } from '@api/modules/auth/decorators/get-user.decorator'; import { Public } from '@api/modules/auth/decorators/is-public.decorator'; -import { PasswordRecoveryService } from '@api/modules/auth/services/password-recovery.service'; import { tsRestHandler, TsRestHandler } from '@ts-rest/nest'; import { ControllerResponse } from '@api/types/controller-response.type'; import { AuthGuard } from '@nestjs/passport'; import { ResetPassword } from '@api/modules/auth/strategies/reset-password.strategy'; import { authContract } from '@shared/contracts/auth.contract'; import { AuthenticationService } from '@api/modules/auth/authentication.service'; -import { JwtAuthGuard } from '@api/modules/auth/guards/jwt-auth.guard'; import { SignUp } from '@api/modules/auth/strategies/sign-up.strategy'; import { CommandBus } from '@nestjs/cqrs'; import { RequestPasswordRecoveryCommand } from '@api/modules/auth/commands/request-password-recovery.command'; @@ -27,7 +25,6 @@ import { RequestPasswordRecoveryCommand } from '@api/modules/auth/commands/reque export class AuthenticationController { constructor( private authService: AuthenticationService, - private readonly passwordRecovery: PasswordRecoveryService, private readonly commandBus: CommandBus, ) {} @@ -44,10 +41,10 @@ export class AuthenticationController { }); } - @UseGuards(JwtAuthGuard, AuthGuard(SignUp)) + @UseGuards(AuthGuard(SignUp)) @TsRestHandler(authContract.signUp) async signUp(@GetUser() user: User): Promise { - return tsRestHandler(authContract.login, async ({ body }) => { + return tsRestHandler(authContract.signUp, async ({ body }) => { await this.authService.signUp(user, body); return { body: null, diff --git a/api/src/modules/auth/authentication.module.ts b/api/src/modules/auth/authentication.module.ts index 83e4acea..a1621b97 100644 --- a/api/src/modules/auth/authentication.module.ts +++ b/api/src/modules/auth/authentication.module.ts @@ -11,6 +11,7 @@ import { JwtStrategy } from '@api/modules/auth/strategies/jwt.strategy'; import { TOKEN_TYPE_ENUM } from '@shared/schemas/auth/token-type.schema'; import { ResetPasswordJwtStrategy } from '@api/modules/auth/strategies/reset-password.strategy'; import { JwtManager } from '@api/modules/auth/services/jwt.manager'; +import { SignUpStrategy } from '@api/modules/auth/strategies/sign-up.strategy'; @Module({ imports: [ @@ -46,6 +47,13 @@ import { JwtManager } from '@api/modules/auth/services/jwt.manager'; }, inject: [UsersService, ApiConfigService], }, + { + provide: SignUpStrategy, + useFactory: (users: UsersService, config: ApiConfigService) => { + return new SignUpStrategy(users, config); + }, + inject: [UsersService, ApiConfigService], + }, ], exports: [UsersModule, AuthenticationService, JwtManager], }) diff --git a/api/src/modules/auth/services/password-recovery.service.ts b/api/src/modules/auth/services/password-recovery.service.ts deleted file mode 100644 index f3df1478..00000000 --- a/api/src/modules/auth/services/password-recovery.service.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { Injectable, Logger } from '@nestjs/common'; -import { UsersService } from '@api/modules/users/users.service'; -import { AuthMailer } from '@api/modules/auth/services/auth.mailer'; -import { EventBus } from '@nestjs/cqrs'; -import { PasswordRecoveryRequestedEvent } from '@api/modules/events/user-events/password-recovery-requested.event'; -import { User } from '@shared/entities/users/user.entity'; -import * as bcrypt from 'bcrypt'; - -@Injectable() -export class PasswordRecoveryService { - logger: Logger = new Logger(PasswordRecoveryService.name); - constructor( - private readonly users: UsersService, - private readonly authMailer: AuthMailer, - private readonly eventBus: EventBus, - ) {} - - async requestPasswordRecovery(email: string, origin: string): Promise { - const user = await this.users.findByEmail(email); - if (!user) { - this.logger.warn( - `Email ${email} not found when trying to recover password`, - ); - this.eventBus.publish(new PasswordRecoveryRequestedEvent(email, null)); - return; - } - await this.authMailer.sendPasswordRecoveryEmail({ - user, - origin, - }); - this.eventBus.publish(new PasswordRecoveryRequestedEvent(email, user.id)); - } - - async resetPassword(user: User, newPassword: string): Promise { - const newHashedPassword = await bcrypt.hash(newPassword, 10); - await this.users.updatePassword(user, newHashedPassword); - } -} diff --git a/api/test/integration/auth/sign-up.spec.ts b/api/test/integration/auth/sign-up.spec.ts index bf46c5c5..9f28e3b3 100644 --- a/api/test/integration/auth/sign-up.spec.ts +++ b/api/test/integration/auth/sign-up.spec.ts @@ -1,10 +1,10 @@ import { TestManager } from '../../utils/test-manager'; import { HttpStatus } from '@nestjs/common'; -import { ApiConfigService } from '@api/modules/config/app-config.service'; import { TOKEN_TYPE_ENUM } from '@shared/schemas/auth/token-type.schema'; import { authContract } from '@shared/contracts/auth.contract'; import { ROLES } from '@api/modules/auth/roles.enum'; import { JwtManager } from '@api/modules/auth/services/jwt.manager'; +import { User } from '@shared/entities/users/user.entity'; //create-user.feature @@ -33,25 +33,64 @@ describe('Create Users', () => { email: 'random@test.com', }); - const token = jwtManager.signSignUpToken(user.id); + const { signUpToken } = await jwtManager.signSignUpToken(user.id); // When the user creates a new user const response = await testManager .request() .get(authContract.validateToken.path) - .set('Authorization', `Bearer ${token}`) + .set('Authorization', `Bearer ${signUpToken}`) .query({ tokenType: TOKEN_TYPE_ENUM.SIGN_UP }); expect(response.status).toBe(HttpStatus.UNAUTHORIZED); }); - test('Sign up should fail if the current password is incorrect', async () => { + test('Sign up should fail if the auto-generated password is incorrect', async () => { const user = await testManager.mocks().createUser({ role: ROLES.PARTNER, email: 'random@test.com', - isActive: true, + isActive: false, }); - const token = await jwtManager.signSignUpToken(user.id); + const { signUpToken } = await jwtManager.signSignUpToken(user.id); + + const response = await testManager + .request() + .post(authContract.signUp.path) + .set('Authorization', `Bearer ${signUpToken}`) + .query({ tokenType: TOKEN_TYPE_ENUM.SIGN_UP }) + .send({ password: 'wrongpassword', newPassword: 'newpassword' }); + + expect(response.status).toBe(HttpStatus.UNAUTHORIZED); + }); + + test('Sign up should succeed if the auto-generated password is correct and the user should be activated and allowed to get a access token', async () => { + const user = await testManager.mocks().createUser({ + role: ROLES.PARTNER, + email: 'test@test.com', + isActive: false, + }); + const { signUpToken } = await jwtManager.signSignUpToken(user.id); + + const response = await testManager + .request() + .post(authContract.signUp.path) + .set('Authorization', `Bearer ${signUpToken}`) + .send({ password: user.password, newPassword: 'newpassword' }); + + expect(response.status).toBe(HttpStatus.CREATED); + const foundUser = await testManager + .getDataSource() + .getRepository(User) + .findOneBy({ id: user.id }); + + expect(foundUser.isActive).toBe(true); + + const login = await testManager + .request() + .post(authContract.login.path) + .send({ email: user.email, password: 'newpassword' }); + + expect(login.body.accessToken).toBeDefined(); }); });