Protecting API and frontend routes
This guide applies to scenarios which involve SuperTokens Session Access Tokens.
If you are implementing Unified Login, that uses OAuth2 Access Tokens, please check our separate page that shows you how to validate them.
You will have to check for the roles
claim in the token payload.
Protecting API routes
In your API routes you:
- Verify that a session exists
- Validate that the roles/permissions saved in the access token payload have the appropriate value
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.
}
);
- We add the
UserRoleClaim
validator to theverifySession
function which makes sure that the user has anadmin
role. - The
globalValidators
represents other validators that apply to all API routes by default. This may include a validator that enforces that the user's email is verified (if enabled by you). - We can also add a
PermissionClaim
validator to enforce a permission.
If you want to have more complex access control, you can either create your own validator, or you can get the roles list from the session as follows, and check the list yourself:
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..
});
Protecting 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, we show them 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 we are creating a generic component called AdminRoute
which enforces that its child components can only be rendered if the user has the admin role.
In the AdminRoute
component, we use the SessionAuth
wrapper to ensure that the session exists. We also add the UserRoleClaim
validator to the <SessionAuth>
component which checks if the validators pass or not. If all validation passes, we render the props.children
component. If the claim validation has failed, it will display the AccessDeniedScreen
component instead of rendering the children. You can also pass your own 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 be reused to protect all of 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..
}
}