-
Notifications
You must be signed in to change notification settings - Fork 213
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #42 from Abhishek-TG18/main
Implement Backend for Signup, Sign-in, User Details, and Signout Functionality
- Loading branch information
Showing
10 changed files
with
2,290 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,189 @@ | ||
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; | ||
|
||
/// 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; | ||
|
||
// 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: true, | ||
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(), // 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) => { | ||
console.log(req.cookies?.token); | ||
// 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.