JSON Web Token Tutorial: An Example in Laravel and AngularJS

With the rising popularity of single page applications, mobile applications, and RESTful API services, the way web developers write back-end code has changed significantly. With technologies like AngularJS and BackboneJS, we are no longer spending much time building markup, instead we are building APIs that our front-end applications consume. Our back-end is more about business logic and data, while presentation logic is moved exclusively to the front-end or mobile applications. These changes have led to new ways of implementing authentication in modern applications.

Authentication is one of the most important parts of any web application. For decades, cookies and server-based authentication were the easiest solution. However, handling authentication in modern Mobile and Single Page Applications can be tricky, and demand a better approach. The best known solutions to authentication problems for APIs are the OAuth 2.0 and the JSON Web Token (JWT).

What is a JSON Web Token?

A JSON Web Token, or JWT, is used to send information that can be verified and trusted by means of a digital signature. It comprises a compact and URL-safe JSON object, which is cryptographically signed to verify its authenticity, and which can also be encrypted if the payload contains sensitive information.

Because of it’s compact structure, JWT is usually used in HTTP

1
Authorization

headers or URL query parameters.

Structure of a JSON Web Token

A JWT is represented as a sequence of base64url encoded values that are separated by period characters.

JSON web token example in laravel and angularjs

JSON Web Token example:


1
2
3
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJpc3MiOiJ0b3B0YWwuY29tIiwiZXhwIjoxNDI2NDIwODAwLCJodHRwOi8vdG9wdGFsLmNvbS9qd3RfY2xhaW1zL2lzX2FkbWluIjp0cnVlLCJjb21wYW55IjoiVG9wdGFsIiwiYXdlc29tZSI6dHJ1ZX0.
yRQYnWzskCZUxPwaQupWkiUzKELZ49eM7oWxAQK_ZXw

The header contains the metadata for the token and it minimally contains the type of signature and the encryption algorithm.

Example Header


1
2
3
4
{
  “alg”: “HS256”,
  “typ”: “JWT”
}

This JWT Header declares that the encoded object is a JSON Web Token, and that it is signed using the HMAC SHA-256 algorithm.

Once this is base64 encoded, we have the first part of our JWT.


1
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

Payload (Claims)

In the context of JWT, a claim can be defined as a statement about an entity (typically, the user), as well as additional meta data about the token itself. The claim contains the information we want to transmit, and that the server can use to properly handle authentication. There are multiple claims we can provide; these include registered claim names, public claim names and private claim names.

Registered Claims

These are the claims that are registered in the IANA JSON Web Token Claims registry. These claims are not intended to be mandatory but rather to provide a starting point for a set of useful, interoperable claims.

These include:

  • iss: The issuer of the token
  • sub: The subject of the token
  • aud: The audience of the token
  • exp: Token expiration time defined in Unix time
  • nbf: “Not before” time that identifies the time before which the JWT must not be accepted for processing
  • iat: “Issued at” time, in Unix time, at which the token was issued
  • jti: JWT ID claim provides a unique identifier for the JWT

Public Claims

Public claims need to have collision-resistant names. By making the name a URI or URN naming collisions are avoided for JWTs where the sender and receiver are not part of a closed network.

An example of a public claim name could be:

1
https://www.toptal.com/jwt_claims/is_admin

, and the best practice is to place a file at that location describing the claim so that it can be dereferenced for documentation.

Private Claims

Private claim-names may be used in places where JWTs are only exchanged in a closed environment between known systems, such as inside an enterprise. These are claims that we can define ourselves, like user IDs, user roles, or any other information.

Using claim-names that might have conflicting semantic meanings outside of a closed or private system are subject to collision, so use them with caution.

It is important to note that we want to keep a web token as small as possible, so use only necessary data inside public and private claims.

Example Payload


1
2
3
4
5
6
7
{
  “iss”: “toptal.com”,
  “exp”: 1426420800,
  “https://www.toptal.com/jwt_claims/is_admin”: true,
  “company”: “Toptal”,
  “awesome”: true
}

This example payload has two registered claims, one public claim and two private claims. Once it is base64 encoded, we have the second part of our JWT.


1
eyJpc3MiOiJ0b3B0YWwuY29tIiwiZXhwIjoxNDI2NDIwODAwLCJodHRwOi8vdG9wdGFsLmNvbS9qd3RfY2xhaW1zL2lzX2FkbWluIjp0cnVlLCJjb21wYW55IjoiVG9wdGFsIiwiYXdlc29tZSI6dHJ1ZX0

Signature

The JWT standard follows the JSON Web Signature (JWS) specification to generate the final signed token. It is generated by combining the encoded JWT Header and the encoded JWT Payload, and signing it using a strong encryption algorithm, such as HMAC SHA-256. The signature’s secret key is held by the server so it will be able to verify existing tokens and sign new ones.


1
2
$encodedContent = base64UrlEncode(header) + “.” + base64UrlEncode(payload);
$signature = hashHmacSHA256($encodedContent);

This gives us the final part of our JWT.


1
yRQYnWzskCZUxPwaQupWkiUzKELZ49eM7oWxAQK_ZXw

Security and Encryption with JWT

It is critical to use TLS/SSL in conjunction with JWT, to prevent man-in-the-middle attacks. In most cases, this will be sufficient to encrypt the JWT payload if it contains sensitive information. However, if we want to add an additional layer of protection, we can encrypt the JWT payload itself using the JSON Web Encryption (JWE) specification.

Of course, if we want to avoid the additional overhead of using JWE, another option is to simply keep sensitive information in our database, and use our token for additional API calls to the server whenever we need to access sensitive data.

Why the need for Web Tokens?

Before we can see all the benefits of using token authentication, we have to look at the way authentication has been done in the past.

Server-Based Authentication

Server-Based Authentication

Because the HTTP protocol is stateless, there needs to be a mechanism for storing user information and a way to authenticate the user on every subsequent request after login. Most websites use cookies for storing user’s session ID.

How it Works

The browser makes a POST request to the server that contains the user’s identification and password. The server responds with a cookie, which is set on the user’s browser, and includes a session ID to identify the user.

On every subsequent request, the server needs to find that session and deserialize it, because user data is stored on the server.

Drawbacks of Server-Based Authentication

  • Hard to scale: The server needs to create a session for a user and persist it somewhere on the server. This can be done in memory or in a database. If we have a distributed system, we have to make sure that we use a separate session storage that is not coupled to the application server.
  • Cross-origin request sharing (CORS): When using AJAX calls to fetch a resource from another domain (cross-origin) we could run into problems with forbidden requests because, by default, HTTP requests don’t include cookies on cross-origin requests.
  • Coupling with the web framework: When using server-based authentication we are tied to our framework’s authentication scheme. It is really hard, or even impossible, to share session data between different web frameworks written in different programming languages.

Token-Based Authentication

Token-Based Authentication

Token based authentication is stateless, so there is no need to store user information in the session. This gives us the ability to scale our application without worrying where the user has logged in. We can easily use the same token for fetching a secure resource from a domain other than the one we are logged in to.

How JSON Web Tokens Work

A browser or mobile client makes a request to the authentication server containing user login information. The authentication server generates a new JWT access token and returns it to the client. On every request to a restricted resource, the client sends the access token in the query string or

1
Authorization

header. The server then validates the token and, if it’s valid, returns the secure resource to the client.

The authentication server can sign the token using any secure signature method. For example, a symmetric key algorithm such as HMAC SHA-256 can be used if there is a secure channel to share the secret key among all parties. Alternatively, an asymmetric, public-key system, such as RSA, can be used as well, eliminating the need for further key-sharing.

Advantages of Token-Based Authentication

Stateless, easier to scale: The token contains all the information to identify the user, eliminating the need for the session state. If we use a load balancer, we can pass the user to any server, instead of being bound to the same server we logged in on.

Reusability: We can have many separate servers, running on multiple platforms and domains, reusing the same token for authenticating the user. It is easy to build an application that shares permissions with another application.

Security: Since we are not using cookies, we don’t have to protect against cross-site request forgery (CSRF) attacks. We should still encrypt our tokens using JWE if we have to put any sensitive information in them, and transmit our tokens over HTTPS to prevent man-in-the-middle attacks.

Performance: There is no server side lookup to find and deserialize the session on each request. The only thing we have to do is calculate the HMAC SHA-256 to validate the token and parse its content.

A JSON Web Token Example using Laravel 5 and AngularJS

In this tutorial I am going to demonstrate how to implement the basic authentication using JSON Web Tokens in two popular web technologies: Laravel 5 for the backend code and AngularJS for the frontend Single Page Application (SPA) example. (You can find the entire demo here, and the source code in this GitHub repository so that you can follow along with the tutorial.)

This JSON web token example will not use any kind of encryption to ensure the confidentiality of the information transmitted in the claims. In practice this is often okay, because TLS/SSL encrypts the request. However, if the token is going to contain sensitive information, such as the user’s social security number, it should also be encrypted using JWE.

Laravel Backend Example

We will use Laravel to handle user registration, persisting user data to a database and providing some restricted data that needs authentication for the Angular app to consume. We will create an example API subdomain to simulate Cross-origin resource sharing (CORS) as well.

Installation and Project Bootstrapping

In order to use Laravel, we have to install the Composer package manager on our machine. When developing in Laravel I recommend using the Laravel Homestead pre-packaged “box” of Vagrant. It provides us with a complete development environment regardless of our operating system.

The easiest way to bootstrap our Laravel application is to use a Composer package Laravel Installer.


1
composer global require "laravel/installer=~1.1"

Now we are all ready to create a new Laravel project by running

1
laravel new jwt

.

For any questions about this process please refer to the official Laravel documentation.

After we have created the basic Laravel 5 application, we need to set up our

1
Homestead.yaml

, which will configure folder mappings and domains configuration for our local environment.

Example of a

1
Homestead.yaml

file:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
---
ip: "192.168.10.10"
memory: 2048
cpus: 1

authorize: /Users/ttkalec/.ssh/public.psk

keys:
    - /Users/ttkalec/.ssh/private.ppk
folders:
    - map: /coding/jwt
      to: /home/vagrant/coding/jwt
sites:
    - map: jwt.dev
      to: /home/vagrant/coding/jwt/public
    - map: api.jwt.dev
      to: /home/vagrant/coding/jwt/public
variables:
    - key: APP_ENV
      value: local

After we’ve booted up our Vagrant box with the

1
vagrant up

command and logged into it using

1
vagrant ssh

, we navigate to the previously defined project directory. In the example above this would be

1
/home/vagrant/coding/jwt

. We can now run

1
php artisan migrate

command in order to create the necessary user tables in our database.

Installing Composer Dependencies

Fortunately, there is a community of developers working on Laravel and maintaining many great packages that we can reuse and extend our application with. In this example we will use

1
tymon/jwt-auth

, by Sean Tymon, for handling tokens on the server side, and

1
barryvdh/laravel-cors

, by Barry vd. Heuvel, for handling CORS.

jwt-auth

Require the

1
tymon/jwt-auth

package in our

1
composer.json

and update our dependencies.


1
composer require tymon/jwt-auth 0.5.*

Add the

1
JWTAuthServiceProvider

to our

1
app/config/app.php

providers array.


1
<span class="hljs-string">'Tymon\JWTAuth\Providers\JWTAuthServiceProvider'</span>

Next, in

1
app/config/app.php

file, under the

1
aliases

array, we add the

1
JWTAuth

facade.


1
<span class="hljs-string">'JWTAuth'</span> =&gt; <span class="hljs-string">'Tymon\JWTAuth\Facades\JWTAuth'</span>

Finally, we will want to publish the package config using the following command: php artisan config:publish tymon/jwt-auth

JSON Web tokens are encrypted using a secret key. We can generate that key using the

1
php artisan jwt:generate

command. It will be placed inside our

1
config/jwt.php

file. In the production environment, however, we never want to have our passwords or API keys inside configuration files. Instead, we should place them inside server environment variables and reference them in the configuration file with the

1
env

function. For example:


1
'secret' =&gt; env('JWT_SECRET')

We can find out more about this package and all of it’s config settings on Github.

laravel-cors

Require the

1
barryvdh/laravel-cors

package in our

1
composer.json

and update our dependencies.


1
composer require barryvdh/laravel-cors 0.4.x@dev

Add the

1
CorsServiceProvider

to our

1
app/config/app.php

providers array.


1
<span class="hljs-string">'Barryvdh\Cors\CorsServiceProvider'</span>

Then add the middleware to our

1
app/Http/Kernel.php

.


1
<span class="hljs-string">'Barryvdh\Cors\Middleware\HandleCors'</span>

Publish the configuration to a local

1
config/cors.php

file by using the

1
php artisan vendor:publish

command.

Example of a

1
cors.php

file configuration:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<span class="hljs-keyword">return</span> [
   <span class="hljs-string">'defaults'</span> =&gt; [
       <span class="hljs-string">'supportsCredentials'</span> =&gt; <span class="hljs-keyword">false</span>,
       <span class="hljs-string">'allowedOrigins'</span> =&gt; [],
       <span class="hljs-string">'allowedHeaders'</span> =&gt; [],
       <span class="hljs-string">'allowedMethods'</span> =&gt; [],
       <span class="hljs-string">'exposedHeaders'</span> =&gt; [],
       <span class="hljs-string">'maxAge'</span> =&gt; <span class="hljs-number">0</span>,
       <span class="hljs-string">'hosts'</span> =&gt; [],
   ],

   <span class="hljs-string">'paths'</span> =&gt; [
       <span class="hljs-string">'v1/*'</span> =&gt; [
           <span class="hljs-string">'allowedOrigins'</span> =&gt; [<span class="hljs-string">'*'</span>],
           <span class="hljs-string">'allowedHeaders'</span> =&gt; [<span class="hljs-string">'*'</span>],
           <span class="hljs-string">'allowedMethods'</span> =&gt; [<span class="hljs-string">'*'</span>],
           <span class="hljs-string">'maxAge'</span> =&gt; <span class="hljs-number">3600</span>,
       ],
   ],
];

Routing and Handling HTTP Requests

For the sake of brevity, I will put all my code inside the routes.php file that is responsible for Laravel routing and delegating requests to controllers. We would usually create dedicated controllers for handling all our HTTP requests and keep our code modular and clean.

We will load our AngularJS SPA view using


1
2
3
Route::get(<span class="hljs-string">'/'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">()</span> {</span>
   <span class="hljs-keyword">return</span> view(<span class="hljs-string">'spa'</span>);
});

User Registration

When we make a

1
POST

request to

1
/signup

with a username and password, we will try to create a new user and save it to the database. After the user has been created, a JWT is created and returned via JSON response.


1
2
3
4
5
6
7
8
9
10
11
12
13
Route::post(<span class="hljs-string">'/signup'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">()</span> {</span>
   <span class="hljs-variable">$credentials</span> = Input::only(<span class="hljs-string">'email'</span>, <span class="hljs-string">'password'</span>);

   <span class="hljs-keyword">try</span> {
       <span class="hljs-variable">$user</span> = User::create(<span class="hljs-variable">$credentials</span>);
   } <span class="hljs-keyword">catch</span> (<span class="hljs-keyword">Exception</span> <span class="hljs-variable">$e</span>) {
       <span class="hljs-keyword">return</span> Response::json([<span class="hljs-string">'error'</span> =&gt; <span class="hljs-string">'User already exists.'</span>], HttpResponse::HTTP_CONFLICT);
   }

   <span class="hljs-variable">$token</span> = JWTAuth::fromUser(<span class="hljs-variable">$user</span>);

   <span class="hljs-keyword">return</span> Response::json(compact(<span class="hljs-string">'token'</span>));
});

User Sign In

When we make a

1
POST

request to

1
/signin

with a username and password, we verify that the user exists and returns a JWT via the JSON response.


1
2
3
4
5
6
7
8
9
Route::post(<span class="hljs-string">'/signin'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">()</span> {</span>
   <span class="hljs-variable">$credentials</span> = Input::only(<span class="hljs-string">'email'</span>, <span class="hljs-string">'password'</span>);

   <span class="hljs-keyword">if</span> ( ! <span class="hljs-variable">$token</span> = JWTAuth::attempt(<span class="hljs-variable">$credentials</span>)) {
       <span class="hljs-keyword">return</span> Response::json(<span class="hljs-keyword">false</span>, HttpResponse::HTTP_UNAUTHORIZED);
   }

   <span class="hljs-keyword">return</span> Response::json(compact(<span class="hljs-string">'token'</span>));
});

Fetching a Restricted Resource on the Same Domain

Once the user is signed in, we can fetch the restricted resource. I’ve created a route

1
/restricted

that simulates a resource that needs an authenticated user. In order to do this, the request

1
Authorization

header or query string needs to provide the JWT for the backend to verify.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
Route::get(<span class="hljs-string">'/restricted'</span>, [
   <span class="hljs-string">'before'</span> =&gt; <span class="hljs-string">'jwt-auth'</span>,
   <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">()</span> {</span>
       <span class="hljs-variable">$token</span> = JWTAuth::getToken();
       <span class="hljs-variable">$user</span> = JWTAuth::toUser(<span class="hljs-variable">$token</span>);

       <span class="hljs-keyword">return</span> Response::json([
           <span class="hljs-string">'data'</span> =&gt; [
               <span class="hljs-string">'email'</span> =&gt; <span class="hljs-variable">$user</span>-&gt;email,
               <span class="hljs-string">'registered_at'</span> =&gt; <span class="hljs-variable">$user</span>-&gt;created_at-&gt;toDateTimeString()
           ]
       ]);
   }
]);

In this example, I’m using

1
jwt-auth

middleware provided in the

1
jwt-auth

package using

1
'before' =&gt; 'jwt-auth'

. This middleware is used to filter the request and validate the JWT token. If the token is invalid, not present, or expired, the middleware will throw an exception that we can catch.

In Laravel 5, we can catch exceptions using the

1
app/Exceptions/Handler.php

file. Using the

1
render

function we can create HTTP responses based on the thrown exception.


1
2
3
4
5
6
7
8
9
10
11
12
13
<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">render</span><span class="hljs-params">(<span class="hljs-variable">$request</span>, Exception <span class="hljs-variable">$e</span>)</span>
{</span>
  <span class="hljs-keyword">if</span> (<span class="hljs-variable">$e</span> <span class="hljs-keyword">instanceof</span> \Tymon\JWTAuth\Exceptions\TokenInvalidException)
  {
     <span class="hljs-keyword">return</span> response([<span class="hljs-string">'Token is invalid'</span>], <span class="hljs-number">401</span>);
  }
  <span class="hljs-keyword">if</span> (<span class="hljs-variable">$e</span> <span class="hljs-keyword">instanceof</span> \Tymon\JWTAuth\Exceptions\TokenExpiredException)
  {
     <span class="hljs-keyword">return</span> response([<span class="hljs-string">'Token has expired'</span>], <span class="hljs-number">401</span>);
  }

  <span class="hljs-keyword">return</span> <span class="hljs-keyword">parent</span>::render(<span class="hljs-variable">$request</span>, <span class="hljs-variable">$e</span>);
}

If the user is authenticated and the token is valid, we can safely return the restricted data to the frontend via JSON.

Fetching Restricted Resources from the API Subdomain

In the next JSON web token example, we’ll take a different approach for token validation. Instead of using

1
jwt-auth

middleware, we will handle exceptions manually. When we make a

1
POST

request to an API server

1
api.jwt.dev/v1/restricted

, we are making a cross-origin request, and have to enable CORS on the backend. Fortunately, we have already configured CORS in the

1
config/cors.php

file.


1
2
3
4
5
6
7
8
9
10
11
Route::group([<span class="hljs-string">'domain'</span> =&gt; <span class="hljs-string">'api.jwt.dev'</span>, <span class="hljs-string">'prefix'</span> =&gt; <span class="hljs-string">'v1'</span>], <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">()</span> {</span>
   Route::get(<span class="hljs-string">'/restricted'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">()</span> {</span>
       <span class="hljs-keyword">try</span> {
           JWTAuth::parseToken()-&gt;toUser();
       } <span class="hljs-keyword">catch</span> (<span class="hljs-keyword">Exception</span> <span class="hljs-variable">$e</span>) {
           <span class="hljs-keyword">return</span> Response::json([<span class="hljs-string">'error'</span> =&gt; <span class="hljs-variable">$e</span>-&gt;getMessage()], HttpResponse::HTTP_UNAUTHORIZED);
       }

       <span class="hljs-keyword">return</span> [<span class="hljs-string">'data'</span> =&gt; <span class="hljs-string">'This has come from a dedicated API subdomain with restricted access.'</span>];
   });
});

AngularJS Frontend Example

We are using AngularJS as a front-end, relying on the API calls to the Laravel back-end authentication server for user authentication and sample data, plus the API server for cross-origin example data. Once we go to the homepage of our project, the backend will serve the

1
resources/views/spa.blade.php

view that will bootstrap the Angular application.

Here is the folder structure of the Angular app:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public/
  |-- css/
      `-- bootstrap.superhero.min.css
  |-- lib/
      |-- loading-bar.css
      |-- loading-bar.js
      `-- ngStorage.js
  |-- partials/
      |-- home.html
      |-- restricted.html
      |-- signin.html
      `-- signup.html
  `-- scripts/
      |-- app.js
      |-- controllers.js
      `-- services.js

Bootstrapping the Angular Application

1
spa.blade.php

contains the bare essentials needed to run the application. We’ll use Twitter Bootstrap for styling, along with a custom theme from Bootswatch. To have some visual feedback when making an AJAX call, we’ll use the angular-loading-bar script, which intercepts XHR requests and creates a loading bar. In the header section, we have the following stylesheets:


1
2
3
<span class="hljs-tag">&lt;<span class="hljs-title">link</span> <span class="hljs-attribute">rel</span>=<span class="hljs-value">"stylesheet"</span> <span class="hljs-attribute">href</span>=<span class="hljs-value">"//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-title">link</span> <span class="hljs-attribute">rel</span>=<span class="hljs-value">"stylesheet"</span> <span class="hljs-attribute">href</span>=<span class="hljs-value">"/css/bootstrap.superhero.min.css"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-title">link</span> <span class="hljs-attribute">rel</span>=<span class="hljs-value">"stylesheet"</span> <span class="hljs-attribute">href</span>=<span class="hljs-value">"/lib/loading-bar.css"</span>&gt;</span>

The footer of our markup contains references to libraries, as well as our custom scripts for Angular modules, controllers and services.


1
2
3
4
5
6
7
8
9
10
<span class="hljs-tag">&lt;<span class="hljs-title">script</span> <span class="hljs-attribute">src</span>=<span class="hljs-value">"http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.1/jquery.min.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-title">script</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-title">script</span> <span class="hljs-attribute">src</span>=<span class="hljs-value">"http://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/js/bootstrap.min.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-title">script</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-title">script</span> <span class="hljs-attribute">src</span>=<span class="hljs-value">"http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.14/angular.min.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-title">script</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-title">script</span> <span class="hljs-attribute">src</span>=<span class="hljs-value">"http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.14/angular-route.min.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-title">script</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-title">script</span> <span class="hljs-attribute">src</span>=<span class="hljs-value">"/lib/ngStorage.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-title">script</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-title">script</span> <span class="hljs-attribute">src</span>=<span class="hljs-value">"/lib/loading-bar.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-title">script</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-title">script</span> <span class="hljs-attribute">src</span>=<span class="hljs-value">"/scripts/app.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-title">script</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-title">script</span> <span class="hljs-attribute">src</span>=<span class="hljs-value">"/scripts/controllers.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-title">script</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-title">script</span> <span class="hljs-attribute">src</span>=<span class="hljs-value">"/scripts/services.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-title">script</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-title">body</span>&gt;</span>

We are using

1
ngStorage

library for AngularJS, to save tokens into the browser’s local storage, so that we can send it on each request via the

1
Authorization

header.

In the production environment, of course, we would minify and combine all our script files and stylesheets in order to improve performance.

I’ve created a navigation bar using Bootstrap that will change the visibility of appropriate links, depending on the sign-in status of the user. The sign-in status is determined by the presence of a

1
token

variable in the controller’s scope.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<span class="hljs-tag">&lt;<span class="hljs-title">div</span> <span class="hljs-attribute">class</span>=<span class="hljs-value">"navbar-header"</span>&gt;</span>
   <span class="hljs-tag">&lt;<span class="hljs-title">button</span> <span class="hljs-attribute">type</span>=<span class="hljs-value">"button"</span> <span class="hljs-attribute">class</span>=<span class="hljs-value">"navbar-toggle collapsed"</span> <span class="hljs-attribute">data-toggle</span>=<span class="hljs-value">"collapse"</span> <span class="hljs-attribute">data-target</span>=<span class="hljs-value">".navbar-collapse"</span>&gt;</span>
       <span class="hljs-tag">&lt;<span class="hljs-title">span</span> <span class="hljs-attribute">class</span>=<span class="hljs-value">"sr-only"</span>&gt;</span>Toggle navigation<span class="hljs-tag">&lt;/<span class="hljs-title">span</span>&gt;</span>
       <span class="hljs-tag">&lt;<span class="hljs-title">span</span> <span class="hljs-attribute">class</span>=<span class="hljs-value">"icon-bar"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-title">span</span>&gt;</span>
       <span class="hljs-tag">&lt;<span class="hljs-title">span</span> <span class="hljs-attribute">class</span>=<span class="hljs-value">"icon-bar"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-title">span</span>&gt;</span>
       <span class="hljs-tag">&lt;<span class="hljs-title">span</span> <span class="hljs-attribute">class</span>=<span class="hljs-value">"icon-bar"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-title">span</span>&gt;</span>
   <span class="hljs-tag">&lt;/<span class="hljs-title">button</span>&gt;</span>
   <span class="hljs-tag">&lt;<span class="hljs-title">a</span> <span class="hljs-attribute">class</span>=<span class="hljs-value">"navbar-brand"</span> <span class="hljs-attribute">href</span>=<span class="hljs-value">"#"</span>&gt;</span>JWT Angular example<span class="hljs-tag">&lt;/<span class="hljs-title">a</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-title">div</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-title">div</span> <span class="hljs-attribute">class</span>=<span class="hljs-value">"navbar-collapse collapse"</span>&gt;</span>
   <span class="hljs-tag">&lt;<span class="hljs-title">ul</span> <span class="hljs-attribute">class</span>=<span class="hljs-value">"nav navbar-nav navbar-right"</span>&gt;</span>
       <span class="hljs-tag">&lt;<span class="hljs-title">li</span> <span class="hljs-attribute">data-ng-show</span>=<span class="hljs-value">"token"</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-title">a</span> <span class="hljs-attribute">ng-href</span>=<span class="hljs-value">"#/restricted"</span>&gt;</span>Restricted area<span class="hljs-tag">&lt;/<span class="hljs-title">a</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-title">li</span>&gt;</span>
       <span class="hljs-tag">&lt;<span class="hljs-title">li</span> <span class="hljs-attribute">data-ng-hide</span>=<span class="hljs-value">"token"</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-title">a</span> <span class="hljs-attribute">ng-href</span>=<span class="hljs-value">"#/signin"</span>&gt;</span>Sign in<span class="hljs-tag">&lt;/<span class="hljs-title">a</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-title">li</span>&gt;</span>
       <span class="hljs-tag">&lt;<span class="hljs-title">li</span> <span class="hljs-attribute">data-ng-hide</span>=<span class="hljs-value">"token"</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-title">a</span> <span class="hljs-attribute">ng-href</span>=<span class="hljs-value">"#/signup"</span>&gt;</span>Sign up<span class="hljs-tag">&lt;/<span class="hljs-title">a</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-title">li</span>&gt;</span>
       <span class="hljs-tag">&lt;<span class="hljs-title">li</span> <span class="hljs-attribute">data-ng-show</span>=<span class="hljs-value">"token"</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-title">a</span> <span class="hljs-attribute">ng-click</span>=<span class="hljs-value">"logout()"</span>&gt;</span>Logout<span class="hljs-tag">&lt;/<span class="hljs-title">a</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-title">li</span>&gt;</span>
   <span class="hljs-tag">&lt;/<span class="hljs-title">ul</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-title">div</span>&gt;</span>

Routing

We have a file named

1
app.js

which is responsible for configuring all our front end routes.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
angular.module(<span class="hljs-string">'app'</span>, [
   <span class="hljs-string">'ngStorage'</span>,
   <span class="hljs-string">'ngRoute'</span>,
   <span class="hljs-string">'angular-loading-bar'</span>
])
   .constant(<span class="hljs-string">'urls'</span>, {
       BASE: <span class="hljs-string">'http://jwt.dev:8000'</span>,
       BASE_API: <span class="hljs-string">'http://api.jwt.dev:8000/v1'</span>
   })
   .config([<span class="hljs-string">'$routeProvider'</span>, <span class="hljs-string">'$httpProvider'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">($routeProvider, $httpProvider)</span> {</span>
       $routeProvider.
           when(<span class="hljs-string">'/'</span>, {
               templateUrl: <span class="hljs-string">'partials/home.html'</span>,
               controller: <span class="hljs-string">'HomeController'</span>
           }).
           when(<span class="hljs-string">'/signin'</span>, {
               templateUrl: <span class="hljs-string">'partials/signin.html'</span>,
               controller: <span class="hljs-string">'HomeController'</span>
           }).
           when(<span class="hljs-string">'/signup'</span>, {
               templateUrl: <span class="hljs-string">'partials/signup.html'</span>,
               controller: <span class="hljs-string">'HomeController'</span>
           }).
           when(<span class="hljs-string">'/restricted'</span>, {
               templateUrl: <span class="hljs-string">'partials/restricted.html'</span>,
               controller: <span class="hljs-string">'RestrictedController'</span>
           }).
           otherwise({
               redirectTo: <span class="hljs-string">'/'</span>
           });

Here we can see that we have defined four routes that are handled by either

1
HomeController

or

1
RestrictedController

. Every route corresponds to a partial HTML view. We have also defined two constants that contain URLs for our HTTP requests to the backend.

Request Interceptor

The $http service of AngularJS allows us to communicate with the backend and make HTTP requests. In our case we want to intercept every HTTP request and inject it with an

1
Authorization

header containing our JWT if the user is authenticated. We can also use an interceptor to create a global HTTP error handler. Here is an example of our interceptor that injects a token if it’s available in browser’s local storage.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$httpProvider.interceptors.push([<span class="hljs-string">'$q'</span>, <span class="hljs-string">'$location'</span>, <span class="hljs-string">'$localStorage'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">($q, $location, $localStorage)</span> {</span>
   <span class="hljs-keyword">return</span> {
       <span class="hljs-string">'request'</span>: <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">(config)</span> {</span>
           config.headers = config.headers || {};
           <span class="hljs-keyword">if</span> ($localStorage.token) {
               config.headers.Authorization = <span class="hljs-string">'Bearer '</span> + $localStorage.token;
           }
           <span class="hljs-keyword">return</span> config;
       },
       <span class="hljs-string">'responseError'</span>: <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">(response)</span> {</span>
           <span class="hljs-keyword">if</span> (response.status === <span class="hljs-number">401</span> || response.status === <span class="hljs-number">403</span>) {
               $location.path(<span class="hljs-string">'/signin'</span>);
           }
           <span class="hljs-keyword">return</span> $q.reject(response);
       }
   };
}]);

Controllers

In the

1
controllers.js

file, we have defined two controllers for our application:

1
HomeController

and

1
RestrictedController

.

1
HomeController

handles sign-in, sign-up and logout functionality. It passes the username and password data from the sign-in and sign-up forms to the

1
Auth

service, which sends HTTP requests to the backend. It then saves the token to local storage, or shows an error message, depending on the response from the backend.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
angular.module(<span class="hljs-string">'app'</span>)
   .controller(<span class="hljs-string">'HomeController'</span>, [<span class="hljs-string">'$rootScope'</span>, <span class="hljs-string">'$scope'</span>, <span class="hljs-string">'$location'</span>, <span class="hljs-string">'$localStorage'</span>, <span class="hljs-string">'Auth'</span>,
       <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">($rootScope, $scope, $location, $localStorage, Auth)</span> {</span>
           <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">successAuth</span><span class="hljs-params">(res)</span> {</span>
               $localStorage.token = res.token;
               window.location = <span class="hljs-string">"/"</span>;
           }

           $scope.signin = <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">()</span> {</span>
               <span class="hljs-keyword">var</span> formData = {
                   email: $scope.email,
                   password: $scope.password
               };

               Auth.signin(formData, successAuth, <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">()</span> {</span>
                   $rootScope.error = <span class="hljs-string">'Invalid credentials.'</span>;
               })
           };

           $scope.signup = <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">()</span> {</span>
               <span class="hljs-keyword">var</span> formData = {
                   email: $scope.email,
                   password: $scope.password
               };

               Auth.signup(formData, successAuth, <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">()</span> {</span>
                   $rootScope.error = <span class="hljs-string">'Failed to signup'</span>;
               })
           };

           $scope.logout = <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">()</span> {</span>
               Auth.logout(<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">()</span> {</span>
                   window.location = <span class="hljs-string">"/"</span>
               });
           };
           $scope.token = $localStorage.token;
           $scope.tokenClaims = Auth.getTokenClaims();
       }])
1
RestrictedController

behaves the same way, only it fetches the data by using the

1
getRestrictedData

and

1
getApiData

functions on the

1
Data

service.


1
2
3
4
5
6
7
8
9
10
11
12
   .controller('RestrictedController', ['$rootScope', '$scope', 'Data', function ($rootScope, $scope, Data) {
       Data.getRestrictedData(function (res) {
           $scope.data = res.data;
       }, function () {
           $rootScope.error = 'Failed to fetch restricted content.';
       });
       Data.getApiData(function (res) {
           $scope.api = res.data;
       }, function () {
           $rootScope.error = 'Failed to fetch restricted API content.';
       });
   }]);

The backend is responsible for serving the restricted data only if the user is authenticated. This means that in order to respond with the restricted data, the request for that data needs to contain a valid JWT inside its

1
Authorization

header or query string. If that is not the case, the server will respond with a 401 Unauthorized error status code.

Auth Service

The Auth service is responsible for making the sign in and sign up HTTP requests to the backend. If the request is successful, the response contains the signed token, which is then base64 decoded, and the enclosed token claims information is saved into a

1
tokenClaims

variable. This is passed to the controller via the

1
getTokenClaims

function.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
angular.module(<span class="hljs-string">'app'</span>)
   .factory(<span class="hljs-string">'Auth'</span>, [<span class="hljs-string">'$http'</span>, <span class="hljs-string">'$localStorage'</span>, <span class="hljs-string">'urls'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">($http, $localStorage, urls)</span> {</span>
       <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">urlBase64Decode</span><span class="hljs-params">(str)</span> {</span>
           <span class="hljs-keyword">var</span> output = str.replace(<span class="hljs-string">'-'</span>, <span class="hljs-string">'+'</span>).replace(<span class="hljs-string">'_'</span>, <span class="hljs-string">'/'</span>);
           <span class="hljs-keyword">switch</span> (output.length % <span class="hljs-number">4</span>) {
               <span class="hljs-keyword">case</span> <span class="hljs-number">0</span>:
                   <span class="hljs-keyword">break</span>;
               <span class="hljs-keyword">case</span> <span class="hljs-number">2</span>:
                   output += <span class="hljs-string">'=='</span>;
                   <span class="hljs-keyword">break</span>;
               <span class="hljs-keyword">case</span> <span class="hljs-number">3</span>:
                   output += <span class="hljs-string">'='</span>;
                   <span class="hljs-keyword">break</span>;
               <span class="hljs-keyword">default</span>:
                   <span class="hljs-keyword">throw</span> <span class="hljs-string">'Illegal base64url string!'</span>;
           }
           <span class="hljs-keyword">return</span> window.atob(output);
       }

       <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getClaimsFromToken</span><span class="hljs-params">()</span> {</span>
           <span class="hljs-keyword">var</span> token = $localStorage.token;
           <span class="hljs-keyword">var</span> user = {};
           <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> token !== <span class="hljs-string">'undefined'</span>) {
               <span class="hljs-keyword">var</span> encoded = token.split(<span class="hljs-string">'.'</span>)[<span class="hljs-number">1</span>];
               user = <span class="hljs-built_in">JSON</span>.parse(urlBase64Decode(encoded));
           }
           <span class="hljs-keyword">return</span> user;
       }

       <span class="hljs-keyword">var</span> tokenClaims = getClaimsFromToken();

       <span class="hljs-keyword">return</span> {
           signup: <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">(data, success, error)</span> {</span>
               $http.post(urls.BASE + <span class="hljs-string">'/signup'</span>, data).success(success).error(error)
           },
           signin: <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">(data, success, error)</span> {</span>
               $http.post(urls.BASE + <span class="hljs-string">'/signin'</span>, data).success(success).error(error)
           },
           logout: <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">(success)</span> {</span>
               tokenClaims = {};
               <span class="hljs-keyword">delete</span> $localStorage.token;
               success();
           },
           getTokenClaims: <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">()</span> {</span>
               <span class="hljs-keyword">return</span> tokenClaims;
           }
       };
   }
   ]);

Data Service

This is a simple service that makes requests to the authentication server as well as the API server for some dummy restricted data. It makes the request, and delegates success and error callbacks to the controller.


1
2
3
4
5
6
7
8
9
10
11
12
13
angular.module(<span class="hljs-string">'app'</span>)
   .factory(<span class="hljs-string">'Data'</span>, [<span class="hljs-string">'$http'</span>, <span class="hljs-string">'urls'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">($http, urls)</span> {</span>

       <span class="hljs-keyword">return</span> {
           getRestrictedData: <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">(success, error)</span> {</span>
               $http.get(urls.BASE + <span class="hljs-string">'/restricted'</span>).success(success).error(error)
           },
           getApiData: <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">(success, error)</span> {</span>
               $http.get(urls.BASE_API + <span class="hljs-string">'/restricted'</span>).success(success).error(error)
           }
       };
   }
   ]);

Conclusion

Token-based authentication enables us to construct decoupled systems that are not tied to a particular authentication scheme. The token might be generated anywhere and consumed on any system that uses the same secret key for signing the token. They are mobile ready, and do not require us to use cookies.

JSON Web Tokens work across all popular programming languages and are quickly gaining in popularity. They are backed by companies like Google, Microsoft and Zendesk. Their standard specification by Internet Engineering Task Force (IETF) is still in the draft version and may change slightly in the future.

There is still a lot to cover about JWTs, such with how to handle the security details, and refreshing tokens when they expire, but the examples above should demonstrate the basic usage and, more importantly, advantages of using JSON Web Tokens.

HOW TO USE: Paste a JSON snippet or URL in the box or just click here to see an example.

This article originally appeared on Toptal.

1 Comment

  1. Pingback: JSON Web Token Tutorial: An Example in Laravel and AngularJS - Angular News

Leave a Reply

Your email address will not be published. Required fields are marked *