From b38261e3870925aa0a552dcd92479fc4d56bf66a Mon Sep 17 00:00:00 2001 From: dimitar Date: Tue, 1 Apr 2025 04:51:58 +0200 Subject: [PATCH] reset forgoten password tweak --- backend/.env | 1 + backend/src/auth/auth.service.ts | 204 +++++++++++++++------------ backend/src/client/client.service.ts | 13 +- backend/src/email/email.service.ts | 14 +- 4 files changed, 127 insertions(+), 105 deletions(-) diff --git a/backend/.env b/backend/.env index 163531f..e743613 100644 --- a/backend/.env +++ b/backend/.env @@ -34,3 +34,4 @@ DEFAULT_ADMIN_NAME=admin # CORS_ORIGIN=http://localhost:5173 # NODE_ENV=development +FRONTEND_URL=https://placebo.mk diff --git a/backend/src/auth/auth.service.ts b/backend/src/auth/auth.service.ts index 9cfb419..34b92f3 100644 --- a/backend/src/auth/auth.service.ts +++ b/backend/src/auth/auth.service.ts @@ -1,11 +1,17 @@ -import { Injectable, ConflictException, Logger, UnauthorizedException, NotFoundException } from '@nestjs/common'; -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'; +import { + Injectable, + ConflictException, + Logger, + UnauthorizedException, + NotFoundException, +} from "@nestjs/common"; +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() export class AuthService { @@ -17,12 +23,12 @@ export class AuthService { private configService: ConfigService, private emailService: EmailService, ) { - this.logger.log('AuthService initialized with EmailService'); + this.logger.log("AuthService initialized with EmailService"); } async validateUser(email: string, password: string): Promise { - this.logger.debug('Validating user:', { email }); - + this.logger.debug("Validating user:", { email }); + try { const user = await this.prisma.user.findUnique({ where: { email }, @@ -35,41 +41,43 @@ export class AuthService { }, }); - this.logger.debug('Database query result:', { + this.logger.debug("Database query result:", { userFound: !!user, - userData: user ? { - id: user.id, - email: user.email, - name: user.name, - isAdmin: user.isAdmin, - } : null, + userData: user + ? { + id: user.id, + email: user.email, + name: user.name, + isAdmin: user.isAdmin, + } + : null, }); if (!user) { - this.logger.debug('User not found:', { email }); + this.logger.debug("User not found:", { email }); return null; } const isPasswordValid = await bcrypt.compare(password, user.password); - this.logger.debug('Password validation result:', { isPasswordValid }); - + this.logger.debug("Password validation result:", { isPasswordValid }); + if (!isPasswordValid) { - this.logger.debug('Invalid password for user:', { email }); + this.logger.debug("Invalid password for user:", { email }); return null; } // eslint-disable-next-line @typescript-eslint/no-unused-vars const { password: _, ...result } = user; - this.logger.debug('User validated successfully:', { + this.logger.debug("User validated successfully:", { id: result.id, email: result.email, name: result.name, isAdmin: result.isAdmin, }); - + return result; } catch (error) { - this.logger.error('Error validating user:', { + this.logger.error("Error validating user:", { error: error.message, stack: error.stack, }); @@ -78,7 +86,7 @@ export class AuthService { } async login(user: any) { - this.logger.debug('Login called with user:', { + this.logger.debug("Login called with user:", { id: user.id, email: user.email, name: user.name, @@ -86,19 +94,19 @@ export class AuthService { }); try { - const payload = { + const payload = { email: user.email, sub: user.id, }; - - this.logger.debug('Generated JWT payload:', payload); - + + this.logger.debug("Generated JWT payload:", payload); + const token = this.jwtService.sign(payload, { - secret: this.configService.get('JWT_SECRET'), + secret: this.configService.get("JWT_SECRET"), }); - this.logger.debug('JWT token generated successfully'); - + this.logger.debug("JWT token generated successfully"); + return { access_token: token, user: { @@ -109,7 +117,7 @@ export class AuthService { }, }; } catch (error) { - this.logger.error('Error generating JWT token:', { + this.logger.error("Error generating JWT token:", { error: error.message, stack: error.stack, }); @@ -121,33 +129,41 @@ export class AuthService { createUserDto: CreateUserDto, isAdmin: boolean = false, ): Promise { - console.log('=== Starting user creation process ==='); - this.logger.log('=== Starting user creation process ==='); - console.log('Creating user:', { ...createUserDto, isAdmin, password: '[REDACTED]' }); - this.logger.log('Creating user:', { ...createUserDto, isAdmin, password: '[REDACTED]' }); + console.log("=== Starting user creation process ==="); + this.logger.log("=== Starting user creation process ==="); + console.log("Creating user:", { + ...createUserDto, + isAdmin, + password: "[REDACTED]", + }); + this.logger.log("Creating user:", { + ...createUserDto, + isAdmin, + password: "[REDACTED]", + }); try { // Check for existing user - console.log('Checking for existing user...'); - this.logger.log('Checking for existing user...'); + console.log("Checking for existing user..."); + this.logger.log("Checking for existing user..."); const existingUser = await this.prisma.user.findUnique({ where: { email: createUserDto.email }, }); if (existingUser) { - console.log('User already exists:', createUserDto.email); - this.logger.warn('User already exists:', createUserDto.email); - throw new ConflictException('Email already exists'); + console.log("User already exists:", createUserDto.email); + this.logger.warn("User already exists:", createUserDto.email); + throw new ConflictException("Email already exists"); } // Hash password - console.log('Hashing password...'); - this.logger.log('Hashing password...'); + console.log("Hashing password..."); + this.logger.log("Hashing password..."); const hashedPassword = await bcrypt.hash(createUserDto.password, 10); // Create user - console.log('Creating user in database...'); - this.logger.log('Creating user in database...'); + console.log("Creating user in database..."); + this.logger.log("Creating user in database..."); const newUser = await this.prisma.user.create({ data: { email: createUserDto.email, @@ -157,22 +173,28 @@ export class AuthService { }, }); - console.log('User created successfully:', { id: newUser.id, email: newUser.email }); - this.logger.log('User created successfully:', { id: newUser.id, email: newUser.email }); + console.log("User created successfully:", { + id: newUser.id, + email: newUser.email, + }); + this.logger.log("User created successfully:", { + id: newUser.id, + email: newUser.email, + }); // Send welcome email - console.log('Attempting to send welcome email...'); - this.logger.log('Attempting to send welcome email...'); - + console.log("Attempting to send welcome email..."); + this.logger.log("Attempting to send welcome email..."); + try { - console.log('Calling EmailService.sendWelcomeEmail...'); - this.logger.log('Calling EmailService.sendWelcomeEmail...'); + console.log("Calling EmailService.sendWelcomeEmail..."); + this.logger.log("Calling EmailService.sendWelcomeEmail..."); await this.emailService.sendWelcomeEmail(newUser.email, newUser.name); - console.log('Welcome email sent successfully'); - this.logger.log('Welcome email sent successfully'); + console.log("Welcome email sent successfully"); + this.logger.log("Welcome email sent successfully"); } catch (emailError) { - console.error('Failed to send welcome email:', emailError); - this.logger.error('Failed to send welcome email:', { + console.error("Failed to send welcome email:", emailError); + this.logger.error("Failed to send welcome email:", { error: emailError.message, code: emailError.code, command: emailError.command, @@ -184,12 +206,12 @@ export class AuthService { // Return user data const { password, ...result } = newUser; - console.log('=== User creation completed ==='); - this.logger.log('=== User creation completed ==='); + console.log("=== User creation completed ==="); + this.logger.log("=== User creation completed ==="); return result; } catch (error) { - console.error('Error in createUser:', error); - this.logger.error('Error in createUser:', { + console.error("Error in createUser:", error); + this.logger.error("Error in createUser:", { error: error.message, code: error.code, command: error.command, @@ -212,25 +234,25 @@ export class AuthService { // } async getUserInfo(userId: number) { if (!userId) { - throw new Error('User ID is required'); + throw new Error("User ID is required"); } - + return this.prisma.user.findUnique({ 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: { id: true, name: true, email: true, - isAdmin: true - } + isAdmin: true, + }, }); } async sendPasswordResetToken(email: string) { - this.logger.log('=== Starting password reset token process ==='); - this.logger.debug('Reset token requested for:', email); + this.logger.log("=== Starting password reset token process ==="); + this.logger.debug("Reset token requested for:", email); try { // Find the user @@ -244,13 +266,15 @@ export class AuthService { }); 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 { 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 - const token = randomBytes(32).toString('hex'); + const token = randomBytes(32).toString("hex"); const expiresAt = new Date(); expiresAt.setHours(expiresAt.getHours() + 1); // Token expires in 1 hour @@ -268,11 +292,11 @@ export class AuthService { await this.emailService.sendPasswordResetEmail( user.email, user.name, - token + token, ); - this.logger.debug('Password reset email sent successfully'); + this.logger.debug("Password reset email sent successfully"); } catch (emailError) { - this.logger.error('Failed to send password reset email:', { + this.logger.error("Failed to send password reset email:", { error: emailError.message, code: emailError.code, stack: emailError.stack, @@ -280,9 +304,11 @@ export class AuthService { 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) { - this.logger.error('Error in sendPasswordResetToken:', { + this.logger.error("Error in sendPasswordResetToken:", { error: error.message, code: error.code, stack: error.stack, @@ -292,8 +318,8 @@ export class AuthService { } async resetPasswordWithToken(token: string, newPassword: string) { - this.logger.log('=== Starting password reset with token process ==='); - this.logger.debug('Password reset attempted with token'); + this.logger.log("=== Starting password reset with token process ==="); + this.logger.debug("Password reset attempted with token"); try { // Find valid reset token @@ -311,8 +337,8 @@ export class AuthService { }); if (!resetRecord) { - this.logger.warn('Invalid or expired reset token used'); - throw new UnauthorizedException('Invalid or expired reset token'); + this.logger.warn("Invalid or expired reset token used"); + throw new UnauthorizedException("Invalid or expired reset token"); } // 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 try { await this.emailService.sendPasswordChangeConfirmation( 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) { - this.logger.error('Failed to send password change confirmation:', { + this.logger.error("Failed to send password change confirmation:", { error: emailError.message, code: emailError.code, 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) { - this.logger.error('Error in resetPasswordWithToken:', { + this.logger.error("Error in resetPasswordWithToken:", { error: error.message, code: error.code, stack: error.stack, diff --git a/backend/src/client/client.service.ts b/backend/src/client/client.service.ts index 62e1c1e..d3b839e 100644 --- a/backend/src/client/client.service.ts +++ b/backend/src/client/client.service.ts @@ -1,17 +1,10 @@ -import { Injectable } from '@nestjs/common'; -import { PrismaService } from '../prisma/prisma.service'; +import { Injectable } from "@nestjs/common"; +import { PrismaService } from "../prisma/prisma.service"; @Injectable() export class ClientService { constructor(private prisma: PrismaService) {} - // async getClientDocuments(userId: string) { - // return this.prisma.document.findMany({ - // where: { - // authorId: Number(userId), - // }, - // }); - // } async getDocuments(userId: string) { return this.prisma.document.findMany({ where: { @@ -19,7 +12,7 @@ export class ClientService { some: { id: Number(userId), }, - }, + }, }, include: { sharedWith: { diff --git a/backend/src/email/email.service.ts b/backend/src/email/email.service.ts index a3cb526..7372f77 100644 --- a/backend/src/email/email.service.ts +++ b/backend/src/email/email.service.ts @@ -19,7 +19,7 @@ export class EmailService { const pass = this.configService.get("SMTP_PASS"); this.from = this.configService.get("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.transporter = nodemailer.createTransport({ @@ -27,8 +27,8 @@ export class EmailService { host: host, port: port, auth: { user, pass }, - debug: true, // Enable debug logs - logger: true, // Enable transport level logging + debug: true, + logger: true, }); // Verify connection @@ -37,15 +37,15 @@ export class EmailService { private async verifyConnection() { try { - console.log("Verifying SMTP connection..."); // Direct console log + console.log("Verifying SMTP connection..."); this.logger.log("Verifying SMTP connection..."); 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); } catch (error) { - console.error("SMTP connection failed:", error); // Direct console log + console.error("SMTP connection failed:", error); this.logger.error("SMTP connection failed:", { error: error.message, code: error.code, @@ -59,7 +59,7 @@ export class EmailService { } async sendWelcomeEmail(userEmail: string, username: string): Promise { - 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}...`); const mailOptions = {