Enhance Frontend to Send CSRF Token

Update your frontend code to send the CSRF token as a request header to your backend APIs.

When using session cookies for authentication, you must protect against CSRF attacks. CSRF is a type of web security vulnerability where an attacker tricks a user into unknowingly submitting a request to a web application where they're already authenticated. Since browsers automatically include cookies with requests, the malicious request appears legitimate to the server. This allows attackers to perform actions on behalf of the user without their consent.

The require_session_auth dependency you created and used for your Session Endpoint implements the Synchronizer Token pattern automatically on the server. It issues a CSRF token with each authenticated session and validates it on incoming requests. If the CSRF token in the request header does not match the CSRF token that comes from the session cookie, then a 403 response will be returned.

For a high-level overview of how this pattern works, please refer to the following documentation.


Pass the CSRF Token to Your Server

Since your backend already verifies CSRF tokens, you'll need to configure your frontend HTTP clients to extract the CSRF Token from the CSRF cookie and then set it into a custom CSRF header in the outgoing request. Below are some examples of how to do this for popular JavaScript HTTP clients.


Axios

Configure Axios to include the CSRF token in the request headers.

// api-client.ts
import axios from 'axios';

const apiClient = axios.create({
  baseURL: `<backend-apis-base-url>`,
  headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
  xsrfCookieName: 'CSRF-TOKEN',
  xsrfHeaderName: 'X-CSRF-TOKEN',
  // withCredentials: true, // if dealing with CORS
});

export { apiClient };

To handle any 403 Forbidden HTTP errors returned by the CSRF middleware, you can add an Axios interceptor to handle that appropriately. For example:

// api-client.ts

import axios from 'axios';

const apiClient = axios.create({
  baseURL: `<backend-apis-base-url>`,
  headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
  xsrfCookieName: 'CSRF-TOKEN',
  xsrfHeaderName: 'X-CSRF-TOKEN',
  // withCredentials: true, // If dealing with CORS
});

// Any HTTP 401 OR 403 response should redirect the user to the Login page.
const unauthorizedAccessInterceptor = (error: unknown) => {
  if (axios.isAxiosError(error) && (error.response?.status === 401 || error.response?.status === 403)) {
    window.location.href = '<Replace with your application Login Endpoint>';
    return;
  }
  
  return Promise.reject(error);
};

apiClient.interceptors.response.use(undefined, unauthorizedAccessInterceptor);

export { apiClient };

Alternatively, you can handle forbidden errors with more precision by adding custom error handling in your API request's catch block.

// example-component.ts

import axios from 'axios';

export function ExampleComponent() {
  const handleApiCall = async () => {
    try {
      const response = await axios.get(`<your-csharp-server-domain>/test`);
      alert('Success!');
    } catch (error: unknown) {
      if (axios.isAxiosError(error) && (error.response?.status === 401 || error.response?.status === 403)) { 
        window.location.href = '<Replace with your application Login Endpoint>';
      } else {
        alert('Something went wrong!');
      }
    }
  };

  return (
    <button onClick={handleApiCall}>
      Make API Call
    </button>
  );
}

Fetch

Fetch doesn't have automatic handling for CSRF, so you'll have to implement the logic of extracting the CSRF token from the cookie and passing it in the X-CSRF-TOKEN request header from scratch.

function getCookie(name: string): string | null {
  const match = document.cookie.match(new RegExp('(^|;\\s*)' + name + '=([^;]*)'));
  return match ? decodeURIComponent(match[2]) : null;
}

async function execApi() {
  const csrfToken = getCookie('CSRF-TOKEN');

  try {
    const response = await fetch('/api/endpoint', {
      credentials: 'include',
      headers: {
        'X-CSRF-TOKEN': csrfToken ?? ''
      }
    });

    if (!response.ok) {
      if (response.status === 401 || response.status === 403) {
        window.location.href = '<Replace with your application Login Endpoint>';
        return;
      }

      const errorText = await response.text();
      throw new Error(`HTTP error! status: ${response.status}, Message: ${errorText}`);
    }

    ...
  } catch (error) {
    ...
  }
}

Congratulations on making it this far! You've now learned how to integrate Wristband authentication into both your FastAPI server and React frontend!



What’s Next

Before we test the session management, let's first determine if your application needs to configure CORS.