Skip to main content

Token Verification

FunctionDescriptionMeta
io.jwt.decode

output := io.jwt.decode(jwt)

Decodes a JSON Web Token and outputs it as an object.

Arguments:
jwt (string)

JWT token to decode

Returns:
output (array<object[any: any], object[any: any], string>)

[header, payload, sig], where header and payload are objects; sig is the hexadecimal representation of the signature on the token.

SDK-dependent
io.jwt.decode_verify

output := io.jwt.decode_verify(jwt, constraints)

Verifies a JWT signature under parameterized constraints and decodes the claims if it is valid. Supports the following algorithms: HS256, HS384, HS512, RS256, RS384, RS512, ES256, ES384, ES512, PS256, PS384, PS512, and EdDSA.

Arguments:
jwt (string)

JWT token whose signature is to be verified and whose claims are to be checked

constraints (object[string: any])

claim verification constraints

Returns:
output (array<boolean, object[any: any], object[any: any]>)

[valid, header, payload]: if the input token is verified and meets the requirements of constraints then valid is true; header and payload are objects containing the JOSE header and the JWT claim set; otherwise, valid is false, header and payload are {}

SDK-dependent
io.jwt.verify_eddsa

result := io.jwt.verify_eddsa(jwt, certificate)

Verifies if an EdDSA JWT signature is valid.

Arguments:
jwt (string)

JWT token whose signature is to be verified

certificate (string)

PEM encoded certificate, PEM encoded public key, or the JWK key (set) used to verify the signature

Returns:
result (boolean)

true if the signature is valid, false otherwise

v1.8.0 SDK-dependent
io.jwt.verify_es256

result := io.jwt.verify_es256(jwt, certificate)

Verifies if a ES256 JWT signature is valid.

Arguments:
jwt (string)

JWT token whose signature is to be verified

certificate (string)

PEM encoded certificate, PEM encoded public key, or the JWK key (set) used to verify the signature

Returns:
result (boolean)

true if the signature is valid, false otherwise

SDK-dependent
io.jwt.verify_es384

result := io.jwt.verify_es384(jwt, certificate)

Verifies if a ES384 JWT signature is valid.

Arguments:
jwt (string)

JWT token whose signature is to be verified

certificate (string)

PEM encoded certificate, PEM encoded public key, or the JWK key (set) used to verify the signature

Returns:
result (boolean)

true if the signature is valid, false otherwise

v0.20.0 SDK-dependent
io.jwt.verify_es512

result := io.jwt.verify_es512(jwt, certificate)

Verifies if a ES512 JWT signature is valid.

Arguments:
jwt (string)

JWT token whose signature is to be verified

certificate (string)

PEM encoded certificate, PEM encoded public key, or the JWK key (set) used to verify the signature

Returns:
result (boolean)

true if the signature is valid, false otherwise

v0.20.0 SDK-dependent
io.jwt.verify_hs256

result := io.jwt.verify_hs256(jwt, secret)

Verifies if a HS256 (secret) JWT signature is valid.

Arguments:
jwt (string)

JWT token whose signature is to be verified

secret (string)

plain text secret used to verify the signature

Returns:
result (boolean)

true if the signature is valid, false otherwise

SDK-dependent
io.jwt.verify_hs384

result := io.jwt.verify_hs384(jwt, secret)

Verifies if a HS384 (secret) JWT signature is valid.

Arguments:
jwt (string)

JWT token whose signature is to be verified

secret (string)

plain text secret used to verify the signature

Returns:
result (boolean)

true if the signature is valid, false otherwise

v0.20.0 SDK-dependent
io.jwt.verify_hs512

result := io.jwt.verify_hs512(jwt, secret)

Verifies if a HS512 (secret) JWT signature is valid.

Arguments:
jwt (string)

JWT token whose signature is to be verified

secret (string)

plain text secret used to verify the signature

Returns:
result (boolean)

true if the signature is valid, false otherwise

v0.20.0 SDK-dependent
io.jwt.verify_ps256

result := io.jwt.verify_ps256(jwt, certificate)

Verifies if a PS256 JWT signature is valid.

Arguments:
jwt (string)

JWT token whose signature is to be verified

certificate (string)

PEM encoded certificate, PEM encoded public key, or the JWK key (set) used to verify the signature

Returns:
result (boolean)

true if the signature is valid, false otherwise

SDK-dependent
io.jwt.verify_ps384

result := io.jwt.verify_ps384(jwt, certificate)

Verifies if a PS384 JWT signature is valid.

Arguments:
jwt (string)

JWT token whose signature is to be verified

certificate (string)

PEM encoded certificate, PEM encoded public key, or the JWK key (set) used to verify the signature

Returns:
result (boolean)

true if the signature is valid, false otherwise

v0.20.0 SDK-dependent
io.jwt.verify_ps512

result := io.jwt.verify_ps512(jwt, certificate)

Verifies if a PS512 JWT signature is valid.

Arguments:
jwt (string)

JWT token whose signature is to be verified

certificate (string)

PEM encoded certificate, PEM encoded public key, or the JWK key (set) used to verify the signature

Returns:
result (boolean)

true if the signature is valid, false otherwise

v0.20.0 SDK-dependent
io.jwt.verify_rs256

result := io.jwt.verify_rs256(jwt, certificate)

Verifies if a RS256 JWT signature is valid.

Arguments:
jwt (string)

JWT token whose signature is to be verified

certificate (string)

PEM encoded certificate, PEM encoded public key, or the JWK key (set) used to verify the signature

Returns:
result (boolean)

true if the signature is valid, false otherwise

SDK-dependent
io.jwt.verify_rs384

result := io.jwt.verify_rs384(jwt, certificate)

Verifies if a RS384 JWT signature is valid.

Arguments:
jwt (string)

JWT token whose signature is to be verified

certificate (string)

PEM encoded certificate, PEM encoded public key, or the JWK key (set) used to verify the signature

Returns:
result (boolean)

true if the signature is valid, false otherwise

v0.20.0 SDK-dependent
io.jwt.verify_rs512

result := io.jwt.verify_rs512(jwt, certificate)

Verifies if a RS512 JWT signature is valid.

Arguments:
jwt (string)

JWT token whose signature is to be verified

certificate (string)

PEM encoded certificate, PEM encoded public key, or the JWK key (set) used to verify the signature

Returns:
result (boolean)

true if the signature is valid, false otherwise

v0.20.0 SDK-dependent

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:

[
true, // verified ok
{ "alg":"HS256", "typ":"JWT" }, // header
{ // 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.

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
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
}
input.json
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaXNzIjoicGtpLmV4YW1wbGUuY29tIn0.EiAS-4_ecAe3Fx3GDzZkvNPmhIaDQTHnmpLAHdWWe60"
}
data.json
{}

Open in OPA Playground

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
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
}
input.json
{
"token": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJwa2kuZXhhbXBsZS5jb20ifQ.ViJTHHv5FuJM9LsRrTpzts6tZkN8deKiu5x49-M8-nq6Rs6ta-Wn8fN_YVLlpZvwhFu_yfxpfUGhBRc33QSSsw"
}
data.json
{}

Open in OPA Playground

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
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
}
input.json
{
"token": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJwa2kuZXhhbXBsZS5jb20ifQ.ADOSFDJMBu7EBjiDa4-_uoI0PeAnAz8Ap8-GjsoKb_spdWCbGJR4QxBTMoBfoPbA1HajGinIgbbbL8yaqBlzKQ"
}
data.json
{}

Open in OPA Playground

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
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
}
input.json
{
"token": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE4OTM0NTYwMDAsImlzcyI6InBraS5leGFtcGxlLmNvbSIsIm5iZiI6MTU3NzgzNjgwMH0.AVJC13glrKi92cIoH7ZXInDxL2ulikkOepL7LbTlAjApUlj_To8T9m4lf8Z-VoxfxLF5vVneQinn7q-niN2TQQ"
}
data.json
{}

Open in OPA Playground

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
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
}
input.json
{
"token": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJncm91cHMiOlsiZGV2ZWxvcGVycyJdLCJpc3MiOiJwa2kuZXhhbXBsZS5jb20ifQ.hf-ew6HF_ziZ2H02UAHyCWekqpM9n0ET9bawh_T8gj3PiVROKmCDPz7d7sJev7bwASOTgKlXum2TE_j21x2zmw"
}
data.json
{}

Open in OPA Playground

verify_es256

The example below uses the following token:

package jwt

es256_token := "eyJ0eXAiOiAiSldUIiwgImFsZyI6ICJFUzI1NiJ9.eyJuYmYiOiAxNDQ0NDc4NDAwLCAiaXNzIjogInh4eCJ9.lArczfN-pIL8oUU-7PU83u-zfXougXBZj6drFeKFsPEoVhy9WAyiZlRshYqjTSXdaw8yw2L-ovt4zTUZb2PWMg"

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.

package jwt

jwks := `{
"keys": [{
"kty":"EC",
"crv":"P-256",
"x":"z8J91ghFy5o6f2xZ4g8LsLH7u2wEpT2ntj8loahnlsE",
"y":"7bdeXLH61KrGWRdh7ilnbcGQACxykaPKfmBccTHIOUo"
}]
}`

JWKS Verify

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.

data.json
{}
input.json
{}
package jwt

result.verify := io.jwt.verify_es256(es256_token, jwks) # Verify the token with the JWKS
result.payload := io.jwt.decode(es256_token) # Decode the token
result.check := result.payload[1].iss == "xxx" # Ensure the issuer (`iss`) claim is the expected value

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

package jwt

cert := `-----BEGIN CERTIFICATE-----
MIIBcDCCARagAwIBAgIJAMZmuGSIfvgzMAoGCCqGSM49BAMCMBMxETAPBgNVBAMM
CHdoYXRldmVyMB4XDTE4MDgxMDE0Mjg1NFoXDTE4MDkwOTE0Mjg1NFowEzERMA8G
A1UEAwwId2hhdGV2ZXIwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATPwn3WCEXL
mjp/bFniDwuwsfu7bASlPae2PyWhqGeWwe23Xlyx+tSqxlkXYe4pZ23BkAAscpGj
yn5gXHExyDlKo1MwUTAdBgNVHQ4EFgQUElRjSoVgKjUqY5AXz2o74cLzzS8wHwYD
VR0jBBgwFoAUElRjSoVgKjUqY5AXz2o74cLzzS8wDwYDVR0TAQH/BAUwAwEB/zAK
BggqhkjOPQQDAgNIADBFAiEA4yQ/88ZrUX68c6kOe9G11u8NUaUzd8pLOtkKhniN
OHoCIHmNX37JOqTcTzGn2u9+c8NlnvZ0uDvsd1BmKPaUmjmm
-----END CERTIFICATE-----`

Certificate Verify

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.

data.json
{}
input.json
{}
package jwt

result.verify := io.jwt.verify_es256(es256_token, cert) # Verify the token with the certificate
result.payload := io.jwt.decode(es256_token) # Decode the token
result.check := result.payload[1].iss == "xxx" # Ensure the issuer (`iss`) claim is the expected value