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 and Reset Password Implemented #220

Merged
merged 3 commits into from
Jun 2, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
10 changes: 10 additions & 0 deletions server/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
PORT = 4000
DATABASE_URL = mongodb://localhost:27017/shopy
JWT_SECRET= dejiohwoizinedhideahnoirzahiofewapp
CLIENT_URL = http://localhost:3000

SMTP_SERVICES = "gmail"
SMTP_MAIL = [email protected]
SMTP_PASSWORD = gzot hzge znlb mihe
SMTP_HOST = "smtp.gmail.com"
SMTP_PORT = 465
22 changes: 22 additions & 0 deletions server/config/JWTAuthentication.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import passport from "passport";
const authenticateJWT = (req, res, next) => {
passport.authenticate("jwt", { session: false }, (err, user, info) => {
if (err) {
return res.status(500).json({
success: false,
message: "Internal Server Error",
});
}
if (!user) {
return res.status(401).json({
success: false,
isLoggedIn: false,
message: "Invalid Request. Please Sign In Your Account",
});
}
req.user = user;
next();
})(req, res, next);
};

export { authenticateJWT };
177 changes: 177 additions & 0 deletions server/controllers/auth/authController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
import { UserModel } from "../../models/userModel.js";
import { OtpModel } from "../../models/OtpModel.js";
import { generateOTP } from "../../utils/generateOTP.js";
import { sendEmail } from "../../utils/sendEmail.js";
import { generateToken } from "../../utils/generateJwtToken.js";
import { decodeJWT } from "../../utils/decodeJwtToken.js";
/*
Forgot Password

*/
const forgotPassword = async (req, res) => {
try {
const { email } = req.body;
const user = await UserModel.findOne({ email });
if (!user) {
return res.status(404).json({
message: "Invalid Email or User Not Found",
success: false,
});
}

// generating otp
const otp = generateOTP();
await OtpModel.create({ email: user.email, otp });
// Send OTP via email
const emailOptions = {
mail: user.email,
subject: "FORGOT PASSWORD - OTP",

messageContent: `
<div style="max-width: 600px; margin: 0 auto; color:black;">
<h2 style="text-align: center;">Verify Password Reset Request</h2>
<p>Dear ${user.fullname},</p>
<p>We noticed that you recently requested to reset your password for your account. Please use the following One-Time Password (OTP) to reset your password:</p>
<p style="font-weight: bold;">OTP: ${otp}</p>

<p>This OTP Will expires in 2:00 Minutes.<p/>
<p> <b>Note: </b>If you did not request this password reset, please ignore this email. Your account security is important to us, and we apologize for any inconvenience.</p>
<p>If you encounter any issues or need further assistance, please don't hesitate to contact our support team at <a href="mailto:[Support Email]">[Support Email]</a> or call us at [Support Phone Number].</p>
<p>Thank you for choosing Shopy !</p>
<p>Best regards,<br> <strong>[Company Name]</strong></p>
</div>
`,
};
await sendEmail(emailOptions);
res.status(201).json({
success: true,
message: `An OTP is send to your email ${email} .`,
otp: otp,
});
} catch (error) {
return res.status(500).json({
success: false,
error: error.message,
});
}
};
const verifyAndResetPassword = async (req, res) => {
try {
const { email, otp } = req.body;
// console.log(req.body);

// Retrieve the OTP stored in the database for the user's email
const otpData = await OtpModel.findOne({ email }).sort({ createdAt: -1 });

// Check if OTP exists in the database
if (!otpData) {
return res
.status(404)
.json({ success: false, message: "OTP Expired. Click on Resend OTP" });
}

// Compare the OTP provided by the user with the OTP stored in the database
if (otpData.otp !== otp) {
return res.status(400).json({
success: false,
message: "Invalid OTP.",
});
}
// - Delete the OTP record from the database
await otpData.deleteOne();
const user = await UserModel.findOne({ email });
if (!user) {
return res.status(404).json({
message: "User Not Found",
success: false,
});
}

const password_Reset_Token = generateToken(user._id, "300s");
const password_Reset_URL = `${process.env.CLIENT_URL}/reset-password/${password_Reset_Token}`;
// Send OTP via email
const emailOptions = {
mail: user.email,
subject: "RESET PASSWORD ",

messageContent: `
<div style="max-width: 600px; margin: 0 auto; color:black;">
<h2 style="text-align: center;">Reset Your Password</h2>
<p>Dear ${user.fullname},</p>
<p>We have received a request to reset the password associated with your account. To proceed with the password reset, please follow the instructions below:</p>

<ol>
<li><strong>Click on the following link to reset your password:</strong> <a href=${password_Reset_URL}>Password Reset Link</a></li>
<li>Please note that this link is valid for 5:00 Minutes. After this period, you will need to request another password reset.</li>
<li><strong>If you did not request this password reset,</strong> please change your Password Immediately by Logging into your account.</li>
</ol>
<p><strong>Note:</strong> If the above link does not work then copy and paste this url into browser.<br/> ${password_Reset_URL}</p>
<p><strong>Ensure the security of your account:</strong></p>
<ul>
<li>Please Don't share this email to anyone it contains your accounts Credentials.</li>
<li>Choose a strong password that includes a combination of letters, numbers, and special characters.</li>
<li>Avoid using easily guessable passwords and refrain from sharing your password with anyone.</li>
<li>Regularly update your password to enhance security.</li>
</ul>

<p>If you encounter any issues or require further assistance, please feel free to reach out to our support team at <a href="mailto:[Support Email]">[Support Email]</a>.</p>

<p>Thank you for your attention to this matter.</p>
<p>Best regards,<br> <strong>Shopy!!</strong></p>
</div>
`,
};
await sendEmail(emailOptions);

return res.status(200).json({
success: true,
message: "Password Reset Link has been Sent to your email.",
password_Reset_URL,
});
} catch (error) {
return res.status(500).json({
success: false,
error: error.message,
});
}
};
const setNewPassword = async (req, res) => {
try {
const { token, newPassword } = req.body;
console.log(token);

//decodes token id
const decoded = await decodeJWT(token);
console.log(decoded);
if (!decoded) {
return res.status(404).json({
success: true,
message: "Please make another new password Reset request",
});
}
let user = await UserModel.findOne({ _id: decoded.id }).select("password");
// console.log(user);
if (!user) {
return res.status(400).json({
success: false,
message: "Invalid Token or User not found",
});
}

user.password = newPassword;
user = await user.save();

res.status(200).send({
message: "Password changed successfully",
success: true,
// user,
});
} catch (error) {
return res.status(500).json({
success: false,
error: error.message,
});
}
};

export { forgotPassword, verifyAndResetPassword, setNewPassword };
52 changes: 28 additions & 24 deletions server/index.js
Original file line number Diff line number Diff line change
@@ -1,34 +1,38 @@
import express from 'express';
import dotenv from 'dotenv';
import cors from 'cors';
import morgan from 'morgan';
import { MensRouter } from './routes/mens-route.js';
import connectDB from './config/DBconnect.js';
import { WomensRouter } from './routes/womens-route.js';
import { KidsRouter } from './routes/kids-route.js';
import express from "express";
import dotenv from "dotenv";
import cors from "cors";
import morgan from "morgan";
import { MensRouter } from "./routes/mens-route.js";
import connectDB from "./config/DBconnect.js";
import { WomensRouter } from "./routes/womens-route.js";
import { KidsRouter } from "./routes/kids-route.js";
import { authRouter } from "./routes/auth/authRoutes.js";

const app=express();
const app = express();

dotenv.config({path:'./config.env'});
dotenv.config();

connectDB();

app.use(express.json());

app.use(morgan('dev'))
app.use(morgan("dev"));

app.use(cors({
origin:"https://shopy-mohitparmar1s-projects.vercel.app/",
methods:['GET','POST'],
credentials:true,
}))
app.use(
cors({
origin: "https://shopy-mohitparmar1s-projects.vercel.app/",
methods: ["GET", "POST"],
credentials: true,
})
);

app.use('/api/v1/mens',MensRouter);
app.use('/api/v1/womens',WomensRouter);
app.use('/api/v1/kids',KidsRouter);

const port=process.env.PORT||7000;
app.use("/api/v1/auth", authRouter);
app.use("/api/v1/mens", MensRouter);
app.use("/api/v1/womens", WomensRouter);
app.use("/api/v1/kids", KidsRouter);

app.listen(port,()=>{
console.log(`Server is running on port ${port}`);
})
const port = process.env.PORT || 7000;

app.listen(port, () => {
console.log(`Server is running on port ${port}`);
});
20 changes: 20 additions & 0 deletions server/models/OtpModel.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import mongoose from "mongoose";

const OtpSchema = new mongoose.Schema({
email: {
type: String,
required: true,
},
otp: {
type: String,
required: true,
},
createdAt: {
type: Date,
default: Date.now,
expires: 60, // Set expiration time in seconds (e.g., 60 seconds)
},
});

const OtpModel = mongoose.model("otp", OtpSchema);
export { OtpModel };
101 changes: 101 additions & 0 deletions server/models/userModel.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import mongoose from "mongoose";
import bcrypt from "bcryptjs";
import jwt from "jsonwebtoken";

const { genSalt, hash, compare } = bcrypt;
const UserSchema = new mongoose.Schema(
{
fullname: {
type: String,
required: true,
},
email: {
type: String,
required: true,
},
password: {
type: String,
select: false,
},
role: {
type: [String],
default: ["user"],
},
isVerified: {
type: Boolean,
default: false,
},
},

{
timestamps: true,
}
);

// Attachments : Genrating a JWT Token
UserSchema.methods.generateJwtToken = function () {
return jwt.sign({ user: this._id.toString() }, process.env.JWT_SECRET, {
expiresIn: "7d",
});
};

// Encrypting the user Password
UserSchema.pre("save", function (next) {
// gives the data of current user
const user = this;

//password is modified
if (!user.isModified("password")) return next();

//generate bcrypt salt : means mkaing encrypting password more stronger
bcrypt.genSalt(8, (error, salt) => {
if (error) return next(error);

// hash the password
bcrypt.hash(user.password, salt, (error, hash) => {
if (error) return next(error);

// assigning hashed password
user.password = hash;

return next();
});
});
});

// Helper Function :
UserSchema.statics.isUserExist = async ({ email }) => {
const isEmailExist = await UserModel.findOne({ email });

if (isEmailExist) {
// return res.status(404).json({
// success: false,
// message: "User already Exists...",
// });
throw new Error("User already Exists....");
}
};

UserSchema.statics.findByEmailAndPassword = async ({ email, password }) => {
const user = await UserModel.findOne({ email }).select("password");

if (!user) {
// return res.status(404).json({
// success: false,
// message: "User does not Exists.... !",
// });
throw new Error("User does not Exists.... !");
}

// compare password
const doesPasswordMatch = await bcrypt.compare(password, user.password);

if (!doesPasswordMatch) {
throw new Error("Invalid Credentials !!!");
}

return user;
};

const UserModel = mongoose.model("user", UserSchema);
export { UserModel };
Loading