
Company News
/Security News
Socket Selected for OpenAI's Cybersecurity Grant Program
Socket is an initial recipient of OpenAI's Cybersecurity Grant Program, which commits $10M in API credits to defenders securing open source software.
token-refresh
Advanced tools
Lightweight, framework-agnostic Axios token refresh helper with robust error handling and loop prevention.
npm install token-refresh
# or
yarn add token-refresh
import axios from 'axios';
import { installAuthRefresh, setAuthHeaders } from 'token-refresh';
const api = axios.create({ baseURL: '/api' });
// Token storage functions
const getTokens = async () => {
return JSON.parse(localStorage.getItem('tokens') || 'null');
};
const setTokens = async (tokens) => {
localStorage.setItem('tokens', JSON.stringify(tokens));
};
const { uninstall } = installAuthRefresh(api, {
refresh: async (refreshToken) => {
// IMPORTANT: avoid refresh loops — use a separate client OR set skipAuthRefresh: true
const res = await api.post('/auth/refresh', { refreshToken }, { skipAuthRefresh: true });
return res.data; // <- AuthTokens: { accessToken, refreshToken, ... }
},
getTokens,
setTokens,
isSessionExpired: (ctx) => {
return ctx.status === 401;
},
isRefreshFailureTerminal: (error) => {
const status = (error as any)?.response?.status ?? 0;
return status === 401;
},
header: {
name: 'Authorization', // default: 'Authorization'
format: (t) => `Bearer ${t}` // default: Bearer format
},
onBeforeRefresh: () => {
// called right before refresh starts
console.log('Refreshing tokens...');
},
onRefreshed: (tokens) => {
// called after successful refresh with new tokens
console.log('Tokens refreshed!', tokens);
},
onRefreshFailed: (error) => {
// called when refresh fails - includes the error
console.error('Refresh failed:', error);
// e.g., redirect to login
},
maxRetries: 3,
refreshTimeout: 10_000,
// Enable logging (disabled by default for library usage)
logger: {
debug: (msg) => console.log(msg),
error: (msg, err) => console.error(msg, err),
},
logout: async () => {
// optional server-side logout
await api.post('/auth/logout', {}, { skipAuthRefresh: true });
}
});
// Recommended: seed the default header before making requests
// This avoids a cold-start race where the first request might leave without a header.
const existing = await getTokens();
if (existing?.accessToken) setAuthHeaders(api, existing.accessToken);
// Clean up when needed (tests, hot reloads, etc.)
// uninstall();
import type { AuthRefreshOptions, AuthTokens } from 'token-refresh';
const options: AuthRefreshOptions = {
refresh: async (refreshToken: string): Promise<AuthTokens> => {
// Your refresh implementation
},
// ... other options
};
The library includes specific error types for better analytics and debugging:
import { RefreshTimeoutError } from 'token-refresh';
installAuthRefresh(api, {
// ... other options
onRefreshFailed: (error) => {
if (error instanceof RefreshTimeoutError) {
// Handle timeout specifically
analytics.track('auth_refresh_timeout');
} else {
// Handle other refresh failures
analytics.track('auth_refresh_failed', { error: error.message });
}
}
});
The library prevents infinite refresh loops at multiple levels:
Failed requests are queued during refresh and automatically retried with the new token.
Refresh operations have configurable timeouts to prevent hanging requests.
For APIs that don't use Authorization: Bearer <token>:
installAuthRefresh(api, {
// ... other options
header: {
name: 'XAuthToken',
format: (token) => token // no "Bearer " prefix
}
});
When a request fails with a condition that your isSessionExpired function returns true for, the library:
isSessionExpired predicate and the request was sent with an older token than the library currently holds in memory (e.g., you fixed the header or a previous refresh already updated it), the request is retried once with the current token — no refresh call is made.Parameters:
api: AxiosInstance - The Axios instance to install the interceptor onoptions: AuthRefreshOptionsReturns: { uninstall: () => void }
interface AuthRefreshOptions {
refresh: (refreshToken: string) => Promise<AuthTokens>;
getTokens: () => Promise<AuthTokens | null>;
setTokens: (tokens: AuthTokens) => Promise<void>;
isSessionExpired: (context: { status: number; data?: any; error: AxiosError }) => boolean;
isRefreshFailureTerminal: (error: unknown) => boolean;
header?: {
name?: string; // default: 'Authorization'
format?: (token: string) => string; // default: (t) => `Bearer ${t}`
};
onBeforeRefresh?: () => void;
onRefreshed?: (tokens: AuthTokens) => void;
onRefreshFailed?: (error: unknown) => void;
maxRetries?: number; // default: 3
refreshTimeout?: number; // ms, default: 10000
logger?: { debug(msg), error(msg, err?) }; // default: no-op (quiet)
logout?: () => Promise<void>;
}
Note: isSessionExpired is required. A common implementation is:
isSessionExpired: (ctx) => ctx.status === 401,
isRefreshFailureTerminal: (error) => {
// Axios example:
const status = (error as any)?.response?.status ?? 0;
return status === 401;
}
Custom error thrown when refresh operations timeout. Useful for analytics and specific error handling.
Update or remove the instance's default auth header.
installAuthRefresh(api, {
// ... other options
// Decide when an original request failure should trigger a refresh
isSessionExpired: (ctx) => {
if (ctx.status === 401) return true;
const code = ctx.data?.code;
return ctx.status === 403 && code === 'TOKEN_EXPIRED';
},
// Decide when a refresh failure means auth is terminally lost
isRefreshFailureTerminal: (error) => {
// Axios example; adapt for your HTTP client
const status = (error as any)?.response?.status ?? 0;
const code = (error as any)?.response?.data?.code;
if (status === 401) return true; // typical: invalid/expired refresh token
return (status === 400 || status === 403 || status === 409) &&
['INVALID_GRANT', 'INVALID_REFRESH_TOKEN', 'TOKEN_REVOKED', 'TOKEN_REUSE_DETECTED'].includes(code);
}
});
const publicApi = axios.create({ baseURL: '/api' });
const protectedApi = axios.create({ baseURL: '/api' });
// Only install on the protected API
installAuthRefresh(protectedApi, {
refresh: (refreshToken) => publicApi.post('/auth/refresh', { refreshToken }),
// ... other options
});
const { uninstall } = installAuthRefresh(api, options);
// In tests or during hot reloads
afterEach(() => {
uninstall();
});
The library includes full TypeScript support and augments Axios types to support the skipAuthRefresh flag:
// This is automatically available
api.get('/data', { skipAuthRefresh: true });
FAQs
A library for refreshing expired access tokens
We found that token-refresh demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Company News
/Security News
Socket is an initial recipient of OpenAI's Cybersecurity Grant Program, which commits $10M in API credits to defenders securing open source software.

Security News
Socket CEO Feross Aboukhadijeh joins 10 Minutes or Less, a podcast by Ali Rohde, to discuss the recent surge in open source supply chain attacks.

Research
/Security News
Campaign of 108 extensions harvests identities, steals sessions, and adds backdoors to browsers, all tied to the same C2 infrastructure.