diff --git a/src/services/auth/auth.controller.test.ts b/src/services/auth/auth.controller.test.ts index 47c6bf5..7e2372a 100644 --- a/src/services/auth/auth.controller.test.ts +++ b/src/services/auth/auth.controller.test.ts @@ -17,8 +17,7 @@ import { DID } from 'dids'; import { Ed25519Provider } from 'key-did-provider-ed25519'; import { INestApplication, Module, ValidationPipe } from '@nestjs/common'; import * as KeyResolver from 'key-did-resolver'; -import { TestingModule } from '@nestjs/testing'; -import crypto, { randomUUID } from 'crypto'; +import crypto from 'crypto'; import { PrivateKey } from '@hiveio/dhive'; import { AuthGuard } from '@nestjs/passport'; import { MockAuthGuard, MockDidUserDetailsInterceptor, UserDetailsInterceptor } from '../api/utils'; @@ -27,13 +26,14 @@ import { HiveModule } from '../hive/hive.module'; import { LegacyUserRepository } from '../../repositories/user/user.repository'; import { EmailService } from '../email/email.service'; import { LegacyUserAccountRepository } from '../../repositories/userAccount/user-account.repository'; +import * as jest from 'jest-mock'; // Import Jest mock describe('AuthController', () => { - let app: INestApplication - const seedBuf = new Uint8Array(32) - seedBuf.fill(27) - const key = new Ed25519Provider(seedBuf) - const did = new DID({ provider: key, resolver: KeyResolver.getResolver() }) + let app: INestApplication; + const seedBuf = new Uint8Array(32); + seedBuf.fill(27); + const key = new Ed25519Provider(seedBuf); + const did = new DID({ provider: key, resolver: KeyResolver.getResolver() }); let mongod: MongoMemoryServer; let authService: AuthService; let hiveService: HiveService; @@ -41,6 +41,8 @@ describe('AuthController', () => { let emailService: EmailService; let userAccountRepository: LegacyUserAccountRepository; + let verificationCode: string; + beforeEach(async () => { mongod = await MongoMemoryServer.create(); const uri: string = mongod.getUri(); @@ -106,6 +108,12 @@ describe('AuthController', () => { userAccountRepository = moduleRef.get(LegacyUserAccountRepository); emailService = moduleRef.get(EmailService); + // Mocking the EmailService to capture the verification code + jest.spyOn(emailService, 'sendRegistration').mockImplementation(async (email, code) => { + verificationCode = code; // Store the verification code + return; + }); + app = moduleRef.createNestApplication(); app.useGlobalPipes(new ValidationPipe()); await app.init(); @@ -147,6 +155,105 @@ describe('AuthController', () => { }); }); + describe('/POST /login', () => { + it('Logs in successfully', async () => { + + const email = 'test@test.com'; + const password = 'testpass' + + await authService.registerEmailAndPasswordUser(email, password); + await authService.verifyEmail(verificationCode); // Use the captured verification code + + // Make the request to the endpoint + return request(app.getHttpServer()) + .post('/v1/auth/login') + .send({ + username: email, + password: password + }) + .expect(201) + .then(async response => { + expect(response.body).toEqual({ + access_token: expect.any(String) + }); + }); + }); + + it('Throws unauthorized when the password is wrong', async () => { + + const email = 'test@test.com'; + const password = 'testpass' + + await authService.registerEmailAndPasswordUser(email, password); + await authService.verifyEmail(verificationCode); + + // Make the request to the endpoint + return request(app.getHttpServer()) + .post('/v1/auth/login') + .send({ + username: email, + password: password + 'im a hacker' + }) + .expect(401) + .then(async response => { + expect(response.body).toEqual({ + error: "Unauthorized", + message: "Email or password was incorrect or email has not been verified", + statusCode: 401, + }); + }); + }); + + it('Throws when the email does not exist', async () => { + + const email = 'test@test.com'; + const password = 'testpass' + + await authService.registerEmailAndPasswordUser(email, password); + await authService.verifyEmail(verificationCode); + + // Make the request to the endpoint + return request(app.getHttpServer()) + .post('/v1/auth/login') + .send({ + username: 'different@email.com', + password: password + }) + .expect(401) + .then(async response => { + expect(response.body).toEqual({ + error: "Unauthorized", + message: "Email or password was incorrect or email has not been verified", + statusCode: 401, + }); + }); + }); + + it('Throws when the user has not verified their email', async () => { + + const email = 'test@test.com'; + const password = 'testpass' + + await authService.registerEmailAndPasswordUser(email, password); + + // Make the request to the endpoint + return request(app.getHttpServer()) + .post('/v1/auth/login') + .send({ + username: email, + password: password + }) + .expect(401) + .then(async response => { + expect(response.body).toEqual({ + error: "Unauthorized", + message: "Email or password was incorrect or email has not been verified", + statusCode: 401, + }); + }); + }); + }) + describe('/POST /request_hive_account', () => { it('creates a Hive account successfully', async () => { diff --git a/src/services/auth/auth.controller.ts b/src/services/auth/auth.controller.ts index ba408f7..b545af8 100644 --- a/src/services/auth/auth.controller.ts +++ b/src/services/auth/auth.controller.ts @@ -66,9 +66,8 @@ export class AuthController { description: 'Login success', type: LoginResponseDto, }) - async login(@Request() req, @Body() body: LoginDto) { - const request = parseAndValidateRequest(req, this.#logger); - return this.authService.login(request.user); + async login(@Body() body: LoginDto): Promise { + return await this.authService.login(body.username); } //@UseGuards(AuthGuard('local')) diff --git a/src/services/auth/auth.service.ts b/src/services/auth/auth.service.ts index f90497f..3f5a4c1 100644 --- a/src/services/auth/auth.service.ts +++ b/src/services/auth/auth.service.ts @@ -43,7 +43,7 @@ export class AuthService { if (!user.password) { throw new InternalServerErrorException('Email does not have associated password'); } - if (await bcrypt.compare(user.password, pass)) { + if (await bcrypt.compare(pass, user.password)) { const { password, ...result } = user; return result; } @@ -78,10 +78,14 @@ export class AuthService { return !!(await this.legacyUserRepository.findOneBySub(this.generateDidSub(did))); } - async login(user: User) { - return { - access_token: this.jwtSign(user), - }; + async login(email: string) { + const user = await this.legacyUserAccountRepository.findOneVerifiedByEmail({ email }); + if (!user) { + throw new InternalServerErrorException( + 'User was validated but cannot be found or has not verified their email', + ); + } + return { access_token: this.jwtSign({ network: 'email', user_id: user.username }) }; } async getUserByUserId({ user_id }: { user_id: string }) {