Tuesday, June 03, 2014

Using JSON Web Tokens authentication in Angular Web API request


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.
In this post we will implement API authentication based on standard JSON Web Token (JWT).


ASP.Net Web API


There is an open project JwtAuthForWebAPI implement a DelegatingHandler that creates a new ClaimsPrincipal based on the incoming token and assigns it to the current thread.

1. You can install the NuGet package for this library at https://www.nuget.org/packages/JwtAuthForWebAPI/.

2. Register the handler in your WebApiConfig.Register() method:


var tokenBuilder = new SecurityTokenBuilder();
var configReader = new ConfigurationReader();

var jwtHandlerSharedKey = new JwtAuthenticationMessageHandler
{
    AllowedAudience = configReader.AllowedAudience,
    Issuer = configReader.Issuer,
    SigningToken = tokenBuilder.CreateFromKey(configReader.SymmetricKey),
    PrincipalTransformer = new SimplePrincipalTransformer()
};

config.MessageHandlers.Add(jwtHandlerSharedKey);

3. Secure your controllers and/or their actions with the [Authorize] attribute - per standard ASP.NET authorization practices.

[Authorize]
public string Get(string id)
{
...
}

4. Assign a token when user login with correct password.

var configReader = new ConfigurationReader();

var key = Convert.FromBase64String(configReader.SymmetricKey);
var credentials = new SigningCredentials(
    new InMemorySymmetricSecurityKey(key),
    "http://www.w3.org/2001/04/xmldsig-more#hmac-sha256",
    "http://www.w3.org/2001/04/xmlenc#sha256");

var tokenDescriptor = new SecurityTokenDescriptor
{
    Subject = new ClaimsIdentity(new[]
    {
        new Claim(ClaimTypes.Name, "bsmith"), 
        new Claim(ClaimTypes.GivenName, "Bob"),
        new Claim(ClaimTypes.Surname, "Smith"),
        new Claim(ClaimTypes.Role, "Customer Service")
    }),
    TokenIssuerName = "corp",
    AppliesToAddress = configReader.AllowedAudience,
    SigningCredentials = credentials
};

var tokenHandler = new JwtSecurityTokenHandler();
var token = tokenHandler.CreateToken(tokenDescriptor);
var tokenString = tokenHandler.WriteToken(token);

return tokenString;

Angular Client Side



1. The first step on the client side using AngularJS is to retrieve the JWT Token and have the JWT saved on sessionStorage.

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';
      });
  };
});


2. Add Authorization header for every outgoing request:


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');
});

Reference:
1.  Cookies vs Tokens. Getting auth right with Angular.JS
2. Techniques for authentication in AngularJS applications
3. JwtAuthForWebAPI