Skip to main content

Token Signing

FunctionDescriptionMeta
io.jwt.encode_sign

output := io.jwt.encode_sign(headers, payload, key)

Encodes and optionally signs a JSON Web Token. Inputs are taken as objects, not encoded strings (see io.jwt.encode_sign_raw).

Arguments:
headers (object[string: any])

JWS Protected Header

payload (object[string: any])

JWS Payload

key (object[string: any])

JSON Web Key (RFC7517)

Returns:
output (string)

signed JWT

SDK-dependent
io.jwt.encode_sign_raw

output := io.jwt.encode_sign_raw(headers, payload, key)

Encodes and optionally signs a JSON Web Token.

Arguments:
headers (string)

JWS Protected Header

payload (string)

JWS Payload

key (string)

JSON Web Key (RFC7517)

Returns:
output (string)

signed JWT

SDK-dependent

OPA provides two builtins that implement JSON Web Signature RFC7515 functionality.

io.jwt.encode_sign_raw() takes three JSON Objects (strings) as parameters and returns their JWS Compact Serialization. This builtin should be used by those that want maximum control over the signing and serialization procedure. It is important to remember that StringOrURI values are compared as case-sensitive strings with no transformations or canonicalizations applied. Therefore, line breaks and whitespaces are significant.

io.jwt.encode_sign() takes three Rego Objects as parameters and returns their JWS Compact Serialization. This builtin should be used by those that want to use rego objects for signing during policy evaluation.

info

Note that with io.jwt.encode_sign the Rego objects are serialized to JSON with standard formatting applied whereas the io.jwt.encode_sign_raw built-in will not affect whitespace of the strings passed in. This will mean that the final encoded token may have different string values, but the decoded and parsed JSON will match.

The following algorithms are supported:

  • ES256: ECDSA using P-256 and SHA-256
  • ES384: ECDSA using P-384 and SHA-384
  • ES512: ECDSA using P-521 and SHA-512
  • HS256: HMAC using SHA-256
  • HS384: HMAC using SHA-384
  • HS512: HMAC using SHA-512
  • PS256: RSASSA-PSS using SHA256 and MGF1-SHA256
  • PS384: RSASSA-PSS using SHA384 and MGF1-SHA384
  • PS512: RSASSA-PSS using SHA512 and MGF1-SHA512
  • RS256: RSASSA-PKCS-v1.5 using SHA-256
  • RS384: RSASSA-PKCS-v1.5 using SHA-384
  • RS512: RSASSA-PKCS-v1.5 using SHA-512
info

Note that the key's provided should be base64 URL encoded (without padding) as per the specification (RFC7517). This differs from the plain text secrets provided with the algorithm specific verify built-ins described below.

Examples

encode_sign

Sign and Verify

This one demonstrates how to encode the and sign the same token contents as in the example above but with io.jwt.encode_sign instead of the raw variant.

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

result_hs256 := io.jwt.encode_sign(
{
"alg":"HS256",
"typ":"JWT"
},
{},
{
"kty":"oct",
"k":"Zm9v"
}
)

# Important! - Use the un-encoded plain text secret to verify and decode
result_parts_hs256 := io.jwt.decode_verify(result_hs256, {"secret": "foo"})
result_valid_hs256 := io.jwt.verify_hs256(result_hs256, "foo")

Symmetric Key (HMAC with SHA-256)

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

result := io.jwt.encode_sign({
"typ": "JWT",
"alg": "HS256"
}, {
"iss": "joe",
"exp": 1300819380,
"aud": ["bob", "saul"],
"http://example.com/is_root": true,
"privateParams": {
"private_one": "one",
"private_two": "two"
}
}, {
"kty": "oct",
"k": "AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow"
})

Symmetric Key with empty JSON payload

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

result := io.jwt.encode_sign({
"typ": "JWT",
"alg": "HS256"},
{}, {
"kty": "oct",
"k": "AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow"
})

RSA Key (RSA Signature with SHA-256)

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

result := io.jwt.encode_sign({
"alg": "RS256"
}, {
"iss": "joe",
"exp": 1300819380,
"aud": ["bob", "saul"],
"http://example.com/is_root": true,
"privateParams": {
"private_one": "one",
"private_two": "two"
}
},
{
"kty": "RSA",
"n": "ofgWCuLjybRlzo0tZWJjNiuSfb4p4fAkd_wWJcyQoTbji9k0l8W26mPddxHmfHQp-Vaw-4qPCJrcS2mJPMEzP1Pt0Bm4d4QlL-yRT-SFd2lZS-pCgNMsD1W_YpRPEwOWvG6b32690r2jZ47soMZo9wGzjb_7OMg0LOL-bSf63kpaSHSXndS5z5rexMdbBYUsLA9e-KXBdQOS-UTo7WTBEMa2R2CapHg665xsmtdVMTBQY4uDZlxvb3qCo5ZwKh9kG4LT6_I5IhlJH7aGhyxXFvUK-DWNmoudF8NAco9_h9iaGNj8q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXpoQ",
"e": "AQAB",
"d": "Eq5xpGnNCivDflJsRQBXHx1hdR1k6Ulwe2JZD50LpXyWPEAeP88vLNO97IjlA7_GQ5sLKMgvfTeXZx9SE-7YwVol2NXOoAJe46sui395IW_GO-pWJ1O0BkTGoVEn2bKVRUCgu-GjBVaYLU6f3l9kJfFNS3E0QbVdxzubSu3Mkqzjkn439X0M_V51gfpRLI9JYanrC4D4qAdGcopV_0ZHHzQlBjudU2QvXt4ehNYTCBr6XCLQUShb1juUO1ZdiYoFaFQT5Tw8bGUl_x_jTj3ccPDVZFD9pIuhLhBOneufuBiB4cS98l2SR_RQyGWSeWjnczT0QU91p1DhOVRuOopznQ",
"p": "4BzEEOtIpmVdVEZNCqS7baC4crd0pqnRH_5IB3jw3bcxGn6QLvnEtfdUdiYrqBdss1l58BQ3KhooKeQTa9AB0Hw_Py5PJdTJNPY8cQn7ouZ2KKDcmnPGBY5t7yLc1QlQ5xHdwW1VhvKn-nXqhJTBgIPgtldC-KDV5z-y2XDwGUc",
"q": "uQPEfgmVtjL0Uyyx88GZFF1fOunH3-7cepKmtH4pxhtCoHqpWmT8YAmZxaewHgHAjLYsp1ZSe7zFYHj7C6ul7TjeLQeZD_YwD66t62wDmpe_HlB-TnBA-njbglfIsRLtXlnDzQkv5dTltRJ11BKBBypeeF6689rjcJIDEz9RWdc",
"dp": "BwKfV3Akq5_MFZDFZCnW-wzl-CCo83WoZvnLQwCTeDv8uzluRSnm71I3QCLdhrqE2e9YkxvuxdBfpT_PI7Yz-FOKnu1R6HsJeDCjn12Sk3vmAktV2zb34MCdy7cpdTh_YVr7tss2u6vneTwrA86rZtu5Mbr1C1XsmvkxHQAdYo0",
"dq": "h_96-mK1R_7glhsum81dZxjTnYynPbZpHziZjeeHcXYsXaaMwkOlODsWa7I9xXDoRwbKgB719rrmI2oKr6N3Do9U0ajaHF-NKJnwgjMd2w9cjz3_-kyNlxAr2v4IKhGNpmM5iIgOS1VZnOZ68m6_pbLBSp3nssTdlqvd0tIiTHU",
"qi": "IYd7DHOhrWvxkwPQsRM2tOgrjbcrfvtQJipd-DlcxyVuuM9sQLdgjVk2oy26F0EmpScGLq2MowX7fhd_QJQ3ydy5cY7YIBi87w93IKLEdfnbJtoOPLUW0ITrJReOgo1cq9SbsxYawBgfp_gh6A5603k2-ZQwVK0JKSHuLFkuQ3U"
})

encode_sign_raw

Sign and Verify Raw

This exambles demonstrates how to do this using the io.jwt.encode_sign_raw built-in:

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

raw_result_hs256 := io.jwt.encode_sign_raw(
`{"alg":"HS256","typ":"JWT"}`,
`{}`,
`{"kty":"oct","k":"Zm9v"}` # "Zm9v" == base64url.encode_no_pad("foo")
)

# Important! - Use the un-encoded plain text secret to verify and decode
raw_result_valid_hs256 := io.jwt.verify_hs256(raw_result_hs256, "foo")
raw_result_parts_hs256 := io.jwt.decode_verify(raw_result_hs256, {"secret": "foo"})