Files
dsc-api/src/modules/auth/providers/tokens.service.ts
T
morteza c925d2e539
Build and Deploy Docker Images / build_and_deploy (push) Has been cancelled
multi role
2026-07-22 12:19:37 +03:30

142 lines
5.1 KiB
TypeScript
Executable File

import { randomBytes } from "node:crypto";
import { Injectable, Logger, UnauthorizedException } from "@nestjs/common";
import { ConfigService } from "@nestjs/config";
import { JwtService } from "@nestjs/jwt";
import dayjs from "dayjs";
import { DataSource, QueryRunner } from "typeorm";
import { AuthMessage } from "../../../common/enums/message.enum";
import { User } from "../../users/entities/user.entity";
import { RefreshTokensRepository } from "../../users/repositories/refresh-token.repository";
import { getPermissionsFromRoles } from "../../users/utils/role-permissions.util";
import { ITokenPayload } from "../interfaces/IToken-payload";
import { ResellerType } from "../DTO/verify-reseller-otp.dto";
@Injectable()
export class TokensService {
private readonly logger = new Logger(TokensService.name);
constructor(
private readonly configService: ConfigService,
private readonly jwtService: JwtService,
private readonly refreshTokensRepository: RefreshTokensRepository,
private readonly dataSource: DataSource,
) { }
// ----------- generate token -----------------
async generateTokens(user: User, queryRunner?: QueryRunner) {
return this.generateAccessAndRefreshToken(
{
id: user.id,
isAdmin: user.roles.some((r) => r.isAdmin),
permissions: getPermissionsFromRoles(user.roles),
},
queryRunner,
);
}
// -----------------------------
async generateTokensForReseller(user: User, resellerType: ResellerType, queryRunner?: QueryRunner) {
return this.generateAccessAndRefreshToken(
{
id: user.id,
isAdmin: false,
resellerType,
permissions: [],
},
queryRunner,
);
}
//**************************************************************** */
private async generateAccessAndRefreshToken(payload: ITokenPayload, queryRunner?: QueryRunner) {
const accessExpire = this.configService.getOrThrow<number>("ACCESS_TOKEN_EXPIRE");
const refreshExpire = this.configService.getOrThrow<number>("REFRESH_TOKEN_EXPIRE");
const accessToken = this.jwtService.sign(payload, { expiresIn: `${accessExpire}m` });
const refreshToken = await this.generateRefreshToken();
await this.storeRefreshToken(payload.id, refreshToken, queryRunner);
return {
accessToken: { token: accessToken, expire: dayjs().add(accessExpire, "minute").valueOf() },
refreshToken: { token: refreshToken, expire: dayjs().add(refreshExpire, "day").valueOf() },
};
}
//**************************************************************** */
async storeRefreshToken(userId: string, refreshToken: string, queryRunner = this.dataSource.createQueryRunner()) {
let transactionStarted = false;
try {
if (!queryRunner.isTransactionActive) {
await queryRunner.connect();
await queryRunner.startTransaction();
transactionStarted = true;
}
const refreshExpire = this.configService.getOrThrow<number>("REFRESH_TOKEN_EXPIRE");
const expiresAt = dayjs().add(refreshExpire, "day").toDate();
const token = queryRunner.manager.create(this.refreshTokensRepository.target, {
token: refreshToken,
user: { id: userId },
expiresAt,
});
await queryRunner.manager.save(this.refreshTokensRepository.target, token);
if (transactionStarted) await queryRunner.commitTransaction();
} catch (error) {
this.logger.error(error);
if (transactionStarted) await queryRunner.rollbackTransaction();
throw error;
} finally {
if (transactionStarted) await queryRunner.release();
}
}
/************************************************************ */
async refreshToken(oldRefreshToken: string) {
const queryRunner = this.dataSource.createQueryRunner();
try {
await queryRunner.connect();
await queryRunner.startTransaction();
const token = await queryRunner.manager.findOne(this.refreshTokensRepository.target, {
where: { token: oldRefreshToken },
relations: { user: { roles: { permissions: true } } },
});
if (!token) throw new UnauthorizedException(AuthMessage.INVALID_REFRESH_TOKEN);
if (dayjs(token.expiresAt).isBefore(dayjs())) {
await queryRunner.manager.delete(this.refreshTokensRepository.target, { id: token.id });
throw new UnauthorizedException(AuthMessage.REFRESH_TOKEN_EXPIRED);
}
await queryRunner.manager.delete(this.refreshTokensRepository.target, { id: token.id });
const tokens = await this.generateTokens(token.user, queryRunner);
await queryRunner.commitTransaction();
return tokens;
} catch (error) {
this.logger.error(error);
await queryRunner.rollbackTransaction();
throw error;
} finally {
await queryRunner.release();
}
}
/************************************************************ */
async invalidateRefreshToken(userId: string) {
await this.refreshTokensRepository.delete({ user: { id: userId } });
this.logger.log(`Successfully deleted all refresh tokens for user ${userId}`);
}
private async generateRefreshToken() {
return randomBytes(32).toString("hex");
}
}