Skip to content

Commit

Permalink
Added backend authendication using Mongo node and express (Anjaliavv5…
Browse files Browse the repository at this point in the history
…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**

![image](https://github.com/user-attachments/assets/9c1609b2-1c71-42a9-8e33-77e4e301a6ca)

![image](https://github.com/user-attachments/assets/758f5530-646e-49cf-a3fc-ac8ec2b56cf7)


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
Anjaliavv51 authored Oct 15, 2024
2 parents 2c4c0cd + e576bf1 commit 0fab530
Show file tree
Hide file tree
Showing 10 changed files with 2,291 additions and 0 deletions.
2 changes: 2 additions & 0 deletions backend/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules
.env
24 changes: 24 additions & 0 deletions backend/app.js
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;
14 changes: 14 additions & 0 deletions backend/config/databaseConfig.js
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;
190 changes: 190 additions & 0 deletions backend/controller/authController.js
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({
email
})
.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
};
9 changes: 9 additions & 0 deletions backend/index.js
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}`);
})
24 changes: 24 additions & 0 deletions backend/middleware/jwtAuth.js
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;
73 changes: 73 additions & 0 deletions backend/model/userSchema.js
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;
Loading

0 comments on commit 0fab530

Please sign in to comment.