reset forgoten password tweak

This commit is contained in:
dimitar 2025-04-01 04:51:58 +02:00
parent 239cd14521
commit b38261e387
4 changed files with 127 additions and 105 deletions

View File

@ -34,3 +34,4 @@ DEFAULT_ADMIN_NAME=admin
# CORS_ORIGIN=http://localhost:5173 # CORS_ORIGIN=http://localhost:5173
# NODE_ENV=development # NODE_ENV=development
FRONTEND_URL=https://placebo.mk

View File

@ -1,11 +1,17 @@
import { Injectable, ConflictException, Logger, UnauthorizedException, NotFoundException } from '@nestjs/common'; import {
import { JwtService } from '@nestjs/jwt'; Injectable,
import { PrismaService } from '../prisma/prisma.service'; ConflictException,
import * as bcrypt from 'bcrypt'; Logger,
import { CreateUserDto } from '../dto/create-user.dto'; UnauthorizedException,
import { ConfigService } from '@nestjs/config'; NotFoundException,
import { EmailService } from '../email/email.service'; } from "@nestjs/common";
import { randomBytes } from 'crypto'; import { JwtService } from "@nestjs/jwt";
import { PrismaService } from "../prisma/prisma.service";
import * as bcrypt from "bcrypt";
import { CreateUserDto } from "../dto/create-user.dto";
import { ConfigService } from "@nestjs/config";
import { EmailService } from "../email/email.service";
import { randomBytes } from "crypto";
@Injectable() @Injectable()
export class AuthService { export class AuthService {
@ -17,12 +23,12 @@ export class AuthService {
private configService: ConfigService, private configService: ConfigService,
private emailService: EmailService, private emailService: EmailService,
) { ) {
this.logger.log('AuthService initialized with EmailService'); this.logger.log("AuthService initialized with EmailService");
} }
async validateUser(email: string, password: string): Promise<any> { async validateUser(email: string, password: string): Promise<any> {
this.logger.debug('Validating user:', { email }); this.logger.debug("Validating user:", { email });
try { try {
const user = await this.prisma.user.findUnique({ const user = await this.prisma.user.findUnique({
where: { email }, where: { email },
@ -35,41 +41,43 @@ export class AuthService {
}, },
}); });
this.logger.debug('Database query result:', { this.logger.debug("Database query result:", {
userFound: !!user, userFound: !!user,
userData: user ? { userData: user
id: user.id, ? {
email: user.email, id: user.id,
name: user.name, email: user.email,
isAdmin: user.isAdmin, name: user.name,
} : null, isAdmin: user.isAdmin,
}
: null,
}); });
if (!user) { if (!user) {
this.logger.debug('User not found:', { email }); this.logger.debug("User not found:", { email });
return null; return null;
} }
const isPasswordValid = await bcrypt.compare(password, user.password); const isPasswordValid = await bcrypt.compare(password, user.password);
this.logger.debug('Password validation result:', { isPasswordValid }); this.logger.debug("Password validation result:", { isPasswordValid });
if (!isPasswordValid) { if (!isPasswordValid) {
this.logger.debug('Invalid password for user:', { email }); this.logger.debug("Invalid password for user:", { email });
return null; return null;
} }
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
const { password: _, ...result } = user; const { password: _, ...result } = user;
this.logger.debug('User validated successfully:', { this.logger.debug("User validated successfully:", {
id: result.id, id: result.id,
email: result.email, email: result.email,
name: result.name, name: result.name,
isAdmin: result.isAdmin, isAdmin: result.isAdmin,
}); });
return result; return result;
} catch (error) { } catch (error) {
this.logger.error('Error validating user:', { this.logger.error("Error validating user:", {
error: error.message, error: error.message,
stack: error.stack, stack: error.stack,
}); });
@ -78,7 +86,7 @@ export class AuthService {
} }
async login(user: any) { async login(user: any) {
this.logger.debug('Login called with user:', { this.logger.debug("Login called with user:", {
id: user.id, id: user.id,
email: user.email, email: user.email,
name: user.name, name: user.name,
@ -86,19 +94,19 @@ export class AuthService {
}); });
try { try {
const payload = { const payload = {
email: user.email, email: user.email,
sub: user.id, sub: user.id,
}; };
this.logger.debug('Generated JWT payload:', payload); this.logger.debug("Generated JWT payload:", payload);
const token = this.jwtService.sign(payload, { const token = this.jwtService.sign(payload, {
secret: this.configService.get<string>('JWT_SECRET'), secret: this.configService.get<string>("JWT_SECRET"),
}); });
this.logger.debug('JWT token generated successfully'); this.logger.debug("JWT token generated successfully");
return { return {
access_token: token, access_token: token,
user: { user: {
@ -109,7 +117,7 @@ export class AuthService {
}, },
}; };
} catch (error) { } catch (error) {
this.logger.error('Error generating JWT token:', { this.logger.error("Error generating JWT token:", {
error: error.message, error: error.message,
stack: error.stack, stack: error.stack,
}); });
@ -121,33 +129,41 @@ export class AuthService {
createUserDto: CreateUserDto, createUserDto: CreateUserDto,
isAdmin: boolean = false, isAdmin: boolean = false,
): Promise<any> { ): Promise<any> {
console.log('=== Starting user creation process ==='); console.log("=== Starting user creation process ===");
this.logger.log('=== Starting user creation process ==='); this.logger.log("=== Starting user creation process ===");
console.log('Creating user:', { ...createUserDto, isAdmin, password: '[REDACTED]' }); console.log("Creating user:", {
this.logger.log('Creating user:', { ...createUserDto, isAdmin, password: '[REDACTED]' }); ...createUserDto,
isAdmin,
password: "[REDACTED]",
});
this.logger.log("Creating user:", {
...createUserDto,
isAdmin,
password: "[REDACTED]",
});
try { try {
// Check for existing user // Check for existing user
console.log('Checking for existing user...'); console.log("Checking for existing user...");
this.logger.log('Checking for existing user...'); this.logger.log("Checking for existing user...");
const existingUser = await this.prisma.user.findUnique({ const existingUser = await this.prisma.user.findUnique({
where: { email: createUserDto.email }, where: { email: createUserDto.email },
}); });
if (existingUser) { if (existingUser) {
console.log('User already exists:', createUserDto.email); console.log("User already exists:", createUserDto.email);
this.logger.warn('User already exists:', createUserDto.email); this.logger.warn("User already exists:", createUserDto.email);
throw new ConflictException('Email already exists'); throw new ConflictException("Email already exists");
} }
// Hash password // Hash password
console.log('Hashing password...'); console.log("Hashing password...");
this.logger.log('Hashing password...'); this.logger.log("Hashing password...");
const hashedPassword = await bcrypt.hash(createUserDto.password, 10); const hashedPassword = await bcrypt.hash(createUserDto.password, 10);
// Create user // Create user
console.log('Creating user in database...'); console.log("Creating user in database...");
this.logger.log('Creating user in database...'); this.logger.log("Creating user in database...");
const newUser = await this.prisma.user.create({ const newUser = await this.prisma.user.create({
data: { data: {
email: createUserDto.email, email: createUserDto.email,
@ -157,22 +173,28 @@ export class AuthService {
}, },
}); });
console.log('User created successfully:', { id: newUser.id, email: newUser.email }); console.log("User created successfully:", {
this.logger.log('User created successfully:', { id: newUser.id, email: newUser.email }); id: newUser.id,
email: newUser.email,
});
this.logger.log("User created successfully:", {
id: newUser.id,
email: newUser.email,
});
// Send welcome email // Send welcome email
console.log('Attempting to send welcome email...'); console.log("Attempting to send welcome email...");
this.logger.log('Attempting to send welcome email...'); this.logger.log("Attempting to send welcome email...");
try { try {
console.log('Calling EmailService.sendWelcomeEmail...'); console.log("Calling EmailService.sendWelcomeEmail...");
this.logger.log('Calling EmailService.sendWelcomeEmail...'); this.logger.log("Calling EmailService.sendWelcomeEmail...");
await this.emailService.sendWelcomeEmail(newUser.email, newUser.name); await this.emailService.sendWelcomeEmail(newUser.email, newUser.name);
console.log('Welcome email sent successfully'); console.log("Welcome email sent successfully");
this.logger.log('Welcome email sent successfully'); this.logger.log("Welcome email sent successfully");
} catch (emailError) { } catch (emailError) {
console.error('Failed to send welcome email:', emailError); console.error("Failed to send welcome email:", emailError);
this.logger.error('Failed to send welcome email:', { this.logger.error("Failed to send welcome email:", {
error: emailError.message, error: emailError.message,
code: emailError.code, code: emailError.code,
command: emailError.command, command: emailError.command,
@ -184,12 +206,12 @@ export class AuthService {
// Return user data // Return user data
const { password, ...result } = newUser; const { password, ...result } = newUser;
console.log('=== User creation completed ==='); console.log("=== User creation completed ===");
this.logger.log('=== User creation completed ==='); this.logger.log("=== User creation completed ===");
return result; return result;
} catch (error) { } catch (error) {
console.error('Error in createUser:', error); console.error("Error in createUser:", error);
this.logger.error('Error in createUser:', { this.logger.error("Error in createUser:", {
error: error.message, error: error.message,
code: error.code, code: error.code,
command: error.command, command: error.command,
@ -212,25 +234,25 @@ export class AuthService {
// } // }
async getUserInfo(userId: number) { async getUserInfo(userId: number) {
if (!userId) { if (!userId) {
throw new Error('User ID is required'); throw new Error("User ID is required");
} }
return this.prisma.user.findUnique({ return this.prisma.user.findUnique({
where: { where: {
id: userId, // Make sure userId is properly passed and converted to number if needed id: userId, // Make sure userId is properly passed and converted to number if needed
}, },
select: { select: {
id: true, id: true,
name: true, name: true,
email: true, email: true,
isAdmin: true isAdmin: true,
} },
}); });
} }
async sendPasswordResetToken(email: string) { async sendPasswordResetToken(email: string) {
this.logger.log('=== Starting password reset token process ==='); this.logger.log("=== Starting password reset token process ===");
this.logger.debug('Reset token requested for:', email); this.logger.debug("Reset token requested for:", email);
try { try {
// Find the user // Find the user
@ -244,13 +266,15 @@ export class AuthService {
}); });
if (!user) { if (!user) {
this.logger.warn('User not found for password reset:', email); this.logger.warn("User not found for password reset:", email);
// Return success anyway to prevent email enumeration // Return success anyway to prevent email enumeration
return { message: 'If an account exists, a password reset link has been sent' }; return {
message: "If an account exists, a password reset link has been sent",
};
} }
// Generate reset token // Generate reset token
const token = randomBytes(32).toString('hex'); const token = randomBytes(32).toString("hex");
const expiresAt = new Date(); const expiresAt = new Date();
expiresAt.setHours(expiresAt.getHours() + 1); // Token expires in 1 hour expiresAt.setHours(expiresAt.getHours() + 1); // Token expires in 1 hour
@ -268,11 +292,11 @@ export class AuthService {
await this.emailService.sendPasswordResetEmail( await this.emailService.sendPasswordResetEmail(
user.email, user.email,
user.name, user.name,
token token,
); );
this.logger.debug('Password reset email sent successfully'); this.logger.debug("Password reset email sent successfully");
} catch (emailError) { } catch (emailError) {
this.logger.error('Failed to send password reset email:', { this.logger.error("Failed to send password reset email:", {
error: emailError.message, error: emailError.message,
code: emailError.code, code: emailError.code,
stack: emailError.stack, stack: emailError.stack,
@ -280,9 +304,11 @@ export class AuthService {
throw emailError; throw emailError;
} }
return { message: 'If an account exists, a password reset link has been sent' }; return {
message: "If an account exists, a password reset link has been sent",
};
} catch (error) { } catch (error) {
this.logger.error('Error in sendPasswordResetToken:', { this.logger.error("Error in sendPasswordResetToken:", {
error: error.message, error: error.message,
code: error.code, code: error.code,
stack: error.stack, stack: error.stack,
@ -292,8 +318,8 @@ export class AuthService {
} }
async resetPasswordWithToken(token: string, newPassword: string) { async resetPasswordWithToken(token: string, newPassword: string) {
this.logger.log('=== Starting password reset with token process ==='); this.logger.log("=== Starting password reset with token process ===");
this.logger.debug('Password reset attempted with token'); this.logger.debug("Password reset attempted with token");
try { try {
// Find valid reset token // Find valid reset token
@ -311,8 +337,8 @@ export class AuthService {
}); });
if (!resetRecord) { if (!resetRecord) {
this.logger.warn('Invalid or expired reset token used'); this.logger.warn("Invalid or expired reset token used");
throw new UnauthorizedException('Invalid or expired reset token'); throw new UnauthorizedException("Invalid or expired reset token");
} }
// Hash new password // Hash new password
@ -330,27 +356,29 @@ export class AuthService {
}), }),
]); ]);
this.logger.log('Password reset successful for user:', resetRecord.userId); this.logger.log(
"Password reset successful for user:",
resetRecord.userId,
);
// Send confirmation email // Send confirmation email
try { try {
await this.emailService.sendPasswordChangeConfirmation( await this.emailService.sendPasswordChangeConfirmation(
resetRecord.user.email, resetRecord.user.email,
resetRecord.user.name resetRecord.user.name,
); );
this.logger.debug('Password change confirmation email sent'); this.logger.debug("Password change confirmation email sent");
} catch (emailError) { } catch (emailError) {
this.logger.error('Failed to send password change confirmation:', { this.logger.error("Failed to send password change confirmation:", {
error: emailError.message, error: emailError.message,
code: emailError.code, code: emailError.code,
stack: emailError.stack, stack: emailError.stack,
}); });
// Don't throw error here as password is already changed
} }
return { message: 'Password reset successful' }; return { message: "Password reset successful" };
} catch (error) { } catch (error) {
this.logger.error('Error in resetPasswordWithToken:', { this.logger.error("Error in resetPasswordWithToken:", {
error: error.message, error: error.message,
code: error.code, code: error.code,
stack: error.stack, stack: error.stack,

View File

@ -1,17 +1,10 @@
import { Injectable } from '@nestjs/common'; import { Injectable } from "@nestjs/common";
import { PrismaService } from '../prisma/prisma.service'; import { PrismaService } from "../prisma/prisma.service";
@Injectable() @Injectable()
export class ClientService { export class ClientService {
constructor(private prisma: PrismaService) {} constructor(private prisma: PrismaService) {}
// async getClientDocuments(userId: string) {
// return this.prisma.document.findMany({
// where: {
// authorId: Number(userId),
// },
// });
// }
async getDocuments(userId: string) { async getDocuments(userId: string) {
return this.prisma.document.findMany({ return this.prisma.document.findMany({
where: { where: {
@ -19,7 +12,7 @@ export class ClientService {
some: { some: {
id: Number(userId), id: Number(userId),
}, },
}, },
}, },
include: { include: {
sharedWith: { sharedWith: {

View File

@ -19,7 +19,7 @@ export class EmailService {
const pass = this.configService.get<string>("SMTP_PASS"); const pass = this.configService.get<string>("SMTP_PASS");
this.from = this.configService.get<string>("EMAIL_FROM"); this.from = this.configService.get<string>("EMAIL_FROM");
console.log("Email Config:", { host, port, user, from: this.from }); // Direct console log console.log("Email Config:", { host, port, user, from: this.from });
this.logger.log("Email Config:", { host, port, user, from: this.from }); this.logger.log("Email Config:", { host, port, user, from: this.from });
this.transporter = nodemailer.createTransport({ this.transporter = nodemailer.createTransport({
@ -27,8 +27,8 @@ export class EmailService {
host: host, host: host,
port: port, port: port,
auth: { user, pass }, auth: { user, pass },
debug: true, // Enable debug logs debug: true,
logger: true, // Enable transport level logging logger: true,
}); });
// Verify connection // Verify connection
@ -37,15 +37,15 @@ export class EmailService {
private async verifyConnection() { private async verifyConnection() {
try { try {
console.log("Verifying SMTP connection..."); // Direct console log console.log("Verifying SMTP connection...");
this.logger.log("Verifying SMTP connection..."); this.logger.log("Verifying SMTP connection...");
const verification = await this.transporter.verify(); const verification = await this.transporter.verify();
console.log("SMTP connection verified successfully!", verification); // Direct console log console.log("SMTP connection verified successfully!", verification);
this.logger.log("SMTP connection verified successfully!", verification); this.logger.log("SMTP connection verified successfully!", verification);
} catch (error) { } catch (error) {
console.error("SMTP connection failed:", error); // Direct console log console.error("SMTP connection failed:", error);
this.logger.error("SMTP connection failed:", { this.logger.error("SMTP connection failed:", {
error: error.message, error: error.message,
code: error.code, code: error.code,
@ -59,7 +59,7 @@ export class EmailService {
} }
async sendWelcomeEmail(userEmail: string, username: string): Promise<void> { async sendWelcomeEmail(userEmail: string, username: string): Promise<void> {
console.log(`Sending welcome email to ${userEmail}...`); // Direct console log console.log(`Sending welcome email to ${userEmail}...`);
this.logger.log(`Sending welcome email to ${userEmail}...`); this.logger.log(`Sending welcome email to ${userEmail}...`);
const mailOptions = { const mailOptions = {