在我的 React Native 应用程序中,登录后我会获得访问令牌和刷新令牌。
有一个常用函数,当访问令牌无效时,使用刷新令牌再次调用访问令牌
在我的主页上,有6个API,但是当访问令牌失效时,这6个API并行调用公共函数并刷新访问令牌。这使得该特定用户被阻止。
如何预防这种情况?当第一个 API 的令牌失效时,使用标志阻止其余 API 是不可行的,因为在第一个响应之前,所有 API 都将刷新访问令牌。
首先触发
handleResponse
函数,然后代码到达getNewToken
函数,然后到达dispatch(logout());
,因为对accesstoken的调用没有成功,并且由于多次调用注销而导致应用程序崩溃。
const handleResponse = async (
res: ApiResponse<BaseResponseProp, BaseResponseProp> | null,
) => {
if (res?.data?.error === 'invalid_token') {
let tokenVal;
try {
if (token.refresh_token) {
tokenVal = await getNewToken(token.refresh_token);
} else {
//refresh the public token
tokenVal = await refreshPublicToken();
}
if (tokenVal?.access_token && tokenVal?.access_token.length > 0) {
const newTokenHeader = {
Authorization: `Bearer ${tokenVal.access_token}`,
};
invoke({
requestHeader: { ...requestHeader, ...newTokenHeader },
requestBody,
requestParams,
requestQuery,
url: formattedEndpoint,
onResultCallback,
onErrorCallback,
});
dispatch(setToken(tokenVal));
} else {
setInvalidToken(true);
navigation.dispatch(
StackActions.replace(NAVIGATION_SCREENS.ROOT),
);
return;
}
return;
} catch (e) {
const newToken = await refreshPublicToken();
dispatch(setToken(newToken));
return;
}
}
const err = new CustomError(
res?.data?.error || res?.originalError?.message,
res?.data?.status || 200,
);
if (typeof onErrorCallback === 'function') {
onErrorCallback(err);
} else if (typeof onError === 'function') {
onError(err as CustomError);
}
handleParseError(err);
} else {
const responseData = res?.data as unknown as R;
if (renderData) {
setData(res?.data);
}
if (typeof onResultCallback === 'function') {
onResultCallback(res?.data as unknown as R);
} else if (typeof onResult === 'function') {
onResult(res?.data);
}
}
};
const getNewToken = async (refToken: string) => {
let formattedEndpoint = ENDPOINT.LOGIN;
let tokenVal = {};
const header = { refresh_token: refToken };
const requestQuery = {
...REFRESH_AUTH_HEADERS,
...header,
};
const queryParams = new URLSearchParams({
...requestQuery,
}).toString();
formattedEndpoint = `${formattedEndpoint}?${queryParams}`;
const refreshResponse = await api.post<LoginSuccess>(
formattedEndpoint,
{},
{
headers: basicAuthHeaders,
},
);
if (refreshResponse.status === 200) {
tokenVal = {
access_token: refreshResponse.data?.access_token,
refresh_token: refreshResponse.data?.refresh_token,
expires_in: refreshResponse.data?.expires_in,
type: 'private',
};
} else {
// The logout gets called multiple times and app crashes
dispatch(logout());
tokenVal = {
access_token: null,
token_type: null,
refresh_token: null,
expires_in: null,
scope: null,
id_token: null,
type: 'public',
};
tokenVal = await refreshPublicToken();
navigation.dispatch(StackActions.replace(NAVIGATION_SCREENS.ROOT));
}
return tokenVal;
};
在 OAuth 架构中发出并发 API 请求时,需要使用 2 种同步技术:
对于第一个,请考虑使用这样的代码对承诺集合进行排队,并且仅对第一个进行令牌刷新。然后为每个 API 请求返回相同的刷新结果并允许它们恢复:
await this._concurrencyHandler.execute(this._performTokenRefresh);
要处理会话过期,请等待所有 API 请求完成,然后查看是否有任何令牌刷新请求失败并出现
invalid_grant
错误代码。如果其中至少有一个这样做,则重新运行登录流程,并且仅执行一次。我的示例使用此代码来实现这一点。