Skip to content

LEP 0002: Move to JSON Web Token (JWT) for Authentication

James Booth edited this page Oct 20, 2015 · 1 revision

Right now the landmarker.io server only offers basic auth. This is not bad if we insist on HTTPS, but is still troubling that the username and password is sent in each request to the server.

Why not move to a different form of authentication sooner?

I've held off on adding any more sophisticated form of authentication for a number of reasons.

Firstly, security is something we have to get right, especially if we are annotating sensitive data, and good security is hard. Although Basic Auth has it's weaknesses, at least I can leave the job of securing the username/password on the client side to the browser vendors.

Secondly, modern authentication flows based around oauth/oauth2 are complex. They are really designed for the more challenging use case of 3rd party authentication (Login with Facebook), and I didn't really want to take on a security system that I didn't know all the ins and outs of which is way overcomplicated for landmarker.io's needs.

Thirdly, security adds complexity. The traditional mechanism for secure access to a service involves a session id token that is held on the client, whilst the server needs to track state about sessions. The landmarker.io server is currently pretty simple, and I don't like the idea of adding a bunch of code to it to do all this kind of work.

JSON Web Tokens (JWT)

I think though that there is a modern solution to authentication that is a perfect fit for landmarker.io - JSON Web Tokens (JWTs).

A JWT is a standard for sending a signed trusted message between two parties. The idea is simple:

  1. The user logs in to the API via a PUT to some /auth/ endpoint, specifying their username and password (over HTTPS)
  2. The server synthesises a small JSON payload that records key state that will be needed like the user ID, along with metadata like the time of authentication, or time of expiry of the token. The token is base64 encoded. The encoded token is hashed against a secret and the hash appended to the encoded token - now the server can validate that the entire contents of the token indeed came from the server, as only the server has access to the secret. This token is a JWT.
  3. The client stores the token somewhere, typically local storage. This is the most challenging part of the system from a security point of view.
  4. On all future requests, the client knows to provide the token to the server. As the whole token is URL friendly, this is just sent in the Authentication header. The server checks the hash of the token with it's secret matches the signature, so it knows the message is valid. The userid inside is used to decide on access level to resources.
  5. The token's expiry is fairly aggressive. Prior to expiration, the server can issue a refreshed token to ensure the user remains logged in. If the token does expire, the user will have to log in again.
  6. Logging out means just deleting the client side token.

Note that in this system, the server does not need to track sessions.

For now, this an be implemented with no change to the actual use of authentication on the server. This places us in a far better position to move to a system where users have specific privileges to assets and landmarks.

Challenges

Implementation is actually pretty straightforward. We can use Flask-JWT to do most of the heavy lifting server side. Open questions are:

  • How will we secure the token on the client? Does localStorage seem secure enough? How about XSS?
  • How will the server secret be created, and when? Will the secret be stored?
  • How will the UI on landmarker.io change to accommodate this? Key point - we know need a username and password box on the server screen.
  • How will this impact URL-based logins? Maybe we need a separate username/password modal for such cases? Or just always use a modal (so the flow is tell us your sever, then if necessary we will authenticate)?