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.

  1. package oidc
  2. issuers = {"https://issuer1.example.com", "https://issuer2.example.com"}
  3. metadata_discovery(issuer) = http.send({
  4. "url": concat("", issuers[issuer], "/.well-known/openid-configuration",
  5. "method": "GET",
  6. "force_cache": true,
  7. "force_cache_duration_seconds": 86400 # Cache response for 24 hours
  8. }).body
  9. claims := jwt.decode(input.token)[1]
  10. metadata = metadata_discovery(claims.iss)
  11. jwks_endpoint := metadata.jwks_uri
  12. 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.

  1. package oidc
  2. jwks_request(url) = http.send({
  3. "url": url,
  4. "method": "GET",
  5. "force_cache": true,
  6. "force_cache_duration_seconds": 3600 # Cache response for an hour
  7. })
  8. jwks = jwks_request("https://authorization-server.example.com/jwks").body
  9. 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.

  1. package oidc
  2. jwks_request(url) = http.send({
  3. "url": url,
  4. "method": "GET",
  5. "force_cache": true,
  6. "force_cache_duration_seconds": 3600
  7. })
  8. jwt_unverified := io.jwt.decode(input.token)
  9. jwt_header := jwt_unverified[0]
  10. jwks = jwks_cached {
  11. jwks_cached := jwks_request("https://authorization-server.example.com/jwks").body
  12. jwt_header.kid == jwks_cached.keys[_].kid
  13. } else = jwks_rotated {
  14. # Add query param to second request to avoid both getting the same cache key
  15. jwks_rotated := jwks_request("https://authorization-server.example.com/jwks?r=1").body
  16. }
  17. jwt_verified = jwt_unverified {
  18. io.jwt.verify_rs256(input.token, json.marshal(jwks))
  19. }
  20. claims_verified := jwt_verified[1]

Token retrieval

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

  1. package oauth2
  2. token = t {
  3. response := http.send({
  4. "url": "https://authorization-server.example.com/token",
  5. "method": "POST",
  6. "headers": {
  7. "Content-Type": "application/x-www-form-urlencoded",
  8. "Authorization": concat(" ", [
  9. "Basic",
  10. base64.encode(sprintf("%v:%v", [client_id, client_secret]))
  11. ]),
  12. },
  13. # To use the resource owner password credentials flow, change grant_type
  14. # to "password" and add username and password parameters to the body
  15. "raw_body": "grant_type=client_credentials",
  16. "force_cache": true,
  17. "force_cache_duration_seconds": 3595, # Given an `expires_in` value of 3600
  18. })
  19. response.status_code == 200
  20. t := response.body.access_token
  21. }