Skip to content

Commit

Permalink
Merge pull request #14 from richard483/ET-16--hash-password
Browse files Browse the repository at this point in the history
ET-16--hash-password
  • Loading branch information
richard483 authored Aug 27, 2023
2 parents f2cd07c + 130bfaa commit 525a2b8
Show file tree
Hide file tree
Showing 19 changed files with 645 additions and 194 deletions.
358 changes: 331 additions & 27 deletions package-lock.json

Large diffs are not rendered by default.

6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "8T-Auth",
"version": "0.0.1",
"version": "1.0.1",
"description": "",
"author": "8tech",
"private": true,
Expand All @@ -9,7 +9,7 @@
"build": "nest build",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"start": "node dist/main",
"dev": "SET NODE_ENV=dev && nest start",
"dev": "nest start",
"start:dev": "nest start --watch",
"start:debug": "nest start --debug --watch",
"start:prod": "node dist/main",
Expand All @@ -31,6 +31,7 @@
"@nestjs/swagger": "^6.3.0",
"@prisma/client": "^5.0.0",
"ajv": "^6.12.6",
"bcrypt": "^5.1.1",
"class-validator": "^0.14.0",
"passport": "^0.6.0",
"passport-google-oauth20": "^2.0.0",
Expand All @@ -44,6 +45,7 @@
"@nestjs/cli": "^9.0.0",
"@nestjs/schematics": "^9.0.0",
"@nestjs/testing": "^9.4.3",
"@types/bcrypt": "^5.0.0",
"@types/express": "^4.17.13",
"@types/jest": "29.5.1",
"@types/node": "18.16.12",
Expand Down
8 changes: 1 addition & 7 deletions src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,7 @@ import { UsersModule } from './users/users.module';
import { ConfigModule } from '@nestjs/config';

@Module({
imports: [
AuthModule,
UsersModule,
ConfigModule.forRoot({
isGlobal: true,
}),
],
imports: [ConfigModule.forRoot(), AuthModule, UsersModule],
controllers: [AppController],
providers: [AppService],
})
Expand Down
30 changes: 25 additions & 5 deletions src/auth/auth.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,50 +9,70 @@ import {
UseGuards,
} from '@nestjs/common';
import { AuthService } from './auth.service';
import { ApiBearerAuth, ApiTags } from '@nestjs/swagger';
import { ApiBearerAuth, ApiCookieAuth, ApiTags } from '@nestjs/swagger';
import { Roles } from './roles/role.decorator';
import { Role } from './roles/role.enum';
import { AuthenticateDto } from './dto/authenticate.dto';
import { AuthenticateRequest } from './requests/authenticate.request';
import { JwtAuthGuard } from './jwt/jwt-auth.guard';
import { RoleGuard } from './roles/role.guard';
import { GoogleGuard } from './google/google.guard';
import { RegisterRequest } from './requests/register.request';

@ApiTags('Auth')
@Controller('auth')
export class AuthController {
constructor(private authService: AuthService) {}

@Post('login')
async signIn(@Res() res, @Body() authenticateDto: AuthenticateDto) {
async signIn(@Res() res, @Body() authenticateDto: AuthenticateRequest) {
console.info('#AuthLogin request incoming with: ', authenticateDto);
try {
const response = await this.authService.login(authenticateDto, res);
return res.status(HttpStatus.OK).json({ ...response });
} catch (error) {
console.error('#AuthLogin error caused by: ', error);
return res.status(error.status).json({ error: error.message });
}
}

@Post('register')
async signUp(@Res() res, @Body() request: RegisterRequest) {
console.info('#AuthRegister request incoming with: ', request);
try {
const response = await this.authService.register(request);
return res.status(HttpStatus.OK).json({ ...response });
} catch (error) {
console.error('#AuthRegister error caused by: ', error);
return res.status(error.status).json({ error: error.message });
}
}

@Get('google')
@UseGuards(GoogleGuard)
// this end point is needed for google authentication
// eslint-disable-next-line @typescript-eslint/no-empty-function
async googleAuth() {}

@Get('google/redirect')
@UseGuards(GoogleGuard)
async googleAuthRedirect(@Request() req, @Res() res) {
console.info('#AuthGoogleAuthRedirect google auth request incoming');
try {
const response = await this.authService.googleLogin(req, res);
// TODO : redirect to frontend
return res.status(HttpStatus.OK).json({ ...response });
} catch (error) {
console.error('#AutGoogleAuthRedirect error caused by: ', error);
return res.status(error.status).json({ error: error.message });
}
}

@ApiBearerAuth()
@ApiCookieAuth()
@Roles(Role.USER)
@UseGuards(JwtAuthGuard, RoleGuard)
@Get('info')
async getProfileInfo(@Request() req) {
return req.user;
async getProfileInfo(@Request() req, @Res() res) {
return res.status(HttpStatus.OK).json({ ...req.user });
}
}
5 changes: 2 additions & 3 deletions src/auth/auth.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { UsersModule } from '../users/users.module';
import { JwtModule } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport';
import { JwtStrategy } from './jwt/jwt.strategy';
import { env } from 'process';
import { GoogleStrategy } from './google/google.strategy';

@Module({
Expand All @@ -14,8 +13,8 @@ import { GoogleStrategy } from './google/google.strategy';
PassportModule,
JwtModule.register({
global: true,
secret: env.JWT_SECRET,
signOptions: { expiresIn: '6000s' },
secret: process.env.JWT_SECRET,
signOptions: { expiresIn: '600000s' },
}),
PassportModule,
],
Expand Down
54 changes: 47 additions & 7 deletions src/auth/auth.service.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
import { Injectable, UnauthorizedException } from '@nestjs/common';
import {
BadRequestException,
Injectable,
UnauthorizedException,
} from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { UsersService } from '../users/users.service';
import { AuthenticateDto } from './dto/authenticate.dto';
import { IAuthenticate, IGoogleUser } from './interface/authenticate.interface';
import { AuthenticateRequest } from './requests/authenticate.request';
import { IAuthenticate, IGoogleUser } from './interface/auth.interface';
import { IUser } from '../users/interface/user.interface';
import { Role } from './roles/role.enum';
import { compare, genSaltSync, hashSync } from 'bcrypt';
import { RegisterRequest } from './requests/register.request';

@Injectable()
export class AuthService {
Expand All @@ -13,25 +19,53 @@ export class AuthService {
private jwtService: JwtService,
) {}

async validateUser(authenticateDto: AuthenticateDto): Promise<IAuthenticate> {
const user = await this.usersService.findOne(authenticateDto);
async validateUser(
authenticateRequest: AuthenticateRequest,
): Promise<IAuthenticate> {
const user = await this.usersService.findOne(authenticateRequest.email);
if (!user) {
throw new UnauthorizedException('INVALID_CREDENTIALS');
}

const isPasswordMatch = await compare(
authenticateRequest.password,
user.password,
);

if (!isPasswordMatch) {
throw new UnauthorizedException('INVALID_CREDENTIALS');
}

// remove password from user object
const { password, ...userData } = user;
const data = { user: userData };
return data;
}

async login(authenitcateDto: AuthenticateDto, res): Promise<IAuthenticate> {
const user = await this.validateUser(authenitcateDto);
async login(
authenticateRequest: AuthenticateRequest,
res,
): Promise<IAuthenticate> {
const user = await this.validateUser(authenticateRequest);
user.token = this.jwtService.sign(user);
res.cookie('EToken', user.token);
return user;
}

async register(registerRequest: RegisterRequest): Promise<IUser> {
const { repeatPassword, ...userCreate } = registerRequest;

if (userCreate.password !== repeatPassword) {
throw new BadRequestException('PASSWORD_NOT_MATCH');
}

userCreate.password = this.hashPassword(userCreate.password);
const user = await this.usersService.create({
...userCreate,
});
return user;
}

async googleLogin(req, res): Promise<IAuthenticate> {
const user: IGoogleUser = req.user;

Expand All @@ -57,4 +91,10 @@ export class AuthService {

return userData;
}

private hashPassword(password: string): string {
const salt = genSaltSync(10);
const hashedPassword = hashSync(password, salt);
return hashedPassword;
}
}
File renamed without changes.
3 changes: 1 addition & 2 deletions src/auth/jwt/jwt.strategy.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { env } from 'process';

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
Expand All @@ -12,7 +11,7 @@ export class JwtStrategy extends PassportStrategy(Strategy) {
ExtractJwt.fromAuthHeaderAsBearerToken(),
]),
ignoreExpiration: false,
secretOrKey: env.JWT_SECRET,
secretOrKey: process.env.JWT_SECRET,
});
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsNotEmpty, IsString } from 'class-validator';
import { IsEmail, IsNotEmpty, IsString } from 'class-validator';

export class AuthenticateDto {
export class AuthenticateRequest {
@ApiProperty()
@IsNotEmpty()
@IsString()
@IsEmail()
readonly email: string;

@ApiProperty()
Expand Down
24 changes: 24 additions & 0 deletions src/auth/requests/register.request.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsEmail, IsNotEmpty, IsString } from 'class-validator';

export class RegisterRequest {
@ApiProperty()
@IsNotEmpty()
@IsEmail()
readonly email: string;

@ApiProperty()
@IsNotEmpty()
@IsString()
readonly password: string;

@ApiProperty()
@IsNotEmpty()
@IsString()
readonly repeatPassword: string;

@ApiProperty()
@IsNotEmpty()
@IsString()
readonly userName: string;
}
Loading

0 comments on commit 525a2b8

Please sign in to comment.