75 lines
2.7 KiB
TypeScript
75 lines
2.7 KiB
TypeScript
import { createAppAuth, type DefaultAuthUser } from '@nstart/auth'
|
||
import { createContext, type ReactNode, useContext, useEffect } from 'react'
|
||
import { setApiAuthToken } from '../api/apiFetch'
|
||
import { env } from '../config/env'
|
||
|
||
export type AppUser = DefaultAuthUser
|
||
|
||
// Состояние, совместимое с AuthState из @nstart/auth (headless-ядро). Используем
|
||
// ТОЛЬКО headless-слой пакета: createAppAuth/useAppAuth дают токен, login, logout,
|
||
// loading и коды ошибок — без единого компонента @nstart/ui (их UI: ProfilePage/
|
||
// UserPanel — намеренно не подключаем).
|
||
interface AppAuthState {
|
||
user: AppUser | null
|
||
token: string | null
|
||
loading: boolean
|
||
errorCode: 'notAuthenticated' | 'initFailed' | null
|
||
errorDetails: string | null
|
||
login: () => void
|
||
logout: () => void
|
||
}
|
||
|
||
// Anonymous-режим: фейковый контекст, чтобы useAppAuth() работал везде в коде
|
||
// без условий. Токен null → apiFetch не добавляет Authorization, поведение как у
|
||
// исходного сервиса (открывается сразу, без Keycloak).
|
||
const ANONYMOUS_STATE: AppAuthState = {
|
||
user: null,
|
||
token: null,
|
||
loading: false,
|
||
errorCode: null,
|
||
errorDetails: null,
|
||
login: () => undefined,
|
||
logout: () => undefined,
|
||
}
|
||
|
||
const AnonymousAuthContext = createContext<AppAuthState>(ANONYMOUS_STATE)
|
||
|
||
function AnonymousAuthProvider({ children }: { children: ReactNode }) {
|
||
return (
|
||
<AnonymousAuthContext.Provider value={ANONYMOUS_STATE}>
|
||
{children}
|
||
</AnonymousAuthContext.Provider>
|
||
)
|
||
}
|
||
|
||
function useAnonymousAuth(): AppAuthState {
|
||
return useContext(AnonymousAuthContext)
|
||
}
|
||
|
||
const newStartIdAuth = createAppAuth({
|
||
url: env.newStartIdUrl,
|
||
realm: env.newStartIdRealm,
|
||
clientId: env.newStartIdClientId,
|
||
loginMode: 'redirect',
|
||
})
|
||
|
||
export const { AppAuthProvider, useAppAuth } =
|
||
env.authMode === 'new-start-id'
|
||
? newStartIdAuth
|
||
: { AppAuthProvider: AnonymousAuthProvider, useAppAuth: useAnonymousAuth }
|
||
|
||
/**
|
||
* Прокидывает токен из useAppAuth() в сетевой слой (держатель apiFetch). Ничего
|
||
* не рендерит; монтируется внутри AppAuthProvider. В режиме anonymous token === null,
|
||
* поэтому заголовок Authorization не выставляется и поведение запросов не меняется.
|
||
*
|
||
* @returns Ничего не отображает (null).
|
||
*/
|
||
export function AuthTokenBridge(): null {
|
||
const { token } = useAppAuth()
|
||
useEffect(() => {
|
||
setApiAuthToken(token ?? null)
|
||
}, [token])
|
||
return null
|
||
}
|