Protect frontend and backend routes
Overview
To limit access to your application resources based on roles and permissions you have to use the UserRoleClaim
inside the session validation logic.
Before you start
This guide only applies to scenarios which involve SuperTokens Session Access Tokens.
If you are implementing Unified Login, which uses OAuth2 Access Tokens, please check the separate page that shows you how to validate them.
You have to check for the roles
claim in the token payload.
Protect backend routes
Override the global claim validators to integrate role verification in the standard flow.
The GlobalValidators
represents other validators that apply to all API routes by default.
This may include a validator that enforces that the user has verified their email.
To perform the verification follow these steps:
- Add the
UserRoleClaim
validator to theVerify Session
function which makes sure that the user has specific roles. - Optionally, add a
PermissionClaim
validator to enforce a permission.
import { verifySession } from "supertokens-node/recipe/session/framework/express";
import express from "express";
import { SessionRequest } from "supertokens-node/framework/express";
import UserRoles from "supertokens-node/recipe/userroles";
let app = express();
app.post(
"/update-blog",
verifySession({
overrideGlobalClaimValidators: async (globalValidators) => [
...globalValidators,
UserRoles.UserRoleClaim.validators.includes("admin"),
// UserRoles.PermissionClaim.validators.includes("edit")
],
}),
async (req: SessionRequest, res) => {
// All validator checks have passed and the user is an admin.
}
);
Custom validation
If you want to have more complex access control you can get the list of roles attached to the session and introduce your own logic.
import express from "express";
import { verifySession } from "supertokens-node/recipe/session/framework/express";
import { SessionRequest } from "supertokens-node/framework/express";
import UserRoles from "supertokens-node/recipe/userroles";
import { Error as STError } from "supertokens-node/recipe/session"
let app = express();
app.post("/update-blog", verifySession(), async (req: SessionRequest, res) => {
const roles = await req.session!.getClaimValue(UserRoles.UserRoleClaim);
if (roles === undefined || !roles.includes("admin")) {
// this error tells SuperTokens to return a 403 to the frontend.
throw new STError({
type: "INVALID_CLAIMS",
message: "User is not an admin",
payload: [{
id: UserRoles.UserRoleClaim.key
}]
})
}
// user is an admin..
});
Protect frontend routes
On your frontend:
- Verify that a session exists
- Use the roles / permissions claim validators to enforce certain roles and permissions.
- If the user doesn't have the right roles, the system shows an error message indicating they don't have access.
What type of UI are you using?
import React from "react";
import { SessionAuth } from 'supertokens-auth-react/recipe/session';
import { AccessDeniedScreen } from 'supertokens-auth-react/recipe/session/prebuiltui';
import { UserRoleClaim, /*PermissionClaim*/ } from 'supertokens-auth-react/recipe/userroles';
const AdminRoute = (props: React.PropsWithChildren<any>) => {
return (
<SessionAuth
accessDeniedScreen={AccessDeniedScreen}
overrideGlobalClaimValidators={(globalValidators) => [
...globalValidators, UserRoleClaim.validators.includes("admin"),
]
}>
{props.children}
</SessionAuth>
);
}
Above, you create a generic component called AdminRoute
which enforces that its child components render only if the user has the admin role.
In the AdminRoute
component, the SessionAuth
wrapper ensures that the session exists. The UserRoleClaim
validator is also added to the <SessionAuth>
component which checks if the validators pass or not. If all validation passes, the props.children
component renders. If the claim validation has failed, it displays the AccessDeniedScreen
component instead of rendering the children. You can also pass a custom component to the accessDeniedScreen
prop.
You can extend the AdminRoute
component to check for other types of validators as well. This component can then reuse to protect all your app's components (In this case, you may want to rename this component to something more appropriate, like ProtectedRoute
).
If you want to have more complex access control, you can get the roles list from the session as follows, and check the list yourself:
import Session from "supertokens-auth-react/recipe/session";
import {UserRoleClaim} from "supertokens-auth-react/recipe/userroles"
function ProtectedComponent() {
let claimValue = Session.useClaimValue(UserRoleClaim)
if (claimValue.loading || !claimValue.doesSessionExist) {
return null;
}
let roles = claimValue.value;
if (Array.isArray(roles) && roles.includes("admin")) {
// User is an admin
} else {
// User doesn't have any roles, or is not an admin..
}
}