-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #13 from Brints/authentication
feat: authentication and login
- Loading branch information
Showing
14 changed files
with
436 additions
and
27 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import { ApiProperty } from '@nestjs/swagger'; | ||
import { IsEmail, IsNotEmpty, IsString } from 'class-validator'; | ||
|
||
export class LoginUserDto { | ||
@ApiProperty() | ||
@IsNotEmpty() | ||
@IsEmail() | ||
@IsString() | ||
email: string; | ||
|
||
@ApiProperty() | ||
@IsNotEmpty() | ||
@IsString() | ||
password: string; | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
POST http://localhost:3001/auth/register | ||
Content-Type: application/json | ||
|
||
{ | ||
"first_name": "aniebiet", | ||
"last_name": "afia", | ||
"email": "aniebietafia@gmail.com", | ||
"password": "Test1234$", | ||
"confirm_password": "Test1234$", | ||
"phone_number": "08012345678", | ||
"gender": "male", | ||
"country_code": "+234" | ||
} | ||
|
||
POST http://localhost:3001/auth/login | ||
Content-Type: application/json | ||
|
||
{ | ||
"email": "aniebietafia@gmail.com", | ||
"password": "Test1234$" | ||
} |
154 changes: 154 additions & 0 deletions
154
brints-estate-api/src/auth/providers/create-user.provider.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
import { forwardRef, HttpStatus, Inject, Injectable } from '@nestjs/common'; | ||
import { Repository } from 'typeorm'; | ||
import { InjectRepository } from '@nestjs/typeorm'; | ||
|
||
import { User } from 'src/users/entities/user.entity'; | ||
import { UserAuth } from 'src/users/entities/userAuth.entity'; | ||
import { CreateUserDto } from 'src/users/dto/create-user.dto'; | ||
import { CreateUserAuthDto } from 'src/users/dto/create-userauth.dto'; | ||
import { CustomException } from 'src/exceptions/custom.exception'; | ||
import { UserHelper } from 'src/utils/userHelper.lib'; | ||
import { HashingProvider } from './hashing.provider'; | ||
import { VerificationStatus } from 'src/enums/roles.model'; | ||
import { GenerateTokenHelper } from 'src/utils/generate-token.lib'; | ||
|
||
@Injectable() | ||
export class CreateUserProvider { | ||
constructor( | ||
@InjectRepository(User) | ||
private readonly userRepository: Repository<User>, | ||
|
||
@InjectRepository(UserAuth) | ||
private readonly userAuthRepository: Repository<UserAuth>, | ||
|
||
@Inject(forwardRef(() => HashingProvider)) | ||
private readonly hashingProvider: HashingProvider, | ||
|
||
@Inject(forwardRef(() => UserHelper)) | ||
private readonly userHelper: UserHelper, | ||
|
||
@Inject(forwardRef(() => GenerateTokenHelper)) | ||
private readonly generateTokenHelper: GenerateTokenHelper, | ||
) {} | ||
|
||
public async createUser( | ||
createUserDto: CreateUserDto, | ||
createUserAuthDto: CreateUserAuthDto, | ||
): Promise<User> { | ||
const { | ||
first_name, | ||
last_name, | ||
email, | ||
password, | ||
confirm_password, | ||
phone_number, | ||
gender, | ||
country_code, | ||
} = createUserDto; | ||
|
||
if (gender.toLowerCase() !== 'female' && gender.toLowerCase() !== 'male') { | ||
throw new CustomException( | ||
HttpStatus.BAD_REQUEST, | ||
`${gender} is not a valid gender`, | ||
); | ||
} | ||
|
||
if (!country_code.startsWith('+')) { | ||
throw new CustomException( | ||
HttpStatus.BAD_REQUEST, | ||
'Country code must start with a + followed by a number', | ||
); | ||
} | ||
|
||
const fullPhoneNumber = this.userHelper.formatPhoneNumber( | ||
country_code, | ||
phone_number, | ||
); | ||
|
||
if (password !== confirm_password) { | ||
throw new CustomException( | ||
HttpStatus.BAD_REQUEST, | ||
'Passwords do not match. Please try again', | ||
); | ||
} | ||
|
||
if (password === email) { | ||
throw new CustomException( | ||
HttpStatus.BAD_REQUEST, | ||
'Password cannot be the same as email', | ||
); | ||
} | ||
|
||
const formattedFirstName = | ||
this.userHelper.capitalizeFirstLetter(first_name); | ||
const formattedLastName = this.userHelper.capitalizeFirstLetter(last_name); | ||
|
||
const userExists = await this.userRepository.findOne({ | ||
where: { email: email.toLowerCase() }, | ||
}); | ||
if (userExists) { | ||
throw new CustomException( | ||
HttpStatus.CONFLICT, | ||
'User Exists already. Please login', | ||
); | ||
} | ||
|
||
const phoneNumberExists = await this.userRepository.findOne({ | ||
where: { phone_number: fullPhoneNumber }, | ||
}); | ||
if (phoneNumberExists) { | ||
throw new CustomException( | ||
HttpStatus.CONFLICT, | ||
'Phone number Exists already. Use another phone number', | ||
); | ||
} | ||
|
||
const user = this.userRepository.create({ | ||
...CreateUserDto, | ||
first_name: formattedFirstName, | ||
last_name: formattedLastName, | ||
email: email.toLowerCase(), | ||
phone_number: fullPhoneNumber, | ||
password: await this.hashingProvider.hashPassword(password), | ||
gender, | ||
}); | ||
|
||
const verificationToken = | ||
this.generateTokenHelper.generateVerificationToken(); | ||
const verificationTokenExpiry = new Date(); | ||
verificationTokenExpiry.setHours(verificationTokenExpiry.getHours() + 1); | ||
|
||
const newOtp = this.generateTokenHelper.generateOTP(6); | ||
const otpExpiry = new Date(); | ||
otpExpiry.setMinutes(otpExpiry.getMinutes() + 20); | ||
|
||
const emailVerificationToken = verificationToken; | ||
const emailVerificationTokenExpiresIn = verificationTokenExpiry; | ||
|
||
const otp = parseInt(newOtp); | ||
const otpExpiresIn = otpExpiry; | ||
|
||
const isEmailVerified = false; | ||
const isPhoneNumberVerified = false; | ||
const status = VerificationStatus.PENDING; | ||
|
||
const userAuth = this.userAuthRepository.create({ | ||
...createUserAuthDto, | ||
emailVerificationToken, | ||
emailVerificationTokenExpiresIn, | ||
otp, | ||
otpExpiresIn, | ||
isEmailVerified, | ||
isPhoneNumberVerified, | ||
status, | ||
user, | ||
}); | ||
|
||
user.user_auth = userAuth; | ||
|
||
await this.userAuthRepository.save(userAuth); | ||
await this.userRepository.save(user); | ||
|
||
return user; | ||
} | ||
} |
43 changes: 43 additions & 0 deletions
43
brints-estate-api/src/auth/providers/login-user.provider.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import { forwardRef, HttpStatus, Inject, Injectable } from '@nestjs/common'; | ||
import { Repository } from 'typeorm'; | ||
import { InjectRepository } from '@nestjs/typeorm'; | ||
|
||
import { User } from 'src/users/entities/user.entity'; | ||
import { HashingProvider } from './hashing.provider'; | ||
import { LoginUserDto } from '../dto/login.dto'; | ||
import { CustomException } from 'src/exceptions/custom.exception'; | ||
|
||
@Injectable() | ||
export class LoginUserProvider { | ||
constructor( | ||
@InjectRepository(User) | ||
private readonly userRepository: Repository<User>, | ||
|
||
@Inject(forwardRef(() => HashingProvider)) | ||
private readonly hashingProvider: HashingProvider, | ||
) {} | ||
|
||
public async loginUser(loginUserDto: LoginUserDto): Promise<User> { | ||
const user = await this.userRepository.findOne({ | ||
where: { email: loginUserDto.email }, | ||
}); | ||
|
||
if (!user) { | ||
throw new CustomException(HttpStatus.NOT_FOUND, 'User not found'); | ||
} | ||
|
||
const passwordMatch = await this.hashingProvider.comparePassword( | ||
loginUserDto.password, | ||
user.password, | ||
); | ||
|
||
if (!passwordMatch) { | ||
throw new CustomException( | ||
HttpStatus.BAD_REQUEST, | ||
'Invalid login credentials', | ||
); | ||
} | ||
|
||
return user; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,25 @@ | ||
import * as crypto from 'crypto'; | ||
|
||
export function generateVerificationToken() { | ||
return crypto.randomBytes(40).toString('hex'); | ||
} | ||
// export function generateVerificationToken() { | ||
// return crypto.randomBytes(40).toString('hex'); | ||
// } | ||
|
||
// export function generateOTP(length: number) { | ||
// return Math.floor(100000 + Math.random() * 900000) | ||
// .toString() | ||
// .slice(0, length); | ||
// } | ||
|
||
export class GenerateTokenHelper { | ||
constructor() {} | ||
|
||
public generateVerificationToken() { | ||
return crypto.randomBytes(40).toString('hex'); | ||
} | ||
|
||
export function generateOTP(length: number) { | ||
return Math.floor(100000 + Math.random() * 900000) | ||
.toString() | ||
.slice(0, length); | ||
public generateOTP(length: number) { | ||
return Math.floor(100000 + Math.random() * 900000) | ||
.toString() | ||
.slice(0, length); | ||
} | ||
} |
Oops, something went wrong.