---
title: 4. Checking for sessions in frontend routes
description: Learn to protect frontend routes by checking user sessions and handling session-related events.
sidebar:
  order: 5
---

:::warning[OAuth2 token verification]
Check authentication with your OAuth2/OIDC library when using Unified Login.
:::

Protecting a website route means that it cannot be accessed unless a user is signed in. If a non signed in user tries to access it, they will be redirected to the login page.

<UITypeSwitch />

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

## Sessions with Client Components

Lets create a client component for the `/` route of our website.

### Using the `SessionAuth` wrapper component

```tsx title="app/components/homeClientComponent.tsx"
"use client";

import { SessionAuth } from "supertokens-auth-react/recipe/session";

export const HomeClientComponent = () => {
  return (
    <SessionAuth>
      <div>Hello world</div>
    </SessionAuth>
  );
};
```

`SessionAuth` is a component exposed by the SuperTokens React SDK, it checks if a session exists and if it does not exist it will redirect the user to the login page. It also does session claim checking on the frontend and take appropriate action if the claim validators fail. For example, if you have set the email verification recipe to be `"REQUIRED"`, and the user's email is not verified, this component will redirect the user to the email verification page.

:::warning[At the moment the `SessionAuth` component does not support server side rendering and will only work on the client side. On the server side, this component renders an empty screen.]

Refer to the <a href="#sessions-with-server-components">next section</a> of this page to learn how to use sessions on the server side.
:::

### Using `useSessionContext`

```tsx title="app/components/homeClientComponent.tsx"
"use client";

import { useSessionContext } from "supertokens-auth-react/recipe/session";

export const HomeClientComponent = () => {
  const session = useSessionContext();

  if (session.loading) {
    return <div>Loading...</div>;
  }

  if (session.doesSessionExist === false) {
    return <div>Session does not exist</div>;
  }

  return (
    <div>
      <div>
        <p>
          Client side component got userId: {session.userId}
          <br />
        </p>
      </div>
    </div>
  );
};
```

`useSessionContext` lets you access the session information on the client side using the React Context API. `session.loading` indicates if the session is currently being loaded into the context, this will also refresh the session for you if it is expired. You can use `session.doesSessionExist` to check if a valid session exists and handle it accordingly.

:::info[`useSessionContext` does not need to be used along with `SessionAuth`. Since our app is wrapped by the `SuperTokensWrapper` component, the `useSessionContext` hook can be used in any of our components.]
:::

:::tip[Test by navigating to `/`]
You should be redirected to the login page. After that, sign in, and then visit `/` again. This time, there should be no redirection.
:::

## Sessions with Server Components

### Creating some helper Components

#### Creating a wrapper around `SessionAuth`
Let's say we want to protect the home page of your website (`/` route). First we will create a wrapper around the `SessionAuth` component to add the `"use client"` directive on top.

```tsx title="app/components/sessionAuthForNextJS.tsx"
"use client";

import React, { useState, useEffect } from "react";
import { SessionAuth } from "supertokens-auth-react/recipe/session";

type Props = Parameters<typeof SessionAuth>[0] & {
  children?: React.ReactNode | undefined;
};

export const SessionAuthForNextJS = (props: Props) => {
  const [loaded, setLoaded] = useState(false);
  useEffect(() => {
    setLoaded(true);
  }, []);
  if (!loaded) {
    return props.children;
  }
  return <SessionAuth {...props}>{props.children}</SessionAuth>;
};
```

This is a client component that renders just its children on the server side and renders the children wrapped with `SessionAuth` on the client side. This way, the server side returns the page content, and on the client, the same page content is wrapper with `SessionAuth` which will handle session related events on the frontend - for example, if the user's session expires whilst they are on this page, then `SessionAuth` will auto redirect them to the login page.

#### Creating the `TryRefreshComponent`

This component will refresh the user's session if their current session has expired.

```tsx title="app/components/tryRefreshClientComponent.tsx"
"use client";

import { useEffect, useState } from "react";
import { useRouter } from "next/navigation";
import Session from "supertokens-auth-react/recipe/session";
import SuperTokens from "supertokens-auth-react";

export const TryRefreshComponent = () => {
  const router = useRouter();
  const [didError, setDidError] = useState(false);

  useEffect(() => {
    /**
     * `attemptRefreshingSession` will call the refresh token endpoint to try and
     * refresh the session. This will throw an error if the session cannot be refreshed.
     */
    void Session.attemptRefreshingSession()
      .then((hasSession) => {
        /**
         * If the user has a valid session, we reload the page to restart the flow
         * with valid session tokens
         */
        if (hasSession) {
          router.refresh();
        } else {
          SuperTokens.redirectToAuth();
        }
      })
      .catch(() => {
        setDidError(true);
      });
  }, [router]);

  /**
   * We add this check to make sure we handle the case where the refresh API fails with
   * an unexpected error
   */
  if (didError) {
    return <div>Something went wrong, please reload the page</div>;
  }

  return <div>Loading...</div>;
};
```

### Using `SessionAuthForNextJS` and checking for sessions

We then create a server component that can check if the session exists and return any session information we may need:

```tsx title="app/components/home.tsx" check=false reason="Requires surrounding framework application context"
import { cookies } from "next/headers";
import { redirect } from "next/navigation";
import { getSSRSession } from "supertokens-node/nextjs";

import { TryRefreshComponent } from "./tryRefreshClientComponent";
import { SessionAuthForNextJS } from "./sessionAuthForNextJS";
import { ensureSuperTokensInit } from "../config/backend";

ensureSuperTokensInit();

export async function HomePage() {
  const cookieStore = await cookies();
  const { accessTokenPayload, hasToken, error } = await getSSRSession(cookieStore.getAll());

  if (error) {
    console.error("Unable to read the SSR session", { component: "HomePage" });
    return <div role="alert">Unable to verify your session. Please try again.</div>;
  }

  // `accessTokenPayload` will be undefined if it the session does not exist or has expired
  if (accessTokenPayload === undefined) {
    if (!hasToken) {
      /**
       * This means that the user is not logged in. If you want to display some other UI in this
       * case, you can do so here.
       */
      return redirect("/auth");
    }

    /**
     * This means that the session does not exist but we have session tokens for the user. In this case
     * the `TryRefreshComponent` will try to refresh the session.
     *
     * To learn about why the 'key' attribute is required refer to: https://github.com/supertokens/supertokens-node/issues/826#issuecomment-2092144048
     */
    return <TryRefreshComponent key={Date.now()} />;
  }

  /**
   * SessionAuthForNextJS will handle proper redirection for the user based on the different session states.
   * It will redirect to the login page if the session does not exist etc.
   */
  return (
    <SessionAuthForNextJS>
      <div>Your user id is: {accessTokenPayload.sub}</div>
    </SessionAuthForNextJS>
  );
}
```

The `TryRefreshComponent` is a client component that checks if a session exists and tries to refresh the session if it is expired.

And then we can modify the `/app/page.tsx` file to use our server component

```tsx title="app/page.tsx" check=false reason="Requires surrounding framework application context"
import styles from "./page.module.css";
import { HomePage } from "./components/home";

export default function Home() {
  return (
    <main className={styles.main}>
      <HomePage />
    </main>
  );
}
```

:::tip[Test by navigating to `/`]
You should be redirected to the login page. After that, sign in, and then visit `/` again. This time, there should be no redirection.
:::

:::note
An example of this can be seen [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/page.tsx).
:::

</VariantContent>

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

## Sessions with Client Components

Checking for sessions in client components involves:

- Using the `Session` recipe to manually check if a session exists, rendering some default UI while you check.
- Render your UI if a session exists.

To learn more about how to do this refer to [this page](/additional-verification/session-verification/protect-frontend-routes).

## Sessions with Server Components

### Creating a helper component for session refreshing

Lets start by creating a component that will refresh the session if it exists and has expired.

```tsx title="app/components/tryRefreshClientComponent.tsx"
"use client";

import { useEffect, useState } from "react";
import { useRouter } from "next/navigation";
import Session from "supertokens-web-js/recipe/session";

export const TryRefreshComponent = () => {
  const router = useRouter();
  const [didError, setDidError] = useState(false);

  useEffect(() => {
    void Session.attemptRefreshingSession()
      .then((hasSession) => {
        if (hasSession) {
          router.refresh();
        } else {
          /**
           * This means that the session is expired and cannot be refreshed.
           * In this example we redirect the user back to the login page.
           */
          router.replace("/auth");
        }
      })
      .catch(() => {
        setDidError(true);
      });
  }, [router]);

  if (didError) {
    return <div>Something went wrong, please reload the page</div>;
  }

  return <div>Loading...</div>;
};
```

`Session.attemptRefreshingSession` will call the refresh endpoint. `hasSession` will be:
- `true` if the session was refreshed
- `false` if the session could not be refreshed

### Modify home page to check for sessions

Lets modify the Home page server component we created earlier:

```tsx title="app/components/home.tsx" check=false reason="Requires surrounding framework application context"
import { cookies } from "next/headers";
import { redirect } from "next/navigation";
import { getSSRSession } from "supertokens-node/nextjs";

import { TryRefreshComponent } from "./tryRefreshClientComponent";
import { ensureSuperTokensInit } from "../config/backend";

ensureSuperTokensInit();

export async function HomePage() {
  const cookieStore = await cookies();
  const { accessTokenPayload, hasToken, error } = await getSSRSession(cookieStore.getAll());

  if (error) {
    console.error("Unable to read the SSR session", { component: "HomePage" });
    return <div role="alert">Unable to verify your session. Please try again.</div>;
  }

  // `accessTokenPayload` will be undefined if it the session does not exist or has expired
  if (accessTokenPayload === undefined) {
    if (!hasToken) {
      /**
       * This means that the user is not logged in. If you want to display some other UI in this
       * case, you can do so here.
       */
      return redirect("/auth");
    }

    /**
     * This means that the session does not exist but we have session tokens for the user. In this case
     * the `TryRefreshComponent` will try to refresh the session.
     *
     * To learn about why the 'key' attribute is required refer to: https://github.com/supertokens/supertokens-node/issues/826#issuecomment-2092144048
     */
    return <TryRefreshComponent key={Date.now()} />;
  }

  return <div>Your user id is: {accessTokenPayload.sub}</div>;
}
```

The `TryRefreshComponent` is a client component that checks if a session exists and tries to refresh the session if it is expired.

And then we can modify the `/app/page.tsx` file to use our server component

```tsx title="app/page.tsx" check=false reason="Requires surrounding framework application context"
import styles from "./page.module.css";
import { HomePage } from "./components/home";

export default function Home() {
  return (
    <main className={styles.main}>
      <HomePage />
    </main>
  );
}
```

:::tip[Test by navigating to `/`]
You should be redirected to the login page. After that, sign in, and then visit `/` again. This time, there should be no redirection.

For custom UI SuperTokens provides no login UI, the code above will redirect the user to the `/auth` route but you will have to build some UI that is served on that route.
:::

</VariantContent>
