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.
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:
- A boolean indicating if the token was successfully verified.
- A map of the token's headers.
- 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"
}
]
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
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.
package play
import rego.v1
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
}
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaXNzIjoicGtpLmV4YW1wbGUuY29tIn0.EiAS-4_ecAe3Fx3GDzZkvNPmhIaDQTHnmpLAHdWWe60"
}
{}
Rule | Output Value | Notes |
---|---|---|
allow | true | The 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.
package play
import rego.v1
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
}
{
"token": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJwa2kuZXhhbXBsZS5jb20ifQ.ViJTHHv5FuJM9LsRrTpzts6tZkN8deKiu5x49-M8-nq6Rs6ta-Wn8fN_YVLlpZvwhFu_yfxpfUGhBRc33QSSsw"
}
{}
Rule | Output Value | Notes |
---|---|---|
allow | true | The 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.
package play
import rego.v1
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
}
{
"token": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJwa2kuZXhhbXBsZS5jb20ifQ.ADOSFDJMBu7EBjiDa4-_uoI0PeAnAz8Ap8-GjsoKb_spdWCbGJR4QxBTMoBfoPbA1HajGinIgbbbL8yaqBlzKQ"
}
{}
Rule | Output Value | Notes |
---|---|---|
allow | true | The 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.
package play
import rego.v1
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
}
{
"token": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE4OTM0NTYwMDAsImlzcyI6InBraS5leGFtcGxlLmNvbSIsIm5iZiI6MTU3NzgzNjgwMH0.AVJC13glrKi92cIoH7ZXInDxL2ulikkOepL7LbTlAjApUlj_To8T9m4lf8Z-VoxfxLF5vVneQinn7q-niN2TQQ"
}
{}
Rule | Output Value | Notes |
---|---|---|
allow | true | The token is verified and valid at the current time. |
allow_future | false | The 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.
package play
import rego.v1
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
}
{
"token": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJncm91cHMiOlsiZGV2ZWxvcGVycyJdLCJpc3MiOiJwa2kuZXhhbXBsZS5jb20ifQ.hf-ew6HF_ziZ2H02UAHyCWekqpM9n0ET9bawh_T8gj3PiVROKmCDPz7d7sJev7bwASOTgKlXum2TE_j21x2zmw"
}
{}
Rule | Output Value | Notes |
---|---|---|
allow | true | In addition to other checks, the token has a 'developers' group value. |