api-portfolio/src/auth/auth.service.ts
Заид Омар Медхат | Zaid Omar Medhat c53f2bfbc9
All checks were successful
Deploy Production / deploy (push) Successful in 54s
fix
2026-01-27 15:03:04 +05:00

172 lines
4.7 KiB
TypeScript

import {
Injectable,
UnauthorizedException,
BadRequestException,
} from "@nestjs/common";
import { ConfigService } from "@nestjs/config";
import { JwtService } from "@nestjs/jwt";
import { UsersService } from "../users/users.service";
import { LoginDto, ChangePasswordDto, SetupPasswordDto } from "./dto";
import { TokenPair } from "./interfaces";
@Injectable()
export class AuthService {
constructor(
private jwtService: JwtService,
private configService: ConfigService,
private usersService: UsersService,
) {}
async validateUser(
loginDto: LoginDto,
): Promise<{ id: string; username: string }> {
const { username, password } = loginDto;
const user = await this.usersService.findByUsername(username);
if (!user) {
throw new UnauthorizedException("Invalid credentials");
}
if (!user.isPasswordSet) {
throw new BadRequestException(
"Password not set. Please use /auth/setup endpoint first.",
);
}
const isPasswordValid = await this.usersService.validatePassword(
user,
password,
);
if (!isPasswordValid) {
throw new UnauthorizedException("Invalid credentials");
}
if (!user.isAdmin) {
throw new UnauthorizedException("Access denied");
}
return { id: user.id, username: user.username };
}
async setupPassword(
setupPasswordDto: SetupPasswordDto,
): Promise<{ id: string; username: string }> {
const { username, password } = setupPasswordDto;
const user = await this.usersService.findByUsername(username);
if (!user) {
throw new UnauthorizedException("User not found");
}
if (!user.isAdmin) {
throw new UnauthorizedException("Access denied");
}
if (user.isPasswordSet) {
throw new BadRequestException(
"Password already set. Use /auth/change-password to change it.",
);
}
await this.usersService.setupPassword(username, password);
return { id: user.id, username: user.username };
}
async needsSetup(): Promise<boolean> {
return this.usersService.needsPasswordSetup();
}
async generateTokens(userId: string, username: string): Promise<TokenPair> {
const accessPayload = {
sub: userId,
username,
type: "access",
};
const refreshPayload = {
sub: userId,
username,
type: "refresh",
};
const accessSecret =
this.configService.get<string>("JWT_ACCESS_SECRET") || "default-secret";
const refreshSecret =
this.configService.get<string>("JWT_REFRESH_SECRET") ||
"default-refresh-secret";
const [accessToken, refreshToken] = await Promise.all([
this.jwtService.signAsync(accessPayload, {
secret: accessSecret,
expiresIn: "15m",
}),
this.jwtService.signAsync(refreshPayload, {
secret: refreshSecret,
expiresIn: "7d",
}),
]);
return { accessToken, refreshToken };
}
async refreshTokens(userId: string, username: string): Promise<TokenPair> {
const user = await this.usersService.findById(userId);
if (!user || !user.isAdmin) {
throw new UnauthorizedException("User not found or no longer authorized");
}
return this.generateTokens(userId, username);
}
getCookieOptions(isRefreshToken = false) {
// const isProduction =
// this.configService.get<string>("NODE_ENV") === "production";
// const cookieSecure =
// this.configService.get<string>("COOKIE_SECURE") === "true";
// const domain = this.configService.get<string>("COOKIE_DOMAIN");
return {
httpOnly: true,
// secure: isProduction || cookieSecure,
sameSite: "none" as const,
path: isRefreshToken ? "/auth/refresh" : "/",
// domain: domain,
maxAge: isRefreshToken ? 7 * 24 * 60 * 60 * 1000 : 15 * 60 * 1000,
};
}
getClearCookieOptions(isRefreshToken = false) {
return {
httpOnly: true,
path: isRefreshToken ? "/auth/refresh" : "/",
};
}
async changePassword(
userId: string,
changePasswordDto: ChangePasswordDto,
): Promise<void> {
const { currentPassword, newPassword } = changePasswordDto;
const user = await this.usersService.findById(userId);
if (!user) {
throw new UnauthorizedException("User not found");
}
const isCurrentPasswordValid = await this.usersService.validatePassword(
user,
currentPassword,
);
if (!isCurrentPasswordValid) {
throw new BadRequestException("Current password is incorrect");
}
if (currentPassword === newPassword) {
throw new BadRequestException(
"New password must be different from current password",
);
}
await this.usersService.updatePassword(userId, newPassword);
}
}