Pre-Request Authorization Handshakes in Axios

Executing a pre-request authorization handshake in Axios is primarily achieved through asynchronous request interceptors. This mechanism allows the client to pause an outgoing HTTP request, perform an independent authentication handshake—such as obtaining an OAuth token or fetching a CSRF token—and attach the resulting credentials to the original request headers before it is transmitted across the network.

The Core Mechanism: Request Interceptors

Axios provides the axios.interceptors.request.use method, which accepts an asynchronous callback function. When an HTTP request is initiated, Axios evaluates this interceptor pipeline before dispatching the network call. Because the interceptor function can return a Promise, Axios waits for the promise to resolve before proceeding with the request.

import axios from 'axios';

let authToken = null;

// Create an Axios instance
const apiClient = axios.create({
  baseURL: 'https://api.example.com',
});

// Configure the request interceptor for authorization handshakes
apiClient.interceptors.request.use(async (config) => {
  // Check if token is already present or valid
  if (!authToken) {
    // Perform the pre-request authorization handshake
    const authResponse = await axios.post('https://auth.example.com/handshake', {
      clientId: 'YOUR_CLIENT_ID',
      clientSecret: 'YOUR_CLIENT_SECRET',
    });

    authToken = authResponse.data.token;
  }

  // Inject the authorization credential into the headers
  config.headers.Authorization = `Bearer ${authToken}`;

  // Return the modified configuration to proceed with the request
  return config;
}, (error) => {
  return Promise.reject(error);
});

Execution Flow

  1. Trigger: A call to apiClient.get(), apiClient.post(), or another request method is made.
  2. Interception: Axios intercepts the execution flow and passes the request configuration object (config) to the registered request interceptor.
  3. Handshake Execution: The interceptor checks for an existing, valid authorization artifact (such as a JWT, session token, or CSRF cookie). If none exists or the token has expired, an asynchronous HTTP call is made directly to the identity provider or authorization server.
  4. Header Mutation: Once the handshake resolves, the credentials are added to config.headers.
  5. Request Continuation: The interceptor returns the modified config object, signaling Axios to proceed with sending the original request.

Handling Concurrent Requests

When multiple requests are fired simultaneously, uncoordinated interceptors can cause multiple redundant handshake requests. To prevent this, the handshake promise should be shared across pending requests.

let handshakePromise = null;

apiClient.interceptors.request.use(async (config) => {
  if (!authToken) {
    // If a handshake is not already running, initiate one
    if (!handshakePromise) {
      handshakePromise = axios.post('https://auth.example.com/handshake', {
        clientId: 'YOUR_CLIENT_ID',
      }).then((res) => {
        authToken = res.data.token;
        handshakePromise = null; // Reset lock
      }).catch((err) => {
        handshakePromise = null;
        return Promise.reject(err);
      });
    }

    // Await the shared handshake resolution
    await handshakePromise;
  }

  config.headers.Authorization = `Bearer ${authToken}`;
  return config;
});

By leveraging asynchronous request interceptors alongside promise caching, Axios ensures that pre-request authorization handshakes are executed reliably, securely, and without redundant network overhead.