JSON Web Tokens - attach the token to requests - json

I am trying to set up an authentication via token for my web app. I am using nodejs for the back end and the jwt-simple module to encode/decode the tokens.
I manage to create and handle the tokens from the server to the client. However I struggle to handle the token from the client to the server.
For example, I have a page called profile for which I handle the requests the following way:
app.get('/profile', [bodyParser(), jwtauth], function(req, res) {
res.render('profile.ejs', {
user : req.user // get the user out of session and pass to template
});
});
Where jwtauth is the following:
var jwt = require('jwt-simple');
var User = require('../server/models/user');
 
module.exports = function(req, res, next) {
var token = (req.user && req.user.access_token) || (req.body && req.body.access_token) || (req.query && req.query.access_token) || req.headers['x-access-token'];
if (!token)
return next('invalid or no token');
try {
var decoded = jwt.decode(token, app.get('jwtTokenSecret'));
if (decoded.exp <= Date.now())
return res.end('Access token has expired', 400);
User.findOne({ _id: decoded.id }, function(err, user) {
req.user = user;
});
return next();
} catch (err) {
return next('couldn\'t decode token');
}
};
On the client side I attached the token once the user is logged in the following way:
$(document).ready(function() {
var token = __token;
if (token) {
$.ajaxSetup({
headers: {
'x-access-token': token
}
});
}
});
But if I try to get the url '/profile' in my browser there is no 'x-access-token' in the headers and I get the 'invalid or no token' message.
How can I set the token on the client side so that it is attached to every request to my server?
Many thanks

What are you seeing when you console.log(req.headers) in your jwtauth middleware? Are you deifning $.ajaxSetup more than once? See this post

Related

Passing authentication token to HTML pages

I have a user authentication system that generates an access token upon successful authentication. I need to pass this token to other pages as the header parameter to allow usage of that page.
module.exports.authenticate=function(req,res){
var email=req.body.email;
var password=req.body.password;
connection.query('SELECT * FROM org WHERE email = ?',[email], function (error, results, fields) {
if (error) {
res.json({
status:false,
message:'query error'
})
}else{
if(results.length >0){
if(bcrypt.compareSync(password, results[0].password)){
var access_token = jwt.sign({ id: email }, 'secretpassword123', {expiresIn: 3600});
var decoded = jwt.decode(access_token, 'secretpassword123');
var expires_in = decoded.exp-decoded.iat;
var token_type = "org";
console.log(decoded);
req.headers.access_token = access_token;
res.cookie('access-token', access_token, { expires: new Date(Date.now() + 3600)})
res.status(200).send({ auth: true, access_token: access_token, expires_in, token_type});
}
else{
res.json({
status:false,
message:"Email or password does not match"
});
}
}
else{
connection.query('SELECT * FROM client WHERE email = ?',[email], function (error, results, fields) {
if (error) {
res.json({
status:false,
message:'query error'
})
}else{
if(results.length >0){
if(bcrypt.compareSync(password, results[0].password)){
var access_token = jwt.sign({ id: email }, 'secretpassword123', {expiresIn: 3600});
var decoded = jwt.decode(access_token, 'secretpassword123');
var expires_in = decoded.exp-decoded.iat;
var token_type = "client";
//res.status(200).send({ auth: true, access_token: access_token, expires_in, token_type});
connection.query('UPDATE client SET fingerprint = ?', access_token, function(error, results, fields){
if(error){
console.log(error);
}
else{
console.log(results);
}
})
return res.redirect('/dashboard.html');
}
else{
res.json({
status:false,
message:"Email and password does not match"
});
}
}
else{
res.json({
status:false,
message:"Email does not exist"
});
}
}
});
}
}
});}
I want to pass the access-token to other pages and controllers as a way to authorize.
For example, this is my get-user controller:
module.exports.getUser = function(req,res){
var email = req.body.email;
req.headers.access_token = authenticate.authenticate.access_token
connection.query('SELECT clientid, email, orgid, name, phone, type, role, fingerprint, verified FROM client WHERE email = ?', req.body.email, function(error,results, fields){
if(error){
console.log(error)
res.redirect('/dashboard.html');
}
else{
console.log(req.headers)
console.log(results)
//res.redirect('/dashboard.html');
res.status(200).send(results);
}
})
}
How should I approach this?
I have added res.cookie to the authentication module, and I can see that the cookie gets stored in the browser. But when I try to read the cookie in another page with req.cookies or req.signedCookies it says undefined.
I ended up using localStorage to store the tokens. This is obviously not secure by oAuth standards, but it works. How can I use cookies to get the same functionality as local storage. I need to use the token generated in the authentication module to verify authorization in other pages.
This is usually achieved using cookies. After a cookie is set, it will be attached to every request the browser makes to the server. E.g. if you're using a framework like express, you could do something like
res.cookie('access-token', access_token, { expires: new Date(Date.now() + 300000), httpOnly: true })
But actually this is just a convenience method to add the "Set-Cookie"-HTTP-Header to your response, which causes the browser to create a cookie: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie
Btw, for security reasons you should probably set the 'Secure' and the 'HttpOnly' flags, which make sure, that the cookie is only sent using TLS (HTTPS) and cannot be read by JavaScript respectively. The 'SameSite'-Directive is also useful for preventing CSRF attacks.

How to implement session with express-session?

I’m considering using session in my node.js application.
I understand the following:
- session-cookie use cookie to save session data on the client side
- express-session use cookie to store a sessionID and all the session data are stored on the server side
I’m worried about security so I would choose express-session.
But the Documentation say that by default express-session store data in memory and this not envisageable in production.
So my questions are:
How do you implement session in your application?
Do session stores are not influencing performance?
If not, which session store would you recommend me? (my application is using MySql as database)
Thank you very much for your help.
Regards.
The easiest way I found to manage session data is tokens.
You can easily use 'passport' for expressjs and nodejs.
You can generate a token that is signed in your NodeJS backend with a private key and identifiable by the public key. These tokens can even be revoked.
They are passed into the 'authorization' header as a web standard.
Here is an example of validation I use for extracting and checking a generated token a user has provided.
module.exports.ensureAuthorized = function ensureAuthorized(req, res) {
return new Promise((resolve) => {
let bearerToken;
let bearerHeader = req.headers["authorization"];
if (typeof bearerHeader !== 'undefined') {
let bearer = bearerHeader.split(" ");
bearerToken = bearer[1];
req.token = bearerToken;
this.userPayload(req.token).then((result) => {
if (!result) {
return res.status(403).json({message:"Failed to verify token supplied in authorization header", data: null});
}else{
resolve(result);
}
});
} else {
return res.status(403).json({message:"Failed to supply token in authorization header.", data: null});
}
});
};
And here is my REST API call for a user attempting to login: (that generates a valid token)
let jwt = require('jsonwebtoken');
let config = require('../../misc/config');
global.atob = require("atob");
let mongoose = require('mongoose');
exports.getLogin = function(req, res) {
const btoaAuth = (req.headers.authorization || '').split(' ')[1] || '';
const [username, password, rememberMe] = atob(btoaAuth).toString().split(':');
if(username && password) {
usersModel.findOneAndUpdate({username: username},{lastLogin: new Date(),})
.exec(function (err, userResult) {
if(err) return res.status(500).json({message: "Server failed search users", data: err});
if(!userResult) return res.status(500).json({message: "Username invalid", data: err});
userResult.verifyPassword(password, function(err, isMatch) {
if (err) { return res.status(500).json({message: "Server failed to process user login", data: err});}
// Password did not match
if (!isMatch) { return res.status(403).json({message: "Password incorrect", data: err}); }
// Success
let token = jwt.sign({_id: userResult._id,username: userResult.username, exp: rememberMe === 'true'? Math.floor(Date.now() / 1000) + (60 * 60 * 24 * 365 * 100) : Math.floor(Date.now() / 1000) + (60 * 60) }, config.jwtSecret);
let obj = {};
obj['profile'] = userResult;
obj['profile']['password'] = undefined;
obj['token'] = token;
return res.status(200).json({message: "Successful login", data: obj});
});
});
}else{
return res.status(400).json({message: "Username and password are required", data: req.body});
}
};

Node Function not returning JSON object

This is my Library function file named verifyToken.js
require('dotenv').config();
const CognitoExpress = require("cognito-express");
//Initializing CognitoExpress constructor
const cognitoExpress = new CognitoExpress({
cognitoUserPoolId: process.env.USER_POOL_ID, // User Pool id (created via Console->Cognito Manage User Pool)
region: process.env.REGION, //User Pool Region
tokenUse: "id", //Possible Values: access | id
tokenExpiration: 3600000 //Up to default expiration of 1 hour (3600000 ms)
});
//Our middleware that authenticates all APIs under our 'authenticatedRoute' Router
module.exports = function(){
this.verifyToken=(function(token, req, res) {
//I'm passing in the id token in header under key Token
let TokenFromClient = token;
//Fail if token not present in header.
if (!TokenFromClient) return "Token missing from header";
cognitoExpress.validate(TokenFromClient, function(err, response) {
//If API is not authenticated, Return 401 with error message.
if (err) {
return res.send({success:false, msg:"Token Authenication Error", Error:err});
}
//Else API has been authenticated. Proceed.
return res.send({success:true, msg:"Token Authenication result", result: response});
});
});
}
I am calling this function from controller
var verify_tokenController = {};
//Verify AWS token recieved through client header by calling verifyToken Library function
verify_tokenController.verify = function(req, res) {
//Import verifyToken Library function
require('../library/verifyToken')();
var auth = verifyToken(req.header('Token'), req, res);
console.log(auth);
};
module.exports = verify_tokenController;
But I am always receiving undefined at console. How to return err or response back to the calling controller. Both of the return variable are JSON object type.
It is becuase cognitoExpress.validate is an async operation. it will run and the callback of this function will run. But by the time this all happens. Your following line
console.log(auth); gets hit already and you see undefined becuase you are actually not returning anything from verifyToken method at that time.
This how javascript and async operations work.
You can either use the callback approach but that's not what I would recommend. So I'm modifying your code with promises and see if it works for you.
Your verifyToken.js should look as follow:
require('dotenv').config();
const CognitoExpress = require("cognito-express");
//Initializing CognitoExpress constructor
const cognitoExpress = new CognitoExpress({
cognitoUserPoolId: process.env.USER_POOL_ID, // User Pool id (created via Console->Cognito Manage User Pool)
region: process.env.REGION, //User Pool Region
tokenUse: "id", //Possible Values: access | id
tokenExpiration: 3600000 //Up to default expiration of 1 hour (3600000 ms)
});
//Our middleware that authenticates all APIs under our 'authenticatedRoute' Router
module.exports = function(){
this.verifyToken=(function(token, req, res) {
return new Promise(function(resolve, reject) {
//I'm passing in the id token in header under key Token
let TokenFromClient = token;
//Fail if token not present in header.
if (!TokenFromClient) return "Token missing from header";
cognitoExpress.validate(TokenFromClient, function(err, response) {
//If API is not authenticated, Return 401 with error message.
if (err) {
reject(err);
} else {
//Else API has been authenticated. Proceed.
resolve(response);
}
});
});
});
}
Your controller should look like as follow:
var verify_tokenController = {};
//Verify AWS token recieved through client header by calling verifyToken Library function
verify_tokenController.verify = function(req, res) {
//Import verifyToken Library function
require('../library/verifyToken')();
verifyToken(req.header('Token'), req, res)
.then(function(response){
console.log(resposne, 'response')
res.send({success:true, msg:"Token Authenication result", result: response});
})
.catch(function(err) {
res.send({success:false, msg:"Token Authenication Error", Error:err});
});
};
module.exports = verify_tokenController;
Try the above solution.

Is there a way to set middleware in express (node.js) to have specific values return in every route?

The situation that I am specifically referring to involves the use of json web tokens (jwt). For example how would I automatically add middleware to only return a new json web token if that web token is expired without duplicating code? The sample code below will show what I mean.
var express = require("express");
var jwt = require('jsonwebtoken');
var router = express.Router();
router.use(function(req,res,next){
var token = req.body.token || req.query.token;
if (token) {
jwt.verify(token,"secretTingz",{algorithms:["RS256"]},function(err,decoded){
if (err) {
if (token.expired) {
// HOW DO I GET THIS PIECE OF CODE TO RUN FOR EVERY VALID ROUTE THAT NEEDS A NEW TOKEN WITHOUT DUPLICATING CODE?
var token = jwt.sign({user:"MilnerJenkins"},cert,{algorithm:"RS256",expiresInMinutes:1});
req.token = token;
next();
}else{
return res.json({message:"Failed to authenticate token"});
}
}else{
req.decoded = decoded;
next();
}
})
}else{
return res.status(403).send({
message: "No token!"
});
}
});
apiRoutes.get("/stuff",function(req,res){
var token;
if (req.token) {
token = req.token;
};
res.json({message: "Dope API son!",token:token});
});
apiRoutes.get("/users",function(req,res){
var token;
if (req.token) {
token = req.token;
};
User.find({},function(err,users){
res.json({users: users, token: token});
});
});
As you can see code is being duplicated in both routes with this block:
var token;
if (req.token) {
token = req.token;
};
What can I do with express middleware to prevent this duplication?
The only way you could do this with a "middleware" is by having a handler at the end of your middlware chain that would send the response. That would mean all your handlers would have to pass the request and response down the chain. It would essentially be the last handler before your 404 handler. I do not recommend attempting that. Alternatively you can create a custom response method for your express app.
var app = require('express')();
app.response.myJsonRes = function(obj) {
if (this.req.token) {
obj.token = this.req.token;
}
this.json(obj);
};
app.get('/myRoute', function(req, res) {
// get data somehow
res.myJsonRes(data);
});
You coulld even change the standard json method.
var prevJson = app.response.json;
app.response.json = function(obj) {
if (this.req.token) {
obj.token = this.req.token;
}
prevJson.call(this, obj);
};

passport send error by json

I'm making an app with express + passport and angularJS; I want to be able to send any errors produced from passport (such as username taken or no email provided) by json so my angularJS app can receive these errors in a json response. More specifically right now I want to have a json response to my signup POST method that outputs any errors. I have tried to do this for myself and I've search all over the web and stack overflow I just cannot work this out!
Here is my users route file in express:
var express = require('express');
var router = express.Router();
var isAuthenticated = require('../config/isAuthenticated');
module.exports = function(passport){
router.get('/loggedin', function(req, res){
res.send(req.isAuthenticated() ? req.user : '0');
});
router.post('/signup', passport.authenticate('local-signup', {
successRedirect : '/',
failureRedirect : '/signup',
failureFlash: true
}));
router.post('/login', passport.authenticate('local-login'), function(req, res){
res.send(req.user);
});
router.post('/signout', function(req,res){
req.logout();
res.json({redirect: '/'});
});
router.get('/authtest', isAuthenticated, function(req, res){
res.render('authtest', {user: req.user});
});
return router;
};
This is my passport signup strategy:
passport.use('local-signup', new LocalStrategy({
usernameField : 'username',
passwordField : 'password',
passReqToCallback : true
},
function(req, username, password, done){
process.nextTick(function(){
User.findOne({'local.username' : username}, function(err, user){
if(err) return done(err);
if (user) { //username already exists
return done(null, false, {message: 'Username already exists'});
} else if(!req.body.email) { //no email address provided
return done(null, false, {message: 'You must provide an email address!'});
} else {
var newUser = new User();
newUser.local.username = username;
newUser.generateHash(password, function(err, hash){
if(err) return done(err);
newUser.local.password = hash;
});
newUser.email = req.body.email;
newUser.servers = [];
newUser.save(function(err){
if(err) throw err;
return done(null, newUser);
});
};
});
});
}
));
I know looking at my code right now it looks like I haven't tried to solve this myself at all but this is just my latest working code; I have been stuck at this for the past few days!
Any help would be greatly appreciated :)
According to the current code of passport this is probably achievable by passing custom callback to handle all results of authentiction yourself. This callback is given after options or instead of those.
passport( "local-signup", { ... }, callbackFn );
or
passport( "local-login", callbackFn );
This callback is used in all resulting situations of trying to authenticae. It is thus invoked on processing errors like this:
callbackFn( err )
If (all configured) authentications have failed it is called with
callbackFn( null, false, challenge(s), status(es) )
On successfully having authenticated user the callback is invoked like so:
callbackFn( null, user, infos )
with infos optionally provided by strategies.
Now comes the bottom-side: In either situation passport.authenticate() skips usual processing but instantly invokes provided callback to care for the rest. This includes processing of any options passed in call for passport.authenticate() like flashing messages, preparing session and request for containing authenticated user etc.
Since options given passport.authenticate() are never passed into callback there is actually no obvious reason to use both.
When I was stumbling over the very same problem (linking passport-service with angular-js POST request) I declined to consider use of callback a proper solution. This callback isn't documented. And it doesn't even look quite useful for it isn't passing req, res and next to pass any actual request in callback. Thus it makes very little sense to use it at all and I'd expect it to vanish soon or to change its behaviour quite much.
So the second approach was about trying to figure out why there is a problem in AngularJS. Passport is sending plain text Unauthorized in response with status code 401. AngularJS is trying to parse this as JSON and produces Syntax error. The text Unauthorized results from passprt ending response very simply by invoking
res.statusCode = 401;
res.end(http.STATUS_CODES[res.statusCode]);
Thus a proper workaround might try to replace
either text in http.STATUS_CODES though this is having impact on processing further requests and thus isn't preferrable
or res.end() by an overloaded method acting differently if res.statusCode is 401.
Due to affecting any current request, only, I tried the latter. Replaced res.end() might be used to send any text you want:
router.post('/login',
function(req, res, next) {
var _end = res.end;
res.end = function() {
if (res.statusCode === 401) {
return _end('{"status":"Unauthorized"}');
}
return _end.apply(this, arguments);
};
next();
},
passport.authenticate('local-login'),
function(req, res) {
res.send(req.user);
}
);
Alternatively the replaced method might add previously missing response header information on content type, for this was actually causing issues in AngularJS processing that response as JSON by default.
router.post('/login',
function(req, res, next) {
var _end = res.end;
res.end = function() {
if (res.statusCode === 401) {
res.set("Content-Type", "text/plain");
}
return _end.apply(this, arguments);
};
next();
},
passport.authenticate('local-login'),
function(req, res) {
res.send(req.user);
}
);
Finally, either approach is really just a workaround. I think passport is in the need for revising this annoying limitation.