Protect frontend routes
Protect frontend routes by requiring user sessions and verifying session claims for access control.
Protect frontend routes by requiring user sessions and verifying session claims for access control.
Before you start
Protect a route
You can wrap your components with the <SessionAuth> react component.
This ensures that your component renders only if the user has logged in.
If they are not logged in, the user gets redirected to the login page.
You can use the doesSessionExist function to check if a session exists in all your routes.
import React from "react";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import { SuperTokensWrapper } from "supertokens-auth-react";
import { SessionAuth } from "supertokens-auth-react/recipe/session";
import MyDashboardComponent from "./dashboard";
class App extends React.Component {
render() {
return (
<SuperTokensWrapper>
<BrowserRouter>
<Routes>
<Route
path="/dashboard"
element={
<SessionAuth>
{/*Components that require to be protected by authentication*/}
<MyDashboardComponent />
</SessionAuth>
}
/>
</Routes>
</BrowserRouter>
</SuperTokensWrapper>
);
}
}import Session from "supertokens-web-js/recipe/session";
async function doesSessionExist() {
if (await Session.doesSessionExist()) {
// user is logged in
} else {
// user has not logged in yet
}
}Optional session requirement
You can provide the requireAuth={false} prop when using <SessionAuth> as shown below:
import React from "react";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import { SuperTokensWrapper } from "supertokens-auth-react";
import Session, { SessionAuth } from "supertokens-auth-react/recipe/session";
class App extends React.Component {
render() {
return (
<SuperTokensWrapper>
<BrowserRouter>
<Routes>
<Route
path="/dashboard"
element={
<SessionAuth requireAuth={false}>
<MyDashboardComponent />
</SessionAuth>
}
/>
</Routes>
</BrowserRouter>
</SuperTokensWrapper>
);
}
}
function MyDashboardComponent(props: any) {
let sessionContext = Session.useSessionContext();
if (sessionContext.loading) {
return null;
}
if (sessionContext.doesSessionExist) {
// TODO:
} else {
// TODO:
}
return null;
}Check the claims of a session
Sometimes, you may also want to check if there are certain claims in the session before granting access to a route. For example, you may want to check that the session has the admin role claim for certain APIs, or that the user has completed 2FA.
You can achieve this using the session claims validator feature. Let’s take an example of using the user roles claim to check if the session has the admin claim:
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>
);
};import Session from "supertokens-web-js/recipe/session";
import { UserRoleClaim /*PermissionClaim*/ } from "supertokens-web-js/recipe/userroles";
async function shouldLoadRoute(): Promise<boolean> {
if (await Session.doesSessionExist()) {
let validationErrors = await Session.validateClaims({
overrideGlobalClaimValidators: (globalValidators) => [
...globalValidators,
UserRoleClaim.validators.includes("admin"),
/* PermissionClaim.validators.includes("modify") */
],
});
if (validationErrors.length === 0) {
// user is an admin
return true;
}
for (const err of validationErrors) {
if (err.id === UserRoleClaim.id) {
// user roles claim check failed
} else {
// some other claim check failed (from the global validators list)
}
}
}
// either a session does not exist, or one of the validators failed.
// so we do not allow access to this page.
return false;
}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 your own custom component to the accessDeniedScreen prop.
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:
- We call the
validateClaimsfunction with theUserRoleClaimvalidator which makes sure that the user has anadminrole. - The
globalValidatorsrepresents other validators that apply to all calls to thevalidateClaimsfunction. This may include a validator that enforces that you have verified the user’s email (if enabled by you). - We can also add a
PermissionClaimvalidator to enforce a permission.
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..
}
}import Session from "supertokens-web-js/recipe/session";
import { UserRoleClaim } from "supertokens-web-js/recipe/userroles";
async function shouldLoadRoute(): Promise<boolean> {
if (await Session.doesSessionExist()) {
let roles = await Session.getClaimValue({ claim: UserRoleClaim });
if (Array.isArray(roles) && roles.includes("admin")) {
// User is an admin
return true;
}
}
// either a session does not exist, or the user is not an admin
return false;
}Protect a route
You can use the doesSessionExist function to check if a session exists in all your routes.
import Session from "supertokens-web-js/recipe/session";
async function doesSessionExist() {
if (await Session.doesSessionExist()) {
// user is logged in
} else {
// user has not logged in yet
}
}async function doesSessionExist() {
if (await supertokensSession.doesSessionExist()) {
// user is logged in
} else {
// user has not logged in yet
}
}import SuperTokens from "supertokens-react-native";
async function doesSessionExist() {
if (await SuperTokens.doesSessionExist()) {
// user is logged in
} else {
// user has not logged in yet
}
}import android.app.Application
import com.supertokens.session.SuperTokens
import org.json.JSONObject
class MainApplication: Application() {
fun doesSessionExist() {
if (!SuperTokens.doesSessionExist(this)) {
// user has not logged in yet
return
}
try {
SuperTokens.getAccessTokenPayloadSecurely(this)
// user is logged in
} catch (error: java.io.IOException) {
// the session expired, refresh failed, or the payload could not be read
}
}
}import UIKit
import SuperTokensIOS
fileprivate class ViewController: UIViewController {
func doesSessionExist() {
if let accessTokenPayload: [String: Any] = try? SuperTokens.getAccessTokenPayloadSecurely() {
// user is logged in
} else {
// user has not logged
}
}
}import 'package:supertokens_flutter/supertokens.dart';
Future<void> doesSessionExist() async {
if (!await SuperTokens.doesSessionExist()) {
// user has not logged in yet
return;
}
try {
await SuperTokens.getAccessTokenPayloadSecurely();
// user is logged in
} catch (error) {
// the session expired, refresh failed, or the payload could not be read
}
}Check the claims of a session
Sometimes, you may also want to check if there are certain claims in the session before granting access to a route. For example, you may want to check that the session has the admin role claim for certain APIs, or that the user has completed 2FA.
You can achieve this using the session claims validator feature. Let’s take an example of using the user roles claim to check if the session has the admin claim:
import Session from "supertokens-web-js/recipe/session";
import { UserRoleClaim /*PermissionClaim*/ } from "supertokens-web-js/recipe/userroles";
async function shouldLoadRoute(): Promise<boolean> {
if (await Session.doesSessionExist()) {
let validationErrors = await Session.validateClaims({
overrideGlobalClaimValidators: (globalValidators) => [
...globalValidators,
UserRoleClaim.validators.includes("admin"),
/* PermissionClaim.validators.includes("modify") */
],
});
if (validationErrors.length === 0) {
// user is an admin
return true;
}
for (const err of validationErrors) {
if (err.id === UserRoleClaim.id) {
// user roles claim check failed
} else {
// some other claim check failed (from the global validators list)
}
}
}
// either a session does not exist, or one of the validators failed.
// so we do not allow access to this page.
return false;
}async function shouldLoadRoute(): Promise<boolean> {
if (await supertokensSession.doesSessionExist()) {
let validationErrors = await supertokensSession.validateClaims({
overrideGlobalClaimValidators: (globalValidators) => [
...globalValidators,
supertokensUserRoles.UserRoleClaim.validators.includes("admin"),
/* supertokensUserRoles.PermissionClaim.validators.includes("modify") */
],
});
if (validationErrors.length === 0) {
// user is an admin
return true;
}
for (const err of validationErrors) {
if (err.id === supertokensUserRoles.UserRoleClaim.id) {
// user roles claim check failed
} else {
// some other claim check failed (from the global validators list)
}
}
}
// either a session does not exist, or one of the validators failed.
// so we do not allow access to this page.
return false;
}import SuperTokens from "supertokens-react-native";
async function getRole() {
if (await SuperTokens.doesSessionExist()) {
let roles: string[] = (await SuperTokens.getAccessTokenPayloadSecurely())["st-role"].v;
if (roles.includes("admin")) {
// TODO..
} else {
// TODO..
}
}
}import android.app.Application
import com.supertokens.session.SuperTokens
import org.json.JSONArray
import org.json.JSONObject
class MainApplication: Application() {
fun checkIfUserIsAnAdmin() {
if (!SuperTokens.doesSessionExist(this)) return
try {
val accessTokenPayload: JSONObject = SuperTokens.getAccessTokenPayloadSecurely(this)
val rolesJson: JSONArray = accessTokenPayload.getJSONObject("st-role").getJSONArray("v")
val roles = (0 until rolesJson.length()).map { rolesJson.getString(it) }
if (roles.contains("admin")) {
// user is an admin
} else {
// user is not an admin
}
} catch (error: java.io.IOException) {
// the session expired, refresh failed, or the payload could not be read
}
}
}import UIKit
import SuperTokensIOS
fileprivate class ViewController: UIViewController {
func checkIfUserIsAnAdmin() {
if let accessTokenPayload: [String: Any] = try? SuperTokens.getAccessTokenPayloadSecurely(), let roleObject: [String: Any] = accessTokenPayload["st-role"] as? [String: Any], let roles: [String] = roleObject["v"] as? [String] {
if roles.contains("admin") {
// user is an admin
} else {
// user is not an admin
}
}
}
}import 'package:supertokens_flutter/supertokens.dart';
Future<void> checkIfUserIsAnAdmin() async {
if (!await SuperTokens.doesSessionExist()) return;
try {
final accessTokenPayload = await SuperTokens.getAccessTokenPayloadSecurely();
if (accessTokenPayload.containsKey("st-role")) {
final roleObject = accessTokenPayload["st-role"] as Map<String, dynamic>;
if (roleObject.containsKey("v")) {
final roles = (roleObject["v"] as List<dynamic>).whereType<String>().toList();
if (roles.contains("admin")) {
// user is an admin
} else {
// user is not an admin
}
}
}
} catch (error) {
// the session expired, refresh failed, or the payload could not be read
}
}- We call the
validateClaimsfunction with theUserRoleClaimvalidator which makes sure that the user has anadminrole. - The
globalValidatorsrepresents other validators that apply to all calls to thevalidateClaimsfunction. This may include a validator that enforces that you have verified the user’s email (if enabled by you). - We can also add a
PermissionClaimvalidator to enforce a permission.
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:
- We call the
validateClaimsfunction with theUserRoleClaimvalidator which makes sure that the user has anadminrole. - The
globalValidatorsrepresents other validators that apply to all calls to thevalidateClaimsfunction. This may include a validator that enforces that you have verified the user’s email (if enabled by you). - We can also add a
PermissionClaimvalidator to enforce a permission.
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-web-js/recipe/session";
import { UserRoleClaim } from "supertokens-web-js/recipe/userroles";
async function shouldLoadRoute(): Promise<boolean> {
if (await Session.doesSessionExist()) {
let roles = await Session.getClaimValue({ claim: UserRoleClaim });
if (roles !== undefined && roles.includes("admin")) {
// User is an admin
return true;
}
}
// either a session does not exist, or the user is not an admin
return false;
}async function shouldLoadRoute(): Promise<boolean> {
if (await supertokensSession.doesSessionExist()) {
let roles = await supertokensSession.getClaimValue({ claim: supertokensUserRoles.UserRoleClaim });
if (roles !== undefined && roles.includes("admin")) {
// User is an admin
return true;
}
}
// either a session does not exist, or the user is not an admin
return false;
}