---
title: Protect frontend routes
description: Protect frontend routes by requiring user sessions and verifying session claims for access control.
sidebar:
  order: 2
---

Protect frontend routes by requiring user sessions and verifying session claims for access control.

:::caution[Frontend guards are for user experience only]
Users can bypass client-side route guards or modify client-side state. Protect every API used by these pages with
backend session verification and the required role, permission, MFA, or verification claim validators. A frontend check
may control rendering or navigation, but it is not an authorization boundary.
:::


## Before you start

:::info[Access token guidance]
This guide applies to scenarios involving **SuperTokens Session Access Tokens**.
:::

---

<br />

<UITypeSwitch />

<VariantContent storageKey="ui-type" value="prebuilt">

## Protect a route

<DependentContent passive group="frontend-prebuilt-ui">
<ContentOption title="Reactjs" value="reactjs">
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.
</ContentOption>
<ContentOption title="Angular" value="angular">
You can use the `doesSessionExist` function to check if a session exists in all your routes.
</ContentOption>
</DependentContent>

<CodeGroup group="frontend-prebuilt-ui">
<Tab title="Reactjs" value="reactjs">
```tsx check=false reason="application example imports local modules defined elsewhere"
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>
    );
  }
}
```
</Tab>
<Tab title="Angular" value="angular">
```tsx
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
  }
}
```
</Tab>
</CodeGroup>

<DependentContent passive group="frontend-prebuilt-ui">
<ContentOption title="Reactjs" value="reactjs">
### Optional session requirement

You can provide the `requireAuth={false}` prop when using `<SessionAuth>` as shown below:
</ContentOption>
</DependentContent>

<CodeGroup passive group="frontend-prebuilt-ui">
<Tab title="Reactjs" value="reactjs">
```tsx
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;
}
```
</Tab>
</CodeGroup>

## 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:

<CodeGroup group="frontend-prebuilt-ui">
<Tab title="Reactjs" value="reactjs">
```tsx
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>
  );
};
```
</Tab>
<Tab title="Angular" value="angular">
```tsx
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;
}
```
</Tab>
</CodeGroup>

<DependentContent passive group="frontend-prebuilt-ui">
<ContentOption title="Reactjs" value="reactjs">
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.

:::note[You can extend the `AdminRoute` component to check for other types of validators as well.]
You can then reuse this component 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:
</ContentOption>
<ContentOption title="Angular" value="angular">
- We call the `validateClaims` function with the `UserRoleClaim` validator which makes sure that the user has an `admin` role.
- The `globalValidators` represents other validators that apply to all calls to the `validateClaims` function. This may include a validator that enforces that you have verified the user's email (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 get the roles list from the session as follows, and check the list yourself:
</ContentOption>
</DependentContent>

<CodeGroup passive group="frontend-prebuilt-ui">
<Tab title="Reactjs" value="reactjs">
```tsx
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..
  }
}
```
</Tab>
<Tab title="Angular" value="angular">
```tsx
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;
}
```
</Tab>
</CodeGroup>

</VariantContent>

<VariantContent storageKey="ui-type" value="custom">


## Protect a route


You can use the `doesSessionExist` function to check if a session exists in all your routes.


<CodeGroup group="frontend-custom-ui">
<Tab title="Web" value="web">
<DependentContent group="install-method" label="Installation method">
<ContentOption title="npm" value="npm">
```tsx
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
  }
}
```
</ContentOption>
<ContentOption title="Script tag" value="script-tag">
```tsx check=false reason="script-tag example relies on globals provided by loaded SuperTokens bundles"
async function doesSessionExist() {
  if (await supertokensSession.doesSessionExist()) {
    // user is logged in
  } else {
    // user has not logged in yet
  }
}
```
</ContentOption>
</DependentContent>
</Tab>
<Tab title="Mobile" value="mobile">
<DependentContent group="mobile-frameworks" label="Mobile framework">
<ContentOption title="ReactNative" value="reactnative">
```tsx
import SuperTokens from "supertokens-react-native";

async function doesSessionExist() {
  if (await SuperTokens.doesSessionExist()) {
    // user is logged in
  } else {
    // user has not logged in yet
  }
}
```
</ContentOption>
<ContentOption title="Android" value="android">
```kotlin
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
        }
    }
}
```
</ContentOption>
<ContentOption title="iOS" value="ios">
```swift
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 
        }
    }
}
```
</ContentOption>
<ContentOption title="Flutter" value="flutter">
```dart
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
  }
}
```
</ContentOption>
</DependentContent>
</Tab>
</CodeGroup>




---

## 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:


<CodeGroup group="frontend-custom-ui">
<Tab title="Web" value="web">
<DependentContent group="install-method" label="Installation method">
<ContentOption title="npm" value="npm">
```tsx
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;
}
```
</ContentOption>
<ContentOption title="Script tag" value="script-tag">
```tsx check=false reason="script-tag example relies on globals provided by loaded SuperTokens bundles"
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;
}
```
</ContentOption>
</DependentContent>
</Tab>
<Tab title="Mobile" value="mobile">
<DependentContent group="mobile-frameworks" label="Mobile framework">
<ContentOption title="ReactNative" value="reactnative">
```tsx
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..
    }
  }
}
```
</ContentOption>
<ContentOption title="Android" value="android">
```kotlin
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
        }
    }
}
```
</ContentOption>
<ContentOption title="iOS" value="ios">
```swift
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
            }
        }
    }
}
```
</ContentOption>
<ContentOption title="Flutter" value="flutter">
```dart
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
  }
}
```
</ContentOption>
</DependentContent>
</Tab>
</CodeGroup>

<DependentContent passive group="frontend-custom-ui">
<ContentOption title="Web" value="web">
<DependentContent passive group="install-method">
<ContentOption title="npm" value="npm">
- We call the `validateClaims` function with the `UserRoleClaim` validator which makes sure that the user has an `admin` role.
- The `globalValidators` represents other validators that apply to all calls to the `validateClaims` function.
This may include a validator that enforces that you have verified the user's email (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 get the roles list from the session as follows, and check the list yourself:
</ContentOption>
<ContentOption title="Script tag" value="script-tag">
- We call the `validateClaims` function with the `UserRoleClaim` validator which makes sure that the user has an `admin` role.
- The `globalValidators` represents other validators that apply to all calls to the `validateClaims` function. This may include a validator that enforces that you have verified the user's email (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 get the roles list from the session as follows, and check the list yourself:
</ContentOption>
</DependentContent>
</ContentOption>
</DependentContent>

<CodeGroup passive group="frontend-custom-ui">
<Tab title="Web" value="web">
<DependentContent group="install-method" label="Installation method">
<ContentOption title="npm" value="npm">
```tsx
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;
}
```
</ContentOption>
<ContentOption title="Script tag" value="script-tag">
```tsx check=false reason="script-tag example relies on globals provided by loaded SuperTokens bundles"
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;
}
```
</ContentOption>
</DependentContent>
</Tab>
</CodeGroup>



</VariantContent>

:::tip[Feature]
You can also [build your own custom claim validators](/additional-verification/session-verification/claim-validation#using-session-claims) based on your app's requirements.
:::

---

## See also

<CardGroup cols={3}>
  <Card title="Protect backend routes" href="/additional-verification/session-verification/protect-api-routes" />
  <Card title="Claim validation" href="/additional-verification/session-verification/claim-validation" />
  <Card title="WebSockets authentication" href="/additional-verification/session-verification/with-websocket" />
  <Card title="Access session data" href="/post-authentication/session-management/access-session-data" />
</CardGroup>
