Tuesday, April 29, 2014

I’m working on an exciting new project at the moment. The main UI element is a management console built with AngularJS that communicates with a HTTP/JSON API built with NancyFX and hosted using the KatanaOWIN self host. I’m quite new to this software stack, having spent the last three years buried in SOA and messaging, but so far it’s all been a joy to work with. AngularJS makes building single page applications so easy, even for a newbie like me, that it almost feels unfair. I love the dependency injection, templating and model binding, and the speed with which you can get up and running. On the server side, NancyFx is perfect for building HTTP/JSON APIs. I really like the design philosophy behind it. The built-in dependency injection, component oriented design, and convention-over-configuration, for example, is exactly how I like build software. OWIN is a huge breakthrough for C# web applications. Decoupling the web server from the web framework is something that should have happened a long time ago, and it’s really nice to finally say goodbye to ASP.NET.

Rather than using cookie based authentication, I’ve decided to go with JSON Web Tokens (JWT). This is a relatively new authorization standard that uses a signed token, transmitted in a request header, rather than the traditional ASP.NET cookie based authorization.

There are quite a few advantages to JWT:

Cross Domain API calls. Because it’s just a header rather than a cookie, you don’t have any of the cross-domain browser problems that you get with cookies. It makes implementing single-sign-on much easier because the app that issues the token doesn’t need to be in any way connected with the app that consumes it. They merely need to have access to the same shared secret encryption key.

No server affinity. Because the token contains all the necessary user identification, there’s no for shared server state – a call to a database or shared session store.

Simple to implement clients. It’s easy to consume the API from other servers, or mobile apps.

So how does it work? The JWT token is a simple string of three ‘.’ separated base 64 encoded values:

The header and payload are simple JSON strings. In the example above the header looks like this:

{ "typ": "JWT", "alg": "HMACSHA256" }

This is defined in the JWT standard. The ‘typ’ is always ‘JWT’, and the ‘alg’ is the hash algorithm used to sign the token (more on this later).

The payload can be any valid JSON, although the standard does define some keys that client and server libraries should respect:

{ "user": "mike", "exp": 123456789}

Here, ‘user’ is a key that I’ve defined, ‘exp’ is defined by the standard and is the expiration time of the token given as a UNIX time value. Being able to pass around any values that are useful to your application is a great benefit, although you obviously don’t want the token to get too large.

The payload is not encrypted, so you shouldn’t put sensitive information it in. The standard does provide an option for encrypting the JWT inside an encrypted wrapper, but for most applications that’s not necessary. In my case, an attacker could get the user of a session and the expiration time, but they wouldn’t be able to generate new tokens without the server side shared-secret.

The token is signed by taking the header and payload, base 64 encoding them, concatenating with ‘.’ and then generating a hash value using the given algorithm. The resulting byte array is also base 64 encoded and concatenated to produce the complete token. Here’s some code (taken from John Sheehan’s JWT project on GitHub) that generates a token. As you can see, it’s not at all complicated:

Implementing JWT authentication and authorization in NancyFx and AngularJS

There are two parts to this: first we need a login API, that takes a username (email in my case) and a password and returns a token, and secondly we need a piece of OWIN middleware that intercepts each request and checks that it has a valid token.

The login Nancy module is pretty straightforward. I took John Sheehan’s code and pasted it straight into my project with a few tweaks, so it was just a question of taking the email and password from the request, validating them against my user store, generating a token and returning it as the response. If the email/password doesn’t validate, I just return 401:

On the AngularJS side, I have a controller that calls the LoginModule API. If the request is successful, it stores the token in the browser’s sessionStorage, it also decodes and stores the payload information in sessionStorage. To update the rest of the application, and allow other components to change state to show a logged in user, it sends an event (via $rootScope.$emit) and then redirects to the application’s root path. If the login request fails, it simply shows a message to inform the user:

Now that we have the JWT token stored in the browser’s sessionStorage, we can use it to ‘sign’ each outgoing API request. To do this we create an interceptor for Angular’s http module. This does two things: on the outbound request it adds an Authorization header ‘Bearer <token>’ if the token is present. This will be decoded by our OWIN middleware to authorize each request. The interceptor also checks the response. If there’s a 401 (unauthorized) response, it simply bumps the user back to the login screen.

The final piece is the OWIN middleware that intercepts each request to the API and validates the JWT token.

We want some parts of the API to be accessible without authorization, such as the login request and the API root, so we maintain a list of exceptions, currently this is just hard-coded, but it could be pulled from some configuration store. When the request comes in, we first check if the path matches any of the exception list items. If it doesn’t we check for the presence of an authorization token. If the token is not present, we cancel the request processing (by not calling the next AppFunc), and return a 401 status code. If we find a JWT token, we attempt to decode it. If the decode fails, we again cancel the request and return 401. If it succeeds, we add some OWIN keys for the ‘userId’ and ‘email’, so that they will be accessible to the rest of the application and allow processing to continue by running the next AppFunc.

So far this is all working very nicely. There are some important missing pieces. I haven’t implemented an expiry key in the JWT token, or expiration checking in the OWIN middleware. When the token expires, it would be nice if there was some algorithm that decides whether to simply issue a new token, or whether to require the user to sign-in again. Security dictates that tokens should expire relatively frequently, but we don’t want to inconvenience the user by asking them to constantly sign in.

JWT is a really nice way of authenticating HTTP/JSON web APIs. It’s definitely worth looking at if you’re building single page applications, or any API-first software.

15 comments:

If anyone is interested I created a library a few days before this post doing something similar. You could use Mikes JWT stuff and implement it in ITokenValidator https://github.com/jchannon/Owin.StatelessAuth

Using a token instead of a cookie has an additional advantage. Using cookies in subdomains, when an attacker puts a cookie for a root domain puts your app in a situation when it receives two cookies, indistinguishable on the server side. Using tokens, and holding its value in your app is free from this danger.I'd consider using cookies for authentication in SPA apps as an obsolete mechanism.

Thanks for this post. However, I was wondering if you stick the token in the browser sessionStorage/localStorage then how do you prevent another malicious web app from iterating through all items in the localStorage and replaying them ?

Just a question here. Sure the Asp.net model was always wrong, and thats a reason why I never worked with it, and having worked with servlets (mvc) it made it even more dificut to accept the Asp.net model. But since you come from a SOA background, didnt you have that server client separation as well? and if so, why use owin?

we've done something very similar with one addition - instead of using the same app key for all users we've also generated (random string) for each user and used as part of the key to sign their messages.

This ensures that even if the app key is compromised no one can still maliciously sign other user requests

--As for the comments re storing the JWT in localStorage - I'd discourage that based on https://www.owasp.org/index.php/HTML5_Security_Cheat_Sheet#Local_Storage

This is the article i was looking for couple of days. I am very newcomer to angularjs & web api world. It would be better for me if there is a download link. Would you please let me download this project?

About the token expiration.. let's say I set it to expire 30 minutes after it was issued. Then in the middleware I'm already checking if the token is valid and will also need to check if it is expired or not.

Now... to prevent the user from logging in again every 30 minutes I could check if the token is about to expire (eg: will expire in 5 minutes or less) and automatically generate a new one that is valid for another 30 minutes and return it with the response.

Is this "automatic token renewal" bad practice? I know it doesn't solve all problems, in some situations the user might take a long time to do something that interacts with the server and in this moment the token will already have expired and a new login will be needed.

@Sarunas Valaskevicius - Who writes these cheat sheets? Local storage is discounted because of XSS and local user access, but the suggested alternatives, cookies and session storage, share the same "vulnerabilities". The technology that might actually help, https with public key pinning, escapes a mention.