Skip to content

Commit

Permalink
Merge pull request #42 from Abhishek-TG18/main
Browse files Browse the repository at this point in the history
Implement Backend for Signup, Sign-in, User Details, and Signout Functionality
  • Loading branch information
swaraj-das authored Oct 3, 2024
2 parents 9f56d9b + c8f29a8 commit 689ac80
Show file tree
Hide file tree
Showing 10 changed files with 2,290 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;
189 changes: 189 additions & 0 deletions backend/controller/authController.js
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({
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: 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
};
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) => {
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;
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 689ac80

Please sign in to comment.