Protect Backend Endpoints
Learn how the auth dependency can be used to protect authenticated APIs.
Previously, during the Wristband SDK setup, we created an auth dependency named require_session_auth. This section explains how to use it to ensure that protected endpoints are accessible only to authenticated users.
Using the Auth Dependency to Protect Endpoints
To protect an endpoint from unauthenticated access, add require_session_auth as a dependency, as shown below
# src/routes/protected_routes.py
from fastapi import APIRouter, Depends, Response, status
from auth.wristband import require_session_auth
router = APIRouter()
# Use the require_session_auth dependency in your dependencies list to verify that
# incoming requests have a valid session.
@router.get("/protected-api", dependencies=[Depends(require_session_auth)])
async def protected_api() -> Response:
return { "message": "This is a protected endpoint" }Now, if somebody tries to call this API without a valid session, a 401 Unauthorized response will be returned.
Handling 401 Responses in Frontend Code
When your frontend makes calls to APIs protected by the require_session_auth dependency, the following error response could come back:
401 Unauthorized: This response will be returned if the session is missing or invalid.
Your frontend needs to handle this error response gracefully to ensure a smooth user experience. Below are common patterns for handling 401 errors in a JavaScript frontend.
Pattern 1: Use an Axios Interceptor
If you're using Axios, you can create a response interceptor to handle 401 error 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: '<backend-apis-base-url>',
headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
withCredentials: true,
});
// 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('<your-login-endpoint-url>');
return;
}
return Promise.reject(error);
};
apiClient.interceptors.response.use(undefined, unauthorizedAccessInterceptor);
export { apiClient };
Pattern 2: Explicitly Catch Errors When Making API Calls
To handle 401 errors with more precision, you can explicitly catch them when calling your backend APIs. This allows for custom error-handling logic to be created for each API call.
import axios from 'axios';
import { redirectToLogin } from '@wristband/react-client-auth';
async function executeApiCall() {
try {
const response = await axios.get('<your-server-api-url>');
alert('Success!');
} catch (error: unknown) {
if (axios.isAxiosError(error) && error.response?.status === 401) {
redirectToLogin('<your-login-endpoint-url>');
} else {
console.error('Unexpected error:', error);
alert('Something went wrong!');
}
}
}
import { redirectToLogin } from '@wristband/react-client-auth';
function getCookie(name: string): string | null {
const match = document.cookie.match(new RegExp('(^|;\\s*)' + name + '=([^;]*)'));
return match ? decodeURIComponent(match[2]) : null;
}
async function executeApiCall() {
const csrfToken = getCookie('CSRF-TOKEN');
try {
const response = await fetch('/api/protected-endpoint', {
credentials: 'include',
headers: {
'X-CSRF-TOKEN': csrfToken ?? ''
}
});
if (!response.ok) {
if (response.status === 401 || response.status === 403) {
redirectToLogin('<your-login-endpoint-url>');
return;
}
const errorText = await response.text();
throw new Error(`HTTP error! status: ${response.status}, Message: ${errorText}`);
}
...
} catch (error) {
...
}
}Updated 14 days ago
Now that you've finished protecting your backend endpoints, let's run some final tests to ensure everything is working.