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.

Need help integrating this into your project?

Our team of expert developers can help you build your custom application from scratch.

Hire DigitalCodeLabs