Claim validation
Overview
SuperTokens provides two approaches for managing access control:
- Session Claims: An abstraction that includes automatic validation and refresh capabilities
- Access Token Payload: A basic way to check the token payload
In most of the cases the recommended way is to use session claims. You can use next table to understand the differences between the two approaches.
This guide shows you how to use each method.
References
Before you start
This guide only applies to scenarios which involve SuperTokens Session Access Tokens.
Using session claims
SuperTokens sessions have a property called accessTokenPayload
.
This is a JSON
object which you can access on the frontend and backend.
The key-values in this JSON payload refer to claims.
1. Create a custom claim
import { BooleanClaim } from "supertokens-node/recipe/session/claims";
const SecondFactorClaim = new BooleanClaim({
key: "2fa-completed",
fetchValue: () => false,
});
2. Add claim validators
Backend global validation
import SuperTokens from "supertokens-node";
import Session from "supertokens-node/recipe/session";
SuperTokens.init({
recipeList: [
Session.init({
override: {
functions: (originalImplementation) => {
return {
...originalImplementation,
getGlobalClaimValidators: async function (input) {
return [...input.claimValidatorsAddedByOtherRecipes, SecondFactorClaim.validators.isTrue()];
},
};
},
},
}),
],
});
Backend route-specific validation
import express from "express";
import { verifySession } from "supertokens-node/recipe/session/framework/express";
import { UserRoleClaim } from "supertokens-node/recipe/userroles";
let app = express();
app.post(
"/admin-only",
verifySession({
overrideGlobalClaimValidators: async (globalValidators) => [
...globalValidators,
UserRoleClaim.validators.includes("admin"),
],
}),
async (req, res) => {
// Only admin users can access this endpoint
},
);
Frontend validation
import React from "react";
import { SessionAuth } from "supertokens-auth-react/recipe/session";
import { UserRoleClaim } from "supertokens-auth-react/recipe/userroles";
const AdminRoute = (props: React.PropsWithChildren<any>) => {
return (
<SessionAuth
overrideGlobalClaimValidators={(globalValidators) => [
...globalValidators,
UserRoleClaim.validators.includes("admin"),
]}
>
{props.children}
</SessionAuth>
);
};
3. Handle validation failures
Backend custom error handling
import { Error as STError } from "supertokens-node/recipe/session";
if (roles === undefined || !roles.includes("admin")) {
throw new STError({
type: "INVALID_CLAIMS",
message: "User is not an admin",
payload: [
{
id: UserRoleClaim.key,
},
],
});
}
Frontend redirection
const AdminRoute = (props: React.PropsWithChildren<any>) => {
return (
<SessionAuth
overrideGlobalClaimValidators={(globalValidators) => [
...globalValidators,
{
...UserRoleClaim.validators.includes("admin"),
onFailureRedirection: () => "/not-an-admin",
},
]}
>
{props.children}
</SessionAuth>
);
};
Using the Access Token Payload
The access token payload is a simple way to store custom data that needs to be accessible on both the frontend and the backend.
1. Add Custom Claims to the Access Token Payload
The access token payload has a set of default claims that can not be overwritten.
They reserve these for standard or SuperTokens specific use-cases.
Those claims are: sub
, iat
, exp
, sessionHandle
, refreshTokenHash1
, parentRefreshTokenHash1
, antiCsrfToken
Trying to overwrite these values results in errors in the authentication flow process.
You can add custom claims to the access token payload in two ways:
During session creation
import SuperTokens from "supertokens-node";
import Session from "supertokens-node/recipe/session";
SuperTokens.init({
supertokens: {
connectionURI: "...",
},
appInfo: {
apiDomain: "...",
appName: "...",
websiteDomain: "...",
},
recipeList: [
Session.init({
override: {
functions: (originalImplementation) => {
return {
...originalImplementation,
createNewSession: async function (input) {
let userId = input.userId;
// This goes in the access token, and is available to read on the frontend.
input.accessTokenPayload = {
...input.accessTokenPayload,
someKey: "someValue",
};
return originalImplementation.createNewSession(input);
},
};
},
},
}),
],
});
Post Session Creation
import express from "express";
import { verifySession } from "supertokens-node/recipe/session/framework/express";
import { SessionRequest } from "supertokens-node/framework/express";
let app = express();
app.post("/updateinfo", verifySession(), async (req: SessionRequest, res) => {
let session = req.session;
await session!.mergeIntoAccessTokenPayload({ newKey: "newValue" });
res.json({ message: "successfully updated access token payload" });
});
2. Read the Access Token Payload
On the backend
import express from "express";
import { verifySession } from "supertokens-node/recipe/session/framework/express";
let app = express();
app.get("/myApi", verifySession(), async (req, res) => {
let session = req.session;
let accessTokenPayload = session.getAccessTokenPayload();
let customClaimValue = accessTokenPayload.customClaim;
});
On the frontend
import Session from "supertokens-auth-react/recipe/session";
async function someFunc() {
if (await Session.doesSessionExist()) {
let accessTokenPayload = await Session.getAccessTokenPayloadSecurely();
let customClaimValue = accessTokenPayload.customClaim;
}
}