fix(admin-ui): 403 + отсутствует accessToken → trigger reauth handler
Симптом: после ~30 мин админ видит 403 на /admin/*, перелогин чинит. Цепочка событий: 1. access_token истекает 2. oidc-client-ts автоматический silent renew падает (refresh_token expired за 30 мин SSO Session Idle / iframe заблокирован) 3. auth.user становится undefined (oidc убирает после full expiry) 4. TokenSync setAccessToken(null) — accessToken в apiClient зануляется 5. Следующий запрос идёт БЕЗ Authorization header 6. Backend (ORDINIS_AUTH_REQUIRED=false) пропускает как anonymous → ScopeContext = PUBLIC → controller с requireInternal() кидает 403 401-handler не срабатывает на 403 → юзер залипает. Также мой предыдущий useEffect в TokenSync (MR !267) триггерит на auth.user?.expired === true, но если user удалён полностью — optional-chaining вернёт undefined и useEffect не сработает. Fix: расширил interceptor чтобы на (status === 403 && accessToken == null) тоже вызывать unauthorizedHandler. Этот handler — single source of truth, делает signinSilent → fallback signinRedirect. Не цепляет legitimate 403 (когда у юзера ЕСТЬ токен но scope недостаточно — например viewer пытается dropить INTERNAL dict). accessToken != null в таких случаях → ветка не сработает.
This commit is contained in:
@@ -72,13 +72,22 @@ apiClient.interceptors.request.use((config) => {
|
||||
return config
|
||||
})
|
||||
|
||||
// Единственный 401 interceptor. Логика silent renew / redirect / loop guard
|
||||
// делегируется handler'у который TokenSync устанавливает через
|
||||
// setUnauthorizedHandler().
|
||||
// Auth-related interceptor. 401 классический «нет/невалидный JWT» →
|
||||
// handler делает silent renew / redirect.
|
||||
//
|
||||
// 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'а.
|
||||
apiClient.interceptors.response.use(
|
||||
(resp) => resp,
|
||||
(error) => {
|
||||
if (error?.response?.status === 401 && unauthorizedHandler) {
|
||||
const status = error?.response?.status
|
||||
const needsReauth =
|
||||
status === 401 || (status === 403 && accessToken == null)
|
||||
if (needsReauth && unauthorizedHandler) {
|
||||
try {
|
||||
unauthorizedHandler(error)
|
||||
} catch (e) {
|
||||
|
||||
Reference in New Issue
Block a user