authentication module done

This commit is contained in:
VirajBrainvire
2024-02-26 11:23:44 +05:30
parent 4ffd94ec78
commit 0764793874
75 changed files with 2111 additions and 89 deletions

View File

@ -0,0 +1,8 @@
export default () => ({
DB_HOST: process.env.DB_HOST,
DB_PORT: process.env.DB_PORT,
DB_USER: process.env.DB_USER,
DB_PASSWORD: process.env.DB_PASSWORD,
DB_NAME: process.env.DB_NAME,
DB_SYNC: process.env.DB_SYNC,
});

View File

@ -1,3 +1,4 @@
import AuthConfig from './auth.config';
import AppConfig from './app.config'
export default [AuthConfig];
export default [AuthConfig,AppConfig];

View File

@ -0,0 +1,11 @@
import { registerAs } from '@nestjs/config';
export default registerAs(
'jwt',
(): Record<string, any> => ({
secret: process.env.JWT_SECRET,
expire_time: process.env.JWT_EXPIRE_TIME,
secret_refresh: process.env.JWT_SECRET_REFRESH,
expire_refresh: process.env.JWT_EXPIRE_TIME_REFRESH,
})
);

View File

@ -1,8 +1,32 @@
import { NestFactory } from '@nestjs/core';
import { AuthModule } from './auth.module';
import rateLimit from 'express-rate-limit';
import helmet from 'helmet';
import { setupSwaggerAuthentication } from '@app/common/util/user-auth.swagger.utils';
import { ValidationPipe } from '@nestjs/common';
async function bootstrap() {
const app = await NestFactory.create(AuthModule);
app.enableCors();
app.use(
rateLimit({
windowMs: 5 * 60 * 1000,
max: 500,
}),
);
app.use(
helmet({
contentSecurityPolicy: false,
}),
);
setupSwaggerAuthentication(app);
app.useGlobalPipes(new ValidationPipe());
await app.listen(6001);
}
bootstrap();

View File

@ -2,11 +2,24 @@ import { Module } from '@nestjs/common';
import { AuthenticationController } from './controllers/authentication.controller';
import { AuthenticationService } from './services/authentication.service';
import { ConfigModule } from '@nestjs/config';
import { UserRepositoryModule } from '@app/common/modules/user/user.repository.module';
import { CommonModule } from '@app/common';
import { UserAuthController } from './controllers';
import { UserAuthService } from './services';
import { UserRepository } from '@app/common/modules/user/repositories';
import { UserSessionRepository } from '@app/common/modules/session/repositories/session.repository';
import { UserOtpRepository } from '@app/common/modules/user-otp/repositories/user-otp.repository';
@Module({
imports: [ConfigModule],
controllers: [AuthenticationController],
providers: [AuthenticationService],
exports: [AuthenticationService],
imports: [ConfigModule, UserRepositoryModule, CommonModule],
controllers: [AuthenticationController, UserAuthController],
providers: [
AuthenticationService,
UserAuthService,
UserRepository,
UserSessionRepository,
UserOtpRepository,
],
exports: [AuthenticationService, UserAuthService],
})
export class AuthenticationModule {}

View File

@ -0,0 +1,3 @@
export interface ILoginResponse {
access_token: string;
}

View File

@ -1,10 +1,12 @@
import { Controller, Post } from '@nestjs/common';
import { AuthenticationService } from '../services/authentication.service';
import { ApiTags } from '@nestjs/swagger';
@Controller({
version: '1',
path: 'authentication',
})
@ApiTags('Tuya Auth')
export class AuthenticationController {
constructor(private readonly authenticationService: AuthenticationService) {}
@Post('auth')

View File

@ -0,0 +1,2 @@
export * from './authentication.controller';
export * from './user-auth.controller';

View File

@ -0,0 +1,95 @@
import {
Body,
Controller,
Delete,
HttpStatus,
Param,
Post,
UseGuards,
} from '@nestjs/common';
import { UserAuthService } from '../services/user-auth.service';
import { UserSignUpDto } from '../dtos/user-auth.dto';
import { ApiBearerAuth, ApiTags } from '@nestjs/swagger';
import { ResponseMessage } from '@app/common/response/response.decorator';
import { UserLoginDto } from '../dtos/user-login.dto';
import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard';
import { ForgetPasswordDto, UserOtpDto, VerifyOtpDto } from '../dtos';
@Controller({
version: '1',
path: 'authentication',
})
@ApiTags('Auth')
export class UserAuthController {
constructor(private readonly userAuthService: UserAuthService) {}
@ResponseMessage('User Registered Successfully')
@Post('user/signup')
async signUp(@Body() userSignUpDto: UserSignUpDto) {
const signupUser = await this.userAuthService.signUp(userSignUpDto);
return {
statusCode: HttpStatus.CREATED,
data: {
id: signupUser.uuid,
},
message: 'User Registered Successfully',
};
}
@ResponseMessage('user logged in successfully')
@Post('user/login')
async userLogin(@Body() data: UserLoginDto) {
const accessToken = await this.userAuthService.userLogin(data);
return {
statusCode: HttpStatus.CREATED,
data: accessToken,
message: 'User Loggedin Successfully',
};
}
@ApiBearerAuth()
@UseGuards(JwtAuthGuard)
@Delete('user/delete/:id')
async userDelete(@Param('id') id: string) {
await this.userAuthService.deleteUser(id);
return {
statusCode: HttpStatus.OK,
data: {
id,
},
message: 'User Deleted Successfully',
};
}
@Post('user/send-otp')
async sendOtp(@Body() otpDto: UserOtpDto) {
const otpCode = await this.userAuthService.generateOTP(otpDto);
return {
statusCode: HttpStatus.OK,
data: {
otp: otpCode,
},
message: 'Otp Send Successfully',
};
}
@Post('user/verify-otp')
async verifyOtp(@Body() verifyOtpDto: VerifyOtpDto) {
await this.userAuthService.verifyOTP(verifyOtpDto);
return {
statusCode: HttpStatus.OK,
data: {},
message: 'Otp Verified Successfully',
};
}
@Post('user/forget-password')
async forgetPassword(@Body() forgetPasswordDto: ForgetPasswordDto) {
await this.userAuthService.forgetPassword(forgetPasswordDto);
return {
statusCode: HttpStatus.OK,
data: {},
message: 'Password changed successfully',
};
}
}

View File

@ -0,0 +1,4 @@
export * from './user-auth.dto'
export * from './user-login.dto'
export * from './user-otp.dto'
export * from './user-password.dto'

View File

@ -0,0 +1,36 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsEmail, IsNotEmpty, IsString } from 'class-validator';
export class UserSignUpDto {
@ApiProperty({
description:'email',
required:true
})
@IsEmail()
@IsNotEmpty()
public email: string;
@ApiProperty({
description:'password',
required:true
})
@IsString()
@IsNotEmpty()
public password: string;
@ApiProperty({
description:'first name',
required:true
})
@IsString()
@IsNotEmpty()
public firstName: string;
@ApiProperty({
description:'last name',
required:true
})
@IsString()
@IsNotEmpty()
public lastName: string;
}

View File

@ -0,0 +1,14 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsEmail, IsNotEmpty, IsOptional, IsString } from 'class-validator';
export class UserLoginDto {
@ApiProperty()
@IsEmail()
@IsNotEmpty()
email: string;
@ApiProperty()
@IsString()
@IsOptional()
password: string;
}

View File

@ -0,0 +1,22 @@
import { OtpType } from '@app/common/constants/otp-type.enum';
import { ApiProperty } from '@nestjs/swagger';
import { IsEmail, IsEnum, IsNotEmpty, IsString } from 'class-validator';
export class UserOtpDto {
@ApiProperty()
@IsEmail()
@IsNotEmpty()
email: string;
@ApiProperty()
@IsEnum(OtpType)
@IsNotEmpty()
type: OtpType;
}
export class VerifyOtpDto extends UserOtpDto {
@ApiProperty()
@IsString()
@IsNotEmpty()
otpCode: string;
}

View File

@ -0,0 +1,14 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsEmail, IsNotEmpty, IsString } from 'class-validator';
export class ForgetPasswordDto {
@ApiProperty()
@IsEmail()
@IsNotEmpty()
email: string;
@ApiProperty()
@IsString()
@IsNotEmpty()
password: string;
}

View File

@ -0,0 +1,2 @@
export * from './authentication.service';
export * from './user-auth.service';

View File

@ -0,0 +1,151 @@
import { UserRepository } from '@app/common/modules/user/repositories';
import {
BadRequestException,
Injectable,
UnauthorizedException,
} from '@nestjs/common';
import { UserSignUpDto } from '../dtos/user-auth.dto';
import { HelperHashService } from '@app/common/helper/services';
import { UserLoginDto } from '../dtos/user-login.dto';
import { AuthService } from '@app/common/auth/services/auth.service';
import { UserSessionRepository } from '@app/common/modules/session/repositories/session.repository';
import { UserOtpRepository } from '@app/common/modules/user-otp/repositories/user-otp.repository';
import { ForgetPasswordDto, UserOtpDto, VerifyOtpDto } from '../dtos';
import { EmailService } from '@app/common/util/email.service';
import { OtpType } from '@app/common/constants/otp-type.enum';
import { UserEntity } from '@app/common/modules/user/entities/user.entity';
import { ILoginResponse } from '../constants/login.response.constant';
@Injectable()
export class UserAuthService {
constructor(
private readonly userRepository: UserRepository,
private readonly sessionRepository: UserSessionRepository,
private readonly otpRepository: UserOtpRepository,
private readonly helperHashService: HelperHashService,
private readonly authService: AuthService,
private readonly emailService: EmailService,
) {}
async signUp(userSignUpDto: UserSignUpDto): Promise<UserEntity> {
const findUser = await this.findUser(userSignUpDto.email);
if (findUser) {
throw new BadRequestException('User already registered with given email');
}
const salt = this.helperHashService.randomSalt(10);
const password = this.helperHashService.bcrypt(
userSignUpDto.password,
salt,
);
return await this.userRepository.save({ ...userSignUpDto, password });
}
async findUser(email: string) {
return await this.userRepository.findOne({
where: {
email,
},
});
}
async forgetPassword(forgetPasswordDto: ForgetPasswordDto) {
const findUser = await this.findUser(forgetPasswordDto.email);
if (!findUser) {
throw new BadRequestException('User not found');
}
const salt = this.helperHashService.randomSalt(10);
const password = this.helperHashService.bcrypt(
forgetPasswordDto.password,
salt,
);
return await this.userRepository.update(
{ uuid: findUser.uuid },
{ password },
);
}
async userLogin(data: UserLoginDto): Promise<ILoginResponse> {
const user = await this.authService.validateUser(data.email, data.password);
if (!user) {
throw new UnauthorizedException('Invalid login credentials.');
}
const session = await Promise.all([
await this.sessionRepository.update(
{ userId: user.id },
{
isLoggedOut: true,
},
),
await this.authService.createSession({
userId: user.uuid,
loginTime: new Date(),
isLoggedOut: false,
}),
]);
return await this.authService.login({
email: user.email,
userId: user.id,
uuid: user.uuid,
sessionId: session[1].uuid,
});
}
async deleteUser(uuid: string) {
const user = await this.findOneById(uuid);
if (!user) {
throw new BadRequestException('User does not found');
}
return await this.userRepository.delete({ uuid });
}
async findOneById(id: string): Promise<UserEntity> {
return await this.userRepository.findOne({ where: { uuid: id } });
}
async generateOTP(data: UserOtpDto): Promise<string> {
await this.otpRepository.delete({ email: data.email, type: data.type });
const otpCode = Math.floor(100000 + Math.random() * 900000).toString();
const expiryTime = new Date();
expiryTime.setMinutes(expiryTime.getMinutes() + 1);
await this.otpRepository.save({
email: data.email,
otpCode,
expiryTime,
type: data.type,
});
const subject = 'OTP send successfully';
const message = `Your OTP code is ${otpCode}`;
this.emailService.sendOTPEmail(data.email, subject, message);
return otpCode;
}
async verifyOTP(data: VerifyOtpDto): Promise<boolean> {
const otp = await this.otpRepository.findOne({
where: { email: data.email, type: data.type },
});
if (!otp) {
throw new BadRequestException('this email is not registered');
}
if (otp.otpCode !== data.otpCode) {
throw new BadRequestException('You entered wrong otp');
}
if (otp.expiryTime < new Date()) {
await this.otpRepository.delete(otp.id);
throw new BadRequestException('OTP expired');
}
if (data.type == OtpType.VERIFICATION) {
await this.userRepository.update(
{ email: data.email },
{ isUserVerified: true },
);
}
return true;
}
}

View File

@ -4,6 +4,7 @@ import { AppService } from './backend.service';
import { UserModule } from './modules/user/user.module';
import { ConfigModule } from '@nestjs/config';
import config from './config';
import { CommonModule } from '@app/common';
@Module({
imports: [
@ -11,6 +12,7 @@ import config from './config';
load: config,
}),
UserModule,
CommonModule,
],
controllers: [AppController],
providers: [AppService],

View File

@ -0,0 +1,8 @@
export default () => ({
DB_HOST: process.env.DB_HOST,
DB_PORT: process.env.DB_PORT,
DB_USER: process.env.DB_USER,
DB_PASSWORD: process.env.DB_PASSWORD,
DB_NAME: process.env.DB_NAME,
DB_SYNC: process.env.DB_SYNC,
});

View File

@ -1,3 +1,4 @@
import AuthConfig from './auth.config';
export default [AuthConfig];
import AppConfig from './app.config'
import JwtConfig from './jwt.config'
export default [AuthConfig,AppConfig,JwtConfig];

View File

@ -0,0 +1,11 @@
import { registerAs } from '@nestjs/config';
export default registerAs(
'jwt',
(): Record<string, any> => ({
secret: process.env.JWT_SECRET,
expire_time: process.env.JWT_EXPIRE_TIME,
secret_refresh: process.env.JWT_SECRET_REFRESH,
expire_refresh: process.env.JWT_EXPIRE_TIME_REFRESH,
})
);

View File

@ -1,8 +1,26 @@
import { NestFactory } from '@nestjs/core';
import { BackendModule } from './backend.module';
import rateLimit from 'express-rate-limit';
import helmet from 'helmet';
import { ValidationPipe } from '@nestjs/common';
async function bootstrap() {
const app = await NestFactory.create(BackendModule);
app.enableCors();
app.use(
rateLimit({
windowMs: 5 * 60 * 1000, // 5 minutes
max: 500, // limit each IP to 500 requests per windowMs
}),
);
app.use(
helmet({
contentSecurityPolicy: false,
}),
);
app.useGlobalPipes(new ValidationPipe());
await app.listen(6000);
}
bootstrap();

View File

@ -0,0 +1 @@
export * from './user.controller'

View File

@ -1,8 +1,9 @@
import { Controller, Get, Query } from '@nestjs/common';
import { UserService } from '../services/user.service';
import { UserListDto } from '../dtos/user.list.dto';
import { ApiTags } from '@nestjs/swagger';
//@ApiTags('User Module')
@ApiTags('User Module')
@Controller({
version: '1',
path: 'user',

View File

@ -0,0 +1 @@
export * from './user.list.dto'

View File

@ -0,0 +1 @@
export * from './user.service'