Protect Backend Endpoints

Learn how to require valid sessions for authenticated APIs.

This section explains how to use the .RequireWristbandSession() method to ensure that protected endpoints are accessible only to authenticated users.

Protecting Endpoints from Unauthenticated Access

To protect an endpoint from unauthenticated access, add .RequireWristbandSession() as shown below:

// ProtectedRoutes.cs

using Wristband.AspNet.Auth;

public static class ProtectedEndpoints
{
    public static WebApplication MapProtectedEndpoints(this WebApplication app)
    {
        app.MapGet("/api/protected-api", () =>
        {
            return Results.Ok(new { message = "This is a protected endpoint" });
        })
        .RequireWristbandSession();

        return app;
    }
}

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 RequireWristbandSession(), 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) {
    ...
  }
}

What’s Next

Now that you've finished protecting your backend endpoints, let's run some final tests to ensure everything is working.