When i tried to use it with react app i can not reach the value. I use sequelize orm and mysql database. I want to send the session information to the react side when the user logs in
app.js file:
const express = require('express')
const dotenv = require("dotenv")
const app = express()
const body_parser = require("body-parser");
const cookieParser = require("cookie-parser")
const session = require('express-session')
const cors = require("cors")
dotenv.config()
app.use(cors())
const SequelizeStore = require("connect-session-sequelize")(session.Store);
const adminRoute = require("./routes/admin")
const locals = require("./middlewares/locals")
app.use(cookieParser())
app.use(express.urlencoded({ extended: false }))
app.use(body_parser.json())
const sequelize = require("./data/db")
app.use(session({
secret: "hello world",
resave: false,
saveUninitialized: false,
cookie: {
maxAge: 1000 * 60 * 60 * 24
},
store: new SequelizeStore({
db: sequelize
})
}))
app.use(locals);
app.use(adminRoute)
const dummy = require("./data/dummy-data")
async function x() {
// await sequelize.sync({ force: true })
// await dummy()
}
x()
app.listen(process.env.PORT, () => {
console.log(`Server running on Port: ${process.env.PORT}`)
})
locals.js file:
module.exports = (req, res, next) => {
res.locals.auth = req.session.auth
console.log("auth : ", res.locals.auth)
next();
}
admin.js routes:
router.get("/api/session", async (req, res) => {
res.json({ isAuth: res.locals.isAuth });
});
router.post("/login", async (req, res) => {
const { email, password } = req.body
console.log(req.body)
try {
const user = await Users.findOne({
where: {
email: email
}
})
if (!user) {
return res.status(404)
}
const match = await bcrypt.compare(password, user.password)
console.log("match:", match)
if (match) {
req.session.isAuth = 1
return res.status(200).send({ message: "login successful" })
}
else {
req.session.isAuth = 0
return res.status(404)
}
}
catch (err) {
console.log(err)
}
})
React Login.js:
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const login = async (e) => {
e.preventDefault()
const response = await axios.post("http://localhost:5000/login", { email: email, password: password })
console.log(response)
if(response.status === 200){
const response2 = await axios.get("http://localhost:5000/api/session")
console.log(response2)
}
}
Login.js first response = data: {message: 'login successful'}
Login.js second response = data: {}
If i try to react http://localhost:5000/login by hand on the browser auth sets as true. But when i refresh react side it sets undefined again.
I was building authentication for a website
and the req.cookie was returning undefined. Can I know what's wrong with that?
this is my server code
and I imported the auth file here
app.get("/", auth, (req, res) => {
res.render("index")
})
this is my auth.js file
const jwt = require("jsonwebtoken")
const Register = require("../models/registration")
const cookieParser = require("cookie-parser")
const express = require("express")
const app = express()
app.use(cookieParser())
const auth = async (req, res, next) => {
try {
const token = req.cookie.token_name
console.log(token)
const verifyUser = jwt.verify(token, process.env.SECRET_KEY)
next()
} catch (error) {
console.log(error)
res.status(401).send(error)
}
}
module.exports = auth
if required this is my login code as well but yeah i verified its storing cookies on the browser
app.post("/login", async (req, res) => {
try {
const password = req.body.password
const email = req.body.email
const user = await Register.findOne({ email: email })
const isMatch = await bcrypt.compare(password, user.password)
const token = await user.generateAuthenticationToken()
res.cookie('jwt', token, {
maxAge: 600*1000,
httpOnly: true,
})
if (isMatch) {
res.redirect("/")
} else {
res.send("Invalid Credentials")
}
} catch (error) {
res.status(404).send(error)
}
})
I have created a login form that should redirect the user to a dashboard page in case he enters the right password and username. If the user tries to navigate to the dashboard url without being logged in the page should not display as it is a protected route. I am trying to send a jwt token when the user logs in, but that doesn't work I just get the Forbidden message when I log in so it seems that the token is not sent correctly, how can I send the jwt token and access the protected route once the user logs in successfully?
Here is my server.js:
const express = require('express');
const jwt = require('jsonwebtoken');
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
let Post = require('./models/post.model.js');
const app = express();
const cors = require('cors');
require('dotenv').config();
app.use(cors());
app.use("/assets", express.static(__dirname + "/assets"));
app.use(bodyParser.urlencoded({ extended: true }));
const BASE_URL = process.env.BASE_URL;
const PORT = process.env.PORT || 1337;
mongoose.connect(BASE_URL, { useNewUrlParser: true, useUnifiedTopology: true })
const connection = mongoose.connection;
connection.once('open', function () {
console.log('Connection to MongoDB established succesfully!');
});
app.set('view-engine', 'ejs');
app.get('/', (req, res) => {
res.render('index.ejs');
});
app.post('/', (req, res) => {
let username = req.body.username;
let password = req.body.password;
const user = {
username: username,
password: password
}
jwt.sign({ user }, process.env.SECRET_KEY, (err, token) => {
res.json({
token
})
});
if (username !== process.env.USER_NAME && password !== process.env.USER_PASSWORD) {
res.json('Invalid credentials');
} else {
res.setHeader('Authorization', 'Bearer '+ token);
res.redirect('/dashboard')
}
});
app.get('/dashboard', verifyToken, (req, res) => {
jwt.verify(req.token, process.env.SECRET_KEY, (err, authData) => {
if (err) {
res.sendStatus(403);
} else {
res.sendStatus(200);
}
});
res.render('dashboard.ejs');
});
app.get('/dashboard/createPost', verifyToken, (req, res) => {
res.render('post.ejs');
});
app.post('/dashboard/createPost', async (req, res) => {
let collection = connection.collection(process.env.POSTS_WITH_TAGS);
res.setHeader('Content-Type', 'application/json');
let post = new Post(req.body);
collection.insertOne(post)
.then(post => {
res.redirect('/dashboard')
})
.catch(err => {
res.status(400).send(err);
});
});
// TOKEN FORMAT
// Authorization: Bearer <access_token>
//Verifing the Token
function verifyToken(req, res, next) {
// Get auth header value
const bearerHeader = req.headers['authorization'];
// Check if bearer is undefined
if (typeof bearerHeader !== 'undefined') {
// Spliting the bearer
const bearer = bearerHeader.split(' ');
// Get token from array
const bearerToken = bearer[1];
// Set the token
req.token = bearerToken;
// Next middleware
next();
} else {
// Forbid the route
res.sendStatus(403);
}
}
app.listen(PORT);
see this example, i use middleware(checkAuthLogin), this code contains all thing for your question:
index.js:
const express = require('express');
const app = express();
require('./db/mongoose');
const userRouter = require('./routers/user');
app.use(express.json());
app.use(userRouter);
app.listen(3000, ()=> {
console.log('Server is up on port ', 3000)
});
db/mongoose.js:
const mongoose = require('mongoose');
mongoose.connect("mongodb://127.0.0.1:27017/db-test" {
useNewUrlParser : true,
useCreateIndex : true,
useFindAndModify : false,
useUnifiedTopology: true
});
routers/user.js:
const express = require('express');
const router = new express.Router();
const RootUser = require('../models/root-user');
const {checkRootLogin} = require('../middleware/checkAuthLogin');
router.post('/createrootuser', async (req, res) => {
const updates = Object.keys(req.body);
const allowedUpdatesArray = ['name', 'password'];
const isValidOperation = updates.every((update) => allowedUpdatesArray.includes(update));
if (!isValidOperation) {
return res.status(400).send({error: 'Invalid Request Body'})
}
const rootUser = new RootUser(req.body);
try {
await rootUser.save();
// sendWelcomeEmail(user.email, user.name)
const token = await rootUser.generateAuthToken();
//console.log(user)
res.status(201).send({rootUser, token});
} catch (e) {
res.status(400).send(e)
}
});
//use this middleware(checkRootLogin) for check root user can access this function
router.post('/rootconfig', checkRootLogin, async (req, res) => {
res.status(200).send({success: 'success add root config'})
});
module.exports = router;
model/root-user.js:
const mongoose = require('mongoose');
const validator = require('validator');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const userRootSchema = new mongoose.Schema({
name: {
type : String,
required: true,
unique : true,
trim : true,
lowercase : true,
},
password: {
type : String,
required: true,
unique : true,
trim : true,
lowercase : true,
minlength : 6,
validate (value) {
//if (validator.contains(value.toLowerCase(), 'password')){
if (value.toLowerCase().includes('password')){
throw new Error('Password can not contained "password"')
}
}
},
tokens : [{
token : {
type : String ,
required : true
}
}],
}, {
timestamps: true
});
userRootSchema.methods.generateAuthToken = async function(){
const root = this;
// generate token
try {
// const token = jwt.sign({ _id : user._id.toString()}, process.env.JWT_SECRET);
const token = jwt.sign({ _id : root._id.toString()}, "test");
// add token to user model
root.tokens = root.tokens.concat({ token });
await root.save();
return token
} catch (e){
throw new Error(e)
}
};
userRootSchema.pre('save', async function(next){
// this give ccess to individual user
const user = this;
if (user.isModified('password')){
user.password = await bcrypt.hash(user.password, 8)
}
next()
});
const UserRoot = mongoose.model('UserRoot', userRootSchema);
module.exports = UserRoot;
middleware/checkAuthLogin.js:
const jwt = require('jsonwebtoken');
const RootUser = require('../models/root-user');
const checkRootLogin = async (req, res, next) => {
try {
const token = req.header('Authorization').replace('Bearer ', '');
// const decoded = jwt.verify(token, process.env.JWT_SECRET);
const decoded = jwt.verify(token, "test");
const rootUser = await RootUser.findOne({_id: decoded._id, 'tokens.token': token});
if (!rootUser) {
throw new Error("User cannot find!!");
}
req.token = token;
req.rootUser = rootUser;
req.userID = rootUser._id;
next()
} catch (e) {
res.status(401).send({error: 'Authentication problem!!'})
}
};
module.exports = {checkRootLogin};
Your issue is that your token variable is only accessible inside of the callback to the jwt.sign call, so when you try to do this here res.setHeader('Authorization', 'Bearer '+ token);, it won't know what variable you're referring to, hence the undefined error. By the way, if you're going to use jwt.sign asynchronously, then the code that uses it needs to also be inside of the callback, otherwise synchronous code outside of the callback will likely execute first (and thus not be able to access any results of the asynchronous code) as the asynchronous callback executes in the background. The solution here is to either switch your usage to a synchronous usage or place your response code inside of the callback. Also, calling res.json will end the response so I'm not sure what exactly you're trying to accomplish with the multiple response calls
Synchronous version:
app.post('/', (req, res) => {
let username = req.body.username;
let password = req.body.password;
const user = {
username: username,
password: password
};
let token = undefined;
try {
token = jwt.sign({ user }, process.env.SECRET_KEY);
} catch (e) {
// handle error
}
if (username !== process.env.USER_NAME && password !== process.env.USER_PASSWORD) {
res.json('Invalid credentials');
} else {
res.setHeader('Authorization', 'Bearer '+ token);
res.redirect('/dashboard');
}
});
Asynchronous version:
app.post('/', (req, res) => {
let username = req.body.username;
let password = req.body.password;
const user = {
username: username,
password: password
}
jwt.sign({ user }, process.env.SECRET_KEY, (err, token) => {
if (username !== process.env.USER_NAME && password !== process.env.USER_PASSWORD) {
res.json('Invalid credentials');
} else {
res.setHeader('Authorization', 'Bearer '+ token);
res.redirect('/dashboard')
}
});
});
In these examples, I took out res.json({ token }) because you can't use res.json and then perform a redirect, but modify those parts however best fits your code. On another note, you probably don't want to include the password in your token because while JWTs (when using the default/standard algorithms which do not include encryption) are cryptographically guaranteed to be unmodifiable, they are still readable
I have one solution to send jwt token, but you will need to install one more package. If you think it worth maybe you can follow.
I use express only for backend api. But you can use the same logic applied here to your application.
The lib you will need to install is the express-jwt
It handles routes to block access to endpoint that need authentication.
server.js
require('dotenv').config()
const express = require('express');
const logger = require('morgan');
const cors = require('cors');
const jwt = require('jsonwebtoken');
const expressJwt = require('express-jwt');
const app = express();
cors({ credentials: true, origin: true });
app.use(cors());
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use('/secure', expressJwt({ secret: process.env.SECRET }));
app.use(require('./server/index'));
app.get('/secure/dashboard') => {
//now you can only access this route with authorization header
//prependending the '/secure/ to new routes should make them return 401 when accessed without authorization token
//accessing this route without returns 401.
//there is no need to validate because express-jwt is handling.
console.log(res.user)//should print current user and pass signed with token
res.render('dashboard.ejs');
});
app.post('/', (req, res) => {
let username = req.body.username;
let password = req.body.password;
//jwt.sign({ user }, process.env.SECRET_KEY, (err, token) => {
// res.json({
// token
// })
//});
//shouldn't sign json here, because there is no guarantee this is a valid
//username and password it can be an impostor
if (username !== process.env.USER_NAME && password !== process.env.USER_PASSWORD) {
res.json('Invalid credentials');
} else {
const user = {
username: username,
password: password
};
const tk = {};
tk.token = 'Bearer ' + jwt.sign(user, process.env.SECRET_KEY, { expiresIn: 1800 });//expires in 1800 seconds
res.status(200).json(tk);
}
});
Now in your frontend put the authorization token sent by this route in cookies or store in client-side.
Do the next request with the header authorization for the secure dashboard route.
I think the problem in the sign in controller function
you must check first if the user have the correct password before attempting to send him a token
you should save the result of jwt sign function in a variable to send back to the user in case he has the right credintials.
It make no sense to send the password again to the user , only the username is needed
you can try this :
app.post('/', (req, res) => {
const {username , password} = req.body;
if (username !== process.env.USER_NAME && password !== process.env.USER_PASSWORD) {
return res.json('Invalid credentials');
}
const token = jwt.sign({username:username }, SECRET)
res.setHeader('Authorization', token);
res.redirect('/dashboard')
});
I have a nodejs backend with sessions made using express-session npm module. I want to test the sessions using postman.
What I want is a user should be allowed to access the users list via the /getUsers route only if he is currently in a session. But what is happening is when I test the backend using postman even after logging in the user is unable to access the users using the /getUsers route. Is this something to do with postman?
Here is my app.js
const express = require("express");
const app = express();
const authRoutes=require('./routes/auth');
const mongoose=require('mongoose');
const bodyParser = require("body-parser");
require("dotenv").config();
const nodemailer = require("nodemailer");
const session = require('express-session');
const RedisStore = require('connect-redis')(session);
const cors = require('cors');
app.use(cors({
origin:['http://localhost:8080'],
methods:['GET','POST'],
credentials: true // enable set cookie
}));
app.use(
// Creates a session middleware with given options.
session({
name: 'sid',
saveUninitialized: false,
resave: false,
secret: 'sssh, quiet! it\'s a secret!',
cookie: {
httpOnly: true,
maxAge: 1000 * 60 * 60 * 2,
sameSite: true,
secure: true
}
})
);
mongoose.connect(process.env.LOCAL_MONGO_URI,{useNewUrlParser:true},function (err) {
if (err) throw err
console.log("Connected to local mongo db database");
});
app.get("/",(req,res)=> {console.log("A request was made to /")
console.log("/GET called");
});
app.use(bodyParser.json());
app.use("/",authRoutes);
const port = process.env.PORT||8080
app.listen(port,()=> {
console.log("Hello world");
})
Here is my routes/auth.js:
const express = require("express");
const {
signup,login,verifyemail,requiresLogin,getUsers,logout
} = require("../handler/auth")
const router = express.Router();
router.post("/signup",signup);
router.post("/login",login);
router.get("/verifyemail/:token",verifyemail);
router.get("/getUsers",requiresLogin,getUsers);
router.get("/getUsers",requiresLogin,getUsers);
router.get("/logout",requiresLogin,logout);
module.exports=router;
Here is my handler/auth.js
const User = require("../models/user");
const bcrypt = require('bcrypt');
const crypto = require('crypto');
exports.signup = async (req, res) => {
const email = req.body.email;
User.findOne({email},function(err,user){
if(err) return res.status(500).json({message:err.message});
else if(user) return res.status(403).json({"message":"User exists"});
const password = req.body.password;
const name = req.body.name;
bcrypt.hash(password, 10)
.then(async function(hashed_password) {
const user = await new User({email,name,hashed_password});
user.emailVerificationToken = crypto.randomBytes(20).toString('hex');
user.emailVerificationTokenExpires = Date.now() + 3600000*24;
await user.save(function(err) {
if(!err) {
const resetURL = `http://${req.headers.host}/verifyemail/${user.emailVerificationToken}`;
const sgMail = require('#sendgrid/mail');
sgMail.setApiKey(process.env.SENDGRID_API_KEY);
const msg = {
from: 'admin#pinclone.com',
to: email,
subject: 'Email verification link',
html: `Verify your email here to login to your account`,
};
sgMail.send(msg);
return res.json({message:"verify email address to login"});
}
return res.status(500).send({ message: err.message });
});
})
.catch(function(error){
res.status(500).send({message:error.message});
});
});
};
exports.login = (req,res) => {
const email = req.body.email;
const password = req.body.password;
User.findOne({email},function(err,user) {
if(err) return res.status(500).json({message:err.message});
if(!user) return res.status(403).json({"message":"User does not exists"});
bcrypt.compare(password,user.hashed_password,(err,result) => {
if(result) {
if(user.isVerified)
return res.status(200).json({"message":"successfully logged in"});
else
return res.status(403).json({"message":"user is not verified"});
}
else return res.status(403).json({message: "email address password do not match"});
});
});
};
exports.verifyemail = async (req,res) => {
User.findOneAndUpdate({emailVerificationToken: req.params.token,emailVerificationTokenExpires: { $gt: Date.now() }}, {$set:{isVerified:true}}, {new: true}, (err, user) => {
if (err) {
res.status(403).send({message:"Link invalid or expired"});
// res.status(500).send({message:"Something wrong when updating data!"});
}
if(user) {
res.status(200).send({"message":"email verification successful you can login now!"});
}
});
};
exports.requiresLogin = (req, res, next) => {
if (req.cookies.sid) {
return next();
} else {
var err = new Error('You must be logged in to view this page.');
err.status = 401;
return next(err);
}
};
exports.logout = (req, res) => {
res.clearCookie('sid');
res.send("logout success");
};
exports.getUsers = (req,res) => {
User.find({},function(err,users){
res.send(users);
});
};
You can set session in tab Header with your key.
You can read https://www.toolsqa.com/postman/sessions-in-postman/ for more detail.
You should read at How to use Sessions in Postman? part.
Been trying to get user user object by id from parameters, using User.findById and cannot access this route.
Postman response: Cannot GET /users/get/ with status 404 (not found) which is wierd.
Postman JSON response:
Unexpected '<'
Been using express-promise-router but also tried with default express.Router();
Here is my code:
routes/users.js
const express = require('express');
const router = require('express-promise-router')();
const passport = require('passport');
const router1 = express.Router();
require('../passport');
const { validateBody, schemas } = require('../helpers/routeHelpers');
const UsersController = require('../controllers/users');
const passportSignIn = passport.authenticate('local', { session: false });
const passportJWT = passport.authenticate('jwt', { session: false });
router.route('/signup')
.post(validateBody(schemas.authSchema), UsersController.signUp);
router.route('/signin')
.post(validateBody(schemas.authSchema), passportSignIn, UsersController.signIn);
router.route('/get/:id')
.get(UsersController.getUser);
router.route('/secret')
.get(passportJWT, UsersController.secret);
controllers/users
module.exports = router;
const JWT = require('jsonwebtoken');
const User = require('../models/user');
const { JWT_SECRET } = require('../configuration');
const signToken = (user) => {
return JWT.sign({
iss: 'CodeWorkr',
sub: user.id,
iat: new Date().getTime(), // current time
exp: new Date().setDate(new Date().getDate() + 1) // current time + 1 day ahead
}, JWT_SECRET);
};
module.exports = {
signUp: async (req, res) => {
const { email, password } = req.value.body;
// Check if there is a user with the same email
const foundUser = await User.findOne({ 'local.email': email });
if (foundUser) {
return res.status(403).json({ error: 'Email is already in use' });
}
// Create a new user
const newUser = new User({
method: 'local',
local: {
email: email,
password: password
}
});
await newUser.save();
// Generate the token
const token = signToken(newUser);
// Respond with token
return res.status(200).json({ token });
},
signIn: async (req, res) => {
// Generate token
const token = signToken(req.user);
res.status(200).json({ token });
},
getUser: async (req, res) => {
User.findById(req.params.id)
.then((user) => {
res.status(200).json({ user });
console.log('test');
});
},
secret: async (req, res) => {
console.log('I managed to get here!');
res.json({ secret: 'resource' });
}
};
server.js
const express = require('express');
const morgan = require('morgan');
const bodyParser = require('body-parser');
const cors = require('cors');
const mongoose = require('mongoose');
const passport = require('passport');
const db = require('./configuration/config').mongoURI;
const dbTest = require('./configuration/config').mongoURITest;
mongoose.Promise = global.Promise;
if (process.env.NODE_ENV === 'test') {
mongoose.connect(dbTest, { useMongoClient: true });
} else {
mongoose.connect(db, { useMongoClient: true });
}
const app = express();
app.use(cors());
app.use(passport.initialize());
app.use(passport.session());
// Middlewares moved morgan into if for clear tests
if (!process.env.NODE_ENV === 'test') {
app.use(morgan('dev'));
}
app.use(bodyParser.json());
// Routes
app.use('/users', require('./routes/users'));
// Start the server
const port = process.env.PORT || 3001;
app.listen(port);
console.log(`Server listening at ${port}`);
Other post routes works fine,