Back to Opa

Token Verification

docs/docs/policy-reference/builtins/tokens.mdx

1.16.16.2 KB
Original Source

<BuiltinTable category={"tokens"} />

While OPA isn't responsible for issuing JWT tokens, Rego policies commonly operate on HTTP requests which are authenticated using JWT tokens. Because of this, it's important that Rego has native functionality to decode and verify the contents of JWT tokens in order to enforce additional authorization logic on requests.

Tokens often contain information about a user, which can be useful when writing authorization policies. Other information contained in a token's claims can include:

  • Email or user ID. This can be used to look up the user in other datasets.
  • Roles or groups. Often used for role-based access control.
  • Locale, language and location. This can be used to enforce regional restrictions or provide localized messages.

Tokens can also contain any number of custom claims that are specific to your organization and use case.

:::danger Remember that JWT tokens must be verified before being trusted. Using io.jwt.decode() alone is not enough without also verifying the token's signature. io.jwt.decode_verify() is the recommended function to use as it both decodes and verifies the token while supporting a wide range of signature algorithms. :::

The input string is a JSON Web Token encoded with JWS Compact Serialization. JWE and JWS JSON Serialization are not supported. If nested signing was used, the header, payload and signature will represent the most deeply nested token.

Examples

decode_verify

io.jwt.decode_verify() is a built-in function that verifies and decodes a JWT token using the provided constraints. The function accepts two arguments:

  • jwt: the JWT from the request to verify and decode.
  • constraints: a map of constraints to apply when verifying the JWT.

The function can be seen as a combination of io.jwt.verify_* and io.jwt.decode() built-ins. io.jwt.decode_verify() supports all of the following algorithms:

  • HMAC Algorithms: HS256, HS384, HS512
  • RSA Algorithms: RS256, RS384, RS512
  • ECDSA Algorithms: ES256, ES384, ES512
  • RSASSA-PSS Algorithms: PS256, PS384, PS512

The function also supports using a symmetric key to verify the token, though this is not recommended for production use.

:::danger Remember that JWT tokens must be verified before being trusted. Using io.jwt.decode() alone is not enough without also verifying the token's signature. Using either a suitable io.jwt.verify_* function for your token type or io.jwt.decode_verify() is required to ensure the token is valid. :::

constraints should contain either:

  • cert: The JWKS or PEM-encoded certificate to use when verifying (RSA or ECDSA).
  • secret: The secret key for HS256, HS384 and HS512 verification (not recommended for production use).

Optionally, constraints can also contain the following to further verify the token:

  • alg: The JWA algorithm name to use. If it is absent then any algorithm that is compatible with the key is accepted.
  • aud: Check the token was issued for the expected audience.
  • iss: The issuer string. If it is present the only tokens with this issuer are accepted. If it is absent then any issuer is accepted.
  • time: The time in nanoseconds to verify the token at. If this is present then the exp and nbf claims are compared against this value. If it is absent then they are compared against the current time.

The function returns a three element array consisting of:

  1. A boolean indicating if the token was successfully verified.
  2. A map of the token's headers.
  3. A map of the token's claims.

For example, it might return:

json
[
   true, // verified ok
   { "alg":"HS256", "typ":"JWT" }, // header
   { // claims
      "iss":"pki.example.com",
      "name":"John Doe",
      "sub":"1234567890"
   }
]

:::tip

<!-- note the code at the link is also here under tokens.go in case of link rot -->

If you'd like to generate a JWKS and signed JWT for testing below, you can use the example code here. :::

<PlaygroundExample dir={require.context('../_examples/io.jwt/decode_verify/symmetric')} />

<PlaygroundExample dir={require.context('../_examples/io.jwt/decode_verify/jwks')} />

<PlaygroundExample dir={require.context('../_examples/io.jwt/decode_verify/cert')} />

<PlaygroundExample dir={require.context('../_examples/io.jwt/decode_verify/jwks_time')} />

<PlaygroundExample dir={require.context('../_examples/io.jwt/decode_verify/jwks_groups')} />

verify_es256

The example below uses the following token:

rego
package jwt

es256_token := "eyJ0eXAiOiAiSldUIiwgImFsZyI6ICJFUzI1NiJ9.eyJuYmYiOiAxNDQ0NDc4NDAwLCAiaXNzIjogInh4eCJ9.lArczfN-pIL8oUU-7PU83u-zfXougXBZj6drFeKFsPEoVhy9WAyiZlRshYqjTSXdaw8yw2L-ovt4zTUZb2PWMg"
<RunSnippet id="token.rego"/>

This example shows a two-step process to verify the token signature and then decode it for further checks of the payload content. This approach gives more flexibility in verifying only the claims that the policy needs to enforce.

rego
package jwt

jwks := `{
    "keys": [{
        "kty":"EC",
        "crv":"P-256",
        "x":"z8J91ghFy5o6f2xZ4g8LsLH7u2wEpT2ntj8loahnlsE",
        "y":"7bdeXLH61KrGWRdh7ilnbcGQACxykaPKfmBccTHIOUo"
    }]
}`
<RunSnippet id="jwks.rego"/>

<PlaygroundExample files="#jwks.rego #token.rego" dir={require.context("../_examples/io.jwt/verify_es256/jwks")} />

The following example will demonstrate verifying tokens using an X.509 Certificate defined as:

rego
package jwt

cert := `-----BEGIN CERTIFICATE-----
MIIBcDCCARagAwIBAgIJAMZmuGSIfvgzMAoGCCqGSM49BAMCMBMxETAPBgNVBAMM
CHdoYXRldmVyMB4XDTE4MDgxMDE0Mjg1NFoXDTE4MDkwOTE0Mjg1NFowEzERMA8G
A1UEAwwId2hhdGV2ZXIwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATPwn3WCEXL
mjp/bFniDwuwsfu7bASlPae2PyWhqGeWwe23Xlyx+tSqxlkXYe4pZ23BkAAscpGj
yn5gXHExyDlKo1MwUTAdBgNVHQ4EFgQUElRjSoVgKjUqY5AXz2o74cLzzS8wHwYD
VR0jBBgwFoAUElRjSoVgKjUqY5AXz2o74cLzzS8wDwYDVR0TAQH/BAUwAwEB/zAK
BggqhkjOPQQDAgNIADBFAiEA4yQ/88ZrUX68c6kOe9G11u8NUaUzd8pLOtkKhniN
OHoCIHmNX37JOqTcTzGn2u9+c8NlnvZ0uDvsd1BmKPaUmjmm
-----END CERTIFICATE-----`
<RunSnippet id="cert.rego"/>

<PlaygroundExample files="#cert.rego #token.rego" dir={require.context("../_examples/io.jwt/verify_es256/cert")} />