Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

forgot password feature added backend and frontend : #325 done #423

Merged
merged 1 commit into from
Oct 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions backend/.env.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
EMAIL_USER=your_gmail
#your email
EMAIL_USER=your_gmail

# To create a passkey on the phone or computer you’re on:

# 1. Go to https://myaccount.google.com/signinoptions/passkeys.
# 2. Tap Create a passkey and then Continue.(You'll be required to unlock your device.)
# 3. A 16 character passkey is generated which you can use in below.
106 changes: 106 additions & 0 deletions backend/controllers/submitFeedback.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import nodemailer from 'nodemailer';
import User from '../models/User.js';
import { generateOTP , verifyOTP} from '../utils/otputils.js'; // Import the OTP generation function
import { sendOTPEmail } from '../utils/emailUtils.js'; // Import the email sender utility if separated into a different file

import { hashPassword } from '../utils/authFunctions.js';

// Controller to handle user feedback submission
export const submitFeedback = async (req, res) => {
Expand All @@ -21,3 +26,104 @@ export const submitFeedback = async (req, res) => {
return res.status(error.statusCode || 500).json({ message: error.message || 'An error occurred while submitting feedback' });
}
};

export const sendOTPToEmail = async (req, res) => {
try {
const { email } = req.body;

// Check if email is provided
if (!email) {
return res.status(400).json({ error: 'Email is required' });
}

// Find user by email
const user = await User.findOne({ email });

if (!user) {
return res.status(404).json({ error: 'User does not exist' });
}

// Generate OTP and set expiry (10 minutes from now)
const otp = generateOTP();
const otpExpiry = new Date(Date.now() + 10 * 60 * 1000); // OTP expires in 10 minutes

// Store the OTP and expiry in the user's document
user.otp = otp;
user.otpExpiry = otpExpiry;
await user.save();

// Send OTP to user's email using the utility
await sendOTPEmail(email, otp);

res.status(200).json({ message: 'OTP sent to email' });
} catch (error) {
console.error('Error sending OTP:', error);
return res.status(500).json({ error: error.message || 'Internal Server Error' });
}
};

export const verifyOTPController = async (req, res) => {
try {
const { email, otp } = req.body;

// Check if email and OTP are provided
if (!email || !otp) {
return res.status(400).json({ error: 'Email and OTP are required' });
}

// Find user by email
const user = await User.findOne({ email });

if (!user) {
return res.status(404).json({ error: 'User not found' });
}

// Verify the OTP and check expiration
const isValid = verifyOTP(user, otp);

if (!isValid) {
return res.status(400).json({ error: 'Invalid or expired OTP' });
}

// Clear OTP after successful verification (optional, but recommended)
user.otp = null;
user.otpExpiry = null;
await user.save();

// OTP is valid
res.status(200).json({ message: 'OTP verified successfully' });
} catch (error) {
console.error('Error verifying OTP:', error);
res.status(500).json({ error: 'Internal Server Error' });
}
};

export const resetPassword = async (req, res) => {
try {
const { email, newPassword } = req.body;

// Check if both email and new password are provided
if (!email || !newPassword) {
return res.status(400).json({ error: 'Email and new password are required' });
}

// Find the user by email
const user = await User.findOne({ email });

if (!user) {
return res.status(404).json({ error: 'User not found' });
}

// Hash the new password
const hashedPassword = await hashPassword(newPassword);

// Update the user's password in the database
user.password = hashedPassword;
await user.save();

res.status(200).json({ message: 'Password reset successfully' });
} catch (error) {
console.error('Error resetting password:', error);
res.status(500).json({ error: 'Internal Server Error' });
}
};
2 changes: 2 additions & 0 deletions backend/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ app.use("/auth", authRoutes);
app.use("/api", authRoutes);
app.use("/station", stationRoutes);



app.get("/", (req, res) => {
res.send("Working...");
});
Expand Down
8 changes: 8 additions & 0 deletions backend/models/User.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,14 @@ const userSchema = new Schema({
type: String,
trim: true,
default: '',
},
otp: {
type: String, // The OTP code
required: false,
},
otpExpiry: {
type: Date, // The OTP expiry timestamp
required: false,
}
}, {
timestamps: true
Expand Down
10 changes: 10 additions & 0 deletions backend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"jsonwebtoken": "^9.0.2",
"mongoose": "^8.7.0",
"node": "^22.8.0",
"nodemailer": "^6.9.15",
"nodemon": "^3.1.7",
"socket.io": "^4.8.0"
},
Expand Down
6 changes: 4 additions & 2 deletions backend/routes/stationRoutes.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import express from 'express';
import { getCloakroomBookingsByStation, getCoolieBookingsByStation, getStationBookings, getWheelchairBookingsByStation } from '../controllers/stationBookingsController.js';
import { createStation, getAllStations } from '../controllers/StationController.js';

import { sendOTPToEmail ,verifyOTPController, resetPassword} from '../controllers/submitFeedback.js';
const router = express.Router();

// Route to fetch all bookings for a station
Expand All @@ -15,5 +15,7 @@ router.post('/', createStation);
router.get('/:id/coolies', getCoolieBookingsByStation);
router.get('/:id/wheelchairs', getWheelchairBookingsByStation);
router.get('/:id/cloakrooms', getCloakroomBookingsByStation);

router.post('/send-otp', sendOTPToEmail);
router.post('/verify-otp', verifyOTPController);
router.post('/reset-password', resetPassword);
export default router;
28 changes: 28 additions & 0 deletions backend/utils/emailUtils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import nodemailer from 'nodemailer';

// Function to send OTP via email
export const sendOTPEmail = async (email, otp) => {
try {
// Create a transporter using SMTP settings (e.g., Gmail)
const transporter = nodemailer.createTransport({
service: 'gmail',
auth: {
user: process.env.EMAIL_USER, // Your email address
pass: process.env.EMAIL_PASS // Your email password or app-specific password
}
});

const mailOptions = {
from: process.env.EMAIL_USER,
to: email,
subject: 'Your Password Reset OTP',
text: `Your OTP for password reset is ${otp}. It will expire in 10 minutes.`
};

// Send the email
await transporter.sendMail(mailOptions);
} catch (error) {
console.error('Error sending email:', error);
throw new Error('Failed to send OTP email');
}
};
16 changes: 16 additions & 0 deletions backend/utils/otputils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Generate a 6-digit OTP
export const generateOTP = () => {
return Math.floor(100000 + Math.random() * 900000).toString();
};

// Check if OTP is valid (both matching and not expired)
export const verifyOTP = (user, submittedOtp) => {
return user.otp === submittedOtp && user.otpExpiry > Date.now();
};

// Clear OTP after it's used
export const clearOTP = async (user) => {
user.otp = null;
user.otpExpiry = null;
await user.save();
};
Loading
Loading