blog single gear
Engineering

Opaque Access Tokens in Cloud Foundry

revoked

Opaque Tokens

show them nothing but what is shown to him.” – While Sigmund Freud was talking about the doctor being opaque to the patient, much of the same can be applied to the world of identity and security.

With the v3.3.0 release we introduce opaque and revocable tokens to clients and applications that use the UAA as an authorization server.

The Cloud Foundry UAA, User Account and Authentication server is an OAuth 2 authorization server implementation, and responsible for a great deal of identity management in the Cloud Foundry platform.

History of the UAA Access Token

When the UAA was first developed, a lot of the specifications that we have today were still forming. The Oauth 2 specification was being lively, and in many colors, debated and the OpenID Connect spec was not yet started, and JWT was not formalized.
Rather than waiting for specifications to finalize, and to satisfy a wide range of requirement, several decisions in the early development stages of the UAA were made. The UAA has been, and continues to, deliver a hybrid of access and id tokens in JSON Web Token, JWT, format.

JSON Web Tokens

A JWT token consists of three base 64 encoded JSON parts, header, body and a signature,  delimited by the dot (.). As an example

eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9
.
eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFt
cGxlLmNvbS9pc19yb290Ijp0cnVlfQ
.
dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk

JWT Header

The UAA token header contains three fields.

{
  "alg": "HS256",
  "kid": "legacy-token-key",
  "typ": "JWT"
}
  • alg: the signing algorithm, either RSA (asymmetric) or MAC (symmetric) signing keys
  • kid: the key identifier for the key used to sign. A client can download the signing keys from the /token_keys URL and do local validation of the token
  • typ: always set to JWT.

JWT Payload

The meat of the access token is in the body, or the payload, and is often referred to as claims. The claims are key value pairs in a JSON map. Take note that the size of the token can grow rather fast as new claims or values are added.

{
  "jti": "b9b9c3f233014cc7b623e9934efac2b4",
  "sub": "eea9d9ac-9b11-4e11-b345-656f4456e5f3",
  "scope": [
    "uaa.user"
  ],
  "client_id": "cf",
  "cid": "cf",
  "azp": "cf",
  "grant_type": "password",
  "user_id": "eea9d9ac-9b11-4e11-b345-656f4456e5f3",
  "origin": "uaa",
  "user_name": "marissa",
  "email": "[email protected]",
  "auth_time": 1461094433,
  "rev_sig": "bc0e9131",
  "iat": 1461094433,
  "exp": 1461137633,
  "iss": "http://localhost:8080/uaa/oauth/token",
  "zid": "uaa",
  "aud": [
    "cf",
    "uaa"
  ]
}

Most of these claims have their origin in one of the specifications referenced above, and some are custom to the UAA.

JWT Signature

The last section contains a signature for the claims, and can be used to verify that the token has not been tampered with.

Observations

What we can observe from the default UAA access tokens is, that they truly are hybrid tokens in that a token is an:

  1. access_token

    – the scope claim is used for authorization by a resource server

  2. id_token

    – the token contains PII, personally identifiable information

  3. JWT format

    – required by id_tokens

Benefits

Through this model the UAA was, early on, able to address many requirements  the three major specifications later cemented into standards. A resource server can authorize a request, a client application can establish an authenticated session and automatically retrieve basic user information, and both parties can validate the token through an offline mechanism in that the data is available to both of them.

Limitations

The model, however doesn’t come without limitations. Each of the benefits can be countered with a limitation, there are three that stand out, but they are not the only ones

  1. Personally identifiable information in an access token is a privacy issue
  2. Scopes, permissions, gives out information about the endpoint that is being accessed
  3. Off line validation removes the ability to revoke the token
  4. And my personal favorite, token explosion

Token Explosion

This limitation does warrant an honorary mention, mostly because of its name. The Oauth tokens are intended to be carried as a value to the Authorization HTTP header.

Most proxies and web servers limit the size, in bytes, of what they will attempt to read from the request when parsing the headers. Apache Tomcat, for example, defaults to 8192 bytes.explosion

A token larger than than this, will cause the proxy or web server to simply reject the request and return a 400 Bad Request response.

Opaque Tokens to the Rescue

Ongoing development

The UAA has a functional OpenID Connect 1.0 core implementation, also referred to as OP for OpenID Connect Provider, and responds to all the various response_type parameters, in particular the id_token response type.

So a client application that wishes to learn more about the user, can receive an access token and an id token, and only forward the access token to a resource server.

Getting rid of PII in the access token itself can be achieved using a UAA configuration option

jwt:
  token:
    claims:
      exclude:
      - user_name
      - email

This option was first introduced to address token explosion for clients that had a very large authorities claim, causing the token to spill over in size.

We’ve since then run into situations where the scope claim caused token explosion and we had yet not addressed the requirement for the resource server to validate the token with the UAA.

Token revocation does exists in the UAA today, where a user can revoke all user tokens, an action that also takes place if the user changes her password. A client can revoke all its issued tokens and the UAA can revoke all tokens issued by changing the signature key.
All of these revocations are still conditional, it requires the resource server to actually invoke the /check_token endpoint. If the resource server is doing offline validation, there may be a delay between token revocation and when that takes into effect.

Opaque Tokens

In it’s simplest form, you can grab a token from a running UAA server using a simple curl command.

curl 'http://localhost:8080/uaa/oauth/token' -i -X POST \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -u 'login:loginsecret' \
  -d 'grant_type=client_credentials' \
  -d 'token_format=opaque' \
  -d 'response_type=token'

Results in (scopes reduced for readability purposes)

{
  "access_token":"1d52703551c84012a7b0af0930092ea6",
  "token_type":"bearer",
  "expires_in":43199,
  "scope":"clients.read emails.write scim.userids password.write ...",
  "jti":"1d52703551c84012a7b0af0930092ea6"
}

With UAA 3.3.0 we introduced the token_format option, with only one possible value, opaque.

This flag is currently unique to the UAA, and as long as the OAuth library used by your applications supports tacking on parameters to the token request, you can start using it instantly.
An opaque token contains no identifiable information. When a resource server receives it, it requires to invoke the UAA /check_token. This presents the UAA a unique opportunity to reject the request if the token has been revoked. It also introduces an option to reject the validation if the wrong party invokes the endpoint. Today, many applications simply pass the same access token amongst themselves, not knowing that they are using a bad practice of credentials sharing.

Example Opaque User Access Token with ID Token

curl 'http://localhost:8080/uaa/oauth/token' -i -X POST \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -u 'cf:' \
  -d 'username=marissa' \
  -d 'password=koala' \
  -d 'grant_type=password' \
  -d 'token_format=opaque' \
  -d 'response_type=id_token+token'

Returns an id_token, access_token and a refresh_token.

{
  "access_token":"b33361885bf643b49a3db3ba8fd8ece9",
  "token_type":"bearer",
  "id_token": "eyJhbGciOiJIUzI1NiIsImtpZCI6ImxlZ2FjeS10b2tlbi1rZXkiLCJ0eXAiOiJKV1QifQ.eyJzdWIiOiIwZmRkYzg3Mi1mNDRjLTRiMjEtYTNhMS02Y2MyZjRlODg5NGQiLCJ1c2VyX25hbWUiOiJtYXJpc3NhIiwib3JpZ2luIjoidWFhIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL3VhYS9vYXV0aC90b2tlbiIsInJldm9jYWJsZSI6dHJ1ZSwiY2xpZW50X2lkIjoiY2YiLCJhdWQiOlsiY2YiXSwiemlkIjoidWFhIiwiZ3JhbnRfdHlwZSI6InBhc3N3b3JkIiwidXNlcl9pZCI6IjBmZGRjODcyLWY0NGMtNGIyMS1hM2ExLTZjYzJmNGU4ODk0ZCIsImF6cCI6ImNmIiwic2NvcGUiOlsib3BlbmlkIl0sImF1dGhfdGltZSI6MTQ2MTE4ODYyMywiZXhwIjoxNDYxMjMxODIzLCJpYXQiOjE0NjExODg2MjMsImp0aSI6ImIzMzM2MTg4NWJmNjQzYjQ5YTNkYjNiYThmZDhlY2U5IiwiZW1haWwiOiJtYXJpc3NhQHRlc3Qub3JnIiwicmV2X3NpZyI6ImFlODQyZGViIiwiY2lkIjoiY2YifQ.fpdDai7xfLBTiBKIfAOzSR0FEKpYJJNhwL-Xrz0IcnM",
  "refresh_token":"b33361885bf643b49a3db3ba8fd8ece9-r",
  "expires_in":43199,
  "scope":"scim.userids openid uaa.user cloud_controller.read password.write...",
  "jti":"b33361885bf643b49a3db3ba8fd8ece9"
}

To see how JWT translates into values, copy the id_token value and paste it into a decoder, like https://jwt.io.

Summary

By introducing opaque tokens we believe that we have addressed some of the most pressing issues with the current token design. Issues that were not addressed before, like token explosion or credentials sharing, or not intuitively addressed, such as hiding PII, personally identifiable information, in access tokens.
Stay tuned for our next post, the UAA is an OpenID Connect relying party. Authenticate with your OIDC provider of choice!