X

Best Practices from Oracle Development's A‑Team

Validating a JWT bearer token by introspection of its payload in API Platform

Andy Knight
Principal Solution Architect

Use case

The API consumer provides a JWT Bearer token which has a payload that contains a custom key. That key's value is an array of GUIDs. One of these GUIDs has to match a well-known value in order for the API invocation to be deemed valid.

Notes about JWT tokens

My favourite resource for all things to do with JWT is here. If you're unfamiliar with this technology I recommend that as your first point of reference.

For the purposes of this blog post we don't need very much detail about the tokens except that they are made up of 2 or 3 parts. The first 2 parts are Base64 encoded. The first part represents the Header (which we're not interested in), a Payload (which we're very interested in) and a third [optional] part which is the Signature (which we're also not interested in). For the sake of completeness I will mention that the Signature (if present) uses a different form of encoding which is interpreted in a way that is implied by the "alg" key in the Header - e.g. HS256.

Of course, the Signature is very important when it comes to validating the origin of the API consumer - but that's not what we're dealing with here.

Here's an example of a JWT token as it might appear in an HTTP Authorization header:-

Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Here we have a 3-part token. Note how the parts are separated by '.' (period)

The payload

{ "groups": [

"205e5d29-113b-47cd-8170-b75b7e8d7d6c",

"b5106337-d89b-4c35-a975-f490e2397a86"

,"ad5045b6-f87c-4ee0-b1d2-772a19e65438",

"a25804fb-93dd-45ac-8834-9b9e7f0bff0b",

"a67c6f3a-7a8a-4b31-aef2-2680310f1d5d" ] }

Note:- this payload is not represented by the sample token shown above.

Of course, the JSON payload may have many more keys and indeed more or fewer elements in the "groups" array. What we need to do is to extract the "groups" array and iterate over its content in order to check against our well-known value.

 

You'll need to write a Groovy Policy

The code that I will demonstrate here is deliberately concise. It is not necessarily robust. If these techniques are to be used in a production environment, you need to add more checks than you'll see here. For the sake of brevity the underlying assumption of this example is that we're always following the "happy path".

String authorization = context.getApiRequest().getHeader("Authorization") // Get the Authorization header
String token = authorization.substring("Bearer ".length()) // Isolate the JWT token
String payload = token.split("\\.")[1] // Split the token by '.' and get the 2nd element (the payload)
byte[] asBytes = Base64.getDecoder().decode(payload) // Base64 decode payload to a byte array
org.json.JSONObject json = new org.json.JSONObject(new String(asBytes)) // Create a JSON object
org.json.JSONArray array = json.getJSONArray("groups") // Get the "groups" array
String wkv = "4e8ef9be-9c44-4db2-a7c3-ad3ee64cd54" // The well-known-value
for (int i = 0; i < array.length(); i++) { // Iterate over the array and return true if a match is found
  if (wkv.equals(array.getString(i))) {
    return true
  }
}

// If we get here, no match was found so we'll throw an appropriate exception and return HTTP 401 to the consumer
throw new PolicyProcessingException("JWT payload check", 401, "Invalid payload")

This code shows the core techniques required to achieve our stated objective. However, there are 2 caveats that have to be mentioned.

1) As stated earlier, this is not necessarily robust. For example, what if the Authorization header is missing? Or, it exists and is malformed?

2) We have a hard-coded well-known-value (wkv). That might be difficult to maintain. You should probably consider acquiring the wkv by other means

 

Be the first to comment

Comments ( 0 )
Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.Captcha

Recent Content