https://gdstechnology.blog.gov.uk/2016/05/06/testing-json-web-tokens-on-notify/

Testing JSON Web Tokens on Notify

Lock and Key

At GDS we often use REST application programming interfaces (APIs) as a core part of application design. These REST based APIs have data and services that can be accessed by authenticated users.

On GOV.UK Notify we’ve decided to authenticate API calls in a different way to other GDS projects and this post will explain why we’ve taken this new approach.

Common authentication schemes

The most common authentication schemes, Bearer Tokens and Basic Authentication, provide client secrets (passwords) or tokens with every user request, frequently in the request header. The API uses these tokens on the server side to manage access to the API.

The major drawback of these schemes is that the tokens and client secrets are often long lived, and the same ones often provide access to multiple resources. This leads to vulnerabilities because if an attacker steals a token, they can do anything users can do. Tokens can be stolen from the servers, or from the intermediary network security devices that proxy the Transport Level Security (TLS) - these are common in government departments.

Assessing authentication schemes for GOV.UK Notify

When choosing an authentication scheme for Notify, we drew up a checklist of the features we wanted. These were to have:

  • no secrets sent with API requests (secrets should only be shared once on creation)
  • short lived tokens (each token should expire in a short time period)
  • limited in scope (every token should only allow users to perform a specific action)
  • simple to use functionality

We assessed different authentication options, which were:

Firstly we considered OAuth 2.0, which includes support for ‘refresh tokens’, helping us meet some of our needs outlined above. We considered this approach, but the tokens need to be regularly refreshed and although there are obvious benefits to this, there are also notable downsides.

Refreshing can be as simple as calling the OAuth server with a refresh token (without requiring any user interaction) but this can leave your API open to security vulnerabilities. Refreshing with user involvement is more complex for both the client side and the server, and we decided to discount OAuth 2.0 as an authentication method because it didn’t neatly match our anticipated interaction model for Notify. We also want Notify to have a low barrier to entry.

Next we researched the AWS authentication schemes with the possible intent of creating something similar for Notify. The AWS scheme requires you to use your stored AWS API Key and API client secret (both associated with your user ID) to generate an authentication token. To further protect the data in-transit, you also use some of the API request elements to calculate a request signature (according to AWS rules). This is referred to as ‘signing’ the request.

Creating a completely custom authentication scheme for Notify would tick a lot of boxes but it would also require lots of resources. We don’t want to to develop our own Notify authentication standard if we don’t have to.

Finally we looked at JSON Web Tokens (JWT). This authentication method has advantages in that it is is an open industry standard, and has supported client implementations across languages. Information is transmitted between parties as a JSON object and is digitally signed using a secret or a public/private key. JWT was ideal for Notify because secrets are never passed with requests.

Another benefit of JWT is that it allows a certain degree of customisation. You can assign clients a series of ‘claims’, which then dictate how the server will deal with requests. Some claims are standard, but others can be custom ones. For Notify we used standard claims to identify the client and a timestamp (see the first two bullets below). We then added custom claims to sign the request path and the request body (see the last two bullets).

These claims ensured that tokens:

  • are made by clients for each request, with no secrets passed between client and server
  • expire in a defined period
  • are only authorised to hit specific URL paths, eg a token for URL A that is used on a request to URL B will be rejected
  • can only be used inside the defined request body and changes to the JSON payload invalidate the token

If someone acquires a JWT Notify token, they’d only be able to send the same message to the same person for a very short time. They could neither perform another action or renew that token.

Using JSON Web Tokens for our private beta

The benefit of the simpler Basic and Bearer token authentication schemes is their ease of use. Developers generally use things like cURL or REST client browser extensions into which they copy and paste a token and play with an API. This is not possible with a JWT token, which has to be generated each time a request is made.

We decided to use JWT tokens for Notify’s private beta because it provides solutions to all our requirements in that it shares no secrets with API requests and has single use tokens, limited functional scope, as well as a wealth of client libraries and documentation.

However when we ran a hackday to test developer integration we learnt very quickly that our customised JWT tokens caused barriers to developers using Notify. JWT itself was not widely known and the way we asked developers to sign the request path and its body caused confusion.

Moving forward

A core requirement of the Government as a Platform programme is to make all components as easy as possible to adopt for the rest of government. We felt that the first implementation of our JWT tokens did not meet this aim so we have amended our approach.

The major change we have made is removing our requirement to have tokens limited in scope. We no longer ask developers integrating with Notify to sign API request paths or bodies. We feel that tokens limited in time, sent only over HTTPS, with no secrets on the request, meet most of our needs for security, while ensuring Notify continues to have a low barrier to entry.

We will revisit our authentication methods as the API industry matures. There are discussions taking place to develop standards in the authentication area and we will be following these with interest.

You can follow Martyn on Twitter, sign up now for email updates from this blog or subscribe to the feed.

If this sounds like a good place to work, take a look at Working for GDS - we're usually in search of talented people to come and join the team.

2 comments

  1. Comment by Sam Mulube posted on

    Interesting article Martyn, though your description of how you are using JWT really confused me till I thought about for a bit. Just to clarify are you talking about signing JWTs using one of the asymmetric public/private key methods rather than HMAC, meaning a client is able to generate a token on their side, sign it with their private key, and the server is able to validate that token using the client's public key?

    I was also slightly taken aback by the line:

    > "This is not possible with a JWT token, which has to be generated each time a request is made."

    Isn't that purely an implementation decision on your side? From my limited understanding of JWT I don't think there is anything in the spec that requires that to be the case is there?

    Reply
  2. Comment by martyninglis posted on

    Thanks Sam, sorry for confusion, it was a bit of a tricky post to right. With the signing process we just use HMAC, as provided by the JWT libraries. Our main problem was that we confused our users by asking them to also sign, using the same process (the request path and body).

    On the regeneration point, you're correct. In our case we enforce a very tight timeout, and when we asked users to sign the path and payload, it was very much the case that the tokens were single use (unless you were sending the same message to the same person in a very short window).

    Hope this helps.

    Reply

Leave a comment

We only ask for your email address so we know you're a real person