Axios Cookies and Authentication — Complete Guide
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
What does withCredentials do? It tells the browser to include cookies in cross-origin requests and set cookies from cross-origin responses.
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.
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.
Why use a failed request queue during token refresh? To prevent multiple simultaneous refresh attempts when several requests get 401 at the same time.
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
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