Files
ui/src/auth/authActions.ts
Первов Артем 31add10e56 decompose structure
2026-06-20 00:51:18 +03:00

117 lines
3.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { KeycloakInitOptions } from 'keycloak-js';
import { keycloak } from './keycloakClient';
import { getAuthState, getIdentityState, setAuthState, type AuthState } from './authStore';
let initPromise: Promise<boolean> | null = null;
let refreshPromise: Promise<string | null> | null = null;
/** Инициализирует Keycloak один раз и запускает login-required flow при включенном SSO. */
export async function initAuth(): Promise<AuthState> {
if (!keycloak) {
return getAuthState();
}
if (!initPromise) {
const initOptions: KeycloakInitOptions = {
onLoad: 'login-required',
pkceMethod: 'S256',
checkLoginIframe: false,
};
initPromise = keycloak.init(initOptions).then((authenticated) => {
setAuthState({
initialized: true,
authenticated,
...getIdentityState(),
});
return authenticated;
});
}
await initPromise;
return getAuthState();
}
/** Обновляет access token перед API-запросами и повторно использует текущий refresh-promise. */
export async function refreshToken(minValidity = 30): Promise<string | null> {
if (!keycloak) {
return null;
}
const client = keycloak;
if (!refreshPromise) {
refreshPromise = client
.updateToken(minValidity)
.then(() => {
setAuthState({
authenticated: Boolean(client.authenticated),
...getIdentityState(),
});
return client.token ?? null;
})
.catch(async () => {
setAuthState({
authenticated: false,
username: null,
fullName: null,
email: null,
});
await login();
return null;
})
.finally(() => {
refreshPromise = null;
});
}
return refreshPromise;
}
/** Возвращает действующий access token или null, если SSO отключен/пользователь не вошел. */
export async function getAccessToken(): Promise<string | null> {
if (!keycloak?.authenticated) {
return null;
}
await refreshToken(30);
return keycloak.token ?? null;
}
/** Запускает ручной вход через Keycloak с возвратом на текущий адрес. */
export async function login(redirectUri = window.location.href): Promise<void> {
await keycloak?.login({ redirectUri });
}
/** Завершает Keycloak-сессию и возвращает пользователя на origin приложения. */
export async function logout(redirectUri = window.location.origin): Promise<void> {
await keycloak?.logout({ redirectUri });
}
if (keycloak) {
keycloak.onAuthSuccess = () => {
setAuthState({
initialized: true,
authenticated: true,
...getIdentityState(),
});
};
keycloak.onAuthLogout = () => {
setAuthState({
initialized: true,
authenticated: false,
username: null,
fullName: null,
email: null,
});
};
keycloak.onTokenExpired = () => {
void refreshToken(30);
};
}