Understanding the Use of JSON Web Tokens (JWT)
When looking at current API security standards, we see a frequent use of JSON Web Tokens (JWT) in the different steps of a comprehensive API security scenario. As these tokens must apply cryptographic signing and possibly encryption, you may also come across the term “JOSE security” (JOSE stands for JSON Object Signing and Encryption), though the original JOSE drafts have since been superseded by a number of IETF RFC documents (RFC 7515, 7516, and 7519)
In itself, the common use of JWT security should hardly be surprising, since by now they are an acknowledged way to ensure trust between communicating parties - and allowing them to securely transfer data as part of this trust relationship. Arguably the ultimate proof point is the prevalence of JWT in modern open banking standards like the ones applied in the UK and across the European mainland. As these standards typically cover use cases that require a high level of security indeed, the common use of JWT in here confirms their significance in an API security context.
Related blog post: Implementing SCA for Open Banking APIs
Their significance and ubiquity notwithstanding, I often get the feeling that one may easily get confused by how to use JWT and the various ways in which JWT are applied. This is not that surprising, since in a fairly common scenario, you may see as much as four steps in which JWT tokens play a role (though not necessarily as part of a single session - as will become clear below).
APIs and JWT Security
To start, let’s revisit what JWT are all about. Essentially, JWT offer a mechanism to share security details across security domains (which is why you may sometimes hear them referred to as a JSON security token), for example between parties that are exchanging data through APIs. There are two main aspects to this. First, a JWT can strengthen or confirm the trust relationship between two parties that are exchanging data. To this effect, the JWT contains a collection of meta-data (called "claim set") that identify essential elements and/or constraints of the data exchange (like sending party and intended receiver, JWT expiration time, etc.).
Secondly, JWT can allow for the assertion of the identity associated with the JWT, provided that the receiving party trusts the asserting authority. A typical example would be signing the JWT using an asymmetrical algorithm supported by certificates. Essentially, the trusted certificate allows the receiver to assert the identity represented by the certificate.
JWT allow a sending party to securely transfer structured data in a standardized, JSON-based format to a receiving party. The structure of a JWT, how to use JWT, and specifically the way in which a JWT should be serialized, is specified in detail. Note that the JWT specifications (RFC 7515 on JWS and RFC 7516 on JWE, in particular) are usually extended and/or constrained by the standards that apply to the actual use case in which the JWT is used (for example OAuth2.0, OpenID Connect, Software Statements, etc.).
Related reading:The Akana Solution for Open Banking / PSD2
In general, these standards mandate the use of so-called compact serialization, which implies that the JWT can only contain one protected (JOSE) header (one and only one; additional and/or unprotected headers are not allowed). The general structure depends on whether the JWT is a JWS (signed) or a JWE (encrypted); either way, the serialized token shows as a number of sections separated by a period.
In case of a JWS, for example, the structure consists of the JOSE header, the payload (or claim set) and the signature. In compact serialized format, these three sections will appear “period-separated" as in the below example:
- The first two sections are the header and payload sections. Both are Base64URL-encoded;
- The third section is the signature that is generated over the other sections;
- The sections are separated by a period.
The JSON content of the header and payload sections can easily be made visible for human readers by running them through a Base64 decoder.
All scenarios described below require the use of JWS. All but Dynamic Client Registration also allow the use of JWE (usually, on top of JWS - signing the content first, then encrypting the signed token).
In the case of JWE, the structure is more complex than in the case of JWS: a JWE token contains five distinct sections, again separated by a period. It is beyond the scope of this article to describe JWE in detail; please refer to the excellent resources that can be found on the web.
Next, in order to gain a better understanding of the different ways in which JWT can be used, let’s have a look at an API security scenario in which a client application intends to invoke an API secured with authentication, authorization and additional security policies like OAuth/OIDC and JOSE Security.
JWT #1: Software Statement When Using Dynamic Client Registration
In an OAuth2.0 context, any client must be registered with the Authorization Server (AS). The way in which clients are registered is left open in the OAuth specification (RFC 6749). OAuth software solutions may offer proprietary means for client registration, often allowing for effective, one-click registration procedures. However, depending on the type of client, a different approach may have to be used.
Dynamic Client Registration is a specification that describes a generic way in which clients can dynamically register with the AS, typically in an automated fashion. DCR may introduce the first JWT, in the form of a Software Statement that can be included with the client registration request. The objective of this Software Statement is purely to have the AS assert the identity of the client. The Software Statement is certified by a party that is assumed to be trusted by the AS.
An example as given in https://tools.ietf.org/html/rfc7591 shows what a JWT Software Statement may look like, and how it is contained in the JSON request message:
The right cell shows the decoded version of the JWT Software Statement as contained in the “software statement” claim.
JWT #2: OAuth2.0 JWT Bearer Access Token
Once registered, clients are in a position to call an API that is secured with OAuth2.0. In other words, the client can now request for the necessary tokens from the Authorization Server it has registered with. Obviously, the API will have designated this AS as one of a possible number of servers that can be used to get Access Tokens from (as an API may be associated with multiple authorization domains).
The client now needs to get an Access Token that is required to access the API. This Access Token itself can be a JWT (alternatively, it is an opaque token). One of the main benefits of using JWT for access tokens is that the resource server will be able to assert the identity of the client and its granted authorization directly from the token. In other words, the Resource Server (RS) does not need to go back to the AS to verify this. This has a positive effect on performance. Note, however, that there are constraints; for example, access tokens are recommended to be rather short-lived in this scenario (since the RS is not able to verify whether a token has been revoked or not).
JWT #3: OpenID Connect ID Token
When granting access involves explicit interaction with the resource owner (giving/confirming consent), a common approach these days is to use OpenID Connect as a mechanism for resource-owner authentication. OpenID Connect itself leverages OAuth2.0, essentially introducing a redirect to the user agent and allowing the resource-owner to present its credentials in an out-of-bound fashion: the credentials are only shared with the AS, not with the client.
OpenID Connect introduces the third JWT in our scenario, in the form of an ID Token containing claims that allow the client to authenticate the resource owner. The AS will return both ID Token and Access Token to the client. The ID Token only has significance to the client, whereas the Access Token will be presented by the client to the resource server (which, in case of enterprise API, will typically be fronted by an API Gateway that will first validate the Access Token).
Both Access Token and ID Token are returned as compact-serialized JWT. If the Access Token is accepted, the API Gateway will pass the client request on to the downstream resource server.
JWT #4: JOSE Security Applied to the Message
Yet another JWT may come into play at the level of the message itself. Whereas the JWT described in the previous sections have a rather broad scope (client, API), this fourth example specifically applies to the message exchanged between client and API Gateway/RS (and, possibly, beyond).
Applying JWT security to a message is commonly referred to as JOSE Security. JOSE Security offers a means to secure a message payload by presenting it as a JWS or JWE object and, in this way, provides a way to safeguard message integrity and confidentiality. Furthermore, JOSE Security offers a means for end-to-end message security, as the JWT may be propagated across multiple hops all the way to the ultimate downstream resource.
When applying JOSE Security, the payload of the HTTP message is replaced by a JWS that encapsulates the original payload. The JWS is a JWT with a header that is referred to as the JOSE header. As with all JWT header sections, the JOSE header contains a number of claims (that are also referred to as header parameters). As the spec allows for custom claims, the JOSE header may contain information that can be used to verify the validity of elements other than the message payload, for example HTTP header values or attributes from the certificate associated with the key used to sign the JWS.
Because it may not be optimal to actually include the payload in the JWS (as this would also require the payload to be Base64URL-encoded, causing quite some overhead, particularly with large payloads), the spec also allows for non-encoded, detached payloads. In this scenario, the payload will not be included in the JWS (which is now no longer a JWS in its true sense), but it will have been included in the signing process. The remaining token sections (header and signature) are added to the message in an HTTP header. In order for the recipient to validate the JWS signature, the payload will have to be ‘re-inserted’ to kind of reconstruct the JWS, which is a rather lightweight action.
The table below provides a summary overview of the JWT scenarios discussed in this article:
To conclude, it is clear that JWT security can be effectively put to use to help protect APIs. Today, the various standards involved have been broadly accepted in the API domain, though correctly implementing them may be a rather daunting task. Fortunately, solutions like the Akana API Management Platform provide comprehensive support for the various JWT scenarios outlined in this article.