import { Inject, Injectable, Logger, UnauthorizedException } from '@nestjs/common'; import { JwtService, JwtSignOptions } from '@nestjs/jwt'; import * as bcrypt from 'bcrypt'; import dayjs from 'dayjs'; import { isEmpty } from 'lodash'; import { AccountsEntity } from '../database/connections/ebitemp-api/entities'; import { TokenPayload } from './constants/token-payload.interface'; import { ACCESS_TOKEN_JWT_SERVICE } from './strategies/jwt/jwt-access-token.module'; import { REFRESH_TOKEN_JWT_SERVICE } from './strategies/jwt/jwt-refresh-token.module'; import { UsersAuthService } from './users/users-auth.service'; @Injectable() export class AuthService { private readonly logger = new Logger(AuthService.name); constructor( private readonly usersAuthService: UsersAuthService, @Inject(ACCESS_TOKEN_JWT_SERVICE) private readonly accessTokenJwtService: JwtService, @Inject(REFRESH_TOKEN_JWT_SERVICE) private readonly refreshTokenJwtService: JwtService ) {} public async signJwts(account: AccountsEntity) { const { token: accessToken, exp: accessTokenExp } = await this.getAccessToken(account); const { token: refreshToken, exp: refreshTokenExp } = await this.getRefreshToken(account); return { accessToken: accessToken, accessTokenExp: dayjs.unix(accessTokenExp).toJSON(), refreshToken: refreshToken, refreshTokenExp: dayjs.unix(refreshTokenExp).toJSON(), }; } public async getAccessToken(account: AccountsEntity, options?: JwtSignOptions) { const payload: TokenPayload = { username: account.username, sub: account.id }; const token = await this.accessTokenJwtService.signAsync(payload, options); const exp = this.accessTokenJwtService.decode(token).exp; return { token, exp }; } public async getRefreshToken(account: AccountsEntity, options?: JwtSignOptions) { const payload: TokenPayload = { username: account.username, sub: account.id }; const token = await this.refreshTokenJwtService.signAsync(payload, options); await this.usersAuthService.setCurrentRefreshTokenHash(account.id, token); const exp = this.refreshTokenJwtService.decode(token).exp; return { token, exp }; } public async getAuthenticatedUser(username: string, password: string): Promise { try { const account = await this.usersAuthService.getUserByUsername(username); if (!account) throw new UnauthorizedException(`Username not found`); await this.verifyPassword(password, account.password); return account; } catch (error) { throw new UnauthorizedException(`Wrong credentials`); } } private async verifyPassword(plainTextPassword: string, hashedPassword: string | null) { const isPasswordMatching = hashedPassword && !isEmpty(hashedPassword) ? await bcrypt.compare(plainTextPassword, hashedPassword) : null; if (!isPasswordMatching) { throw new UnauthorizedException(`Wrong password`); } } }