This commit is contained in:
2025-05-13 02:20:31 +07:00
parent 6e9bfa4b82
commit 87a7eb0931
3 changed files with 137 additions and 130 deletions

View File

@@ -4,6 +4,8 @@ import { AppService } from './app.service';
import { TypeOrmModule } from '@nestjs/typeorm';
import { ConfigModule, ConfigService } from '@nestjs/config';
import * as Joi from 'joi';
import {UsersModule} from "./users/user.module";
import {AuthenticationModule} from "./authentication/authentication.module";
@Module({
imports: [
@@ -17,6 +19,7 @@ import * as Joi from 'joi';
username: configService.get<string>('POSTGRES_USER'),
password: configService.get<string>('POSTGRES_PASSWORD'),
database: configService.get<string>('POSTGRES_DB'),
autoLoadEntities: true,
ssl: {
rejectUnauthorized: false, // Needed for Neon and similar managed DBs
},
@@ -28,7 +31,9 @@ import * as Joi from 'joi';
JWT_SECRET: Joi.string().required(),
JWT_EXPIRATION_TIME: Joi.string().required(),
})
})
}),
UsersModule,
AuthenticationModule
],
controllers: [AppController],
providers: [AppService],

View File

@@ -1,30 +1,31 @@
import { Module } from '@nestjs/common';
import { AuthenticationService } from './authentication.service';
import { AuthenticationController } from './authentication.controller';
import { PassportModule } from '@nestjs/passport';
import { LocalStrategy } from './local.strategy';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { JwtModule } from '@nestjs/jwt';
import {Module} from '@nestjs/common';
import {AuthenticationService} from './authentication.service';
import {AuthenticationController} from './authentication.controller';
import {PassportModule} from '@nestjs/passport';
import {LocalStrategy} from './local.strategy';
import {ConfigModule, ConfigService} from '@nestjs/config';
import {JwtModule} from '@nestjs/jwt';
import {JwtStrategy} from "./jwt.strategy";
import {UsersModule} from "../users/user.module";
@Module({
imports: [UsersModule, PassportModule,
imports: [UsersModule, PassportModule,
ConfigModule,
JwtModule.registerAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (configService: ConfigService) => ({
secret: configService.get('JWT_SECRET'),
signOptions: {
expiresIn: `${configService.get('JWT_EXPIRATION_TIME')}s`,
},
}),
}),
ConfigModule,
JwtModule.registerAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (configService: ConfigService) => ({
secret: configService.get('JWT_SECRET'),
signOptions: {
expiresIn: `${configService.get('JWT_EXPIRATION_TIME')}s`,
},
}),
}),
] as const,
providers: [AuthenticationService, LocalStrategy, JwtStrategy] as const,
controllers: [AuthenticationController] as const,
] as const,
providers: [AuthenticationService, LocalStrategy, JwtStrategy] as const,
controllers: [AuthenticationController] as const,
})
export class AuthenticationModule {}
export class AuthenticationModule {
}

View File

@@ -1,121 +1,122 @@
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
import {HttpException, HttpStatus, Injectable} from '@nestjs/common';
import RegisterDto from './dto/register.dto';
import * as bcrypt from 'bcrypt';
import { JwtService } from '@nestjs/jwt';
import { ConfigService } from '@nestjs/config';
import {JwtService} from '@nestjs/jwt';
import {ConfigService} from '@nestjs/config';
import {UsersService} from "../users/user.service";
import PostgresErrorCode from 'src/database/postgresErrorCodes.enum';
@Injectable()
export class AuthenticationService {
constructor(
private readonly usersService: UsersService,
private readonly jwtService: JwtService,
private readonly configService: ConfigService,
) {}
constructor(
private readonly usersService: UsersService,
private readonly jwtService: JwtService,
private readonly configService: ConfigService,
) {
}
public async register(registrationData: RegisterDto) {
const hashedPassword = await bcrypt.hash(registrationData.password, 10);
try {
const createdUser = await this.usersService.create({
...registrationData,
password: hashedPassword,
});
createdUser.password = undefined;
return createdUser;
} catch (error) {
if (error?.code === PostgresErrorCode.UniqueViolation) {
throw new HttpException(
'User with that email already exists',
HttpStatus.BAD_REQUEST,
public async register(registrationData: RegisterDto) {
const hashedPassword = await bcrypt.hash(registrationData.password, 10);
try {
const createdUser = await this.usersService.create({
...registrationData,
password: hashedPassword,
});
createdUser.password = undefined;
return createdUser;
} catch (error) {
if (error?.code === PostgresErrorCode.UniqueViolation) {
throw new HttpException(
'User with that email already exists',
HttpStatus.BAD_REQUEST,
);
}
console.log(error);
throw new HttpException(
'Something went wrong',
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}
public getCookieWithJwtAccessToken(
userId: number,
isSecondFactorAuthenticated = false,
) {
const payload: TokenPayload = {userId, isSecondFactorAuthenticated};
const token = this.jwtService.sign(payload, {
secret: this.configService.get('JWT_ACCESS_TOKEN_SECRET'),
expiresIn: `${this.configService.get(
'JWT_ACCESS_TOKEN_EXPIRATION_TIME',
)}s`,
});
return `Authentication=${token}; HttpOnly; Path=/; Max-Age=${this.configService.get(
'JWT_ACCESS_TOKEN_EXPIRATION_TIME',
)}`;
}
public getCookieWithJwtRefreshToken(userId: number) {
const payload: TokenPayload = {userId};
const token = this.jwtService.sign(payload, {
secret: this.configService.get('JWT_REFRESH_TOKEN_SECRET'),
expiresIn: this.configService.get('JWT_ACCESS_TOKEN_EXPIRATION_TIME'),
});
const cookie = `Refresh=${token}; HttpOnly; Path=/; Max-Age=${this.configService.get(
'JWT_REFRESH_TOKEN_EXPIRATION_TIME',
)}`;
return {
cookie,
token,
};
}
public getCookiesForLogOut() {
return [
'Authentication=; HttpOnly; Path=/; Max-Age=0',
'Refresh=; HttpOnly; Path=/; Max-Age=0',
];
}
public async getAuthenticatedUser(email: string, plainTextPassword: string) {
try {
const user = await this.usersService.getByEmail(email);
await this.verifyPassword(plainTextPassword, user.password);
return user;
} catch (error) {
throw new HttpException(
'Wrong credentials provided',
HttpStatus.BAD_REQUEST,
);
}
}
private async verifyPassword(
plainTextPassword: string,
hashedPassword: string,
) {
const isPasswordMatching = await bcrypt.compare(
plainTextPassword,
hashedPassword,
);
}
throw new HttpException(
'Something went wrong',
HttpStatus.INTERNAL_SERVER_ERROR,
);
if (!isPasswordMatching) {
throw new HttpException(
'Wrong credentials provided',
HttpStatus.BAD_REQUEST,
);
}
}
}
public getCookieWithJwtAccessToken(
userId: number,
isSecondFactorAuthenticated = false,
) {
const payload: TokenPayload = { userId, isSecondFactorAuthenticated };
const token = this.jwtService.sign(payload, {
secret: this.configService.get('JWT_ACCESS_TOKEN_SECRET'),
expiresIn: `${this.configService.get(
'JWT_ACCESS_TOKEN_EXPIRATION_TIME',
)}s`,
});
return `Authentication=${token}; HttpOnly; Path=/; Max-Age=${this.configService.get(
'JWT_ACCESS_TOKEN_EXPIRATION_TIME',
)}`;
}
public getCookieWithJwtRefreshToken(userId: number) {
const payload: TokenPayload = { userId };
const token = this.jwtService.sign(payload, {
secret: this.configService.get('JWT_REFRESH_TOKEN_SECRET'),
expiresIn: `${this.configService.get(
'JWT_REFRESH_TOKEN_EXPIRATION_TIME',
)}s`,
});
const cookie = `Refresh=${token}; HttpOnly; Path=/; Max-Age=${this.configService.get(
'JWT_REFRESH_TOKEN_EXPIRATION_TIME',
)}`;
return {
cookie,
token,
};
}
public getCookiesForLogOut() {
return [
'Authentication=; HttpOnly; Path=/; Max-Age=0',
'Refresh=; HttpOnly; Path=/; Max-Age=0',
];
}
public async getAuthenticatedUser(email: string, plainTextPassword: string) {
try {
const user = await this.usersService.getByEmail(email);
await this.verifyPassword(plainTextPassword, user.password);
return user;
} catch (error) {
throw new HttpException(
'Wrong credentials provided',
HttpStatus.BAD_REQUEST,
);
public async getUserFromAuthenticationToken(token: string) {
const payload: TokenPayload = this.jwtService.verify(token, {
secret: this.configService.get('JWT_ACCESS_TOKEN_SECRET'),
});
if (payload.userId) {
return this.usersService.getById(payload.userId);
}
}
}
private async verifyPassword(
plainTextPassword: string,
hashedPassword: string,
) {
const isPasswordMatching = await bcrypt.compare(
plainTextPassword,
hashedPassword,
);
if (!isPasswordMatching) {
throw new HttpException(
'Wrong credentials provided',
HttpStatus.BAD_REQUEST,
);
public getCookieForLogOut() {
return `Authentication=; HttpOnly; Path=/; Max-Age=0`;
}
}
public async getUserFromAuthenticationToken(token: string) {
const payload: TokenPayload = this.jwtService.verify(token, {
secret: this.configService.get('JWT_ACCESS_TOKEN_SECRET'),
});
if (payload.userId) {
return this.usersService.getById(payload.userId);
}
}
public getCookieForLogOut() {
return `Authentication=; HttpOnly; Path=/; Max-Age=0`;
}
}