forked from Anjaliavv51/Retro
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added backend authendication using Mongo node and express (Anjaliavv5…
…1#341) # Pull Request Format ## PR Title ### Issue Anjaliavv51#314 : **Implement Authentication using MongoDB, Node.js, and Express.js [backend]** solved # ## Type of PR - **Add X in the box to specify the improvement type.** - [] Bug fix - [ x] Feature enhancement - [ ] Documentation update - [x ] BackendUpdate ## Description This pull request includes the implementation of the backend functionality for user authentication and management. The following features have been added: - - #Signup: Users can register with their email and password. The server validates the input and stores the user information in the database. - #Sign-in: Registered users can log in by providing valid credentials. - #User Details: Once logged in, users can retrieve their account information from the database. - #Signout: Users can securely log out of their accounts. All routes and logic have been implemented and tested with Postman. Please review and merge. ## Checklist - **Add X in the box to specify.** - [X] I have performed a self-review of my code. - x[ ] I have tested the changes thoroughly before submitting this pull request. - [ ] I have provided relevant issue numbers, screenshots, and videos after making the changes. - [x ] I have commented my code, particularly in hard-to-understand areas. ### **Information to Start the server**   Feel free to ask if you have any doubt I have tested the code using Postman API Testing here are the demo videos https://github.com/user-attachments/assets/c75322b0-5d83-4ef3-a0ae-f4503dba8463 https://github.com/user-attachments/assets/5ed926a1-cec8-4e9c-a51f-f261a32561d3 https://github.com/user-attachments/assets/f4ab790a-96e7-4170-ba99-3d0dc16c24e5 https://github.com/user-attachments/assets/7b222d0a-aaa9-4865-90aa-c9840385a194 Thank you for reviewing my pull request!
- Loading branch information
Showing
10 changed files
with
2,291 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
node_modules | ||
.env |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
const express = require('express'); | ||
const app = express(); | ||
|
||
const authRouter = require('./router/authRoute.js'); | ||
const databaseconnect = require('./config/databaseConfig.js'); | ||
const cookieParser = require('cookie-parser'); | ||
const cors = require('cors'); | ||
|
||
// connect to db | ||
databaseconnect(); | ||
|
||
app.use(express.json()); // Built-in middleware | ||
app.use(cookieParser()); // Third-party middleware | ||
|
||
app.use(cors({ origin: [process.env.CLIENT_URL], credentials: true })); //Third-party middleware | ||
|
||
// Auth router | ||
app.use('/auth', authRouter); | ||
|
||
app.use('/', (req, res) => { | ||
res.status(200).json({ data: 'JWTauth server ;)' }); | ||
}); | ||
|
||
module.exports = app; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
const mongoose = require("mongoose"); | ||
require('dotenv').config(); | ||
const MONGODB_URL = | ||
process.env.MONGODB_URL || "mongodb://localhost:27017/my_database"; | ||
|
||
// mongoDb database connection | ||
const databaseconnect = () => { | ||
mongoose | ||
.connect(MONGODB_URL) | ||
.then((conn) => console.log(`connected to DB: ${conn.connection.host}`)) | ||
.catch((err) => console.log(err.message)); | ||
}; | ||
|
||
module.exports = databaseconnect; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,190 @@ | ||
const userModel = require("../model/userSchema.js"); | ||
const bcrypt = require("bcrypt"); | ||
|
||
const emailValidator = require("email-validator"); | ||
|
||
/****************************************************** | ||
* @SIGNUP | ||
* @route /api/auth/signup | ||
* @method POST | ||
* @description singUp function for creating new user | ||
* @body name, email, password, confirmPassword | ||
* @returns User Object | ||
******************************************************/ | ||
|
||
const signUp = async (req, res, next) => { | ||
const { name, email, password, confirmPassword } = req.body; | ||
console.log(name , email,password,confirmPassword) | ||
/// every field is required | ||
if (!name || !email || !password || !confirmPassword) { | ||
return res.status(400).json({ | ||
success: false, | ||
message: "Every field is required" | ||
}); | ||
} | ||
|
||
//validate email using npm package "email-validator" | ||
const validEmail = emailValidator.validate(email); | ||
if (!validEmail) { | ||
return res.status(400).json({ | ||
success: false, | ||
message: "Please provide a valid email address 📩" | ||
}); | ||
} | ||
|
||
try { | ||
/// send password not match err if password !== confirmPassword | ||
if (password !== confirmPassword) { | ||
return res.status(400).json({ | ||
success: false, | ||
message: "password and confirm Password does not match ❌" | ||
}); | ||
} | ||
|
||
const userInfo = new userModel(req.body); | ||
|
||
// userSchema "pre" middleware functions for "save" will hash the password using bcrypt | ||
// before saving the data into the database | ||
const result = await userInfo.save(); | ||
return res.status(200).json({ | ||
success: true, | ||
data: result | ||
}); | ||
} catch (error) { | ||
/// send the message of the email is not unique. | ||
if (error.code === 11000) { | ||
return res.status(400).json({ | ||
success: false, | ||
message: `Account already exist with the provided email ${email} 😒` | ||
}); | ||
} | ||
|
||
return res.status(400).json({ | ||
message: error.message | ||
}); | ||
} | ||
}; | ||
|
||
/****************************************************** | ||
* @SIGNIN | ||
* @route /api/auth/signin | ||
* @method POST | ||
* @description verify user and send cookie with jwt token | ||
* @body email , password | ||
* @returns User Object , cookie | ||
******************************************************/ | ||
|
||
const signIn = async (req, res, next) => { | ||
const { email, password } = req.body; | ||
console.log(email,password) | ||
|
||
// send response with error message if email or password is missing | ||
if (!email || !password) { | ||
return res.status(400).json({ | ||
success: false, | ||
message: "email and password are required" | ||
}); | ||
} | ||
|
||
try { | ||
// check user exist or not | ||
const user = await userModel | ||
.findOne({ | ||
}) | ||
.select("+password"); | ||
|
||
// If user is null or the password is incorrect return response with error message | ||
if (!user || !(await bcrypt.compare(password, user.password))) { | ||
// bcrypt.compare returns boolean value | ||
return res.status(400).json({ | ||
success: false, | ||
message: "invalid credentials" | ||
}); | ||
} | ||
|
||
// Create jwt token using userSchema method( jwtToken() ) | ||
const token = user.jwtToken(); | ||
user.password = undefined; | ||
|
||
const cookieOption = { | ||
secure:true, | ||
maxAge: 24 * 60 * 60 * 1000, //24hr | ||
httpOnly: true // not able to modify the cookie in client side | ||
}; | ||
|
||
res.cookie("token", token, cookieOption); | ||
res.status(200).json({ | ||
success: true, | ||
data: user | ||
}); | ||
} catch (error) { | ||
return res.status(400).json({ | ||
success: false, | ||
message: error.message | ||
}); | ||
} | ||
}; | ||
|
||
|
||
/****************************************************** | ||
* @LOGOUT | ||
* @route /api/auth/logout | ||
* @method GET | ||
* @description Remove the token form cookie | ||
* @returns logout message and cookie without token | ||
******************************************************/ | ||
|
||
const logout = async (req, res, next) => { | ||
try { | ||
const cookieOption = { | ||
expires: new Date(Date.now()), // current expiry date | ||
httpOnly: true // not able to modify the cookie in client side | ||
}; | ||
|
||
// return response with cookie without token | ||
res.cookie("token", null, cookieOption); | ||
res.status(200).json({ | ||
success: true, | ||
message: "Logged Out" | ||
}); | ||
} catch (error) { | ||
res.stats(400).json({ | ||
success: false, | ||
message: error.message | ||
}); | ||
} | ||
}; | ||
|
||
/****************************************************** | ||
* @GETUSER | ||
* @route /api/auth/user | ||
* @method GET | ||
* @description retrieve user data from mongoDb if user is valid(jwt auth) | ||
* @returns User Object | ||
******************************************************/ | ||
|
||
const getUser = async (req, res, next) => { | ||
const userId = req.user.id; | ||
try { | ||
const user = await userModel.findById(userId); | ||
return res.status(200).json({ | ||
success: true, | ||
data: user | ||
}); | ||
} catch (error) { | ||
return res.status(400).json({ | ||
success: false, | ||
message: error.message | ||
}); | ||
} | ||
}; | ||
|
||
module.exports = { | ||
signUp, | ||
signIn, | ||
|
||
getUser, | ||
|
||
logout | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
require('dotenv').config(); | ||
const PORT = process.env.PORT || 5000; | ||
|
||
|
||
const app = require('./app'); | ||
|
||
app.listen(PORT,()=>{ | ||
console.log(`server is listening at http://localhost:${PORT}`); | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
const JWT = require("jsonwebtoken"); | ||
|
||
// router level middleware function | ||
const jwtAuth = (req, res, next) => { | ||
|
||
// get cookie token(jwt token generated using json.sign()) form the request | ||
const token = ( req.cookies?.token) || null; | ||
|
||
// return response if there is no token(jwt token attached with cookie) | ||
if (!token) { | ||
return res.status(400).json({ success: false, message: "NOT authorized" }); | ||
} | ||
|
||
// verify the token | ||
try { | ||
const payload = JWT.verify(token, process.env.SECRET); | ||
req.user = { id: payload.id, email: payload.email }; | ||
} catch (error) { | ||
return res.status(400).json({ success: false, message: error.message }); | ||
} | ||
next(); | ||
}; | ||
|
||
module.exports = jwtAuth; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
const mongoose = require('mongoose'); | ||
const { Schema } = mongoose; | ||
const crypto = require('crypto'); | ||
const bcrypt = require('bcrypt'); | ||
const JWT = require('jsonwebtoken'); | ||
|
||
const userSchema = new Schema( | ||
{ | ||
name: { | ||
type: String, | ||
require: [true, 'user name is Required'], | ||
|
||
trim: true, | ||
}, | ||
email: { | ||
type: String, | ||
required: [true, 'user email is required'], | ||
unique: true, | ||
lowercase: true, | ||
unique: [true, 'already registered'], | ||
}, | ||
password: { | ||
type: String, | ||
select: false, | ||
}, | ||
forgotPasswordToken: { | ||
type: String, | ||
}, | ||
forgotPasswordExpiryDate: { | ||
type: Date, | ||
}, | ||
}, | ||
{ timestamps: true } | ||
); | ||
|
||
// Hash password before saving to the database | ||
userSchema.pre('save', async function (next) { | ||
// If password is not modified then do not hash it | ||
if (!this.isModified('password')) return next(); | ||
this.password = await bcrypt.hash(this.password, 10); | ||
return next(); | ||
}); | ||
|
||
// FIXME: Check if these methods are working as expected | ||
userSchema.methods = { | ||
//method for generating the jwt token | ||
jwtToken() { | ||
return JWT.sign( | ||
{ id: this._id, email: this.email }, | ||
process.env.SECRET, | ||
{ expiresIn: '24h' } // 24 hours | ||
); | ||
}, | ||
|
||
//userSchema method for generating and return forgotPassword token | ||
getForgotPasswordToken() { | ||
const forgotToken = crypto.randomBytes(20).toString('hex'); | ||
//step 1 - save to DB | ||
this.forgotPasswordToken = crypto | ||
.createHash('sha256') | ||
.update(forgotToken) | ||
.digest('hex'); | ||
|
||
/// forgot password expiry date | ||
this.forgotPasswordExpiryDate = Date.now() + 20 * 60 * 1000; // 20min | ||
|
||
//step 2 - return values to user | ||
return forgotToken; | ||
}, | ||
}; | ||
|
||
const userModel = mongoose.model('user', userSchema); | ||
module.exports = userModel; |
Oops, something went wrong.