Decoding JWT using the API Platform Groovy Policy

Introduction

With the explosion of APIs; most of the today’s computing challenges are being driven by it and therefore, new standards had to emerge to make sure that APIs can be used securely, while allowing developers to avoid having to reinvent the wheel every time they implement aspects such as authorization. One good example is JWT (JSON Web Token) which allows API developers to implement authorization – but without requiring that the user credentials be shared across systems. Moreover, JWT can also be signed (by using JWS) and encrypted (by using JWE) which brings even more robustness to the table.

JWT is widely used in the context of OAuth 2.0, which defines a protocol for authorization. Implementing OAuth 2.0 results in an API granting authorization for a given request as long as the request carries a valid token. If the token is valid, the API considers the request authorized and processes it accordingly. This design is simple but very powerful because it eliminates the need to authenticate every single request and more importantly – it eliminates the need to carry the user credentials with the request.

Untitled

Figure 1: OAuth 2.0 Authorization Workflow.

As shown in figure 1, the token is originally obtained from an authorization server that client applications have access to. Once the token is obtained, it can be re-used over multiple API calls as long the token remains valid. Tokens often became invalid due to expiration. Most authorization servers are configured to define an expiration value on every token created. In this context; the API outsources authorization aspects to the authorization server, therefore becoming free of that responsibility. However, the API is still responsible for performing token verification. That can be either implemented directly in the API or, it can be delegated to a gateway layer responsible for exposing the API to the outside world. While nothing can stop a developer from hard coding the token verification in the API code, it is considered a best practice to delegate that task to a gateway layer such as APIPCS. The reason is simple: that way you can promote better agility while building APIs; since this repeatable and error prone coding will be eliminated. Moreover, the APIs will inherit greater robustness regarding token verification since the gateway implements this functionality very efficiently.

Untitled

Figure 2: Using APIPCS to Handle OAuth 2.0.

With APIPCS taking care of authorization using OAuth 2.0, the called API (referred in this context as Service API) can focus on processing the incoming requests. This design would fit perfectly for most use cases if it wasn’t for the fact that some APIs are badly developed since they expect pieces of the authorization data in order to work. In this scenario, how come any given API can work correctly if all the authorization data is encapsulated in a token that by default won’t be send down to the service API?

This blog is going to detail how you can leverage APIPCS to decode tokens and extract pieces of data contained within it. By using the techniques explained here, you can use the extracted data to invoke the backend API with the expected parameters.

Overview of the Solution

In order to illustrate the solution, we are going to use a fictitious use case that might represent a very common situation. Imagine a customer that has this API – known as Incident API – that collects incident information for a given user. The user in this context represents a Support Engineer. A given user can have zero or multiple incidents associated to it, which represents the service requests that he/she is taking care of. Therefore, the API allows applications to list all incidents associated with a particular user. Naturally; to process this type of request the API must receive the user identity as a parameter, which is provided through the resource URI since the request method is GET. Figure 3 shows the design of the Incident API.

Untitled

Figure 3: Design of the Incident API.

Since the API has been exposed to the outside world through APIPCS, all requests are sent to APIPCS that will receive them and take care of authorization. Once authorized, the request is forwarded to the actual API that is going to process it. However, the customer wants to embrace the industry best practices regarding API authorization and decided to implement OAuth 2.0 in APIPCS. Thus, any client application interested in calling the Incident API must send a JWT to APIPCS which will verify it and handle authorization. The user identity, which is a parameter that the Incident API expects, is contained within the JWT and must be extracted and inserted into the request URI, right before APIPCS forwards the request downstream.

This can be accomplished by using a Groovy policy and a few lines of code. Hence; the first step is to insert the Groovy policy in the request pipeline immediately before the Service Request policy, as shown in figure 4.

Untitled

Figure 4: Using the Groovy policy to modify the Request URI.

The Groovy code necessary to extract the user identity from JWT and insert into the request URI is fairly simple, thanks to the structured way in which JWT’s are created. Listing 1 shows the complete code that you should use in the Groovy policy.

def authHeader = null

try {

   authHeader = context.getApiRequest().getHeader("Authorization")
   
} catch (Exception ex) {}

if (authHeader != null) {

   def tokenParts = authHeader.replaceAll("Bearer ", "").split("\\.")
   def bytes = new javax.xml.bind.DatatypeConverter().parseBase64Binary(tokenParts[1])
   def tokenPayload = new String(bytes, "UTF-8")

   def pInst = new oracle.apiplatform.policies.sdk.util.PayloadIntroSpector()
   def userIdentity = pInst.getValue(tokenPayload, "sub", "JSON")
   println "---> Subject from JWT: " + userIdentity

   def newRequestURL = "http://apis.server.io/incidents/" + userIdentity
   context.getServiceRequest().setRequestURL(newRequestURL)

}

Listing 1: Groovy code that extracts the subject (i.e.: user identity) from JWT and insert into the request URI.

Let’s break down the code shown on listing 1 into smaller parts for better understanding.

def authHeader = null

try {

   authHeader = context.getApiRequest().getHeader("Authorization")
   
} catch (Exception ex) {}

The first step is to retrieve the JWT from the request. This is done by reading a header entry called “Authorization”, which will contain the JWT. You might have noticed that we have used a try/catch block for this. The reason is because the method getHeader() is supposed to throw an java.io.IOException. Therefore, you have to use a try/catch block to avoid any compilation errors during the policy deployment. Once we retrieve the header entry we need to test if it really exists, because the API caller might have not sent the header appropriately.

def tokenParts = authHeader.replaceAll("Bearer ", "").split("\\.")

The OAuth 2.0 specification mandates that all JWTs are prefixed with the word “Bearer” plus a space. For this reason, we need to remove that prefix before trying to read the JWT. Hence why the replaceAll() method call during the parsing. Once we remove the prefix, we need to split the resulting String into three expected entries:

 Header: Typically composed of a field that specifies the token type and another field that specifies the hashing algorithm used, such as HMAC, SHA256 or RSA. Usually the same structure is followed by most (if not all) IDM (Identity Management) providers.

 Payload: Used to describe the token claims and additional metadata. Claims are statements about an entity (typically, the user). Typical additional metadata include the fields “sub”, “iss” and “exp” – which is the subject, token issuer and expiration value respectively. However, the complete set of metadata is IDM provider specific.

 Signature: Contains the signature of the token, used to verify if the sender of the JWT is who it says it is and to ensure that the message wasn’t changed along the way. This is usually accomplished by using certificates installed in the target platform.

For the objective of this blog, we will be leveraging only the payload entry. However, it should be obvious at this point that you can apply the same code to read any part of the JWT.

def bytes = new javax.xml.bind.DatatypeConverter().parseBase64Binary(tokenParts[1])
def tokenPayload = new String(bytes, "UTF-8")

Whether if you are trying to read the header, payload or the signature, you will need to perform a decoding of the entry before being able to read it. This is necessary because all entries are Base64 encoded. Hence why we needed to construct the tokenPayload variable using the code above. Also, keep in mind that JWT always structures its entries in the following order: header, payload, signature. Therefore; trying to access the JWT payload means always access the second entry.

def pInst = new oracle.apiplatform.policies.sdk.util.PayloadIntroSpector()
def userIdentity = pInst.getValue(tokenPayload, "sub", "JSON")
println "---> Subject from JWT: " + userIdentity

Keep in mind that every entry of an JWT is essentially a JSON document. After decoding from Base64, you will end up with a JSON document that will need to be parsed to allow reading of the internal fields. For this reason, to read the “sub” field contained in the JSON document we used a utility class from the API Platform Policy SDK called PayloadIntroSpector. This class allows easy parsing of JSON and XML documents and provides a convenient way to read and write values into fields of a given document. For instance, listing 2 shows an example of JSON document that represents the payload entry of a JWT.

{
   "sub":"riferrei",
   "iss":"www.oracle.example.com",
   "oracle.oauth.svc_p_n":"OAuthServiceProfile",
   "iat":1504702583,
   "oracle.oauth.prn.id_type":"LDAP_UID",
   "exp":1504731383,
   "oracle.oauth.tk_context":"user_assertion",
   "aud":[
      "audapics"
   ],
   "prn":"weblogic",
   "jti":"1f257d49-0fa7-420f-b559-e515639aa67a",
   "oracle.oauth.client_origin_id":"oauthtestapp",
   "user.tenant.name":"DefaultDomain",
   "oracle.oauth.id_d_id":"12345678-1234-1234-1234-123456789012"
}

Listing 2: Sample JSON document that represents the payload entry of an JWT.

By executing the code shown in listing 1 against this JSON document, the message “—> Subject from JWT: riferrei” would be printed out in the gateway output.

def newRequestURL = "http://apis.server.io/incidents/" + userIdentity
context.getServiceRequest().setRequestURL(newRequestURL)

Finally; after reading the user identity from the JWT, we can easily modify the request URI by using the methods exposed by the built-in context object. After accessing the service request object, you can use the setRequestURL() method to conveniently override any URL that has been defined in design-time for the service API.

Conclusion

This blog has shown a way to decode JWT by using the Groovy policy available in the API Platform Cloud Service. Although in most use cases you won’t have to worry about JWT decoding since the OAuth 2.0 policy already does this job for authorization enforcement, you may need to do this if the underlying service API requires pieces of data contained within the JWT. Thus, you may find this blog useful for this situations.

Add Your Comment