09 Mar 2016 - by 'Maurits van der Schee'
This post will discuss how to use JSON Web Token (JWT) to separate your authentication from you API endpoints and how to do so securely in a Single Page Application (SPA).
A JSON Web Token is an alternative for the combination of a session cookie and a server side session object. It typically contains the authentication and authorization "claims". The user identifier (a technical primary key or something uniquely identifying the user, such as an email address) is almost always on of the claims (called the "subject"). Typically you may also have the role of that user in the application as a claim. These claims are sent to the server using a "Authorization" request header and can be trusted, because they are signed by the issuer (typically your authentication service).
The biggest difference between traditional session cookies and JWT based session is that the session data is considered to be limited in size (due to the maximum header size of 8kb) and that it should be read-only. You may argue that technically the cookie can be overwritten by the receiver, but I would strongly recommend against it. It is much cleaner to write the token only once and let your authentication service be the only one doing this. Also you may run into concurrency issues similar to the ones you run into when you do not lock your session.
The disadvantage of using JWT based sessions is that there is not XSS protection comparable to the "HttpOnly" flag on the session cookie. This is due to the nature of the JWT, which purpose it is to let another domain handle the authentication for your application. This problem's effect may be limited when you separate the login part of your web application into a stand-alone SPA. This SPA must call a "login" API to convert the JWT from the "Authorization" header into a traditional (HttpOnly) cookie for all used API endpoints. Note that this session does not copy the "expiration" time from the JWT as it should be set to expire at the end of the session. This means that there is also no need to refresh the token. The expiration time of the JWT may be as short as a few seconds, enough to "login" to all API endpoints.
A JWT should be stored in the HTML5 sessionStorage in the "login" SPA. This storage is isolated per browser tab and does therefor not need any additional CSRF protection. In the "application" SPA you are using a traditional cookie based session, to protect you against XSS. The downside of this is that you now need CSRF protection. If you are using Angular you can use it's built-in CSRF protection. On every "$http" request it will add a "X-XSRF-TOKEN" header with the content of the "XSRF-TOKEN" cookie it has received from the same domain. The API endpoint needs to set this cookie in the "login" API call (and store it's value in the just created session). On subsequent calls the API endpoint should be compare the "X-XSRF-TOKEN" header value to the session stored "XSRF-TOKEN" value.
As long as you are not creating a web application, the usage of JWT is quite simple. As soon as you start to take XSS into account, things become more difficult. Isolate your login functionality into a separate SPA and use cookie based sessions for the rest of your application to reduce the risk of stolen tokens.