revert: уберу proactive re-auth и 403→reauth — спам Keycloak /token
Симптом: пользователь видел ~30+ POST на Keycloak /token endpoint с 400 Bad Request, страница залипала. Корень. MR !267 добавил useEffect в TokenSync который на auth.user?.expired === true делал signinSilent → catch → signinRedirect. MR !268 добавил interceptor триггер на 403 + !accessToken. Цикл: 1. token expires, refresh_token тоже (Keycloak SSO Session Idle прошёл) 2. useEffect видит expired=true → signinSilent → POST /token с refresh 3. Keycloak возвращает 400 invalid_grant (refresh expired) 4. catch → signinRedirect, но oidc-client-ts automaticSilentRenew=true параллельно тоже пытается → ещё POST /token → ещё 400 5. signinRedirect не успевает редиректнуть до следующего render'а 6. reauthing.current=false → useEffect fire'ит снова → новый signinSilent 7. → ещё 400, и так в loop ~30 раз пока что-то не разрулит Откатываю обе ветки. Возвращаемся к v2.42.3 поведению: silent renew автоматически пробует один раз, на failure пользователь видит 403 (или stale data из cache) и делает relogin вручную. Не идеально, но не спамит Keycloak и не залипает на endlessly. Backend ScopeContext admin → RESTRICTED (MR !267 backend часть) остаётся — это безвредная правка, помогает юзерам с admin ролью без явного INTERNAL.
This commit is contained in:
@@ -72,22 +72,21 @@ apiClient.interceptors.request.use((config) => {
|
||||
return config
|
||||
})
|
||||
|
||||
// Auth-related interceptor. 401 классический «нет/невалидный JWT» →
|
||||
// handler делает silent renew / redirect.
|
||||
// Единственный 401 interceptor. Логика silent renew / redirect / loop guard
|
||||
// делегируется handler'у который TokenSync устанавливает через
|
||||
// setUnauthorizedHandler().
|
||||
//
|
||||
// 403 + accessToken=null обрабатывается так же. Корень: на проде
|
||||
// ORDINIS_AUTH_REQUIRED=false → Spring Security пропускает anonymous →
|
||||
// контроллер видит PUBLIC scope → отдаёт 403 (не 401). Это происходит
|
||||
// когда silent-renew падает, oidc-client-ts убирает user, accessToken
|
||||
// зануляется → следующий запрос идёт без Bearer. Без этой ветки юзер
|
||||
// залипал на 403 и видел «forbidden» вместо relogin redirect'а.
|
||||
// 403 НЕ цепляем: на проде ORDINIS_AUTH_REQUIRED=false → backend отдаёт 403
|
||||
// для anonymous (нет JWT) ИЛИ для legitimate scope-deficiency (юзер с
|
||||
// токеном но без INTERNAL). Невозможно reliably различить из ответа,
|
||||
// раннее попытка триггерить reauth на 403+!accessToken приводила к
|
||||
// бесконечному циклу с Keycloak (signinSilent → 400 → catch → ещё
|
||||
// signinSilent через automaticSilentRenew). Лучше показать 403 и оставить
|
||||
// юзеру manual relogin, чем заспамить /token endpoint Keycloak'а.
|
||||
apiClient.interceptors.response.use(
|
||||
(resp) => resp,
|
||||
(error) => {
|
||||
const status = error?.response?.status
|
||||
const needsReauth =
|
||||
status === 401 || (status === 403 && accessToken == null)
|
||||
if (needsReauth && unauthorizedHandler) {
|
||||
if (error?.response?.status === 401 && unauthorizedHandler) {
|
||||
try {
|
||||
unauthorizedHandler(error)
|
||||
} catch (e) {
|
||||
|
||||
Reference in New Issue
Block a user