Skip to main content

Rego Built-in Function: io.jwt.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.
  • secret: A shared secret to use when verifying (not recommended for production use).

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

  • alg: Mandate that a specific algorithm is used to sign the token.
  • aud: Check the token was issued for the expected audience.
  • iss: Check the token comes from the expected issuer.
  • time: Check the token is valid at a given 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:

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

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

Examples

Verification with a shared symmetric key

warning

This example uses a symmetric key to verify the token. This is not recommended for production use. Please see the examples below using JWKs or PEM-encoded certificates more examples.

Sometimes when working with tools like JWT.io it can be useful to decode and verify JWT tokens signed with a symmetric key just to see what the output of io.jwt.decode_verify() looks like.

The not-so-secret symmetric key password was used to sign the token provided in the input for the policy below. We can see that the claims in this example contains secret and iss only. This means that the validity period of the token is not checked, nor is the audience or the algorithm used to sign it.

# policy.rego
default allow := false

allow if {
# perform checks on the claims...
verified_claims.sub == "1234567890"
}

verified_claims := claims if {
[verified, _, claims] := io.jwt.decode_verify(
input.token,
{
"secret": "password",
"iss": "pki.example.com",
},
)

verified == true
}
# input.json
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaXNzIjoicGtpLmV4YW1wbGUuY29tIn0.EiAS-4_ecAe3Fx3GDzZkvNPmhIaDQTHnmpLAHdWWe60"
}

Run in OPA Playground

RuleOutput ValueNotes
allowtrueThe token is verified and has the expected issuer value.
verified_claims{"iss":"pki.example.com","name":"John Doe","sub":"1234567890"}The claims are set since the token is verified.

Verification with a JSON Web Key Set (JWKS)

This example builds on the JWKS example above

Note: You can generate your own JWKS and signed JWT for testing using the example code here.

# policy.rego
jwks := `{
"keys": [
{
"alg": "ES256",
"crv": "P-256",
"kid": "my-key-id",
"kty": "EC",
"use": "sig",
"x": "iTV4PECbWuDaNBMTLmwH0jwBTD3xUXR0S-VWsCYv8Gc",
"y": "-Cnw8d0XyQztrPZpynrFn8t10lyEb6oWqWcLJWPUB5A"
}
]
}`

allow if {
# perform checks on the verified claims...
verified_claims
}

verified_claims := claims if {
[verified, _, claims] := io.jwt.decode_verify(
input.token,
{
"cert": jwks,
"iss": "pki.example.com",
},
)

verified == true
}
# input.json
{
"token": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJwa2kuZXhhbXBsZS5jb20ifQ.ViJTHHv5FuJM9LsRrTpzts6tZkN8deKiu5x49-M8-nq6Rs6ta-Wn8fN_YVLlpZvwhFu_yfxpfUGhBRc33QSSsw"
}

Run in OPA Playground

RuleOutput ValueNotes
allowtrueThe token is verified and has the expected issuer value.

Verification with a PEM-encoded certificate

Some users may prefer to use a certificate to verify the JWT token. This example shows how to use a certificate to verify the JWT. Much like the other examples, it only checks the iss claim.

Note: You can generate your own certificate and signed JWT for testing using the example code here.

# policy.rego
cert := `-----BEGIN CERTIFICATE-----
MIIBMzCB2aADAgECAgEBMAoGCCqGSM49BAMCMBIxEDAOBgNVBAoTB0V4YW1wbGUw
HhcNMDkxMTEwMjMwMDAwWhcNMTAxMTEwMjMwMDAwWjASMRAwDgYDVQQKEwdFeGFt
cGxlMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAERgI9efDtzy0QubqeMgYPyD+k
GWtw2JueCpORUB0hOecwnO5HjPiZ3OsE5wIvwzt8fJzbZFHBoKx5GfbUCiOlpKMg
MB4wDgYDVR0PAQH/BAQDAgWgMAwGA1UdEwEB/wQCMAAwCgYIKoZIzj0EAwIDSQAw
RgIhAIQNOE/SCRr2pkW4XCGIaHuNO6oXHDp/HThPxaHfyTmPAiEA8uPj91awzzdW
xOI1W2BMAnR1VlCHTaaoGCaWUjTo6Sc=
-----END CERTIFICATE-----
`

default allow := false

allow if {
# perform additional checks as required
verified_claims
}

verified_claims := claims if {
[verified, _, claims] := io.jwt.decode_verify(
input.token,
{
"cert": cert,
"iss": "pki.example.com",
},
)

verified == true
}
# input.json
{
"token": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJwa2kuZXhhbXBsZS5jb20ifQ.ADOSFDJMBu7EBjiDa4-_uoI0PeAnAz8Ap8-GjsoKb_spdWCbGJR4QxBTMoBfoPbA1HajGinIgbbbL8yaqBlzKQ"
}

Run in OPA Playground

RuleOutput ValueNotes
allowtrueThe token is verified and has the expected issuer value.

JWKS and time-of-use verification

In addition to verifying the JWT token's signature and issuer, this example also checks the token's validity period. The token in the example is valid for 2020-2030, try it out in the playground and use the example code here to generate your own data to test with.

# policy.rego
jwks := `{
"keys": [
{
"alg": "ES256",
"crv": "P-256",
"kid": "my-key-id",
"kty": "EC",
"use": "sig",
"x": "Uv7zcspR68KLcqZJDV5WYd946uHTeOFhHVi0hAkqkWI",
"y": "YExfQqSyCo3LlX1K7F9NCTQjcNBNRWAZqsbWtNNHwTU"
}
]
}`

t := time.add_date(
time.now_ns(),
100,
0,
0,
)

verified_claims := claims if {
[verified, _, claims] := io.jwt.decode_verify(
input.token,
{
"cert": jwks,
"iss": "pki.example.com",
# a time in 2024
"time": 1720109779675634700,
},
)

verified == true
}

verified_claims_future := claims if {
[verified, _, claims] := io.jwt.decode_verify(
input.token,
{
"cert": jwks,
"iss": "pki.example.com",
# a time in the future
"time": 4875783401643188000,
},
)

verified == true
}

default allow := false

allow if {
verified_claims
}

# default is used since token is not verified
default allow_future := false

allow_future if {
# unset, since the token is not verified
verified_claims_future
}
# input.json
{
"token": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE4OTM0NTYwMDAsImlzcyI6InBraS5leGFtcGxlLmNvbSIsIm5iZiI6MTU3NzgzNjgwMH0.AVJC13glrKi92cIoH7ZXInDxL2ulikkOepL7LbTlAjApUlj_To8T9m4lf8Z-VoxfxLF5vVneQinn7q-niN2TQQ"
}

Run in OPA Playground

RuleOutput ValueNotes
allowtrueThe token is verified and valid at the current time.
allow_futurefalseThe token is not valid after 2030

Validating a token's groups or roles

So far, all the examples on this page have used the constraints parameter to specify the claims that should be checked and their values. The functionality covered by constraints represents the core checks when verifying a JWT token.

JWT tokens however can contain a lot more information than just the claims specified in the constraints parameter. This example makes an additional check in the allow rule to ensure that the token's groups claim contains the expected value.

# policy.rego
jwks := `{
"keys": [
{
"alg": "ES256",
"crv": "P-256",
"kid": "my-key-id",
"kty": "EC",
"use": "sig",
"x": "hV6Tel-bKwtnfgLAn9aPCe24WgKpnoZwDXKbAKdjUV4",
"y": "xmIK_KmwlJ_jrWAaTvdfvqvAYuxDH_2e4dbIzPQmnFM"
}
]
}`

default allow := false

allow if {
"developers" in verified_claims.groups
}

verified_claims := claims if {
[verified, _, claims] := io.jwt.decode_verify(
input.token,
{
"cert": jwks,
"iss": "pki.example.com",
},
)

verified == true
}
# input.json
{
"token": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJncm91cHMiOlsiZGV2ZWxvcGVycyJdLCJpc3MiOiJwa2kuZXhhbXBsZS5jb20ifQ.hf-ew6HF_ziZ2H02UAHyCWekqpM9n0ET9bawh_T8gj3PiVROKmCDPz7d7sJev7bwASOTgKlXum2TE_j21x2zmw"
}

Run in OPA Playground

RuleOutput ValueNotes
allowtrueIn addition to other checks, the token has a 'developers' group value.