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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With