Published on

Let’s stop using JWTs client side

Authors
  • avatar
    Name
    Sedky Haider
    Twitter

OAuth2, OIDC, and their foundation, the JWT, has been an industry standard for many years, with no sign of slowing down.

There are two types of access tokens in the OAuth flow, opaque and JWT (JWS more precisely). The problem with latter is the inherent leakiness. There is a massive debate amongst the community — where some consider JWT for auth as insecure as it leaks information (by b64 decoding the body), others argue that you shouldn’t put sensitive info in there at all.

what is your point

Introduce: the split token flow comes in, a solution that satisfies both camps.

This flow suggests to use the signature of the JWT access token on the client side, and storing the header and claims of the JWT server side. Thus, we get the flexibility of JWTs by being able to store session information in JWT claims, and we get the security of an Opaque access token.

That's really it. There are some IAM solutions shouting about it as well, such as Curity.

Here's a link to a guide that shows a real world example implemented with Tyk.

Or, the tl;dr version

A regular example of password flow using client credentials to get an access token:

$ curl https://my-auth-server/auth/realms/tyk/protocol/openid-connect/token \
-d grant_type=client_credentials \
-d client_id=efd952c8-df3a-4cf5–98e6–868133839433 \
-d client_secret=0ede3532-f042–4120-bece-225e55a4a2d6

{
    "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJlbWFpbCI6ImhlbGxvQHdvcmxkLmNvbSJ9.EwIaRgq4go4R2M2z7AADywZ2ToxG4gDMoG4SQ1X3GJ0",
    "expires_in":300,
    "not-before-policy":0,
    "scope": "email profile",
    ...
}

So here we get a JWT access token back:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJlbWFpbCI6ImhlbGxvQHdvcmxkLmNvbSJ9.MEwIaRgq4go4R2M2z7AADywZ2ToxG4gDMoG4SQ1X3GJ0

We can plug that into jwt.io and see the Base64 decoded payload: jwt example

Clearly, the access token can contain sensitive information that we don’t want to leak.

Split Token Flow

The split token flow adds an API gateway or service such as Tyk in the middle that does the following:

split token flow diagram source

  1. Service/API Gateway receives the request from client
  2. forwards it the IAM solution & receives the access token
  3. Splits it, returning the signature to the client only, and storing in a local cache/key value store, where the key is the signature and the value is the whole JWT

So the request now looks like this:

$ curl http://api-gw.com/auth/token \
-d grant_type=client_credentials \
-d client_id=efd952c8-df3a-4cf5–98e6–868133839433 \
-d client_secret=0ede3532-f042–4120-bece-225e55a4a2d6

{
    "access_token":"MEwIaRgq4go4R2M2z7AADywZ2ToxG4gDMoG4SQ1X3GJ0",
    "expires_in":300,
    "not-before-policy":0,
    "scope": "email profile",
    ...
}

Notice that the returned response is considerably smaller than before. As it’s just the signature of the original access token.

Now, our clients use this new opaque token to access APIs, and then the JWT is reconstructed in our API Gateway, far away from the prying eyes of any clients!

Would you use a flow like this today? Let me know :)