import { type ReactNode } from 'react' import { useAuth } from '@/auth/useAuth' /** * Auth gate с гостевым режимом view-only. * *

До v1.1.1 этот компонент force-redirect'ил неаутентифицированных юзеров * на Keycloak. Теперь — guest-friendly: anonymous user видит UI с PUBLIC scope * данными (backend отдаёт PUBLIC dictionaries без auth, см. SecurityConfig * permissive mode + ScopeContext anonymous → PUBLIC). * *

Логин теперь пользовательское действие: клик на кнопку «Войти» в top bar * → {@link useAuth().signinRedirect()}. После возврата из Keycloak пользователь * получает scope по ролям JWT и видит INTERNAL/RESTRICTED данные. * *

Сценарии: *

*/ export function RequireAuth({ children }: { children: ReactNode }) { const auth = useAuth() const oidcError = typeof window !== 'undefined' && new URLSearchParams(window.location.search).get('error') if (oidcError) { return (

Ошибка входа

Keycloak вернул ошибку:{' '} {oidcError}

) } // Keycloak unreachable / OIDC discovery failed — раньше показывали amber // banner вверху страницы, но per user feedback ("Авторизация недоступна: // Failed to fetch.?") это noise: anonymous mode работает без auth, error // banner лишь пугает. Тихо рендерим children в guest mode — AuthBadge // в TopBar показывает Sign in для retry. if (auth.error && !auth.isAuthenticated) { return <>{children} } // ВАЖНО: НЕ блокируем UI пока {@code auth.isLoading=true}. Initial OIDC // bootstrap может зависать на 10-20 сек если Keycloak silent SSO iframe // блокируется X-Frame-Options или сеть медленная. Anonymous user в это // время видит только spinner и может подумать что сайт сломан. // Решение: рендерим children immediately. AuthBadge в top bar сам // показывает loading state своим маленьким индикатором, а PUBLIC данные // (которые backend отдаёт без auth) грузятся параллельно с silent SSO. // Если silent SSO succeed позже — useAuth() обновит scope, кнопки // create/edit активируются автоматически (canMutate реактивен). // Anonymous OR authenticated OR loading — рендерим children всегда. UI в // компонентах сам адаптируется (через useAuth().isAuthenticated): // hide mutation buttons, show «Войти» CTA, тонировать blocked actions. return <>{children} }