1.0.1 • Published 1 year ago

@relotus/keycloak v1.0.1

Weekly downloads
-
License
Apache-2.0
Repository
github
Last release
1 year ago

@relotus/keycloak

Описание

@relotus/keycloak - npm-пакет для работы авторизации в корпоративном Keycloak

Подключение в проект

Установка:

npm install @relotus/keycloak

Пример использования

Для использования Keycloak в приложении его необходимо проинициализировать:

import { initKeycloak } from '@relotus/keycloak';

const KeycloakProvider = initKeycloak({
  url: config.AUTH_HTTP,
  realm: config.AUTH_REALM,
  clientId: config.AUTH_CLIENT_ID,
});

KeycloakProvider - это компонент, в которое необходимо обернуть приложение для работы хуков из этого пакета.

ВАЖНО

Если в вашем проекте используется ResetErrorBoundary из @relotus/utkonos, то он должен быть ребенком для KeycloakProvider, а не наоборот:

<KeycloakProvider onUserLogin={login} onUserLogOut={logout}>
  <ErrorBoundary>
    <Provider store={store}>{appContent}</Provider>
  </ErrorBoundary>
</KeycloakProvider>

В противном случае это приведет к тому, что в случае ошибки будет бесконечный редирект в Keycloak

Установка токена для запросов

Для упрощения работы с токеном, вы можете при вызове initKeycloak передать Аxios и для него будет установлен интерцептор:

import { initKeycloak } from '@relotus/keycloak';

const KeycloakProvider = initKeycloak(config, axios);

Теперь для каждого запроса будет добавлен заголовок Authorization со значением Bearer ${keycloak.token}.

Если в вашем приложении используется адаптер поверх Axios то можно использовать функцию bindInterceptor. Функция возвращает функцию, вызов которой уберет интерцептор.

import { initKeycloak } from '@relotus/keycloak';
import { bindInterceptor } from '@relotus/keycloak/src/interceptor';

const KeycloakProvider = initKeycloak(config, axios);
const { keycloak } = KeycloakProvider;

class Api {
  private _axios: AxiosInstance;

  private removeInterceptor: () => void;

  constructor() {
    this._axios = axios.create({
      baseURL: `${config.API_BASE_URL}/web`,
      paramsSerializer: formatParams,
    });
    this.removeInterceptor = bindInterceptor(this._axios, keycloak);
  }
}

Обработка событий

Есть несколько способов обработать события авторизации:

  1. Самый простой - пробросить в KeycloakProvider обработчики для событий авторизации и завершения сессии:

    import { initKeycloak } from '@relotus/keycloak';
    
    const KeycloakProvider = initKeycloak(config, axios);
    
    function App() {
      const handleLogin = useCallback((profile: KeycloakProfile) => {}, []);
      const handleLogout = useCallback(() => {}, []);
      return (
        <KeycloakProvider onUserLogin={handleLogin} onUserLogOut={handleLogout}>
          <MyApp />
        </KeycloakProvider>
      );
    }
  2. Использовать chanel для саги

    import { createKeycloakChannel, events } '@relotus/keycloak/src/saga';
    
    function* saga() {
      const keycloakChannel = yield call(createKeycloakChannel)
      try {
        while (true) {
          let keycloakEvent = yield take(keycloakChannel)
          if(events.authSuccess(keycloakEvent)){
            // обрабатываем авторизацию;
            const { payload } = keycloakEvent
          }
        }
      } finally {
        if (yield cancelled()) {
          keycloakChannel.close()
        }
      }
    }
  3. Для всех остальных случаев есть возможность подписаться на AuthClientEvent напрямую:

    import { initKeycloak } from '@relotus/keycloak';
    const KeycloakProvider = initKeycloak(config, axios);
    
    const eventName: AuthClientEvent = 'onAuthSuccess';
    
    const unsubscribe = KeycloakProvider.subscribe(
      eventName,
      ({ keycloak, error }: { error?: AuthClientError; keycloak: KeycloakInstance }) => {
        // Обрабатываем событие
      },
    );
    
    // Отписываемся
    unsubscribe();

Получение доступа к Keycloak

Для получения доступа в компонентах можно использовать хук useKeycloak:

import { useKeycloak } from '@relotus/keycloak';

const { keycloak } = useKeycloak();
const logout = useCallback(() => {
  keycloak.logout().catch(() => {
    /* обрабатываем ошибку */
  });
}, [toggleDetails]);

Вне компонентов, например для вызова Keycloak#logout():

const { keycloak } = KeycloakProvider;
keycloak.logout().catch(() => {
  /* обрабатываем ошибку */
});

Проверка ролей

ВАЖНО

Роли настраиваются для clientId или realmId в админке Keycloak

Для проверки ролей в компоненте можно использовать хук useHasRole

import { useHasRole } from '@relotus/keycloak';

function Component() {
  const hasAdminRole = useHasRole('ADMIN');
  return hasAdminRole ? 'Я админ' : 'Я пользователь';
}

Хук проверяет как роли для для clientId, так и для realmId.

Вне компонента можно воспользоваться вызовом методов hasRealmRole (проверка роли для realmId) или hasResourceRole (проверка роли для clientId)

const isRealmManager = keycloak.hasRealmRole('MANAGER');
const isClientHasAccessToDictionaries = keycloak.hasResourceRole('Dictionaries.READ');

Для проверки роли можно воспользоваться утилитой hasRole:

import { hasRole } from '@relotus/keycloak/src/utils';

const { keycloak } = KeycloakProvider;
const hasAdminRole = hasRole(keycloak, 'ADMIN'); // Имеет роль ADMIN для clientId или для realmId

Mock для тестов

Для удобства тестирования в setupTests или в каждом тесте где это необходимо можно добавить:

import '@relotus/keycloak/src/mock';