Author
Matías Nieves
What is it for?
Token based authentication is a new security technique for authenticating a user who attempts to log in to a secure system (e.g. server), using a signed token provided by the server.
The authentication is successful if the system can prove that the tokens belong to a valid user.
It is important to note that Json Web Token (JWT) provides signed tokens but not encrypted ones, so passwords or any critical information must not be included in the token unless you encrypt the data (e.g. using JWE).
Why use it?
Here are some advantages of choosing JWT:
- Standard: JWT is becoming a standard, and there a multiple libraries for a lot of languages (Ruby, Java, Python, Node, Backbone). So the integration with your language or technology should be pretty easy.
- Cross-domain / CORS: Since the information is transmitted using an HTTP header, you are able to make AJAX requests to any server or domain.
- Protection from CSRF: The token must be included in every request made to the server, and will be validated by the server. The token is linked to the user’s current session.
- Server side Scalability: The token is self-contained(i.e. contains all the user info), so there’s no need to keep a session store. The rest of the state lives in the client’s local storage.
The token might be generated anywhere, so you are not tied to any specific authentication scheme, decoupling this process from your application.
Warning: since the information is transmitted in an HTTP header and its size is limited, the token size could be an issue.
How does it work?
Let’s see an example of how to use JWT to authenticate a user. In this example we will be using Ruby, Rails and AngularJS.
Server Side
Let’s suppose we have an authentication controller.
When a post to create a session comes to the server, we validate the user and create a new JWT.
class AuthController < ApplicationController # session creation def create user = User.authenticate!(params[:user]) render json: { token: create_token(user) } end private def create_token(user) secret = ‘secret’ # must be an environment variable JWT.encode(user, secret) end end
Then, for every request we have to validate the token before processing it.
class Api::ExampleController < ApplicationController before_action :validate_token def index render json: Example.all.to_json end private def validate_token secret = ‘secret’ # must be an environment variable begin token = request.headers['Authorization'].split(' ').last JWT.decode(token, 'secret') rescue JWT::DecodeError head :unauthorized end end end
Let’s take a look at the token. When we encode a new token, we get this string:
> user = {username: 'example_user', email: 'mail@example.com', id: 123} => {:username=>"example_user", :email=>"mail@example.com", :id=>123} > token = JWT.encode(user, "secret") => "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6ImV4YW1wbGVfdXNlciIsImVtYWlsIjoibWFpbEBleGFtcGxlLmNvbSIsImlkIjoxMjN9.BEtGLs7sZwLztKsitaFTCysmluXMM6yU2-373JGHKWk"
Then, when we decode the token, we retrieve this:
> JWT.decode(token, "secret") => [{"username"=>"example_user", "email"=>"mail@example.com", "id"=>123}, {"typ"=>"JWT", "alg"=>"HS256"}] [{"username"=>"example_user", "email"=>"mail@example.com", "id"=>123} # user information {"typ"=>"JWT", "alg"=>"HS256"}] # token information, typ is the content type and alg is the encode algorithm used
If we want, we could add some claims to the token:
- exp : identifies the expiration time on or after the token MUST NOT be accepted for processing.
- nbf : identifies the time before the token MUST NOT be accepted for processing.
- iat : identifies the time when the JWT was issued.
Client Side
An example of a sign in request to the server from the Angular app.
myApp.controller('UserCtrl', function ($scope, $http, $window) { $scope.user = {username: ''example_user'', password: 'pass'}; $scope.submit = function () { $http .post('/sessions', $scope.user) .success(function (data, status, headers, config) { $window.sessionStorage.token = data.token; }) }; });
Once we have the token, we can make any request to the server using the token.
angular.module('exampleApp') .service('Example', function Leagues( ENV, $http, Session ) { return { get: function( callback ){ $http.defaults.headers.common['Authorization'] = "Bearer " + $window.sessionStorage.token; $http.get( ENV.apiEndPoint + '/example') .success(function(data, status, headers, config) { callback(data); }) } } });
Reference Links
- http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html
- https://github.com/hokaccha/node-jwt-simple
- https://github.com/jwt/ruby-jwt
- https://jwt.io/