Skip to content

Axios Cookies and Authentication — Complete Guide

DodaTech Updated 2026-06-28 6 min read

In this tutorial, you'll learn about Axios cookies and authentication. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.

Axios cookies and authentication patterns manage user sessions across requests, handling cookie-based auth, JWT tokens, refresh token rotation, and secure credential storage.

What You'll Learn

By the end of this tutorial, you'll configure withCredentials for cross-origin cookies, manage JWT tokens with interceptors, implement refresh token rotation, handle 401 responses, and store tokens securely.

Why It Matters

Authentication is the most critical security layer in any application. Mishandled tokens lead to session hijacking, CSRF attacks, or users being logged out repeatedly. Proper auth patterns keep users secure and sessions stable.

Real-World Use

Durga Antivirus Pro uses Axios interceptors to attach JWT tokens to every request. A response interceptor detects 401 errors and attempts a silent token refresh before redirecting to login, providing seamless authentication.

Where This Fits in Your Learning Path

flowchart LR
    A["Upload Progress"] --> B["**Cookies & Auth**"]
    B --> C["HTTP/2 Adapters"]
    C --> D["Axios Project"]
    D --> E["Axios Advanced"]
    style B fill:#f97316,stroke:#c2410c,color:#fff
    style A fill:#e5e7eb,stroke:#9ca3af,color:#374151
    style E fill:#22c55e,stroke:#16a34a,color:#fff

Cross-Origin Cookies with withCredentials

Set withCredentials to true for cross-origin requests that need cookies.

const api = axios.create({
  baseURL: 'https://api.example.com',
  withCredentials: true,  // Send cookies even for cross-origin
  headers: { 'Content-Type': 'application/json' }
})

const { data } = await api.get('/user/profile')
console.log(data)

Expected output: Cookies stored for api.example.com are sent with every request, even if the frontend is on a different origin.

JWT Token Interceptor

Attach a JWT token to every request using a request interceptor.

const api = axios.create({ baseURL: 'https://api.example.com' })

api.interceptors.request.use(config => {
  const token = localStorage.getItem('accessToken')
  if (token) {
    config.headers.Authorization = `Bearer ${token}`
  }
  return config
})

// Now every request automatically includes the token
const { data } = await api.get('/protected/resource')

Expected output: The interceptor reads the token from localStorage and attaches it as a Bearer token to every request.

Refresh Token Rotation

When a 401 occurs, attempt to refresh the token and retry the original request.

const api = axios.create({ baseURL: 'https://api.example.com' })

let isRefreshing = false
let failedQueue = []

function processQueue(error, token = null) {
  failedQueue.forEach(prom => {
    if (error) prom.reject(error)
    else prom.resolve(token)
  })
  failedQueue = []
}

api.interceptors.response.use(
  response => response,
  async error => {
    const originalRequest = error.config

    if (error.response?.status === 401 && !originalRequest._retry) {
      if (isRefreshing) {
        return new Promise((resolve, reject) => {
          failedQueue.push({ resolve, reject })
        }).then(token => {
          originalRequest.headers.Authorization = `Bearer ${token}`
          return api(originalRequest)
        })
      }

      originalRequest._retry = true
      isRefreshing = true

      try {
        const refreshToken = localStorage.getItem('refreshToken')
        const { data } = await axios.post('https://api.example.com/auth/refresh', {
          refreshToken
        })

        localStorage.setItem('accessToken', data.accessToken)
        processQueue(null, data.accessToken)

        originalRequest.headers.Authorization = `Bearer ${data.accessToken}`
        return api(originalRequest)
      } catch (refreshError) {
        processQueue(refreshError, null)
        localStorage.clear()
        window.location.href = '/login'
        return Promise.reject(refreshError)
      } finally {
        isRefreshing = false
      }
    }

    return Promise.reject(error)
  }
)

Expected output: On a 401, the interceptor attempts a token refresh. If successful, the original request retries with the new token. If refresh fails, the user is redirected to login.

CSRF Token Handling

Send CSRF tokens with state-changing requests for protection.

const api = axios.create({
  baseURL: 'https://api.example.com',
  xsrfCookieName: 'XSRF-TOKEN',   // Cookie name the server sets
  xsrfHeaderName: 'X-XSRF-TOKEN'   // Header name Axios sends
})

// Axios automatically reads the cookie and sets the header
await api.post('/api/data', { key: 'value' })

Expected output: Axios reads the XSRF-TOKEN cookie and sends it as the X-XSRF-TOKEN header automatically.

Common Mistakes

1. Storing tokens in localStorage without encryption

localStorage is accessible to any JavaScript on the same origin. Consider httpOnly cookies for production apps to prevent XSS token theft.

2. Not handling concurrent 401 responses

Multiple simultaneous requests getting 401 causes multiple refresh attempts. Use a queue to only refresh once and share the new token.

3. Forgetting withCredentials for cross-origin cookies

Without withCredentials: true, the browser will not send cookies cross-origin even if they exist.

4. Storing refresh tokens in the same place as access tokens

If access tokens are compromised, refresh tokens should remain safe. Store them in httpOnly cookies or separate secure storage.

5. Not clearing tokens on logout

After logout, stale tokens may still be sent. Clear all tokens from storage and reset axios defaults.

Practice Questions

  1. What does withCredentials do? It tells the browser to include cookies in cross-origin requests and set cookies from cross-origin responses.

  2. How do you attach a JWT token to every request? Use a request interceptor that reads the token from storage and sets the Authorization header.

  3. What is the refresh token pattern? When a 401 occurs, use a refresh token to obtain a new access token, then retry the original request.

  4. Why use a failed request queue during token refresh? To prevent multiple simultaneous refresh attempts when several requests get 401 at the same time.

  5. What are xsrfCookieName and xsrfHeaderName used for? They configure Axios to automatically read a CSRF token from a cookie and send it as a header for CSRF protection.

Challenge

Build a complete authentication module with login, token storage, auto-refresh on 401, request queuing to avoid concurrent refreshes, and automatic redirect on expired refresh tokens.

FAQ

Is localStorage safe for JWT tokens?

localStorage is vulnerable to XSS attacks. For production, use httpOnly cookies for refresh tokens and short-lived in-memory access tokens.

How do I handle logout across tabs?

Use the storage event listener or BroadcastChannel API to detect token removal in other tabs and update the axios instance.

Can Axios handle OAuth2 flows?

Axios can make the HTTP requests for OAuth2, but the redirect-based authorization code flow requires browser navigation management.

What happens if the refresh token expires?

The user must re-authenticate. Redirect to the login page and clear all stored authentication data.

Does Axios support mutual TLS authentication?

In Node.js, Axios supports mTLS via custom httpsAgent with cert and key options. In browsers, mTLS is handled by the browser itself.


Mini Project

Build an authentication wrapper that manages the complete auth lifecycle: login stores tokens, request interceptor attaches access tokens, response interceptor handles 401 with refresh token rotation, and logout clears everything.

class AuthClient {
  constructor(baseURL) {
    this.api = axios.create({ baseURL })
    this.isRefreshing = false
    this.failedQueue = []
    this.setupInterceptors()
  }

  setupInterceptors() {
    this.api.interceptors.request.use(config => {
      const token = sessionStorage.getItem('accessToken')
      if (token) config.headers.Authorization = `Bearer ${token}`
      return config
    })

    this.api.interceptors.response.use(
      response => response,
      async error => this.handleAuthError(error)
    )
  }

  async handleAuthError(error) {
    const originalRequest = error.config
    if (error.response?.status !== 401 || originalRequest._retry) {
      return Promise.reject(error)
    }

    if (this.isRefreshing) {
      return new Promise((resolve, reject) => {
        this.failedQueue.push({ resolve, reject })
      }).then(token => {
        originalRequest.headers.Authorization = `Bearer ${token}`
        return this.api(originalRequest)
      })
    }

    originalRequest._retry = true
    this.isRefreshing = true

    try {
      const refreshToken = localStorage.getItem('refreshToken')
      const { data } = await axios.post(`${this.api.defaults.baseURL}/auth/refresh`, { refreshToken })
      sessionStorage.setItem('accessToken', data.accessToken)
      this.failedQueue.forEach(p => p.resolve(data.accessToken))
      this.failedQueue = []
      originalRequest.headers.Authorization = `Bearer ${data.accessToken}`
      return this.api(originalRequest)
    } catch (err) {
      this.failedQueue.forEach(p => p.reject(err))
      this.failedQueue = []
      this.logout()
      throw err
    } finally {
      this.isRefreshing = false
    }
  }

  async login(email, password) {
    const { data } = await this.api.post('/auth/login', { email, password })
    sessionStorage.setItem('accessToken', data.accessToken)
    localStorage.setItem('refreshToken', data.refreshToken)
  }

  logout() {
    sessionStorage.removeItem('accessToken')
    localStorage.removeItem('refreshToken')
    window.location.href = '/login'
  }
}

export const authClient = new AuthClient('https://api.example.com')

What's Next

Explore advanced Adapter configurations:

Tutorial What You'll Learn
HTTP/2 Adapters Configure HTTP/2 adapters for Node.js
Axios Project Build a complete application with all Axios features

Related topics: JWT and token-based authentication, CSRF protection.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro