Skip to content

Commit

Permalink
[finishes #187354249] reset password via email
Browse files Browse the repository at this point in the history
  • Loading branch information
princenzmw committed May 8, 2024
1 parent 608d497 commit 7d3270e
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 4 deletions.
74 changes: 74 additions & 0 deletions src/controllers/authController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ import { verifyIfSeller } from '../middlewares/authMiddlewares';
import { createOTPToken, saveOTPDB } from '../middlewares/otpAuthMiddleware';
import { userToken } from '../helpers/token.generator';
import { sendErrorResponse } from '../helpers/helper';
// Additional imports
import { generatePasswordResetToken } from '../helpers/token.generator';
import { sendEmail } from '../helpers/send-email';
import bcrypt from 'bcrypt';

export const authenticateViaGoogle = (req: Request, res: Response, next: NextFunction) => {
passport.authenticate('google', (err: unknown, user: UserAttributes | null) => {
Expand Down Expand Up @@ -119,3 +123,73 @@ export const sendOTP = async (req: Request, res: Response, email: string) => {
}
}
};

// Function to Request Password reset token
export const forgotPassword = async (req: Request, res: Response) => {
try {
const { email } = req.body;

// Verify if user exists
const user = await User.findOne({ where: { email } });
if (!user) {
return res.status(404).json({ ok: false, error: 'User with this email does not exist' });
}

// Generate reset token
const token = generatePasswordResetToken(user.id, user.email);

// Send email with token
const link = `${process.env.URL_HOST}:${process.env.PORT}/api/auth/password-reset?token=${token}`;

sendEmail('password_reset', {
name: `${user.firstName} ${user.lastName}`,
email: `${user.email}`,
link,
});

return res.status(200).json({
ok: true,
message: 'A password reset link has been sent to your email.',
});
} catch (error) {
logger.error('Error requesting password reset: ', error);
sendInternalErrorResponse(res, error);
return;
}
};

// Function to Reset Password
export const resetPassword = async (req: Request, res: Response) => {
try {
const { token, newPassword } = req.body;

// Verify token
const verifiedPayload = jwt.verify(token, process.env.SECRET_KEY as string) as UserAttributes;

if (!verifiedPayload.id) {
return res.status(404).json({ ok: false, error: 'Password reset token is invalid or has expired' });
}

// Find user
const user = await User.findOne({ where: { id: verifiedPayload.id } });
if (!user) {
return res.status(404).json({ ok: false, error: 'User does not exist' });
}

// Hash new password
const saltRound = await bcrypt.genSalt(10);
const hashPassword = await bcrypt.hash(newPassword, saltRound);

// Update user's password
await user.update({ password: hashPassword });

return res.status(200).json({
ok: true,
message: 'Password reset successfully',
});
} catch (error) {
logger.error('Error resetting password: ', error);
sendInternalErrorResponse(res, error);
return;
}
};
8 changes: 4 additions & 4 deletions src/helpers/send-email.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,13 +137,13 @@ export const sendEmail = async (type: string, data: IData) => {
email = {
body: {
name: data.name,
intro: "Welcome to Mailgen! We're very excited to have you on board.",
intro: 'You have requested a password reset. Please follow the link below to change your password:',
action: {
instructions: 'To get started with Mailgen, please click here:',
instructions: 'Click on the button below to reset your password:',
button: {
color: '#22BC66',
text: 'Confirm your account',
link: 'https://mailgen.js/confirm?s=d9729feb74992cc3482b350163a1a010',
text: 'Reset password',
link: `${data.link}`,
},
},
outro: "Need help, or have questions? Just reply to this email, we'd love to help.",
Expand Down
8 changes: 8 additions & 0 deletions src/helpers/token.generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ export const userToken = async (userId: string, userEmail?: string) => {
return token;
};

// Function to Create a Password Reset Token
export const generatePasswordResetToken = (userId: string, email: string) => {
const expiresIn = process.env.JWT_EXPIRATION as string;
const secretKey = process.env.SECRET_KEY as string;
const token: string = jwt.sign({ id: userId, email }, secretKey, { expiresIn });
return token;
};

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const verifyToken = (token: string, results?: any) => {
return jwt.verify(token, process.env.SECRET_KEY as string, results);
Expand Down
7 changes: 7 additions & 0 deletions src/routes/authRoute.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import passport from 'passport';
import { authenticateViaGoogle, verifyOTP } from '../controllers/authController';
import { login } from '../controllers/authController';
import { isCheckedOTP } from '../middlewares/otpAuthMiddleware';
import { forgotPassword, resetPassword } from '../controllers/authController';

const router = Router();
// redirect user to google for authentication
Expand All @@ -11,4 +12,10 @@ router.get('/google/callback', authenticateViaGoogle);
router.post('/login', login);
router.post('/:token/otp', isCheckedOTP, verifyOTP);

// Route to request password reset
router.post('/forgot-password', forgotPassword);

// Route to reset password using provided token
router.post('/reset-password', resetPassword);

export default router;

0 comments on commit 7d3270e

Please sign in to comment.