JWT Configuration
This section describes the JSON Web Token (JWT) configuration options available in RelyAuth.
Because RelyAuth is inspired by Hasura Auth's concepts, many sections are borrowed from the official Hasura docs.
Basics
Payload Definition
Example JSON Web Token (JWT) payload configuration definition:
version: v1
kind: RelyAuth
definition:
modes:
- mode: jwt
tokenLocation:
in: header
name: Authorization
scheme: bearer
key:
algorithm: HS256
key:
value: ultra-secret-very-secret-super-secret-key
audience: ["myapp-1234", "myapp-6789"]
allowedSkew: 60
claimsConfig:
namespace:
location: '"claims.jwt.hasura.io"'
claimsFormat: Json
locations:
x-hasura-custom-variable:
path: sub
As a minimum, either the claimsConfig, tokenLocation, and key values have to be present.
Example Decoded Payload
{
"iat": 1735916718,
"exp": 1796916677,
"claims.jwt.hasura.io": {
"x-hasura-default-role": "user",
"x-hasura-allowed-roles": ["user", "admin"],
"x-hasura-user-id": "123",
"x-hasura-org-id": "456",
"x-hasura-custom": "custom-value"
}
}
Example Encoded JWT
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3MzU5MTY3MTgsImV4cCI6MTc5NjkxNjY3NywiY2xhaW1zLmp3dC5oYXN1cmEuaW8iOnsieC1oYXN1cmEtZGVmYXVsdC1yb2xlIjoidXNlciIsIngtaGFzdXJhLWFsbG93ZWQtcm9sZXMiOlsidXNlciIsImFkbWluIl0sIngtaGFzdXJhLXVzZXItaWQiOiIxMjMiLCJ4LWhhc3VyYS1vcmctaWQiOiI0NTYiLCJ4LWhhc3VyYS1jdXN0b20iOiJjdXN0b20tdmFsdWUifX0.5bwSMgxsyULY1uhCJxYd-sO35rCdznRCZ4YMLwDD5u8
See here for the JWT debugger
of this example JWT token. The signature secret to verify this token with the HS256 algorithm is
ultra-secret-very-secret-super-secret-key.
JWT configuration options
claimsConfig
You can specify where the engine should look for the claims within the decoded token with namespace and locations
options.
namespace
The namespace option is used when all of JWT claims are present in a single object within the decoded JWT. Our example
uses claims.jwt.hasura.io in the Example Decoded Payload.
claimsConfig:
namespace:
claimsFormat: Json
location: '"claims.jwt.hasura.io"'
The location field indicates the location of the namespace object that uses JMESPath string
syntax.
The claimsFormat field indicates whether the claims are a regular JSON object or a stringified JSON. The following
possible values are allowed: Json, StringifiedJson.
This is required because providers like AWS Cognito only allow strings in the JWT claims.
Example:
If claimsFormat is Json then the JWT claims should look like:
{
"sub": "1234567890",
"name": "John Doe",
"admin": true,
"iat": 1516239022,
"claims.jwt.hasura.io": {
"x-hasura-allowed-roles": ["editor", "user", "mod"],
"x-hasura-default-role": "user",
"x-hasura-user-id": "1234567890",
"x-hasura-org-id": "123",
"x-hasura-custom": "custom-value"
}
}
If claimsFormat is StringifiedJson then the JWT claims should look like:
{
"sub": "1234567890",
"name": "John Doe",
"admin": true,
"iat": 1516239022,
"claims.jwt.hasura.io": "{\"x-hasura-allowed-roles\":[\"editor\",\"user\",\"mod\"],\"x-hasura-default-role\":\"user\",\"x-hasura-user-id\":\"1234567890\",\"x-hasura-org-id\":\"123\",\"x-hasura-custom\":\"custom-value\"}"
}
locations
This locations option can be used when JWT claims are not all present in the single object, but individual claims are
provided a JMESPath within the decoded JWT. In this option, you can indicate a JMESPath for individual claims and an
optional default value if the claim doesn't exist.
Example: JWT config with JSON path values
{
"sub": "1234567890",
"name": "John Doe",
"admin": true,
"iat": 1516239022,
"user": {
"id": "ujdh739kd"
},
"custom_claims": {
"all_roles": ["user", "editor"]
}
}
The mapping for x-hasura-allowed-roles, x-hasura-default-role and x-hasura-user-id session variables can be
specified in the locations configuration as follows:
claimsConfig:
locations:
x-hasura-default-role:
path: custom_claims.all_roles[0]
x-hasura-allowed-roles:
path: custom_claims.all_roles
x-hasura-user-id:
path: user.id
Example: JWT config with JSON path values and default values
{
"sub": "1234567890",
"name": "John Doe",
"admin": true,
"iat": 1516239022,
"hasura": {
"all_roles": ["user", "editor"]
}
}
claimsConfig:
locations:
x-hasura-default-role:
path: custom_claims.all_roles[0]
x-hasura-allowed-roles:
path: custom_claims.all_roles
x-hasura-user-id:
path: user.id
default:
value: ujdh739kd
env: USER_ID
In the above case, since the /user/id doesn't exist in the JWT token, the default value of the x-hasura-user-id
could be:
- Value of the environment variable
USER_IDif exists. - Otherwise, the literal value
ujdh739kdwill be used.
If the path field is omitted, the x-hasura-user-id claim will automatically use the default value.
claimsConfig:
locations:
x-hasura-default-role:
path: custom_claims.all_roles[0]
x-hasura-allowed-roles:
path: custom_claims.all_roles
x-hasura-user-id:
default:
value: ujdh739kd
If the object key contains special characters such as ., -, you need to wrap it with double quotes, for example,
"claims.jwt.hasura.io"."x-hasura-allowed-roles".
tokenLocation
Indicates the token location to read the JWT. This setting is an object with the following properties.
in: token location the HTTP request. Possible values areheader,cookie, andquery.name: name of the parameter according to the location.scheme: optional prefix scheme of the token, for example,bearerfor the Bearer scheme.
The following are possible options:
header
In this option, RelyChan expects an Authorization header with the Bearer scheme.
tokenLocation:
in: header
name: Authorization
scheme: bearer
The JWT header should look like:
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWI...
Cookie
In the cookie mode, RelyChan will try to parse the cookie header with the given cookie name. The value of the cookie should be the exact JWT.
tokenLocation:
type: cookie
name: cookie_name
The JWT token should look like:
Cookie: cookie_name=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWI...
Query
In this mode, RelyChan expects a query_name parameter in the request URL with the exact JWT token value.
tokenLocation:
type: query
name: query_name
The JWT token should look like:
https://example.com?query_name=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9
key
This field specifies the JWT key configuration according to which the incoming JWT will be decoded. You can configure either a fixed algorithm key or a remote JWK URL.
fixed key
In this option, you must indicate a JWT key and its algorithm so the engine can decode and verify the JWT token.
key:
algorithm: HS256
key:
value: ultra-secret-very-secret-super-secret-key
# env: JWT_KEY
The algorithm field specifies the cryptographic signing algorithm which is used to sign the JWTs. Valid values are:
HS256, HS384, HS512, RS256, RS384, RS512, ES256, ES384, PS256, PS384, PS512, EdDSA.
The key field can be a literal value or an environment variable name.
- In the case of a symmetric key (i.e. a HMAC-based key), just the key as is. (e.g. -"abcdef..."). The key must be long enough for the chosen algorithm, (e.g. for HS256 it must be at least 32 characters long).
- In the case of an asymmetric key (RSA, EdDSA, ECDSA etc.), only the public key, in a PEM-encoded string or as an X509 certificate.
jwkFromUrl
An URL where a provider publishes their JWKs (JSON Web Keys - which are used for signing the JWTs). The URL must publish the JWKs in the standard format as described here.
For example:
- Auth0 publishes their JWK url at:
https://<YOUR_AUTH0_DOMAIN>.auth0.com. - Firebase publishes their JWK url at:
https://www.googleapis.com/service_accounts/v1/jwk/[email protected].
key:
jwkFromUrl:
value: https://www.googleapis.com/service_accounts/v1/jwk/[email protected]
# env: JWK_URL
The JWTs must be signed by the JWK published at the given URL. They can be signed by any algorithm that is compatible
with the key (eg. RS256, RS384, RS512 algorithms require a JWK with an RSA key).
DDN does not currently support rotating JWKs.
audience
This is an optional field. Certain providers might set a claim which indicates the intended audience for the JWT. This must be checked by setting this field.
When this field is set, during the verification process of the JWT, the aud claim in the JWT will be checked to see
whether it is equal to the audience field given in the configuration. If they are not equal then the JWT will be
rejected.
See the RFC for more details.
This field must be a list of strings.
Examples:
version: v1
kind: RelyAuth
definition:
modes:
- mode: jwt
audience: ["myapp-1234", "myapp-6789"]
# ...
Certain JWT providers share JWKs between multiple tenants. They use the aud claim of the JWT to specify the intended
audience. Setting the audience field in the JWT configuration will make sure that the aud claim from the JWT is also
checked during verification. Not doing this check will allow JWTs issued for other tenants to be valid as well.
In these cases, you MUST set the audience field to the appropriate value. Failing to do so is a major security
vulnerability.
issuer
This is an optional field. It takes a string value.
When this field is set, during the verification process of the JWT, the iss claim in the JWT will be checked to see
whether it is equal to the issuer field given in the configuration. If they are not equal then the JWT will be
rejected.
See RFC for more details.
Examples:
version: v1
kind: RelyAuth
definition:
modes:
- mode: jwt
# ...
issuer: https://my-auth-server.com
Issuer Notes
- Certain providers require you to verify the
issclaim on the JWT. To do that you can set this field to the appropriate value. - A JWT configuration without an issuer will match any issuer field present in an incoming JWT.
- An incoming JWT without an issuer specified will match a configuration even if it specifies an issuer.
allowed_skew
allowedSkew is an optional field to provide some leeway (to account for clock skews) while comparing the JWT expiry
time. This field expects an integer value which will be the number of seconds of the skew value.
JWT Config Examples
HMAC-SHA based
Your auth server is using HMAC-SHA algorithms to sign JWTs, and is using a 256-bit key. In this case, the JWT config will look like:
version: v1
kind: RelyAuth
definition:
modes:
- mode: jwt
tokenLocation:
in: header
name: Authorization
scheme: bearer
claimsConfig:
namespace:
location: '"claims.jwt.hasura.io"'
claimsFormat: Json
key:
algorithm: HS256
key:
value: ultra-secret-very-secret-super-secret-key
The key is the actual shared secret, which is used by RelyAuth and the external auth server.
RSA based
If your auth server is using the RSA algorithm to sign JWTs, and is using a 512-bit key, the JWT config only needs to have the public key.
Example 1: public key in PEM format (not OpenSSH format):
version: v1
kind: RelyAuth
definition:
modes:
- mode: jwt
tokenLocation:
in: header
name: Authorization
scheme: bearer
claimsConfig:
namespace:
location: '"claims.jwt.hasura.io"'
claimsFormat: Json
key:
algorithm: RS512
key:
value:
'-----BEGIN PUBLIC
KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDdlatRjRjogo3WojgGHFHYLugd\nUWAY9iR3fy4arWNA1KoS8kVw33cJibXr8bvwUAUparCwlvdbH6dvEOfou0/gCFQs\nHUfQrSDv+MuSUMAe8jzKE4qW+jK+xQU9a03GUnKHkkle+Q0pX/g6jXZ7r1/xAK5D\no2kQ+X5xK9cipRgEKwIDAQAB\n-----END
PUBLIC KEY-----\n'
Example 2: public key as X509 certificate:
version: v1
kind: RelyAuth
definition:
modes:
- mode: jwt
tokenLocation:
in: header
name: Authorization
scheme: bearer
claimsConfig:
namespace:
location: '"claims.jwt.hasura.io"'
claimsFormat: Json
key:
algorithm: RS512
key:
value:
'-----BEGIN
CERTIFICATE-----\nMIIDHDCCAgSgAwIBAgIINw9gva8BPPIwDQYJKoZIhvcNAQEFBQAwMTEvMC0GA1UE\nAxMmc2VjdXJldG9rZW4uc3lzdGVtLmdzZXJ2aWNlYWNjb3VudC5jb20wHhcNMTgQt7dIsMTIU9k1SUrFviZOGnmHWtIAw\nmtYBcM9I0f9/ka45JIRp5Y1NKpAMFSShs7Wv0m1JS1kXQHdJsPSmjmDKcwnBe3R/\nTU3foRRywR/3AJRM15FNjTqvUm7TeaW16LkkRoECAwEAAaM4MDYwDAYDVR0TAQH/\nBAIwADAOBgNVHQ8BAf8EBAMCB4AwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwIwDQYJ\nKoZIhvcNAQEFBQADggEBADfY2DEmc2gb8/pqMNWHYq/nTYfJPpK4VA9A0lFTNeoq\nzmnbGwhKj24X+Nw8trsvkrKxHvCI1alDgBaCyzjGGvgOrh8X0wLtymp1yj6PWwee\nR2ZPdUaB62TCzO0iRv7W6o39ey+mU/FyYRtxF0ecxG2a0KNsIyFkciXUAeC5UVDo\nBNp678/SDDx9Ltuxc6h56a/hpBGf9Yzhr0RvYy3DmjBs6eopiGFmjnOKNxQrZ5t2\n339JWR+yiGEAtoHqk/fINMf1An6Rung1xYowrm4guhCIVi5unAvQ89fq0I6mzPg6\nLhTpeP0o+mVYrBmtYVpDpv0e71cfYowSJCCkod/9YbY=\n-----END
CERTIFICATE-----'
Example 3: public key published as JWKs:
version: v1
kind: RelyAuth
definition:
modes:
- mode: jwt
tokenLocation:
in: header
name: Authorization
scheme: bearer
claimsConfig:
namespace:
location: '"claims.jwt.hasura.io"'
claimsFormat: Json
key:
jwkFromUrl:
value: https://www.googleapis.com/service_accounts/v1/jwk/[email protected]
EdDSA based
If your auth server is using EdDSA to sign JWTs, and is using the Ed25519 variant key, the JWT config only needs to have the public key.
Example 1: public key in PEM format (not OpenSSH format):
version: v1
kind: RelyAuth
definition:
modes:
- mode: jwt
tokenLocation:
in: header
name: Authorization
scheme: bearer
claimsConfig:
namespace:
location: '"claims.jwt.hasura.io"'
claimsFormat: Json
key:
algorithm: Ed25519
key:
value:
'-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAG9I+toAAJicilbPt36tiC4wi7E1Dp9rMmfnwdKyVXi0=\n-----END PUBLIC
KEY-----'
Example 2: public key as X509 certificate:
version: v1
kind: RelyAuth
definition:
modes:
- mode: jwt
tokenLocation:
in: header
name: Authorization
scheme: bearer
claimsConfig:
namespace:
location: '"claims.jwt.hasura.io"'
claimsFormat: Json
key:
algorithm: Ed25519
key:
value:
'-----BEGIN CERTIFICATE
REQUEST-----\nMIIBAzCBtgIBADAnMQswCQYDVQQGEwJERTEYMBYGA1UEAwwPd3d3LmV4YW1wbGUu\nY29tMCowBQYDK2VwAyEA/9DV/InajW02Q0tC/tyr9mCSbSnNP1txICXVJrTGKDSg\nXDBaBgkqhkiG9w0BCQ4xTTBLMAsGA1UdDwQEAwIEMDATBgNVHSUEDDAKBggrBgEF\nBQcDATAnBgNVHREEIDAegg93d3cuZXhhbXBsZS5jb22CC2V4YW1wbGUuY29tMAUG\nAytlcANBAKbTqnTyPcf4ZkVuq2tC108pBGY19VgyoI+PP2wD2KaRz4QAO7Bjd+7S\nljyJoN83UDdtdtgb7aFgb611gx9W4go=\n-----END
CERTIFICATE REQUEST-----'
EC based
If your auth server is using ECDSA to sign JWTs, and is using the ES variant with a 256-bit key, the JWT config only needs to have the public key.
Example 1: public key in PEM format (not OpenSSH format):
version: v1
kind: RelyAuth
definition:
modes:
- mode: jwt
tokenLocation:
in: header
name: Authorization
scheme: bearer
claimsConfig:
namespace:
location: '"claims.jwt.hasura.io"'
claimsFormat: Json
key:
algorithm: ES256
key:
value:
'-----BEGIN PUBLIC
KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEEVs/o5+uQbTjL3chynL4wXgUg2R9\nq9UU8I5mEovUf86QZ7kOBIjJwqnzD1omageEHWwHdBO6B+dFabmdT9POxg==\n-----END
PUBLIC KEY-----'
Example 2: public key as X509 certificate:
version: v1
kind: RelyAuth
definition:
modes:
- mode: jwt
tokenLocation:
in: header
name: Authorization
scheme: bearer
claimsConfig:
namespace:
location: '"claims.jwt.hasura.io"'
claimsFormat: Json
key:
algorithm: ES256
key:
value:
'"-----BEGIN
CERTIFICATE-----\nMIIBbjCCARWgAwIBAgIUGn02F6Y6s88dDGmIfwiNxWxDjhswCgYIKoZIzj0EAwIw\nDTELMAkGA1UEBhMCSU4wHhcNMjMwNTI0MTAzNTI4WhcNMjgwNTIyMTAzNTI4WjAN\nMQswCQYDVQQGEwJJTjBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABBFbP6OfrkG0\n4y93Icpy+MF4FINkfavVFPCOZhKL1H/OkGe5DgSIycKp8w9aJmoHhB1sB3QTugfn\nRWm5nU/TzsajUzBRMB0GA1UdDgQWBBSaqFjzps1qG+x2DPISjaXTWsTOdDAfBgNV\nHSMEGDAWgBSaqFjzps1qG+x2DPISjaXTWsTOdDAPBgNVHRMBAf8EBTADAQH/MAoG\nCCqGSM49BAMCA0cAMEQCIBDHHWa/uLAVdGFEk82auTmw995+MsRwv52VXLw2Z+ji\nAiAXzOWIcGN8p25uhUN/7v9gEcADGIS4yUiv8gsn/Jk2ow==\n-----END
CERTIFICATE-----'
Example 3: public key published as JWKs:
version: v1
kind: RelyAuth
definition:
modes:
- mode: jwt
tokenLocation:
in: header
name: Authorization
scheme: bearer
claimsConfig:
namespace:
location: '"claims.jwt.hasura.io"'
claimsFormat: Json
key:
jwkFromUrl:
value: https://www.gstatic.com/iap/verify/public_key-jwk
Security considerations
Setting audience check
Certain JWT providers share JWKs between multiple tenants (like Firebase). They use the aud claim of JWT to specify
the intended tenant for the JWT. Setting the audience field in the JWT configuration will make sure that the aud
claim from the JWT is also checked during verification. Not doing this check will allow JWTs issued for other tenants to
be valid as well.
In these cases, you MUST set the audience field to appropriate value. Failing to do so is a major security
vulnerability.