How can i send and get jsonweb token in a browser? I need to send on every forbidden page token after user has signed in.
I have code and everything works fine in a postman but when i try in a browser req.headers["authorization"] is always undefined. I was also trying to res.set({'token': 'some token'}); and then request in the next page but still undefined. There is a lot tutorial with postman but I can figure out how can I do this in a browser.
Here is my code:
app.post("/api/login", function(req, res) {
var user = { id: 3 };
var token = jwt.sign({ user }, 'secret');
res.json({token: token});
});
app.get('/api/protected', ensureToken, function(req, res) {
jwt.verify(req.token, 'secret_key_goes_here', function(err, data) {
if (err) {
res.sendStatus(403);
} else {
res.json({
description: 'Protected information. Congrats!'
});
}
});
});
function ensureToken(req, res, next) {
const token = req.headers["authorization"];
console.log(token);
if (typeof bearerHeader !== 'undefined') {
console.log('ok');
const berarer = bearerHeader.split(" ");
const bearerToken = berarer[1];
req.token = bearerToken;
next();
} else {
console.log('no');
res.sendStatus(403);
}
}
Please, any suggestion.
Thanks
Related
Here I'm carrying out the GET method to list the data by authenticating the login credentials, but when I pass the token in value in the header it directly catch the error message. is anything I'm doing wrong?
Authentication Middleware - authentication.js
const jwt = require("jsonwebtoken");
const authenticate = (req, res, next) => {
const access_token = req.headers["authorization"];
if (!access_token) return res.status(401).send("Access denied! no token provided.");
try {
const decoded = jwt.verify(access_token, "SECRET_JWT_CODE");
req.receive = decoded;
next();
} catch (error) {
res.status(400).send("invalid token.");
}
};
module.exports = authenticate;
console.log(req.headers)
GET method
const authenticate = require("./authentication.js");
router.get("/admin", authenticate, async (req, res) => {
try {
const receive = await SomeModel.find();
res.json(receive);
} catch (err) {
res.send(err);
}
});
login
router.post("/admin/sign_in", (req, res) => {
if (!req.body.email || !req.body.password) {
res.json({ error: "email and password is required" });
return;
}
login
.findOne({ email: req.body.email })
.then((admin) => {
if (!admin) {
res.json({ err: "user does not exist" });
} else {
if (!bcrypt.compareSync(req.body.password, admin.password)){
res.json({ err: "password does not match" });
} else {
const token = jwt.sign(
{
id: admin._id,
email: admin.email,
},
SECRET_JWT_CODE
);
res.json({
responseMessage: "Everything worked as expected",
access_token: token,
});
}
}
})
.catch((err) => {
err.message;
});
});
The token is in the form Bearer <token> so you need to split it before verifying it:
const jwt = require("jsonwebtoken");
const authenticate = (req, res, next) => {
const access_token = req.headers["Authorization"];
if (!access_token) return res.status(401).send("Access denied! no token provided.");
const splitToken = access_token.split(' ');
if (splitToken.length !== 2) return res.status(401).send("Access denied! invalid token.");
const token = splitToken[1];
try {
const decoded = jwt.verify(token, "SECRET_JWT_CODE");
req.receive = decoded;
next();
} catch (error) {
res.status(400).send("invalid token.");
}
};
module.exports = authenticate;
Also, make sure that you pass the token via the Authorization tab in Postman.
It should be available in req.headers["Authorization"] (capitalized) in the expected Bearer <token> format.
I'm having a hard time connecting the last dots building a role based access control api in Express.
Following this tutorial and implementing onto my existing program, but I think I am missing the last step and after countless tutorials analysis paralysis has set in. I have since scaled back all my necessary code to what I think is the bare minimum.
Currently I am able to create a new user and save them to the mongoose database. I can see the hash by bcrypt is doing its thing and I can see the token being generated in the response after signing up. However as soon as I navigate to a new page after signup or login, for eg the users own id page/user/:userId as per tutorial, I keep getting You need to be logged in. I know I need to check for a token on every request but my question is, why doesn't it seem like the middleware is checking for the token or something is holding it back?
Since the token is shown in the json reponse surely I should be able to check for the tokens existence with the next get request at for eg the /user/:userId page? Isn't that the idea? Or is the browser just showing the response but I still need to actually store it? I don't understand where it goes to so to speak..
Any advice? Or is this a session thing? I know its a bit hard without all the code but if anyone could spot anything relevant so that I could research my next steps I would much appreciate it!
First this middleware in app.js
app.use(express.json());
app.use(express.urlencoded({extended: true}));
app.use('/', async (req, res, next) => {
if (req.headers['x-access-token']) {
try {
const accessToken = req.headers['x-access-token'];
const {userId, exp} = await jwt.verify(accessToken, process.env.JWT_SECRET);
console.log('token verified'); // not printing to console
// If token has expired
if (exp < Date.now().valueOf() / 1000) {
return res.status(401).json({
error: 'JWT token has expired, please login to obtain a new one',
});
}
res.locals.loggedInUser = await User.findById(userId);
next();
} catch (error) {
next(error);
}
} else {
next();
}
});
app.use('/', userRoutes);
I have built the roles using the module access-control which is required
const AccessControl = require('accesscontrol');
const ac = new AccessControl();
exports.roles = (function() {
ac.grant('basic')
.readOwn('profile')
.updateOwn('profile');
ac.grant('supervisor')
.extend('basic')
.readAny('profile');
ac.grant('admin')
.extend('basic')
.extend('supervisor')
.updateAny('profile')
.deleteAny('profile');
return ac;
})();
routes examples as per tutorial.
router.get('/signup', (req, res, next) => {
res.render('signup', {
viewTitle: 'User SignUp',
});
});
router.post('/signup', userController.signup);
router.get('/login', (req, res, next) => {
res.render('login', {
viewTitle: 'User Login - WTCT OPS',
});
});
router.post('/login', userController.login );
router.get('/add', userController.allowIfLoggedin, userController.grantAccess('readAny', 'profile'), userController.add);
router.get('/users', userController.allowIfLoggedin, userController.grantAccess('readAny', 'profile'), userController.getUsers);
router.get('/user/:userId', userController.allowIfLoggedin, userController.getUser);
router.put('/user/:userId', userController.allowIfLoggedin, userController.grantAccess('updateAny', 'profile'), userController.updateUser);
router.delete('/user/:userId', userController.allowIfLoggedin, userController.grantAccess('deleteAny', 'profile'), userController.deleteUser);
relevant part of controller
async function hashPassword(password) {
return await bcrypt.hash(password, 10);
}
async function validatePassword(plainPassword, hashedPassword) {
return await bcrypt.compare(plainPassword, hashedPassword);
}
// grant access depending on useraccess role
exports.grantAccess = function(action, resource) {
return async (req, res, next) => {
try {
const permission = roles.can(req.user.role)[action](resource);
if (!permission.granted) {
return res.status(401).json({
error: 'You don\'t have enough permission to perform this action',
});
}
next();
} catch (error) {
next(error);
}
};
};
// allow actions if logged in
exports.allowIfLoggedin = async (req, res, next) => {
try {
const user = res.locals.loggedInUser;
if (!user) {
return res.status(401).json({
error: 'You need to be logged in to access this route',
});
}
req.user = user;
next();
} catch (error) {
next(error);
}
};
// sign up
exports.signup = async (req, res, next) => {
try {
const {role, email, password} = req.body;
const hashedPassword = await hashPassword(password);
const newUser = new User({email, password: hashedPassword, role: role || 'basic'});
const accessToken = jwt.sign({userId: newUser._id}, process.env.JWT_SECRET, {
expiresIn: '1d',
});
newUser.accessToken = accessToken;
await newUser.save();
res.send({
data: newUser,
message: 'You have signed up successfully',
});
} catch (error) {
next(error);
}
};
exports.login = async (req, res, next) => {
try {
const {email, password} = req.body;
const user = await User.findOne({email});
if (!user) return next(new Error('Email does not exist'));
const validPassword = await validatePassword(password, user.password);
if (!validPassword) return next(new Error('Password is not correct'));
const accessToken = jwt.sign({userId: user._id}, process.env.JWT_SECRET, {
expiresIn: '1d',
});
await User.findByIdAndUpdate(user._id, {accessToken});
res.status(200).json({
data: {email: user.email, role: user.role},
accessToken,
});
} catch (error) {
next(error);
}
};
// get one user
exports.getUser = async (req, res, next) => {
try {
const userId = req.params.userId;
const user = await User.findById(userId);
if (!user) return next(new Error('User does not exist'));
// console.log(req.params);
res.send(200).json({
data: user,
});
} catch (error) {
next(error);
}
};
Why when trying to post to the endpoint /user/:userId is the middleware not checking for the token?
Thank you for any advice!
Update:
So far I have tried to removed the / from app.use. I saw I made that mistake now, but also tried removing it from the app.use(userRoutes); middleware to make it apply to all http requests but no luck.
app.use(async (req, res, next) => {
if (req.headers['x-access-token']) {
try {
const accessToken = req.headers['x-access-token'];
const {userId, exp} = await jwt.verify(accessToken, process.env.JWT_SECRET);
// If token has expired
if (exp < Date.now().valueOf() / 1000) {
return res.status(401).json({
error: 'JWT token has expired, please login to obtain a new one',
});
}
res.locals.loggedInUser = await User.findById(userId);
// console.log('Time:', Date.now());
next();
} catch (error) {
next(error);
}
} else {
next();
}
});
app.use(userRoutes);
I also thought that maybe because my server makes http requests in the backend maybe that was causing a problem in setting the x-access-token header? So I tried to change the x-access-token mw to use router.use on all routes but still nothing. I don't understand what I am missing. And just to be sure I'm not missing something fundamental, since I am using the JWT I do not need to use local storage or cookies to allow for browsing between pages while logged in since I can use the token set in the header, correct?
Thanks again for any advice!
That's because your middleware is only tied to the / route. Remove it if you want it to be used for every route. Take a look at the ExpressJS Docs regarding middleware.
I have a login system with tokens. When logging in, it checks whether such a user exists, doesn't have information about current user during the login session. What's the easiest way to check it and send response to frontend?
Routes:
function verifyToken(req, res, next) {
if (!req.headers.authorization) {
return res.status(401).send('Unauthorized request');
}
let token = req.headers.authorization.split(' ')[1];
if (token === 'null') {
return res.status(401).send('Unauthorized request');
}
let payload = jwt.verify(token, 'secretKey');
if (!payload) {
return res.status(401).send('Unauthorized request');
}
req.userId = payload.subject;
next();
}
router.post('/register', (req, res) => {
let userData = req.body;
let user = new User(userData);
user.save((error, registeredUser) => {
if (error) {
console.log(error);
} else {
let payload = { subject: registeredUser._id };
let token = jwt.sign(payload, 'secretKey');
res.status(200).send({ token });
}
})
})
router.post('/login', (req, res) => {
let userData = req.body;
User.findOne({ email: userData.email }, (error, user) => {
if (error) {
console.log(error);
} else {
if (!user) {
res.status(401).send('Invalid email');
} else
if (user.password !== userData.password) {
res.status(401).send('Invalid password')
} else {
let payload = { subject: user._id };
let token = jwt.sign(payload, 'secretKey');
res.status(200).send({ token });
}
}
})
})
You could try using a middleware to retrieve the token from the Authorization header and retrieve the userId from there, the middleware could look something like this:
const decodeToken = (token) => {
return jwt.verify(token, 'secretKey', (err, decoded) => {
if (err) {
return undefined;
}
return decoded;
});
};
const authorize = (req, res, next) => {
if (!req.headers.authorization) {
return res.status(401).send({message: 'UNAUTHORIZED'});
}
const token = req.headers.authorization.split(' ')[1];
if (!token) {
return res.status(401).send({message: 'UNAUTHORIZED'});
}
const decodedToken = decodeToken(token);
if (!decodedToken) {
return res.status(401).send({message: 'UNAUTHORIZED'});
}
req.userId = decodedToken.subject;
next();
};
module.exports = authorize;
Hope this helps you, if not, I hope you find your answer :)
EDIT
To use the middleware you only need to add it to your route, I´ll leave you an example with a get request:
const authorize = require('../middleware/authorize');
router.get('/someroute', authorize, (req, res) => {
// authorize will verify the token and attach the userId to your request
// so to use it you'll only need to call req.userId
// something like this
console.log('current logged user id', req.userId);
// YOUR CODE HERE
});
I am working on authentication in nodeJs. I have created successfully login API and it works well on the postman. I'm stuck on client side. It does not set token on headers. I am using the passport, jwt for authentication.
My code is:
app.post('/login', (req, res, next) => {
var name = {
name: req.body.name,
password: req.body.password
}
// let m = '';
// console.log(name)
request({
url: "http://localhost:3000/api/login",
method: "POST",
json: true, // <--Very important!!!
body: name
}, function (error, response) {
if (response.body.error == true) {
req.flash('errorMsg', response.body.message);
res.redirect('/');
}
else {
// localStorage.setItem('token', response.body.token);
// console.log(localStorage.getItem('token'))
// req.headers['authorization'] = response.body.token;
// res.setHeader('authorization', response.body.token);
// req.session['token'] = response.body.token;
// console.log(req.session['token'])
// res.set({
// 'Content-Type': 'text/plain',
// 'authorization':response.body.token
// });
// res.setHeader('authorization', response.body.token);
// req.headers['authorization'] = response.body.token;
res.redirect('/secret');
next();
}
});
// console.log(m);
});
and my middleware is:
app.use((req, res, next) => {
var token = req.body.token || req.session['token'] || req.query.token || req.headers['x-access-token'] || localStorage.getItem('token');
req.headers['authorization'] = token;
console.log(req.session['token'], token)
console.log(req.headers['authorization'], config.jwtSecret);
if (token) {
jwt.verify(token, config.jwtSecret, (err, decoded) => {
if (err) {
res.json({
'message': 'Failed to authenticate user'
});
} else {
req.decoded = decoded;
next();
}
});
} else {
// logger.warn('Unauthorized');
return res.sendStatus(401);
}
console.log(req.headers['authorization'])
});
I have tried all possible to set the token in headers but it didn't work well. If I get my token on app.use middleware then I can verify token easily but it didn't allow to set my token.
How can I do this??
Best way!
router.get('/your-route', async (req, res) => {
//...
res.setHeader('your-key', 'your-value');
//..
})
You can see output in header tab in browser or postman
your-key: your-value
I am trying to make a GET request to my mLab database. I pass a JWT token with the request and logged it on both the client and server. It reads correctly on the client but shows null on the server. Any help would be much appreciated. I am using Node.js and Angular.
I am pretty new to this, so I apologize in advance if the mistake is obvious.
Here is the server's GET route:
router.get('/', (req, res, next) => {
var decoded = jwt.decode(req.query.token);
console.log(decoded);
console.log('employees');
if(decoded) {
return Company.find({_id: decoded._id})
.populate('user', 'firstName')
.exec(function(err, company) {
if (err) {
return res.status(500).json({
title: 'An error occurred',
error: err
});
}
res.status(200).json({
message: 'Success',
obj: company
});
});
} else {
return res.status(401).json({
title: 'Not authenticated',
error: {
message: 'Please create an account or sign in'
}
});
}
console.log(company);
});
Here is the client:
getEmployees() {
const token = localStorage.getItem('token')
? '?token=' + localStorage.getItem('token')
: '';
console.log(token);
return this.http.get('http://localhost:3000/company' + token)
.map((response: Response) => {
const employees = response.json().obj;
console.log(employees);
let transformedEmployees: Employee[] = [];
for (let employee of employees) {
transformedEmployees.push(new Employee(
employee.firstName,
employee.lastName,
employee.email,
employee.password,
employee.jobTitle,
employee.isAdmin,
employee.tasks
));
}
console.log(transformedEmployees)
this.employees = transformedEmployees;
return transformedEmployees;
})
.catch((error: Response) => {
this.errorService.handleError(error.json());
return Observable.throw(error.json())
});
}
You should NOT be placing your token in the Authorization header of your request.
You use an express middleware to decode the token:
const myDecoder = (req, res, next) => {
req.user = jwt.decode(req.headers.authorization);
next();
}
Then, you place in your route:
router.get('/', myDecoder, (req, res) => {
let user = req.user;
console.log(user);
console.log('employees');
if (user) { blah }
...
Should not be passing the entire token over the URL itself.