我有一个使用Google Oauth的React / redux /电子应用程序。我希望能够在到期时自动刷新访问令牌。我已经研究过这个并使用中间件半成功地解决了它,但我的解决方案在某些情况下是错误的。
我已经实现了一个在每个API动作上运行的刷新中间件。它会检查访问令牌是否已过期或即将过期。如果是,则不是分派它收到的动作,而是调度令牌刷新动作并将任何其他动作排队,直到收到新的访问令牌。之后,它将调度其队列中的所有操作。
但是,我的一个动作创建者看起来像这样:
function queryThreads(params) {
return async (dispatch) => {
const threads = await dispatch(fetchThreads(params))
const newPageToken = threads.payload.nextPageToken
}
}
当刷新中间件由于令牌未到期而未运行时,将在此处定义threads.payload
,并且一切都将按预期工作。
然而,当刷新中间件运行时,threads.payload
将是undefined
,因为dispatch
似乎解决了令牌刷新操作的值,而不是fetchThreads
操作。
如何确保令牌被刷新(并在state
/ localStorage
中更新),fetchThreads
将被更新的令牌调度,并且threads
变量被分配给正确Promise的已解析值?
This is my refresh middleware。它的灵感来自this article的kmmbvnr。
This is the token refresh action creator。
This is the line in my queryThreads action creator在令牌必须刷新时抛出(threads.payload
是undefined
)。
看起来我已经通过重写刷新中间件解决了这个问题,如下所示:
function createRefreshMiddleware() {
const postponedRSAAs = [];
return ({ dispatch, getState }) => {
const rsaaMiddleware = apiMiddleware({ dispatch, getState });
return next => action => {
if (isRSAA(action)) {
try {
const auth = JSON.parse(localStorage.getItem('auth'));
const { refresh_token: refreshToken } = auth;
const expirationTime = jwtDecode(auth.id_token).exp * 1000;
const isAccessTokenExpiring =
moment(expirationTime) - moment() < 300000;
if (refreshToken && isAccessTokenExpiring) {
postponedRSAAs.push(action);
if (postponedRSAAs.length === 1) {
return rsaaMiddleware(next)(
dispatch(() => attemptTokenRefresh(refreshToken))
).then(() => {
const postponedRSAA = postponedRSAAs.pop();
return dispatch(postponedRSAA);
});
}
return rsaaMiddleware(next)(action);
}
return rsaaMiddleware(next)(action);
} catch (e) {
console.log(e);
return next(action);
}
}
return next(action);
};
};
}
export default createRefreshMiddleware();
现在,推迟的操作将始终与令牌刷新操作相关联,因此我们没有原始承诺解决错误值的问题;加上它更简洁。