Securing node.js RESTful services with JWT Tokens With JQuery
var app = require(‘express’).express();
var jwt = require(‘jwt-simple’);
app.get(‘/tokenfunction(req, res) {
var username = req.headers.username;
var password = req.headers.password;
if(checkUser(username, password)) {
var token = jwt.encode({username: username}, tokenSecret);
res.json({token : token});
} else {
res.json({result: “AuthError”});
}
});
From the browse I can call this endpoint, passing the username and password in on the header, to retrieve the token:
$.ajax({
type: “GET”,
cache: false,
dataType: “json”,
url: “/token/”,
headers: {username:username, password:password},
success: function(token){
setToken(token);
}
});
Back in Node, I can then add some more endpoints to my API, and check the token on each request to ensure it’s valid.
app.get(‘/XYZfunction(req, res){
var decoded = jwt.decode(req.headers.token, tokenSecret);
if (checkUserCanAccessResource(decoded.username)){
…
}
}
The token is read from the header, so you need to add it to each jQuery request:
$.ajax({
type: “GET”,
cache: false,
dataType: “json”,
url: “/XYZ/”,
headers: { token:getToken(); },
success: function(data){
…
}
});
*********************
Invalidating JSON Web Tokens :
https://stackoverflow.com/questions/21978658/invalidating-json-web-tokens/52407314#52407314
https://github.com/dentarg/blog/blob/master/_posts/2014-01-07-angularjs-authentication-with-cookies-vs-token.markdown
466
I too have been researching this question, and while none of the ideas below are complete solutions, they might help others rule out ideas, or provide further ones.
1) Simply remove the token from the client
Obviously this does nothing for server side security, but it does stop an attacker by removing the token from existence (ie. they would have to have stolen the token prior to logout).
2) Create a token blocklist
You could store the invalid tokens until their initial expiry date, and compare them against incoming requests. This seems to negate the reason for going fully token based in the first place though, as you would need to touch the database for every request. The storage size would likely be lower though, as you would only need to store tokens that were between logout & expiry time (this is a gut feeling, and is definitely dependent on context).
3) Just keep token expiry times short and rotate them often
If you keep the token expiry times at short enough intervals, and have the running client keep track and request updates when necessary, number 1 would effectively work as a complete logout system. The problem with this method, is that it makes it impossible to keep the user logged in between closes of the client code (depending on how long you make the expiry interval).
Contingency Plans
If there ever was an emergency, or a user token was compromised, one thing you could do is allow the user to change an underlying user lookup ID with their login credentials. This would render all associated tokens invalid, as the associated user would no longer be able to be found.
I also wanted to note that it is a good idea to include the last login date with the token, so that you are able to enforce a relogin after some distant period of time.
In terms of similarities/differences with regards to attacks using tokens, this post addresses the question: https://github.com/dentarg/blog/blob/master/_posts/2014-01-07-angularjs-authentication-with-cookies-vs-token.markdown
Introduction
There are basically two different ways of implementing server side authentication for apps with a frontend and an API:
The most adopted one, is Cookie-Based Authentication (you can find an example here) that uses server side cookies to authenticate the user on every request.
A newer approach, Token-Based Authentication, relies on a signed token that is sent to the server on each request.
Token based vs. Cookie based
What are the benefits of using a token-based approach?
Cross-domain / CORS: cookies + CORS don’t play well across different domains. A token-based approach allows you to make AJAX calls to any server, on any domain because you use an HTTP header to transmit the user information.
Stateless (a.k.a. Server side scalability): there is no need to keep a session store, the token is a self-contanined entity that conveys all the user information. The rest of the state lives in cookies or local storage on the client side.
CDN: you can serve all the assets of your app from a CDN (e.g. javascript, HTML, images, etc.), and your server side is just the API.
Decoupling: you are not tied to a particular authentication scheme. The token might be generated anywhere, hence your API can be called from anywhere with a single way of authenticating those calls.
Mobile ready: when you start working on a native platform (iOS, Android, Windows 8, etc.) cookies are not ideal when consuming a secure API (you have to deal with cookie containers). Adopting a token-based approach simplifies this a lot.
CSRF: since you are not relying on cookies, you don’t need to protect against cross site requests (e.g. it would not be possible to >iframe< your site, generate a POST request and re-use the existing authentication cookie because there will be none).
Performance: we are not presenting any hard perf benchmarks here, but a network roundtrip (e.g. finding a session on database) is likely to take more time than calculating an HMACSHA256 to validate a token and parsing its contents.
Login page is not an special case: If you are using Protractor to write your functional tests, you don’t need to handle any special case for login.
Standard-based: your API could accepts a standard JSON Web Token (JWT). This is a standard and there are multiple backend libraries (.NET, Ruby, Java, Python, PHP) and companies backing their infrastructure (e.g. Firebase, Google, Microsoft). As an example, Firebase allows their customers to use any authentication mechanism, as long as you generate a JWT with certain pre-defined properties, and signed with the shared secret to call their API.
What’s JSON Web Token? JSON Web Token (JWT, pronounced jot) is a relatively new token format used in space-constrained environments such as HTTP Authorization headers. JWT is architected as a method for transferring security claims based between parties.
Implementation
Server Side
npm install express-jwt jsonwebtoken
Configure the express middleware to protect every call to /api.
var expressJwt = require(‘express-jwt’);
var jwt = require(‘jsonwebtoken’);
// We are going to protect /api routes with JWT
app.use(‘/api’, expressJwt({secret: secret}));
app.use(express.json());
app.use(express.urlencoded());
The angular app will perform a POST through AJAX with the user’s credentials:
app.post(‘/authenticate’, function (req, res) {
//TODO validate req.body.username and req.body.password
//if is invalid, return 401
if (!(req.body.username === ‘john.doe’ && req.body.password === ‘foobar’)) {
res.send(401, ‘Wrong user or password’);
return;
}
var profile = {
first_name: ‘John’,
last_name: ‘Doe’,
email: ‘john@doe.com’,
id: 123
};
// We are sending the profile inside the token
var token = jwt.sign(profile, secret, { expiresInMinutes: 60*5 });
res.json({ token: token });
});
GET’ing a resource named /api/restricted is straight forward. Notice that the credentials check is performed by the expressJwt middleware.
app.get(‘/api/restricted’, function (req, res) {
console.log(‘user ‘ + req.user.email + ‘ is calling /api/restricted’);
res.json({
name: ‘foo’
});
});
Angular Side
The first step on the client side using AngularJS is to retrieve the JWT Token. In order to do that we will need user credentials. We will start by creating a view with a form where the user can input its username and password.
And a controller where to handle the submit action:
myApp.controller(‘UserCtrl’, function ($scope, $http, $window) {
$scope.user = {username: ‘john.doe’, password: ‘foobar’};
$scope.message = ”;
$scope.submit = function () {
$http
.post(‘/authenticate’, $scope.user)
.success(function (data, status, headers, config) {
$window.sessionStorage.token = data.token;
$scope.message = ‘Welcome’;
})
.error(function (data, status, headers, config) {
// Erase the token if the user fails to log in
delete $window.sessionStorage.token;
// Handle login errors here
$scope.message = ‘Error: Invalid user or password’;
});
};
});
Now we have the JWT saved on sessionStorage. If the token is set, we are going to set the Authorization header for every outgoing request done using $http. As value part of that header we are going to use Bearer >token<.
sessionStorage: Although is not supported in all browsers (you can use a polyfill) is a good idea to use it instead of cookies ($cookies, $cookieStore) and localStorage: The data persisted there lives until the browser tab is closed.
myApp.factory(‘authInterceptor’, function ($rootScope, $q, $window) {
return {
request: function (config) {
config.headers = config.headers || {};
if ($window.sessionStorage.token) {
config.headers.Authorization = ‘Bearer ‘ + $window.sessionStorage.token;
}
return config;
},
response: function (response) {
if (response.status === 401) {
// handle the case where the user is not authenticated
}
return response || $q.when(response);
}
};
});
myApp.config(function ($httpProvider) {
$httpProvider.interceptors.push(‘authInterceptor’);
});
After that, we can send a request to a restricted resource:
$http({url: ‘/api/restricted’, method: ‘GET’})
.success(function (data, status, headers, config) {
console.log(data.name); // Should log ‘foo’
});
The server logged to the console:
user foo@bar.com is calling /api/restricted
The source code is here together with an AngularJS seed app.
What’s next?
In upcoming posts we will revisit:
How to handle social authentication?
How to handle session expiration?
UPDATE: we published two new blog posts
[Token based authentication in realtime frameworks like Socket.io]({{ site.baseurl }}/2014/01/15/auth-with-socket-io/)
[10 Things you should know about Tokens]({{ site.baseurl }}/2014/01/27/ten-things-you-should-know-about-tokens-and-cookies/)