Skip to content

Commit a24d5df

Browse files
Added reset password finctionality (#638)
Co-authored-by: Kritika Singh <kritikasingh6881@gmail.com>
1 parent bd35a76 commit a24d5df

File tree

8 files changed

+316
-1
lines changed

8 files changed

+316
-1
lines changed

forgot-password.html

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<title>Forgot Password</title>
7+
<link rel="stylesheet" href="styles/login.css">
8+
<!-- Font Awesome for eye icon -->
9+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"
10+
integrity="sha512-iecdLmaskl7CVkqkXNQ/ZH/XLlvWZOJyj7Yy7tcenmpD1ypASozpmT/E0iPtmFIB46ZmdtAc9eNBvH0H/ZpiBw=="
11+
crossorigin="anonymous" referrerpolicy="no-referrer">
12+
</head>
13+
<body>
14+
<div class="container">
15+
<h2>Reset Password</h2>
16+
<p class="form-description">Enter your email address and we'll send you a link to reset your password.</p>
17+
<form id="forgot-password-form">
18+
<div class="input-group">
19+
<label for="email">Email</label>
20+
<input id="email" type="email" placeholder="Email" required>
21+
<div class="validation-message" id="email-validation"></div>
22+
</div>
23+
<button type="submit">Send Reset Link</button>
24+
</form>
25+
<p class="form-link">Remember your password? <a href="login.html">Login</a></p>
26+
</div>
27+
<button class="back-button" onclick="window.location.href='index.html'"> ← Back To Home</button>
28+
<script src="scripts/auth.js"></script>
29+
<script src="scripts/forgot-password.js"></script>
30+
</body>
31+
</html>

login.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ <h2>Login</h2>
3434
<button type="submit">Login</button>
3535
</form>
3636
<p class="form-link">Don't have an account? <a href="signup.html">Sign up</a></p>
37+
<p class="form-link"><a href="forgot-password.html">Forgot Password?</a></p>
3738
</div>
3839
<button class="back-button" onclick="window.location.href='index.html'"> ← Back To Home</button>
3940
<script src="scripts/auth.js"></script>

package-lock.json

Lines changed: 21 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"dependencies": {
3+
"nodemailer": "^7.0.5"
4+
}
5+
}

reset-password.html

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<title>Reset Password</title>
7+
<link rel="stylesheet" href="styles/login.css">
8+
<!-- Font Awesome for eye icon -->
9+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"
10+
integrity="sha512-iecdLmaskl7CVkqkXNQ/ZH/XLlvWZOJyj7Yy7tcenmpD1ypASozpmT/E0iPtmFIB46ZmdtAc9eNBvH0H/ZpiBw=="
11+
crossorigin="anonymous" referrerpolicy="no-referrer">
12+
</head>
13+
<body>
14+
<div class="container">
15+
<h2>Set New Password</h2>
16+
<p class="form-description">Please enter your new password below.</p>
17+
<form id="reset-password-form">
18+
<input type="hidden" id="token" value="">
19+
<div class="input-group">
20+
<label for="password">New Password</label>
21+
<div class="password-field-container">
22+
<input id="password" type="password" placeholder="New Password" required>
23+
<button type="button" id="togglePassword" aria-label="Toggle password visibility">
24+
<i class="fas fa-eye" aria-hidden="true"></i>
25+
</button>
26+
</div>
27+
<div class="validation-message" id="password-validation"></div>
28+
</div>
29+
<div class="input-group">
30+
<label for="confirm-password">Confirm Password</label>
31+
<div class="password-field-container">
32+
<input id="confirm-password" type="password" placeholder="Confirm Password" required>
33+
<button type="button" id="toggleConfirmPassword" aria-label="Toggle password visibility">
34+
<i class="fas fa-eye" aria-hidden="true"></i>
35+
</button>
36+
</div>
37+
<div class="validation-message" id="confirm-password-validation"></div>
38+
</div>
39+
<button type="submit">Reset Password</button>
40+
</form>
41+
<p class="form-link"><a href="login.html">Back to Login</a></p>
42+
</div>
43+
<button class="back-button" onclick="window.location.href='index.html'"> ← Back To Home</button>
44+
<script src="scripts/auth.js"></script>
45+
<script src="scripts/reset-password.js"></script>
46+
</body>
47+
</html>

scripts/forgot-password.js

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
document.addEventListener('DOMContentLoaded', function() {
2+
const form = document.getElementById('forgot-password-form');
3+
4+
if (form) {
5+
form.addEventListener('submit', async function(e) {
6+
e.preventDefault();
7+
8+
const email = document.getElementById('email').value.trim();
9+
10+
// Basic validation
11+
if (!email) {
12+
alert('Please enter your email address');
13+
return;
14+
}
15+
16+
// Email validation
17+
const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
18+
if (!emailPattern.test(email)) {
19+
alert('Please enter a valid email address');
20+
return;
21+
}
22+
23+
try {
24+
const response = await fetch('/api/auth/forgot-password', {
25+
method: 'POST',
26+
headers: {
27+
'Content-Type': 'application/json'
28+
},
29+
body: JSON.stringify({ email })
30+
});
31+
32+
const data = await response.json();
33+
34+
if (response.ok) {
35+
alert(data.message);
36+
37+
// For testing purposes, show the reset link if provided
38+
if (data.resetLink) {
39+
alert(`For testing purposes, here's your reset link:\n${data.resetLink}\n\nCopy this link to reset your password.`);
40+
}
41+
42+
form.reset();
43+
} else {
44+
alert(data.message || 'An error occurred. Please try again.');
45+
}
46+
} catch (error) {
47+
console.error('Error:', error);
48+
alert('An error occurred. Please try again.');
49+
}
50+
});
51+
}
52+
53+
// Initialize password toggle if needed
54+
initializePasswordToggle();
55+
});

scripts/reset-password.js

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
document.addEventListener('DOMContentLoaded', function() {
2+
const form = document.getElementById('reset-password-form');
3+
4+
// Get token from URL parameters
5+
const urlParams = new URLSearchParams(window.location.search);
6+
const token = urlParams.get('token');
7+
8+
if (token) {
9+
document.getElementById('token').value = token;
10+
} else {
11+
alert('Invalid or missing reset token. Please request a new password reset link.');
12+
window.location.href = 'forgot-password.html';
13+
return;
14+
}
15+
16+
if (form) {
17+
form.addEventListener('submit', async function(e) {
18+
e.preventDefault();
19+
20+
const password = document.getElementById('password').value;
21+
const confirmPassword = document.getElementById('confirm-password').value;
22+
const token = document.getElementById('token').value;
23+
24+
// Validation
25+
if (!password || !confirmPassword) {
26+
alert('Please fill in all fields');
27+
return;
28+
}
29+
30+
if (password !== confirmPassword) {
31+
alert('Passwords do not match');
32+
return;
33+
}
34+
35+
if (password.length < 6) {
36+
alert('Password must be at least 6 characters long');
37+
return;
38+
}
39+
40+
try {
41+
const response = await fetch('/api/auth/reset-password', {
42+
method: 'POST',
43+
headers: {
44+
'Content-Type': 'application/json'
45+
},
46+
body: JSON.stringify({ token, password })
47+
});
48+
49+
const data = await response.json();
50+
51+
if (response.ok) {
52+
alert(data.message);
53+
window.location.href = 'login.html';
54+
} else {
55+
alert(data.message || 'An error occurred. Please try again.');
56+
}
57+
} catch (error) {
58+
console.error('Error:', error);
59+
alert('An error occurred. Please try again.');
60+
}
61+
});
62+
}
63+
64+
// Initialize password toggle functionality
65+
initializePasswordToggle();
66+
67+
// Add toggle for confirm password field
68+
const toggleConfirmPassword = document.getElementById('toggleConfirmPassword');
69+
const confirmPasswordInput = document.getElementById('confirm-password');
70+
71+
if (toggleConfirmPassword && confirmPasswordInput) {
72+
toggleConfirmPassword.addEventListener('click', function() {
73+
const currentType = confirmPasswordInput.getAttribute('type');
74+
const newType = currentType === 'password' ? 'text' : 'password';
75+
confirmPasswordInput.setAttribute('type', newType);
76+
77+
const icon = this.querySelector('i');
78+
if (icon) {
79+
icon.className = '';
80+
if (newType === 'text') {
81+
icon.className = 'fas fa-eye-slash';
82+
} else {
83+
icon.className = 'fas fa-eye';
84+
}
85+
}
86+
});
87+
}
88+
});

server/routes/auth.js

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,71 @@ router.get('/me', async (req, res) => {
4646
}
4747
})
4848

49-
module.exports = router
49+
router.post('/forgot-password', async (req, res) => {
50+
try {
51+
const { email } = req.body;
52+
const user = await User.findOne({ email });
53+
if (!user) return res.status(400).json({ message: 'User not found' });
54+
55+
// Generate a password reset token (JWT)
56+
const token = jwt.sign({ id: user._id }, process.env.JWT_SECRET, { expiresIn: '1h' });
57+
58+
// Create reset link
59+
const resetLink = `${process.env.FRONTEND_URL || 'http://localhost:5000'}/reset-password.html?token=${token}`;
60+
61+
// Log the reset link for testing (in production, you would send an email)
62+
console.log('Password reset link:', resetLink);
63+
console.log('For email:', email);
64+
65+
// Basic email sending simulation
66+
// In a real application, you would use a service like Nodemailer, SendGrid, etc.
67+
// Example with Nodemailer (commented out):
68+
/*
69+
const nodemailer = require('nodemailer');
70+
const transporter = nodemailer.createTransport({
71+
service: 'gmail',
72+
auth: {
73+
user: process.env.EMAIL_USER,
74+
pass: process.env.EMAIL_PASS
75+
}
76+
});
77+
78+
await transporter.sendMail({
79+
from: process.env.EMAIL_USER,
80+
to: email,
81+
subject: 'Password Reset Request',
82+
html: `<p>Click the link below to reset your password:</p>
83+
<a href="${resetLink}">${resetLink}</a>
84+
<p>This link will expire in 1 hour.</p>`
85+
});
86+
*/
87+
88+
res.status(200).json({
89+
message: 'Password reset link sent to your email',
90+
// For testing purposes, include the reset link in the response
91+
resetLink: process.env.NODE_ENV === 'development' ? resetLink : undefined
92+
});
93+
} catch (err) {
94+
console.error('Forgot password error:', err);
95+
res.status(500).json({ message: 'Server error' });
96+
}
97+
});
98+
99+
router.post('/reset-password', async (req, res) => {
100+
try {
101+
const { token, password } = req.body;
102+
const payload = jwt.verify(token, process.env.JWT_SECRET);
103+
const user = await User.findById(payload.id);
104+
if (!user) return res.status(400).json({ message: 'Invalid token' });
105+
106+
const hashed = await bcrypt.hash(password, 10);
107+
user.password = hashed;
108+
await user.save();
109+
110+
res.status(200).json({ message: 'Password has been reset successfully' });
111+
} catch (err) {
112+
res.status(500).json({ message: 'Server error' });
113+
}
114+
});
115+
116+
module.exports = router

0 commit comments

Comments
 (0)