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

added Auth using mongo node and express [backend] #1489

Merged
merged 1 commit into from
Oct 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
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
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