Skip to main content

4. Checking for sessions in frontend routes

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.

Sessions with Client Components #

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

Using the SessionAuth wrapper component #

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.

caution

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 next section of this page to learn how to use sessions on the server side.

Using useSessionContext#

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.

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.

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.

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:

app/components/home.tsx
import { getSSRSession } from "supertokens-node/nextjs";
import { SessionContainer } from "supertokens-node/recipe/session";
import { cookies, headers } from "next/headers";
import { redirect } from "next/navigation";
import { TryRefreshComponent } from "./tryRefreshClientComponent";
import { SessionAuthForNextJS } from "./sessionAuthForNextJS";
import { ensureSuperTokensInit } from "../config/backend";

ensureSuperTokensInit();

async function getSSRSessionHelper(): Promise<{ session: SessionContainer | undefined; hasToken: boolean; hasInvalidClaims: boolean, error: Error | undefined }> {
let session: SessionContainer | undefined;
let hasToken = false;
let hasInvalidClaims = false;
let error: Error | undefined = undefined;

try {
({ session, hasToken, hasInvalidClaims } = await getSSRSession(cookies().getAll(), headers()));
} catch (err: any) {
error = err;
}
return { session, hasToken, hasInvalidClaims, error };
}

export async function HomePage() {
const { session, hasToken, hasInvalidClaims, error } = await getSSRSessionHelper();

if (error) {
return (
<div>
Something went wrong while trying to get the session. Error - {error.message}
</div>
)
}

// `session` will be undefined if it does not exist or has expired
if (!session) {
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");
}

/**
* `hasInvalidClaims` indicates that session claims did not pass validation. For example if email
* verification is required but the user's email has not been verified.
*/
if (hasInvalidClaims) {
/**
* This will make sure that the user is redirected based on their session claims. For example they
* will be redirected to the email verification screen if needed.
*
* We pass in no children in this case to prevent hydration issues and still be able to redirect the
* user.
*/
return <SessionAuthForNextJS />;
} else {
/**
* 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.
*/
return <TryRefreshComponent />;
}
}

/**
* 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: {session.getUserId()}
</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

app/page.tsx
import styles from './page.module.css'
import { HomePage } from "./components/home";

export default function Home() {
return (
<main className={styles.main}>
<HomePage />
</main>
)
}
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.

important

An example of this can be seen here.