Integrate Sessions With The Frontend (React)
Store the user's session state, protect authenticated routes, and handle Unauthorized errors.
Now that your server manages the user's session and protects APIs, we'll go over how to store that session data into your frontend using the React Client Auth SDK. We'll also show how to use the authenticated state from the SDK's React Context to protect frontend routes that require authentication.
Install the React SDK
Install the React Client Auth SDK using your preferred package manager CLI:
npm install @wristband/react-client-auth
yarn add @wristband/react-client-auth
pnpm add @wristband/react-client-auth
Configure the SDK and Propagate Authenticated State
The SDK contains a WristbandAuthProvider
component that establishes and manages authentication state throughout your React frontend. It will call the Session Endpoint that was implemented earlier to retrieve the user's session data. If the call to the Session Endpoint succeeds, we can be sure that the user has successfully authenticated, and any pertinent session data will be stored in the Provider's React Context. However, if the call to the Session Endpoint returns a 401 status code, then we know that the user does not have a valid session, and the user will be redirected to the Login Endpoint.
WristbandAuthProvider
requires two URL endpoints:
loginUrl
: The URL of your backend server's Login Endpoint.sessionUrl
: The URL of your backend server's Session Endpoint.
Place the WristbandAuthProvider
at your app's root to ensure the user's authenticated state is available throughout your application and verified on initial load. Configure the component with the 2 URLs above.
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import { WristbandAuthProvider } from '@wristband/react-client-auth';
import './index.css';
import { App } from 'app';
const root = createRoot(document.getElementById('root'));
root.render(
<StrictMode>
<WristbandAuthProvider
loginUrl='<your-fastapi-login-endpoint-url>'
sessionUrl='<your-fastapi-session-endpoint-url>'
>
<App />
</WristbandAuthProvider>
</StrictMode>
);
Protect Frontend Routes and Components
The SDK exposes some hooks and utility functions that enable common patterns like conditional rendering of authenticated/unauthenticated views, route protection, or dynamic UI updates based on the user's auth status.
Hooks
useWristbandAuth()
: Get access to the user's authentication state throughout your app.useWristbandSession()
: Get access to the user data provided by your backend server's Session Endpoint.
Utility Functions
redirectToLogin()
: Redirects the user to your backend server's Login Endpoint.redirectToLogout()
: Redirects the user to your backend server's Logout Endpoint.
Below are several common patterns you can apply as needed.
Use The User's Authenticated State to Render Different Components
import React from 'react';
import {
useWristbandAuth, useWristbandSession, redirectToLogin, redirectToLogout
} from '@wristband/react-client-auth';
function App() {
const { isAuthenticated, isLoading } = useWristbandAuth();
const { userId, tenantId } = useWristbandSession();
if (isLoading) {
return <div>Loading...</div>;
}
const AuthenticatedView = () => (
<>
<h1>Welcome to Wristband Auth</h1>
<p>Your User ID: {userId}</p>
<p>Your Tenant ID: {tenantId}</p>
<button onClick={() => redirectToLogout('<<your-server-logout-endpoint-url')}>
Logout
</button>
</>
);
const UnauthenticatedView = () => (
<>
<h1>Welcome to Wristband Auth</h1>
<button onClick={() => redirectToLogin('<your-server-login-endpoint-url')}>
Login
</button>
</>
);
return (
<div>
{isAuthenticated ? <AuthenticatedView /> : <UnauthenticatedView />}
</div>
);
};
export default App;
Create an AuthGuard Component to Protect Explicit Routes
import React from 'react';
import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom';
import { useWristbandAuth } from '@wristband/react-client-auth';
import { Dashboard, Login } from '@/components';
const AuthGuard = ({ children }: { children: React.ReactNode }) => {
const { isAuthenticated, isLoading } = useWristbandAuth();
if (isLoading) {
return <div>Loading...</div>;
}
if (!isAuthenticated) {
return <Navigate to="/login" replace />;
}
return children;
};
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/login" element={<Login />} />
<Route
path="/dashboard"
element={
<AuthGuard>
<Dashboard />
</AuthGuard>
}
/>
</Routes>
</BrowserRouter>
);
}
export default App;
Handle Unauthorized Error Responses
When your frontend makes API calls directly to your backend, the require_session_auth
dependency will raise "401 Unauthorized" responses if the session is missing or invalid. Your frontend needs to handle these responses so users can be redirected to log in or otherwise recover. Below are common patterns for handling 401 errors in a JavaScript frontend.
Use an Axios Interceptor
If you're using Axios, you can create a response interceptor to handle Unauthorized responses. In the example below, the user is redirected to the Login Endpoint if a 401 response is detected.
// api-client.ts
import axios from 'axios';
import { redirectToLogin } from '@wristband/react-client-auth';
const apiClient = axios.create({
baseURL: '<Replace with the base URL of your server APIs>',
headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
});
// If a 401 response is detected, redirect the user to your Login Endpoint.
const unauthorizedAccessInterceptor = (error: unknown) => {
if (axios.isAxiosError(error) && error.response?.status === 401) {
redirectToLogin('<Replace with your application Login Endpoint>');
return;
}
return Promise.reject(error);
};
apiClient.interceptors.response.use(undefined, unauthorizedAccessInterceptor);
export { apiClient };
Explicitly Catch Errors When Making API Calls
To handle unauthorized errors with more precision, you can explicitly catch errors thrown when calling your backend APIs. This allows for custom error-handling logic to be created for each API call.
// example-component.ts
import axios from 'axios';
import { redirectToLogin } from '@wristband/react-client-auth';
export function ExampleComponent() {
...
const executeServerApiCall = async () => {
try {
const response = await axios.get('<your-server-api-url>');
alert('Success!');
} catch (error: unknown) {
if (axios.isAxiosError(error) && error.response?.status === 401) {
redirectToLogin('<Replace with your application Login Endpoint>');
} else {
console.error('Unexpected error: ', error);
alert('Something went wrong!');
}
}
}
...
return <p>Hello World!</p>;
}
Updated about 7 hours ago
Next, let's enhance the frontend to be able to pass a CSRF request header when making API calls to your backend.