You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于React-Redux的Cognito IdToken刷新策略求助

Hey there! Let's tackle this token expiration issue you're facing—it's super common with Cognito and long-lived pages, so we've got a few solid approaches to fix it, tailored to your React/Redux/Redux-saga stack.

解决方案:处理页面停留时IdToken过期导致的API 401问题

1. 提前监听Token过期,自动触发刷新

Since you already handle token refreshes on route changes, let's extend that logic to work while the user stays on a page. Cognito's IdToken includes an exp field (Unix timestamp) that we can use to schedule a refresh before the token actually expires:

  • Store the token's expiration time in your Redux store (e.g., auth.expiresAt).
  • Whenever the token is updated (on login or route refresh), calculate a time point 5 minutes before expiration and set a Redux-saga timer to trigger a refresh.
  • When the timer fires, call Cognito's refresh method, update the store with the new token and expiration time, and reschedule the next refresh.

Example saga code:

import { call, put, takeLatest, delay } from 'redux-saga/effects';
import { updateToken, refreshTokenFailed } from '../actions/authActions';
import { cognitoUser } from '../utils/cognitoConfig';

function* scheduleTokenRefresh(expiresAt) {
  // Calculate delay to refresh 5 minutes before expiration (in ms)
  const refreshDelay = (expiresAt - Date.now() / 1000 - 300) * 1000;
  if (refreshDelay > 0) {
    yield delay(refreshDelay);
    yield call(refreshToken);
  }
}

function* refreshToken() {
  try {
    const session = yield call(
      [cognitoUser, cognitoUser.refreshSession],
      cognitoUser.signInUserSession.refreshToken
    );
    const newIdToken = session.getIdToken().getJwtToken();
    const newExpiresAt = session.getIdToken().getExpiration();
    
    yield put(updateToken({ idToken: newIdToken, expiresAt: newExpiresAt }));
    // Reschedule next refresh after successful update
    yield call(scheduleTokenRefresh, newExpiresAt);
  } catch (error) {
    yield put(refreshTokenFailed(error));
    // Redirect to login if refresh fails
    window.location.href = '/login';
  }
}

// Watch for token updates to schedule refresh timers
export function* watchTokenUpdate() {
  yield takeLatest('AUTH_TOKEN_UPDATED', function*(action) {
    yield call(scheduleTokenRefresh, action.payload.expiresAt);
  });
}

This way, your token gets refreshed automatically before it expires, even if the user stays on the same page.

2. Global API Request Interceptor for 401 Retries

Even if the pre-emptive refresh fails (e.g., network issues), we can add a safety net at the API layer. Intercept all requests, and when a 401 is returned, attempt to refresh the token and retry the original request.

Here's how to set this up with Axios:

import axios from 'axios';
import { store } from '../store';
import { refreshToken } from '../actions/authActions';

// Prevent concurrent 401s from triggering multiple token refreshes
let isRefreshing = false;
let failedQueue = [];

const processQueue = (error, token = null) => {
  failedQueue.forEach(prom => {
    error ? prom.reject(error) : prom.resolve(token);
  });
  failedQueue = [];
};

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

    // Check if this is a 401 and not a retry attempt
    if (error.response.status === 401 && !originalRequest._retry) {
      if (isRefreshing) {
        // Queue the request to retry after token refresh
        return new Promise((resolve, reject) => {
          failedQueue.push({ resolve, reject });
        }).then(token => {
          originalRequest.headers['Authorization'] = `Bearer ${token}`;
          return axios(originalRequest);
        }).catch(err => Promise.reject(err));
      }

      originalRequest._retry = true;
      isRefreshing = true;

      try {
        // Trigger token refresh via Redux-saga
        const action = await store.dispatch(refreshToken());
        if (action.type !== 'AUTH_TOKEN_UPDATED') throw new Error('Refresh failed');
        
        const newToken = action.payload.idToken;
        originalRequest.headers['Authorization'] = `Bearer ${newToken}`;
        processQueue(null, newToken);
        
        return axios(originalRequest);
      } catch (refreshError) {
        processQueue(refreshError, null);
        window.location.href = '/login';
        return Promise.reject(refreshError);
      } finally {
        isRefreshing = false;
      }
    }

    return Promise.reject(error);
  }
);

This interceptor handles unexpected 401s, refreshes the token, and retries the request—plus it manages concurrent requests to avoid duplicate refreshes.

3. Sync Token State Globally

Make sure all API requests pull the latest token directly from your Redux store, not a cached local variable. Add a request interceptor to automatically inject the current token into headers:

import axios from 'axios';
import { store } from '../store';

const apiClient = axios.create();

apiClient.interceptors.request.use(config => {
  const { auth } = store.getState();
  if (auth.idToken) {
    config.headers['Authorization'] = `Bearer ${auth.idToken}`;
  }
  return config;
});

export default apiClient;

This ensures every request uses the most up-to-date token, even after a refresh.

Wrap-Up

Combining these three strategies will cover all bases:

  • Pre-emptive token refreshes to avoid expiration in the first place
  • A fallback interceptor to handle unexpected 401s
  • Global token state sync to keep all requests aligned

内容的提问来源于stack exchange,提问作者davegravy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 07:13:35