Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Email password signin #34

Open
wants to merge 5 commits into
base: email-password-signup
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 114 additions & 7 deletions src/services/auth/auth.controller.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -27,20 +26,23 @@ 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;
let userRepository: LegacyUserRepository;
let emailService: EmailService;
let userAccountRepository: LegacyUserAccountRepository;

let verificationCode: string;

beforeEach(async () => {
mongod = await MongoMemoryServer.create();
const uri: string = mongod.getUri();
Expand Down Expand Up @@ -106,6 +108,12 @@ describe('AuthController', () => {
userAccountRepository = moduleRef.get<LegacyUserAccountRepository>(LegacyUserAccountRepository);
emailService = moduleRef.get<EmailService>(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();
Expand Down Expand Up @@ -147,6 +155,105 @@ describe('AuthController', () => {
});
});

describe('/POST /login', () => {
it('Logs in successfully', async () => {

const email = '[email protected]';
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 = '[email protected]';
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 = '[email protected]';
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 protected]',
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 = '[email protected]';
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 () => {

Expand Down
5 changes: 2 additions & 3 deletions src/services/auth/auth.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<LoginResponseDto> {
return await this.authService.login(body.username);
}

//@UseGuards(AuthGuard('local'))
Expand Down
14 changes: 9 additions & 5 deletions src/services/auth/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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 }) {
Expand Down