Files
backend/libs/common/src/auth/services/auth.service.ts
2025-06-03 09:47:24 +03:00

161 lines
4.7 KiB
TypeScript

import { PlatformType } from '@app/common/constants/platform-type.enum';
import { RoleType } from '@app/common/constants/role.type.enum';
import {
BadRequestException,
Injectable,
UnauthorizedException,
} from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { JwtService } from '@nestjs/jwt';
import * as argon2 from 'argon2';
import { OAuth2Client } from 'google-auth-library';
import { UserSessionEntity } from '../../../../common/src/modules/session/entities';
import { UserSessionRepository } from '../../../../common/src/modules/session/repositories/session.repository';
import { UserRepository } from '../../../../common/src/modules/user/repositories';
import { HelperHashService } from '../../helper/services';
@Injectable()
export class AuthService {
private client: OAuth2Client;
constructor(
private jwtService: JwtService,
private readonly userRepository: UserRepository,
private readonly sessionRepository: UserSessionRepository,
private readonly helperHashService: HelperHashService,
private readonly configService: ConfigService,
) {
this.client = new OAuth2Client(this.configService.get('GOOGLE_CLIENT_ID'));
}
async validateUser(
email: string,
pass: string,
regionUuid?: string,
platform?: PlatformType,
): Promise<any> {
const user = await this.userRepository.findOne({
where: {
email,
region: regionUuid ? { uuid: regionUuid } : undefined,
},
relations: ['roleType', 'project'],
});
if (!user) {
throw new BadRequestException('Invalid credentials');
}
if (
platform === PlatformType.WEB &&
[RoleType.SPACE_OWNER, RoleType.SPACE_MEMBER].includes(
user.roleType.type as RoleType,
)
) {
throw new UnauthorizedException('Access denied for web platform');
}
if (!user.isUserVerified) {
throw new BadRequestException('User is not verified');
}
if (!user.isActive) {
throw new BadRequestException('User is not active');
}
if (!user.hasAcceptedAppAgreement) {
throw new BadRequestException('User has not accepted app agreement');
}
const passwordMatch = await this.helperHashService.bcryptCompare(
pass,
user.password,
);
if (!passwordMatch) {
throw new BadRequestException('Invalid credentials');
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { password, ...result } = user;
return result;
}
async createSession(data): Promise<UserSessionEntity> {
return await this.sessionRepository.save(data);
}
async getTokens(
payload,
isRefreshToken = true,
accessTokenExpiry = '24h',
refreshTokenExpiry = '30d',
) {
const [accessToken, refreshToken] = await Promise.all([
this.jwtService.signAsync(payload, {
secret: this.configService.get<string>('JWT_SECRET'),
expiresIn: accessTokenExpiry,
}),
isRefreshToken
? this.jwtService.signAsync(payload, {
secret: this.configService.get<string>('JWT_SECRET'),
expiresIn: refreshTokenExpiry,
})
: null,
]);
return {
accessToken,
...(isRefreshToken ? { refreshToken } : {}),
};
}
async login(user: any) {
const payload = {
email: user.email,
userId: user.userId,
uuid: user.uuid,
sessionId: user.sessionId,
role: user?.role,
googleCode: user.googleCode,
hasAcceptedWebAgreement: user.hasAcceptedWebAgreement,
hasAcceptedAppAgreement: user.hasAcceptedAppAgreement,
project: user?.project,
};
if (payload.googleCode) {
const profile = await this.getProfile(payload.googleCode);
user = await this.userRepository.findOne({
where: { email: profile.email },
});
if (!user) {
return { profile };
}
}
const tokens = await this.getTokens(payload);
await this.updateRefreshToken(user.uuid, tokens.refreshToken);
return tokens;
}
async updateRefreshToken(userId: string, refreshToken: string) {
const hashedRefreshToken = await this.hashData(refreshToken);
await this.userRepository.update(
{ uuid: userId },
{
refreshToken: hashedRefreshToken,
},
);
}
hashData(data: string) {
return argon2.hash(data);
}
async getProfile(googleCode: string) {
try {
const ticket = await this.client.verifyIdToken({
idToken: googleCode,
audience: this.configService.get('GOOGLE_CLIENT_ID'),
});
const payload = ticket.getPayload();
return {
...payload,
};
} catch (error) {
throw new UnauthorizedException('Google login failed');
}
}
}