OAuth2 and OpenID Connect

OAuth2 and OpenID Connect are both pervasive technologies in modern identity systems. While verification of JSON web tokens issued by these systems is documented in the policy reference, the policy examples below aim to cover some other common use cases.

Metadata discovery

Rather than storing endpoints and other metadata as part of policy data, the authorization server metadata endpoint may be queried for this data.

package oidc

issuers = {"https://issuer1.example.com", "https://issuer2.example.com"}

metadata_discovery(issuer) = http.send({
    "url": concat("", [issuers[issuer], "/.well-known/openid-configuration"]),
    "method": "GET",
    "force_cache": true,
    "force_cache_duration_seconds": 86400 # Cache response for 24 hours
}).body

claims := jwt.decode(input.token)[1]
metadata = metadata_discovery(claims.iss)

jwks_endpoint := metadata.jwks_uri
token_endpoint := metadata.token_endpoint

Token verification using JWKS endpoint

Below example uses the keys published at the JWKS endpoint of the authorization server for token verification.

package oidc

jwks_request(url) = http.send({
    "url": url,
    "method": "GET",
    "force_cache": true,
    "force_cache_duration_seconds": 3600 # Cache response for an hour
})

jwks = jwks_request("https://authorization-server.example.com/jwks").body

verified = io.jwt.verify_rs256(input.token, json.marshal(jwks))

Key rotation

Use the keys published at the JWKS endpoint of the authorization server for token verification, with key rotation taken into account.

package oidc

jwks_request(url) = http.send({
    "url": url,
    "method": "GET",
    "force_cache": true,
    "force_cache_duration_seconds": 3600
})

jwt_unverified := io.jwt.decode(input.token)
jwt_header := jwt_unverified[0]

jwks = jwks_cached {
    jwks_cached := jwks_request("https://authorization-server.example.com/jwks").body
    jwt_header.kid == jwks_cached.keys[_].kid
} else = jwks_rotated {
    # Add query param to second request to avoid both getting the same cache key
    jwks_rotated := jwks_request("https://authorization-server.example.com/jwks?r=1").body
}

jwt_verified = jwt_unverified {
    io.jwt.verify_rs256(input.token, json.marshal(jwks))
}

claims_verified := jwt_verified[1]

Token retrieval

Programmatically obtain an OAuth2 access token following the client credentials or resource owner password credential flow.

package oauth2

token = t {
    response := http.send({
        "url": "https://authorization-server.example.com/token",
        "method": "POST",
        "headers": {
            "Content-Type": "application/x-www-form-urlencoded",
            "Authorization": concat(" ", [
                "Basic",
                base64.encode(sprintf("%v:%v", [client_id, client_secret]))
            ]),
        },
        # To use the resource owner password credentials flow, change grant_type
        # to "password" and add username and password parameters to the body
        "raw_body": "grant_type=client_credentials",
        "force_cache": true,
        "force_cache_duration_seconds": 3595, # Given an `expires_in` value of 3600
    })
    response.status_code == 200

    t := response.body.access_token
}