Set up JWT mode with Hasura
JWT authentication is built-in supported in both Hasura DDN and GraphQL Engine edition. You may not need RelyAuth for JWT authentication except for advanced use cases such as multiple JWT secrets or complex transformation.
Because RelyAuth is inspired by Hasura JWT Auth's concepts, many sections are borrowed from the official Hasura docs.
Introduction
JWT mode requires that the client making the query sends a valid JSON Web Token to the API endpoint. This JWT is provided by an auth service such as Auth0, AWS Cognito, Firebase, Clerk, or your own custom solution.
RelyAuth then verifies and decodes the JWT to extract session variable claim values from a defined namespace in the token.
The x-hasura-default-role and x-hasura-allowed-roles session variables are required, and you will also most likely
utilize the user id and any other information which you need to determine access to your data.
The token can be passed in the header of the request in a dedicated key, or using a header, query, or as a cookie. All
these options are defined in the definitions array in your configuration.
Session variable requirements
Session variables passed via JWT or webhook can contain any information you want, but must at least contain an
x-hasura-default-role property and x-hasura-allowed-roles array.
An x-hasura-role value can optionally be sent as a plain header in the request to indicate the role which should be
used. If this is not provided, the engine will use the x-hasura-default-role value in the JWT.
To clarify, the x-hasura-role header is optional and can be used to override the default role in the JWT allowing the
same verified JWT to be used for different roles.
Only keys prefixed with x-hasura- will be accessible by the engine.
Session variable keys are case-insensitive. Values are case-sensitive.
Enabling JWT authentication
Step 1. Update Configurations
Add a jwt auth mode to the auth.yaml file.
Below, we're showing using the Authorization header location with bearer scheme using a fixed secret key from an
environment variable. However, RelyAuth supports other methods for
where the engine can locate the JWT and
how it is verified.
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
claimsConfig:
namespace:
location: '"claims.jwt.hasura.io"'
claimsFormat: Json
Read more about other setup options here.
Step 2. Define the JWT with custom claims
Your auth service should include an object with a key of claims.jwt.hasura.io in the JWT. Within this, each claim
should be prefixed with x-hasura-* and include the relevant information. Note that an extra optional x-hasura-role
header can be passed to override the default role found in the JWT's custom claims.
| Key | Required | Value |
|---|---|---|
x-hasura-default-role | Yes | The role that will be used when the optional x-hasura-role header is not passed |
x-hasura-allowed-roles | Yes | A list of allowed roles for the user making the request. |
x-hasura-[custom] | No | Where [custom] is any string you wish (e.g., org, user-id, customer). The value can be any JSON value. |
In the simple example below, we're including the required claims by stating the default role is admin and the list of
available roles is limited to user and admin. Additionally, we're passing a custom key of x-hasura-user-id which
can be used with permissions when executing queries.
Read more about the default claims here.
{
"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"
}
}
Your auth service will encode this object using a secret and create a token which can then be passed to the engine. You
can see an example of the above token encoded
here.
The signature secret to verify this token with the HS256 algorithm is ultra-secret-very-secret-super-secret-key.
Certain JWT providers (like Firebase) share JWKs between multiple tenants. They use the aud claim of JWT to specify
the intended tenant for the JWT. Setting the audience field in the Hasura 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. Learn how to set this here.
JWT Configuration
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-expiry:
path: exp
As a minimum, either the claimsConfig, tokenLocation, and key values have to be present.
Basically, the data structure of the configuration item is similar to the JWT authentication mode of Hasura DDN, except for several differences:
- Use JMESPath to lookup and transform session variables from locations. This query language is more powerful than JSON Pointer (Hasura DDN) and JSONPath (Hasura GraphQL Engine).
- Evaluate both
namespaceandlocationsoptions. RelyAuth selects the session variables object at thenamespacefirst then merge it with individual session variables inlocations. Hasura DDN only supports one of them. - Automatically reloading JSON web key from the remote URL by interval or if the key ID does not exist in the cache.
Next steps
If you're looking for step-by-step help to get started with common authentication providers, check this section of tutorials.