fix auth
This commit is contained in:
@@ -4,6 +4,8 @@ import { AppService } from './app.service';
|
|||||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||||
import { ConfigModule, ConfigService } from '@nestjs/config';
|
import { ConfigModule, ConfigService } from '@nestjs/config';
|
||||||
import * as Joi from 'joi';
|
import * as Joi from 'joi';
|
||||||
|
import {UsersModule} from "./users/user.module";
|
||||||
|
import {AuthenticationModule} from "./authentication/authentication.module";
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
@@ -17,6 +19,7 @@ import * as Joi from 'joi';
|
|||||||
username: configService.get<string>('POSTGRES_USER'),
|
username: configService.get<string>('POSTGRES_USER'),
|
||||||
password: configService.get<string>('POSTGRES_PASSWORD'),
|
password: configService.get<string>('POSTGRES_PASSWORD'),
|
||||||
database: configService.get<string>('POSTGRES_DB'),
|
database: configService.get<string>('POSTGRES_DB'),
|
||||||
|
autoLoadEntities: true,
|
||||||
ssl: {
|
ssl: {
|
||||||
rejectUnauthorized: false, // Needed for Neon and similar managed DBs
|
rejectUnauthorized: false, // Needed for Neon and similar managed DBs
|
||||||
},
|
},
|
||||||
@@ -28,7 +31,9 @@ import * as Joi from 'joi';
|
|||||||
JWT_SECRET: Joi.string().required(),
|
JWT_SECRET: Joi.string().required(),
|
||||||
JWT_EXPIRATION_TIME: Joi.string().required(),
|
JWT_EXPIRATION_TIME: Joi.string().required(),
|
||||||
})
|
})
|
||||||
})
|
}),
|
||||||
|
UsersModule,
|
||||||
|
AuthenticationModule
|
||||||
],
|
],
|
||||||
controllers: [AppController],
|
controllers: [AppController],
|
||||||
providers: [AppService],
|
providers: [AppService],
|
||||||
|
|||||||
@@ -1,30 +1,31 @@
|
|||||||
import { Module } from '@nestjs/common';
|
import {Module} from '@nestjs/common';
|
||||||
import { AuthenticationService } from './authentication.service';
|
import {AuthenticationService} from './authentication.service';
|
||||||
import { AuthenticationController } from './authentication.controller';
|
import {AuthenticationController} from './authentication.controller';
|
||||||
import { PassportModule } from '@nestjs/passport';
|
import {PassportModule} from '@nestjs/passport';
|
||||||
import { LocalStrategy } from './local.strategy';
|
import {LocalStrategy} from './local.strategy';
|
||||||
import { ConfigModule, ConfigService } from '@nestjs/config';
|
import {ConfigModule, ConfigService} from '@nestjs/config';
|
||||||
import { JwtModule } from '@nestjs/jwt';
|
import {JwtModule} from '@nestjs/jwt';
|
||||||
import {JwtStrategy} from "./jwt.strategy";
|
import {JwtStrategy} from "./jwt.strategy";
|
||||||
import {UsersModule} from "../users/user.module";
|
import {UsersModule} from "../users/user.module";
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [UsersModule, PassportModule,
|
imports: [UsersModule, PassportModule,
|
||||||
|
|
||||||
ConfigModule,
|
ConfigModule,
|
||||||
JwtModule.registerAsync({
|
JwtModule.registerAsync({
|
||||||
imports: [ConfigModule],
|
imports: [ConfigModule],
|
||||||
inject: [ConfigService],
|
inject: [ConfigService],
|
||||||
useFactory: (configService: ConfigService) => ({
|
useFactory: (configService: ConfigService) => ({
|
||||||
secret: configService.get('JWT_SECRET'),
|
secret: configService.get('JWT_SECRET'),
|
||||||
signOptions: {
|
signOptions: {
|
||||||
expiresIn: `${configService.get('JWT_EXPIRATION_TIME')}s`,
|
expiresIn: `${configService.get('JWT_EXPIRATION_TIME')}s`,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
|
|
||||||
] as const,
|
] as const,
|
||||||
providers: [AuthenticationService, LocalStrategy, JwtStrategy] as const,
|
providers: [AuthenticationService, LocalStrategy, JwtStrategy] as const,
|
||||||
controllers: [AuthenticationController] as const,
|
controllers: [AuthenticationController] as const,
|
||||||
})
|
})
|
||||||
export class AuthenticationModule {}
|
export class AuthenticationModule {
|
||||||
|
}
|
||||||
@@ -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 RegisterDto from './dto/register.dto';
|
||||||
import * as bcrypt from 'bcrypt';
|
import * as bcrypt from 'bcrypt';
|
||||||
import { JwtService } from '@nestjs/jwt';
|
import {JwtService} from '@nestjs/jwt';
|
||||||
import { ConfigService } from '@nestjs/config';
|
import {ConfigService} from '@nestjs/config';
|
||||||
import {UsersService} from "../users/user.service";
|
import {UsersService} from "../users/user.service";
|
||||||
import PostgresErrorCode from 'src/database/postgresErrorCodes.enum';
|
import PostgresErrorCode from 'src/database/postgresErrorCodes.enum';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AuthenticationService {
|
export class AuthenticationService {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly usersService: UsersService,
|
private readonly usersService: UsersService,
|
||||||
private readonly jwtService: JwtService,
|
private readonly jwtService: JwtService,
|
||||||
private readonly configService: ConfigService,
|
private readonly configService: ConfigService,
|
||||||
) {}
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
public async register(registrationData: RegisterDto) {
|
public async register(registrationData: RegisterDto) {
|
||||||
const hashedPassword = await bcrypt.hash(registrationData.password, 10);
|
const hashedPassword = await bcrypt.hash(registrationData.password, 10);
|
||||||
try {
|
try {
|
||||||
const createdUser = await this.usersService.create({
|
const createdUser = await this.usersService.create({
|
||||||
...registrationData,
|
...registrationData,
|
||||||
password: hashedPassword,
|
password: hashedPassword,
|
||||||
});
|
});
|
||||||
createdUser.password = undefined;
|
createdUser.password = undefined;
|
||||||
return createdUser;
|
return createdUser;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error?.code === PostgresErrorCode.UniqueViolation) {
|
if (error?.code === PostgresErrorCode.UniqueViolation) {
|
||||||
throw new HttpException(
|
throw new HttpException(
|
||||||
'User with that email already exists',
|
'User with that email already exists',
|
||||||
HttpStatus.BAD_REQUEST,
|
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,
|
||||||
);
|
);
|
||||||
}
|
if (!isPasswordMatching) {
|
||||||
throw new HttpException(
|
throw new HttpException(
|
||||||
'Something went wrong',
|
'Wrong credentials provided',
|
||||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
HttpStatus.BAD_REQUEST,
|
||||||
);
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public getCookieWithJwtAccessToken(
|
public async getUserFromAuthenticationToken(token: string) {
|
||||||
userId: number,
|
const payload: TokenPayload = this.jwtService.verify(token, {
|
||||||
isSecondFactorAuthenticated = false,
|
secret: this.configService.get('JWT_ACCESS_TOKEN_SECRET'),
|
||||||
) {
|
});
|
||||||
const payload: TokenPayload = { userId, isSecondFactorAuthenticated };
|
if (payload.userId) {
|
||||||
const token = this.jwtService.sign(payload, {
|
return this.usersService.getById(payload.userId);
|
||||||
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,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private async verifyPassword(
|
public getCookieForLogOut() {
|
||||||
plainTextPassword: string,
|
return `Authentication=; HttpOnly; Path=/; Max-Age=0`;
|
||||||
hashedPassword: string,
|
|
||||||
) {
|
|
||||||
const isPasswordMatching = await bcrypt.compare(
|
|
||||||
plainTextPassword,
|
|
||||||
hashedPassword,
|
|
||||||
);
|
|
||||||
if (!isPasswordMatching) {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public getCookieForLogOut() {
|
|
||||||
return `Authentication=; HttpOnly; Path=/; Max-Age=0`;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user