Skip to main content

Set up JWT mode with Hasura

Built-in Hasura JWT authentication

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.

Disclaimer

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.

auth.yaml
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.

KeyRequiredValue
x-hasura-default-roleYesThe role that will be used when the optional x-hasura-role header is not passed
x-hasura-allowed-rolesYesA list of allowed roles for the user making the request.
x-hasura-[custom]NoWhere [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.

Example JWT 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"
}
}

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.

Setting audience check

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:

auth.yaml
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 namespace and locations options. RelyAuth selects the session variables object at the namespace first then merge it with individual session variables in locations. 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.