Protect Backend Endpoints

Learn how the Wristband auth guard restricts backend API access to authenticated users only.

This section explains how to use the WristbandAuthGuard created during setup to restrict endpoint access to authenticated users only.

Using the Auth Guard to Protect Endpoints

Apply the WristbandAuthGuard using the @UseGuards() decorator to protect an endpoint:

// src/protected/protected.controller.ts

import { Controller, Get, UseGuards } from '@nestjs/common';
import { WristbandAuthGuard } from '../guards/auth.guard';

@Controller('api')
export class ProtectedController {
  // Apply the guard to protect this route
  @Get('protected')
  @UseGuards(WristbandAuthGuard)
  getProtected() {
    return { message: 'This is a protected endpoint' };
  }
}

Any requests made to this endpoint without a valid session will now automatically fail with a 401 Unauthorized response.

Handling 401 Responses in Frontend Code

When your frontend requests an API secured by the WristbandAuthGuard, it may receive the following error response:

  • 401 Unauthorized: This response will be returned if the session is missing or invalid.

our frontend must handle this error response gracefully to maintain a seamless user experience. Here are common patterns for managing 401 errors in a JavaScript client:

Pattern 1: Use an Axios Interceptor

An Axios response interceptor provides a centralized way to manage authentication failures. The following configuration intercepts 401 responses and executes a redirect to your backend's Login Endpoint:

// 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

For more granular control, you can explicitly catch 401 errors during individual API calls to implement tailored error-handling logic for each endpoint.

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.

Did this page help you?