JAVASCRIPT
Implementing OAuth2 Token Refresh Flow
Learn to automatically refresh expired OAuth2 access tokens using a refresh token to maintain continuous authenticated API access without user re-authentication.
let accessToken = 'your_initial_access_token';
let refreshToken = 'your_initial_refresh_token';
let tokenExpiryTime = Date.now() + 3600 * 1000; // e.g., 1 hour from now
async function makeAuthenticatedRequest(url, options = {}) {
if (Date.now() >= tokenExpiryTime - (5 * 60 * 1000)) { // Refresh if expired or expiring in 5 mins
console.log('Access token expiring soon or expired. Attempting refresh...');
await refreshAccessToken();
}
const headers = {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json',
...options.headers
};
const response = await fetch(url, { ...options, headers });
if (response.status === 401 && response.headers.get('WWW-Authenticate')?.includes('Bearer error="invalid_token"')) {
console.log('Received 401 with invalid_token. Attempting refresh...');
await refreshAccessToken();
// Retry the original request with the new token
headers['Authorization'] = `Bearer ${accessToken}`; // Update header
return fetch(url, { ...options, headers });
}
return response;
}
async function refreshAccessToken() {
try {
const response = await fetch('/oauth/token', { // Your token refresh endpoint
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
grant_type: 'refresh_token',
refresh_token: refreshToken,
client_id: 'your_client_id',
// client_secret: 'your_client_secret' // Only for confidential clients on backend
}),
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(`Failed to refresh token: ${errorData.error_description || response.statusText}`);
}
const data = await response.json();
accessToken = data.access_token;
refreshToken = data.refresh_token || refreshToken; // Refresh token might also refresh
tokenExpiryTime = Date.now() + data.expires_in * 1000; // expires_in is usually in seconds
console.log('Access token refreshed successfully.');
} catch (error) {
console.error('Error refreshing access token:', error);
// Handle global logout or redirect to login
// window.location.href = '/login';
throw error; // Re-throw to prevent further authenticated requests
}
}
// Example Usage:
// (async () => {
// try {
// const data = await makeAuthenticatedRequest('/api/profile');
// const profile = await data.json();
// console.log('Profile Data:', profile);
// } catch (error) {
// console.error('Failed to fetch profile:', error);
// }
// })();
How it works: This snippet demonstrates an OAuth2 token refresh flow. `makeAuthenticatedRequest` checks if the current access token is about to expire (e.g., within 5 minutes) or if an API call returns a 401 Unauthorized error specifically indicating an invalid token. If either condition is met, `refreshAccessToken` is called. This function sends a POST request to your OAuth provider's token endpoint with the `refresh_token` grant type. Upon successful refresh, the new `access_token` and potentially a new `refresh_token` are stored, and the original request is retried. This ensures seamless user experience without frequent re-login.