I am making a social networking site with MERN Stack. Firstly, the users could simply sign up with email and password but that meant anyone can use any random email and possibly spam the DB. So I turned to this video https://www.youtube.com/watch?v=76tKpVbjhu8 and implemented it to ask for email verification.
Now the problem is even if a user registers and didn't verify his account another user can't create an account with the same email as it says user already exists. This means if some prankster registered an account with:
name: John Doe
email: johndoe#gmail.com
password: 123456
And couldn't verify his account, if someday the real johndoe comes and tries to register an account, he won't be able to register it as it already exists.
Is there anyway for the user to be automatically deleted if it failed to verify his account during the set time.
Here's my User Schema
const mongoose = require('mongoose');
const UserSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
posts: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Post'
}
],
profile: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Profile'
},
email: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
},
isVerified: {
type: Boolean,
default: false
},
date: {
type: Date,
default: Date.now
}
});
const User = mongoose.model('User', UserSchema);
module.exports = User;
Register route
// #route POST api/users
// #desc REgister a user
// #access Public
router.post(
'/',
[
check('name', 'Please add name')
.not()
.isEmpty(),
check('email', 'Please include a valid email').isEmail(),
check(
'password',
'Please enter a password with 6 or more characters'
).isLength({ min: 6 })
],
async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const { name, email, password } = req.body;
try {
let user = await User.findOne({ email });
if (user) {
return res.status(400).json({ msg: 'User already exists' });
}
user = new User({
name,
email,
password
});
const salt = await bcrypt.genSalt(10);
user.password = await bcrypt.hash(password, salt);
await user.save();
const payload = {
user: {
id: user.id
}
};
let profile = new Profile({
user: user.id
});
await profile.save();
user.profile = profile.id;
await user.save();
jwt.sign(
payload,
config.get('jwtSecret'),
{
expiresIn: 36000
},
(err, token) => {
if (err) throw err;
const url = `http://localhost:3000/confirmation/${token}`;
transporter.sendMail({
to: user.email,
subject: 'Confirm Email',
html: `Please click this link to confirm your email for signing up for MERN Blog ${url}`
});
res.json({
msg:
'Thanks for signing up! An email has been sent to your email for verification. Go ahead and verify your account'
});
}
);
} catch (err) {
console.log(err.message);
res.status(500).send('Server Error');
}
}
);
module.exports = router;
Confirmation route
router.post('/confirmation', async (req, res) => {
const token = req.body.token;
//Check if not token
if (!token) {
return res.status(401).json({ msg: 'No token, authorization denied' });
}
try {
const decoded = jwt.verify(token, config.get('jwtSecret'));
const user = await User.findByIdAndUpdate(decoded.user.id, {
isVerified: true
});
const payload = {
user: {
id: user.id
}
};
jwt.sign(
payload,
config.get('jwtSecret'),
{
expiresIn: 36000
},
(err, token) => {
if (err) throw err;
res.json({ token });
}
);
} catch (err) {
res.status(401).json({ msg: 'Token is not valid' });
}
});
I think the best way of doing this would be to check the accounts creation date/time, if it has been over X amount of days/weeks && the email is not verified, then dispatch a delete action.
Related
How to define role to Admin, Customer, Worker and Log in with different roles in frontend ? Im new in coding.
For authentication, I'm using JWT. Now I want to add roles and rules for admin, worker and customer.
1 -> Permissions can be assigned to a role ( Create, Update, delete etc...).
As a result, a user can have one or more roles. A user can use APIs for which he has rights, such as creating data, deleting data, updating data, and so on.
Here is the user schema:
UserSchema.model
const mongoose = require("mongoose");
const bcrypt = require("bcryptjs");
const userSchema = mongoose.Schema(
{
role: {
type: String,
enum: ["admin", "customer", "worker"],
},
firstname: {
type: String,
required: true,
},
lastname: {
type: String,
required: true,
},
email: {
type: String,
required: true,
unique: true,
},
password: {
type: String,
required: true,
},
role: {
type: Boolean,
// required: true,
default: false,
enum: ["admin", "customer", "worker"],
},
pic: {
type: String,
// required: true,
default:
"https://icon-library.com/images/anonymous-avatar-icon/anonymous-avatar-icon-25.jpg",
},
},
{
timestamps: true,
}
);
userSchema.methods.matchPassword = async function (enteredPassword) {
return await bcrypt.compare(enteredPassword, this.password);
};
// will encrypt password everytime its saved
userSchema.pre("save", async function (next) {
if (!this.isModified("password")) {
next();
}
const salt = await bcrypt.genSalt(10);
this.password = await bcrypt.hash(this.password, salt);
});
const User = mongoose.model("User", userSchema);
module.exports = User;
UserController
const asyncHandler = require("express-async-handler");
const User = require("../models/user.model");
const generateToken = require("../utility/token");
const authUser = asyncHandler(async (req, res) => {
const { email, password } = req.body;
const user = await User.findOne({ email });
if (user && (await user.matchPassword(password))) {
res.json({
_id: user._id,
firstname: user.firstname,
lastname: user.lastname,
email: user.email,
isAdmin: user.isAdmin,
pic: user.pic,
token: generateToken(user._id),
});
} else {
res.status(401);
throw new Error("Invalid Email or Password");
}
});
const registerUser = asyncHandler(async (req, res) => {
const { firstname, lastname, email, password, pic } = req.body;
try {
const userExists = await User.findOne({ email });
if (userExists) {
return res.status(400).json({ error: "User already exist." });
}
const user = await User.create({
firstname,
lastname,
email,
password,
pic,
});
res.status(201).json({
_id: user._id,
firstname: user.firstname,
lastname: user.lastname,
email: user.email,
isAdmin: user.isAdmin,
pic: user.pic,
token: generateToken(user._id),
});
} catch (error) {
res.status(500).json({ error: error.message });
}
});
const updateUserProfile = asyncHandler(async (req, res) => {
const user = await User.findById(req.user._id);
if (user) {
user.firstname = req.body.firstname || user.firstname;
user.lastname = req.body.lastname || user.lastname;
user.email = req.body.email || user.email;
user.pic = req.body.pic || user.pic;
if (req.body.password) {
user.password = req.body.password;
}
const updatedUser = await user.save();
res.json({
_id: updatedUser._id,
firstname: updatedUser.firstname,
lastname: updatedUser.lastname,
email: updatedUser.email,
pic: updatedUser.pic,
isAdmin: updatedUser.isAdmin,
token: generateToken(updatedUser._id),
});
} else {
res.status(404);
throw new Error("User Not Found");
}
});
module.exports = { authUser, updateUserProfile, registerUser };
UserSchema.model
const mongoose = require("mongoose");
const bcrypt = require("bcryptjs");
const userSchema = mongoose.Schema(
{
role: {
type: String,
enum: ["admin", "customer", "worker"],
},
firstname: {
type: String,
required: true,
},
lastname: {
type: String,
required: true,
},
email: {
type: String,
required: true,
unique: true,
},
password: {
type: String,
required: true,
},
role: {
type: Boolean,
// required: true,
default: false,
enum: ["admin", "customer", "worker"],
},
pic: {
type: String,
// required: true,
default:
"https://icon-library.com/images/anonymous-avatar-icon/anonymous-avatar-icon-25.jpg",
},
},
{
timestamps: true,
}
);
userSchema.methods.matchPassword = async function (enteredPassword) {
return await bcrypt.compare(enteredPassword, this.password);
};
// will encrypt password everytime its saved
userSchema.pre("save", async function (next) {
if (!this.isModified("password")) {
next();
}
const salt = await bcrypt.genSalt(10);
this.password = await bcrypt.hash(this.password, salt);
});
const User = mongoose.model("User", userSchema);
module.exports = User;
UserController
const asyncHandler = require("express-async-handler");
const User = require("../models/user.model");
const generateToken = require("../utility/token");
const authUser = asyncHandler(async (req, res) => {
const { email, password } = req.body;
const user = await User.findOne({ email });
if (user && (await user.matchPassword(password))) {
res.json({
_id: user._id,
firstname: user.firstname,
lastname: user.lastname,
email: user.email,
isAdmin: user.isAdmin,
pic: user.pic,
token: generateToken(user._id),
});
} else {
res.status(401);
throw new Error("Invalid Email or Password");
}
});
const registerUser = asyncHandler(async (req, res) => {
const { firstname, lastname, email, password, pic } = req.body;
try {
const userExists = await User.findOne({ email });
if (userExists) {
return res.status(400).json({ error: "User already exist." });
}
const user = await User.create({
firstname,
lastname,
email,
password,
pic,
});
res.status(201).json({
_id: user._id,
firstname: user.firstname,
lastname: user.lastname,
email: user.email,
isAdmin: user.isAdmin,
pic: user.pic,
token: generateToken(user._id),
});
} catch (error) {
res.status(500).json({ error: error.message });
}
});
const updateUserProfile = asyncHandler(async (req, res) => {
const user = await User.findById(req.user._id);
if (user) {
user.firstname = req.body.firstname || user.firstname;
user.lastname = req.body.lastname || user.lastname;
user.email = req.body.email || user.email;
user.pic = req.body.pic || user.pic;
if (req.body.password) {
user.password = req.body.password;
}
const updatedUser = await user.save();
res.json({
_id: updatedUser._id,
firstname: updatedUser.firstname,
lastname: updatedUser.lastname,
email: updatedUser.email,
pic: updatedUser.pic,
isAdmin: updatedUser.isAdmin,
token: generateToken(updatedUser._id),
});
} else {
res.status(404);
throw new Error("User Not Found");
}
});
module.exports = { authUser, updateUserProfile, registerUser };
I'm trying to get the profile picture from google profile using passport
I tried this code but all I get is an empty string and not a URL, i need to get the url of the image.
router.get("/googleLogin/success", async (req, res)=>{
if(req.user){
const user = await User.findOne({provider_id: req.user.id,
provider: req.user.provider})
if(user){
res.status(200).json({
success: true,
message: "success",
user: user
})
}else{
const checkUserEmail = await User.findOne({email: req.user.email})
if(checkUserEmail){
res.status(401).json({
success: false,
message: "User already Exist with this email id",
})
}else{
const user = await User.create({
username: req.user.name.givenName+ "_" +req.user.name.familyName,
email: req.user.emails[0].value,
provider: req.user.provider,
provider_id: req.user.id,
profilePic: req.user._json.picture[0].value,
});
res.status(200).json({
success: true,
message: "success",
user: user
})
}
}
console.log("CURRNT USER: ", user);
}
})
My User.js Modal:
,
profilePic: {
type: String,
default: "",
},
I'm trying to log in a user using Passport Js (Google Auth), I configured Passport Js and i'm getting the user profile in my console, but I didn't know how to show the user profile in frontend, the configure for PassportJs goes like:
passport.use(
new GoogleStrategy(
{
clientID: "",
clientSecret: "",
callbackURL: "/auth/google/callback",
scope: ["profile", "email"],
},
async (accessToken, refreshToken, profile, done) => {
// find current user in UserModel
const currentUser = await User.findOne({
googleId: profile.id
});
// create new user if the database doesn't have this user
if (!currentUser) {
const newUser = await new User({
googleId: profile.id,
email: profile.emails[0].value,
displayName: profile.displayName,
firstName: profile.name.givenName,
lastName: profile.name.familyName,
profilePic: profile.photos[0].value,
}).save();
if (newUser) {
done(null, newUser);
}
}
console.log("CURRNT USER: ", currentUser);
done(null, currentUser);
}
)
);
// serialize the user.id to save in the cookie session
// so the browser will remember the user when login
passport.serializeUser((user, done) => {
done(null, user.id);
});
passport.deserializeUser((id, done) => {
User.findById(id)
.then(user => {
done(null, user);
})
.catch(e => {
done(new Error("Failed to deserialize an user"));
});
});
And in the Auth.js route:
// when login is successful, retrieve user info
router.get("/login/success", (req, res) => {
if (req.user) {
res.status(200).json({
error: false,
message: "succesfull",
user: req.user,
cookies: req.cookies
});
} else {
res.status(403).json({ error: true, message: "Not Authorized" });
}
});
// auth with google
router.get("/google", passport.authenticate("google", ["profile", "email"]))
// redirect to home page after successfully login via google
router.get(
"/auth/google/callback",
passport.authenticate("google", {
successRedirect: "http://localhost:3000/",
failureRedirect: "/login/failed"
})
);
I'm using Context to let the app knows if the user is logged in or not.
**Login.jsx: Normal Logging Using express and Mongodb **
const handleSubmit = async (e) => {
e.preventDefault();
dispatch({ type: "LOGIN_START" });
try {
const res = await axios.post("/login", {
email: userRef.current.value,
password: passwordRef.current.value,
});
dispatch({ type: "LOGIN_SUCCESS", payload: res.data });
} catch (err) {
dispatch({ type: "LOGIN_FAILURE" });
setError(true)
}
};
//Now I tried this code to log in a user using Google Auth but it didn't work
useEffect(() => {
fetch(`http://localhost:4000/login/success`, {
method: 'GET',
credentials: 'include',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
'Access-Control-Allow-Credentials': true,
},
})
dispatch({ type: "LOGIN_START" })
.then((response) => {
if (response.status === 200) return response.json();
throw new Error('failed to authenticate user');
})
.then((responseJson) => {
dispatch({ type: "LOGIN_SUCCESS", payload: responseJson.data });
})
.catch((error) => {
dispatch({ type: "LOGIN_FAILURE" });
console.error("Failed to authenticate user", error)
});
}, []);
const google = () => {
window.open("http://localhost:4000/auth/google/callback", "_self");
};
The full code is here: https://codesandbox.io/s/nervous-mountain-e5t9d4?file=/api/routes/auth.js
let me share with you the perfect code.
passportStratergy.js
const GoogleStrategy = require('passport-google-oauth20').Strategy;
const User = require('../model/user');
const { USER_TYPES } = require('../constants/authConstant');
const googlePassportStrategy = passport => {
passport.serializeUser(function (user, cb) {
cb(null, user);
});
passport.deserializeUser(function (user, cb) {
cb(null, user);
});
passport.use(new GoogleStrategy({
clientID: process.env.GOOGLE_CLIENTID,
clientSecret: process.env.GOOGLE_CLIENTSECRET,
callbackURL: process.env.GOOGLE_CALLBACKURL
}, async function (accessToken, refreshToken, profile, done) {
if (profile){
let userObj = {
'username':profile.displayName,
'ssoAuth': { 'googleId': profile.id },
'email': profile.emails !== undefined ? profile.emails[0].value : '',
'password':'',
'userType':USER_TYPES.User
};
let found = await User.findOne(User,{ 'email': userObj.email });
if (found) {
const id = found.id;
await User.updateOne(User, { _id :id }, userObj);
}
else {
await User.create(User, userObj);
}
let user = await User.findOne(User,{ 'ssoAuth.googleId':profile.id });
return done(null, user);
}
return done(null, null);
}
));
};
module.exports = { googlePassportStrategy };
auth.js
const express = require('express');
const router = express.Router();
const passport = require('passport');
const { socialLogin } = require('../services/auth');
router.get('/auth/google/error', (req, res) => {
res.loginFailed({ message:'Login Failed' });
});
router.get('/auth/google',passport.authenticate('google', {
scope: ['profile', 'email'],
session:false
}));
router.get('/auth/google/callback',
(req,res,next)=>{
passport.authenticate('google', { failureRedirect: '/auth/google/error' }, async (error, user , info) => {
if (error){
return res.send({ message:error.message });
}
if (user){
try {
//let result = await socialLogin(user.email);
// here your business logic for login user.
return res.send({
data: result.data,
message:'Login Successful'
});
} catch (error) {
return res.send({ message: error.message });
}
}
})(req,res,next);
});
module.exports = router;
index.js
const passport = require('passport');
const { googlePassportStrategy } = require('./passportStrategy');
googlePassportStrategy(passport);
I'm trying to use Google OAuth in my App, on the Log In page and the Sign Up page, I'm looking for the best way and the easiest! I tried Passport Js, but I'm stuck right now.
I'm using Mongoose right now and I'm signing up and in users perfectly, but now i want to add a feature where the user can sign in using his google account, I'm looking for a way where the app can get the Email the user is using for his google account and then look if the email is already registered if so redirect him to the home page and if not sign his email up, save it to database, and then redirect to the home page.
This is how my Auth.js looks like
//REGISTER
router.post("/register", async (req, res) => {
try {
//generate new password
const salt = await bcrypt.genSalt(10);
const hashedPass = await bcrypt.hash(req.body.password, salt);
//create new user
const newUser = new User ({
username: req.body.username,
firstName: req.body.firstName,
lastName: req.body.lastName,
email: req.body.email,
password: hashedPass,
repeatPassword: hashedPass,
birthday: req.body.birthday,
});
//save user and respond
const user = await newUser.save();
res.status(200).json(user);
} catch (err) {
res.status(500).json(err);
}
});
//LOGIN
router.post("/login", async (req, res) => {
try {
const user = await User.findOne({ email: req.body.email });
// if(!user) return res.status(400).json("Wrong credentials!");
!user && res.status(400).json("Wrong credentials!");
const validated = await bcrypt.compare(req.body.password, user.password);
// if(!validated) return res.status(400).json("Wrong credentials!");
!validated && res.status(400).json("Wrong credentials!");
const { password, ...others } = user._doc;
return res.status(200).json(others);
} catch (err) {
return res.status(500).json(err);
}
});
PassportJs configuration I used: but didn't work
passport.use(
new GoogleStrategy(
{
clientID: "MyClientId",
clientSecret: "Myclientsecret",
callbackURL: "/api/auth/google/callback",
},
function (accessToken, refreshToken, profile, done) {
User.find(
{
social: profile.provider,
social_id: profile.id,
},
(err, user) => {
if (user.length === 0) {
const user = new User({
email: profile.email,
username: profile.displayName,
profilePic: profile.photos[0],
social: profile.provider,
social_id: profile.id,
});
const userModel = new User(data);
userModel.save();
done(null, profile);
}
if (err) {
return done(err);
}
},
);
return done(null, profile);
}
)
);
passport.serializeUser((user, done) => {
done(null, user);
});
passport.deserializeUser((user, done) => {
done(null, user);
});
router.get("/login/success", (req, res) => {
if (req.user) {
res.status(200).json({
success: true,
message: "successfull",
user: req.user,
// cookies: req.cookies
});
}
});
router.get("/login/failed", (req, res) => {
res.status(401).json({
success: false,
message: "failure",
});
});
router.get("/google", passport.authenticate("google", { scope: ["profile"] }));
router.get(
"/google/callback",
passport.authenticate("google", {
successRedirect: CLIENT_URL,
failureRedirect: "/login/failed",
})
);
I have also error with passportjs. Before errors if you know good tutorial passportjs, passport-local-mongoose with react please send me link.
I watch a video youtube and work code in [that][1] . This is github. But I want change authentication passport. In modaljs I do this:
const mongoose = require('mongoose');
const moment = require("moment");
const passportLocalMongoose = require('passport-local-mongoose');
const userSchema = mongoose.Schema({
name: {
type:String,
maxlength:50
},
email: {
type:String,
trim:true,
unique: 1
},
password: {
type: String,
minglength: 5
},
lastname: {
type:String,
maxlength: 50
},
role : {
type:Number,
default: 0
},
image: String
});
userSchema.plugin(passportLocalMongoose, {usernameField: 'email'});
const User = mongoose.model( 'User' , userSchema );
module.exports = {User};
in Userjs I do this:
const { User } = require('../models/model');
const router = require('express').Router();
const passport = require('passport');
passport.use(User.createStrategy());
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser( async function(id, done) {
await User.findById(id, function(err, user) {
done(err, user);
});
});
router.get('/auth', (req, res) => {
// res.send(req.isAuthenticated)
if(req.isAuthenticated()){
res.status(200).json({
_id: req.user._id,
isAdmin: req.user.role === 0 ? false : true,
isAuth: true,
email: req.user.email,
name: req.user.name,
lastname: req.user.lastname,
role: req.user.role,
image: req.user.image,
});
}
else{
return res.json({
isAuth: false,
error: true
});
}
});
router.post('/register', async (req, res) => {
await User.register( {email: req.body.email}, req.body.password, (err,user)=>{
if(err) return res.json({ success: false, err });
else res.status(200).json({
success: true
});
});
});
router.post("/login", function (req, res) {
console.log(req.body);
passport.authenticate("local")(req, res, function () {
console.log("ok!");
res.json({
loginSuccess: false,
message: "Invalid email or password"
});
});
});
router.get('/logout', (req, res) => {
if(req.isAuthenticated){
req.logout();
res.status(200).send({
success: true
});
}
else return res.json({success: false});
});
module.exports = router;
in server.js I add passport and this:
app.use(passport.initialize());
app.use(passport.session());
When first time I start server I get this error:
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
But after I get nothink. only show console req.body. How find error. I am very tired with passportjs. Where I find best tutorial passportjs with plugin database in passport-local-mongoose. And How find error and fix it. Sorry my english:))
[1]: https://github.com/jaewonhimnae/boilerplate-mern-stack
Please try res.status(200).send instead of res.status(200).json