Authentication using JWTs
Using SuperTokens with Hasura requires you to host your own API layer that uses our Backend SDK. If you do not want to host your own server you can use a serverless environment (AWS Lambda for example) to achieve this.
This guide will only work if you are using SuperTokens Session Tokens.
If you are implementing an OAuth2 based feature, like Microservice Authentication or Unified Login, the method of adding custom claims is different. Please check our separate page that shows you how to do this.
1. Complete the setup guides
Follow the quicstart guides to add SuperTokens.
This involves setting up the frontend SDK, the backend SDK, and the SuperTokens core. With this, you should have login / session refreshing / sign out setup on your website. The next steps will be about how to authenticate API calls to Hasura using the SuperTokens session.
2. Expose the access token to the frontend
For cookie based auth, the access token is not available on the frontend by default. In order to expose it, you need to set the exposeAccessTokenToFrontendInCookieBasedAuth
config to true
.
import SuperTokens from "supertokens-node";
import Session from "supertokens-node/recipe/session";
SuperTokens.init({
supertokens: {
connectionURI: "..."
},
appInfo: {
apiDomain: "...",
appName: "...",
websiteDomain: "..."
},
recipeList: [
Session.init({
exposeAccessTokenToFrontendInCookieBasedAuth: true
})
]
});
3. Add custom claims to the JWT
Hasura requires claims to be set in a specific way, read the official documentation to know more.
import SuperTokens from "supertokens-node";
import Session from "supertokens-node/recipe/session";
SuperTokens.init({
supertokens: {
connectionURI: "...",
},
appInfo: {
apiDomain: "...",
appName: "...",
websiteDomain: "..."
},
recipeList: [
Session.init({
exposeAccessTokenToFrontendInCookieBasedAuth: true,
override: {
functions: function (originalImplementation) {
return {
...originalImplementation,
createNewSession: async function (input) {
input.accessTokenPayload = {
...input.accessTokenPayload,
"https://hasura.io/jwt/claims": {
"x-hasura-user-id": input.userId,
"x-hasura-default-role": "user",
"x-hasura-allowed-roles": ["user"],
}
};
return originalImplementation.createNewSession(input);
},
};
}
},
})
]
});
4. Configure Hasura environment variables
Read the official documentation to know about setting the JWT secret environment variable on Hasura
To use JWT based authentication, Hasura requires setting environment variables when configuring your app. With SuperTokens this can be done in 2 ways:
Using the JWKS endpoint
When configuring Hasura, you can set the jwk_url
property.
{
"jwk_url": "{apiDomain}/{apiBasePath}/jwt/jwks.json"
}
You can get the jwks URL for your backend by using the method explained here
Using a key string
Hasura let's you provide a PEM string in the configuration. Refer to this page to know how to get a public key as a string, you can then use that key string in the Hasura config:
{
"type": "RS256",
"key": "CERTIFICATE_STRING",
}
5. Checking for claim values in Hasura
Some checks like if the email is verified, or if 2FA is completed are stored as claim values in the JWT. You should check for the values of these claims in your graphql functions wherever required. For example, if one of your graphql functions requires that the user's email is verified, then it should check for the jwt payload's st-ev
claim value to be {v: true, t:...}
, else it should reject the request.
You can also use a custom Hasura authorizer webhook to check for the vlaues of these claims depending on your app's requirements.
This is required because SuperTokens issues JWTs immediately after the user signs up / logs in, regardless of if all the authorisation checks pass or not. Functions exposed by our SDK like verifySession
or getSession
do these authorisation checks on their own, but since these functions are not used in the Hasura flow, you will have to check them on your own.
6. Making requests to Hasura
a. Getting the JWT on the frontend
import Session from "supertokens-web-js/recipe/session";
async function getToken(): Promise<void> {
const accessToken = await Session.getAccessToken();
console.log(accessToken);
}
b. Making HTTP requests
import axios from "axios";
async function makeRequest() {
let url = "...";
let jwt = "..."; // Refer to step 5.a
let response = await axios.get(url, {
headers: {
"Authorization": `Bearer ${jwt}`,
},
});
}
During Local development
If you are using Hasura cloud and testing your backend APIs in your local environment, JWT verification will fail because Hasura will not be able to query the JWKS endpoint (because the cloud can not query your local environment i.e localhost, 127.0.0.1).
To solve this problem you will need to expose your locally hosted backend APIs to the internet. For example you can use ngrok. After that, you need to configure Hasura to use the {ngrokURL}/{apiBasePath}/jwt/jwks.json
as the JWKS endpoint (explained in step 4)