Skip to main content

Clients

Клиенты - сущности, которые могут отправлять запрос в Keycloak на аутентификацию пользователя. Чтобы приложение могло использовать ресурсы keycloak, оно должно быть зарегистрировано. Клиенты делятся по идентификаторам. 

Настройка через консоль управления реалмом, раздел клиенты. Желательно настраивать на каждый сервер / сервис отдельный клиент.

image.png

Раздел Clients

Client list

Client list - список клиентов. Параметры настройки клиента:

Параметр Описание
Client type Алгоритм аутентификации клиента. OpenID или SAML.
Client ID Идентификатор клиента. Он будет использоваться в запросах, поэтому желательно с маленькой буквы, без пробелов и прочего.
Client authentication

Сможет ли приложение хранить секрет и использовать его для аутентификации на Keycloak.

ON — Конфиденциальный клиент Приложение обязано хранить секрет. При обмене авторизационного кода на токены клиент предъявляет код и Client Secret. Используется для бэкенда. Пример: FastAPI приложение на сервере, обращающееся к Keycloak из защищенной среды.

OFF — Публичный клиент У клиента нет секрета, при обмене кода на токены секрет не используется. Используется для одностраничных приложений, мобильных приложений.

Authorization

Активация RBAC, доступ к конкретным строчкам в БД.

OFF (выключено — поведение по умолчанию)

    Пользователю даются роли (например, user, admin), и клиент сам решает на своей стороне, что с этими ролями делать.

    Решение принимает бэкенд клиента.

ON (включено — тонкая (fine-grained) авторизация)

    Как работает: Вы описываете ресурсы (например: Аккаунт №123, Документ "Отчет.xlsx"), области действий (read, write, delete) и политики (кто, при каких условиях может что делать). Keycloak сам принимает решение: разрешено или запрещено.

    Кто принимает решение: Keycloak (сервер авторизации).

    Ваше приложение: Спрашивает у Keycloak: «Может ли пользователь Вася выполнить read над ресурсом Аккаунт №123?» Keycloak отвечает: Permit или Deny.

 

После включения, в меню клиента появляются новые вкладки:

    Resources — что вы защищаете (например, Документы, Счета, Данные профиля).

    Authorization Scopes — действия (например, view, edit, delete, download).

    Policies — кто и при каких условиях может что-то делать (например, Только владелец ресурса, Только пользователи из отдела продаж, Только между 9:00 и 18:00).

    Permissions — связывание всего вместе: «Разрешить edit для ресурса Документ, если выполнена политика Владелец документа».

 

Включение Authorization усложняет архитектуру. Keycloak становится точкой принятия решений (PEP — Policy Enforcement Point на клиенте, PDP — Policy Decision Point на Keycloak). Это требует:

  • Дополнительных запросов от вашего приложения к Keycloak (проверка каждого доступа).
  • Глубокого понимания политик.
  • Аккуратного проектирования ресурсов.
Root URL Базовый адрес вашего приложения. Используется как префикс для других URL, если они указаны относительно. Может быть пустым, но лучше указать. Некоторые функции Keycloak (например, ссылка "Back to application" на странице логина) используют этот URL.
Home URL Куда пользователь попадет после нажатия на логотип Keycloak или ссылку "Back to application". 

Отличие от Root URL:

    Root URL — это база/префикс.

    Home URL — конкретная страница, которая считается "домашней" для приложения.

Если не указан, используется Root URL.

Valid Redirect URIs

Адреса, на которые Keycloak разрешит отправлять авторизационный код или токены после успешной аутентификации. 

https://myapp.example.com/callback
https://myapp.example.com/oauth/callback
http://localhost:3000/callback  # для разработки
/*                             # любой путь в рамках корневого URL

Для безопасности критично. Как минимум указывать относительный. Просто * убивает всю авторизацию.

 

 

Valid Post Logout Redirect URIs Адреса, на которые Keycloak перенаправит пользователя после успешного выхода (logout). 

Отличие от Valid Redirect URIs:

    Обычные редиректы — после входа (авторизации)

    Post Logout — после выхода

Зачем нужно:
Чтобы пользователь после выхода не попал на поддельную страницу, например: вышли из банка → а вас кинули на https://fake-bank.com/again-login.

Совет: Обычно указывают страницу вроде /goodbye, /logged-out или сам Root URL.

Web Origins

Какие домены имеют право выполнять CORS-запросы к вашему Keycloak из браузера (для SPA-приложений).

Используется для: CORS (Cross-Origin Resource Sharing) на эндпоинтах Keycloak (/token, /logout, /userinfo и т.д.).

Пример для SPA на React:
Ваше SPA работает на https://myapp.com, а Keycloak на https://keycloak.example.com.

Если Web Origins указан правильно, браузер позволит SPA из myapp.com делать fetch-запросы к Keycloak.

+    Плюс означает использовать Valid Redirect URIs как список разрешенных источников

*    Любой источник (все домены) 

Клиенты по умолчанию в Master realm:

Название Описание Адрес
account Клиент пользовательского личного кабинета. Управление профилем, просмотр сеансов входа и разрешения. /realms/{realm}/account
account-console Фронтенд клиента account, вызывающий API account.

admin-cli Клиент взаимодействия с Keycloak через CLI или REST API.
Например получение токена через kcadm.sh или curl для скриптов/CI/CD. Поддерживает client_credentials и password гранты.

broker

Внутренний клиент для федерации идентичности (SSO между провайдерами). Пример: Keycloak как Identity Broker между Google, GitHub и другим Keycloak сервером.


master-realm Формальный клиент, представляющий сам master realm.
Часто используется для определённых внутренних механизмов и ссылок в Admin Console.

security-admin-console Клиент для административной консоли Keycloak. Используется при входе в админ-панель, управлении реалмами, пользователями и клиентами. /admin/{realm}/console

Пример работы с admin-cli: 

import requests

def get_keycloak_token():
    url = "http://192.168.1.195:9090/realms/master/protocol/openid-connect/token"
    payload = {
        "grant_type": "password",
        "client_id": "admin-cli",
        "username": "admin",
        "password": "admin",
    }
    headers = {
        "Content-Type": "application/x-www-form-urlencoded"
    }
    response = requests.post(url, data=payload, headers=headers)
    # Проверка на ошибки HTTP
    response.raise_for_status()
    print(response.json())

get_keycloak_token()

Ответ сервера: 

{
  'access_token': hbG...L7A', 
  'expires_in': 60, 
  'refresh_expires_in': 1800, 
  'refresh_token': 'hbG...gcQ', 
  'token_type': 'Bearer', 
  'not-before-policy': 0, 
  'session_state': 'f...c', 
  'scope': 'profile email'
}

Если затем сформировать например get запрос к endpoint /admin/realms с заголовком Authorization Brearer <access_token> то будет доступ. Полный пример запроса: 

import requests

BASE_URL = "http://192.168.1.195:9090"

def get_keycloak_token():
    url = f"{BASE_URL}/realms/master/protocol/openid-connect/token"
    payload = {
        "grant_type": "password",
        "client_id": "admin-cli",
        "username": "admin",
        "password": "admin",
    }
    headers = {
        "Content-Type": "application/x-www-form-urlencoded"
    }
    response = requests.post(url, data=payload, headers=headers)
    response.raise_for_status()
    return response.json()["access_token"]

def get_realms(access_token: str):
    url = f"{BASE_URL}/admin/realms"
    headers = {
        "Authorization": f"Bearer {access_token}"
    }
    response = requests.get(url, headers=headers)
    response.raise_for_status()
    return response.json()

if __name__ == "__main__":
    try:
        token = get_keycloak_token()
        realms = get_realms(token)
        print("Realms:")
        for realm in realms:
            print("-", realm.get("realm"))
    except requests.HTTPError as e:
        print("HTTP error:", e.response.text)
    except Exception as e:
        print("Unexpected error:", str(e))

Client registration

Параметры, которые будут применяться к пользователям, зарегистрированным в клиентах.

Раздел client scopes

image.png

Определяет данные и разрешения, получаемые клиентом в access token / через ID token при аутентификации пользователя. Взаимосвязь между атрибутами и scopes: 

Маппер (Mapper) внутри Client Scope — это инструкция для Keycloak: «Возьми данные ОТСЮДА (атрибут, роль, контекст) и положи ИХ В ТОКЕН ПОД ТАКИМ ИМЕНЕМ».

Типы scope:

  • Default: всегда включается в токен по умолчанию, даже если клиент не запрашивает его явно.
  • Optional: будет включен в токен в случае явного запроса клиентом в параметре scope=... при авторизации.
  • (Unassigned): не применяется клиенту вообще, пока его не назначат вручную или не укажут явно в токене.

Стандартные scopes: 

Название Тип Протокол Назначение
acr Default OpenID Connect добавляет в токен acr (Authentication Context Class Reference) - уровень аутентификации (например, MFA, пароль и т. д.).
address Optional OpenID Connect добавляет адрес пользователя (address claim) в токен (если заполнено в профиле).
basic Default OpenID Connect включает базовые claims - sub, name, preferred_username, given_name, family_name
e-mail Default OpenID Connect

добавляет email и email_verified в ID и Access токены

microprofile-jwt Optional OpenID Connect

обеспечивает совместимость с Eclipse MicroProfile JWT - популярным стандартом для Java microservices

offline_access Optional OpenID Connect

разрешает выдачу offline refresh token (живёт долго и используется без пользовательского взаимодействия).

role_list Default SAML

включает список ролей (roles) пользователя в SAML assertions

roles Default OpenID Connect

добавляет роли пользователя (realm roles и client roles) в access token (в realm_access.roles и resource_access)

Также есть phone, profile

Добавление маппера:

 

Типы мапперов:

Audience,
Имя Описание
Allowed Web Origins Добавляет

Scope с данным mapper добавляет все разрешенные веб-источники к свойству "allowed-origins" в токенетокене. Иначе это механизм копирования списка доверенных источников браузера из конфигурации Keycloak Client внутрь Access Token, чтобы бэкенд мог программно принять решение о разрешении CORS. При генерации токена он читает список разрешенных веб-источников, настроенный у Client (родителя этого Client Scope), и копирует этот список в указанный claim токена.

Поля конфигурации маппера:

  • Name: Allowed Web Origins (любое имя)
  • Mapper Type: Allowed Web Origins
  • Claim name: Обычно allowed-web-origins (но можно кастомизировать)
  • Claim JSON Type: String или String Array
  • Add to ID token: Вкл/Выкл (обычно выкл, чтобы не раздувать ID токен)
  • Add to access token: Вкл (обычно да)

Пример: SPA (на http://localhost:3000) стучится на бэкенд (http://localhost:8080). Бэкенд получает токен, но как ему узнать, что JavaScript вашего фронта имеет право вызывать его API?

Решение через маппер Allowed Web Origins:

  • Вы идете в Client Scope (например, microservice-scope).
  • Добавляете маппер типа Allowed Web Origins.
  • Протоколируете этот scope к вашему клиенту (SPA).

Результат: Внутри Access Token появляется поле: 

{
  "allowed-web-origins": ["http://localhost:3000", "https://myapp.com"],
  ...
}

Теперь бэкенд:

  • Декодирует токен.
  • Видит значение allowed-web-origins.
  • В middleware CORS сверяет Origin заголовок входящего запроса со списком.
  • Если совпадает — отдает данные. Нет — блокирует.

Используется редко. 

  • Настроить Web Origins в клиенте, а бэкенд будет брать CORS-политику из  конфига вне Keycloak.
  • Использовать маппер, только если ваш бэкенд динамически решает, кому доверять, на основе данных из токена.
Audience ДобавьтеHardcoded указаннуюclaim, аудиторию в поле audience (аудитория) токена
Audience Resolve ДобавляетРешает вопрос "для кого этот токен" (Resource Server). Это поле aud (audience) в JWT токене. Оно указывает, какой ресурс (API/сервис) должен принять этот токен.
{
  "iss": "https://keycloak.local/realms/myrealm",
  "sub": "user-123",
  "aud": ["account", "https://api.mycompany.com/v1"],
  ...
}

Зачем это нужно? Например, есть 3 микросервиса:

  •     payment-api (оплата)
  •     order-api (заказы)
  •     analytics-api (аналитика)

Если токен не имеет audience, любой сервис может принять токен, выданный для другого. Злоумышленник получает токен для payment-api, но пытается вызвать order-api. Без проверки aud — успешно.

Решение: Keycloak добавляет в токен aud = "payment-api". order-api проверяет этот claim и отвергает токен (403 Forbidden).

 

Типы мапперов для Audience

Audience (простой)

Создаете маппер в Client Scope, указываете жестко имя аудитории.

Конфигурация:

  • Included Client Audience: выбираете клиента (например, payment-api)
  • Add to ID token: обычно false
  • Add to access token: true

Результат: В access token появится "aud": ["payment-api"]
Hardcoded claim

Вручную claim aud с любым значением (массивом или строкой).

Конфигурация:

  • Claim name: aud
  • Claim value: ["service-a", "service-b"]
  • Claim JSON Type: String Array

Audience resolve (продвинутый)

Автоматически собирает всех клиентов, которым был выдан токен, и добавляет их в aud.

Например, если клиент spa-app запрашивает токен с scope, включающим audience-resolve маппер, в aud попадут все client_idклиенты, "разрешенных"на клиентов в поле audience токена. Разрешенный клиент означает клиента, для которогокоторые у пользователя есть хотядоступ бычерез однаservice клиентскаяaccounts.

роль

Реальный сценарий использования

Сценарий: SPA + два микросервиса

Настройка в Keycloak:

  •     Создаете Client для SPA: my-spa
  •     Создаете Client для каждого API: payment-api, order-api
  •     Создаете Client Scope с именем audience-payment
  •     Добавляете маппер типа Audience:
  •         Included Client Audience: payment-api

Процесс:
// SPA запрашивает токен с scope = 'audience-payment'
const token = await keycloak.getToken({ scope: 'audience-payment' });

// В токене:
{
  "aud": ["payment-api"],  // Только для payment-api
  "scope": "audience-payment"
}

// SPA вызывает payment-api ✅ разрешено
// SPA вызывает order-api ❌ токен не содержит aud=order-api

Сценарий: Токен для нескольких API

Иногда нужно, чтобы один токен работал с несколькими сервисами:
json

{
  "aud": ["payment-api", "order-api", "analytics-api"]
}

Как сделать: Создаете маппер Hardcoded claim с массивом значений или используете несколько мапперов Audience.

Authentication Context Class Reference (ACR) Сопоставляет достигнутый

Механизм LoAуказывающий способ (уровень аутентификации)или сметод) параметромаутентификации пользователя, а не просто факт. Claim acr. 

{
  "iss": "https://keycloak.local/realms/myrealm",
  "sub": "user-123",
  "acr": "1",
  "auth_time": 1699123456,
  ...
}

Конфигурация маппера:

  • Тип маппера: Authentication Context Class Reference (ACR)
  • Имя: acr-mapper (любое)
  • Включить в ID token: true (рекомендуется)
  • Включить в access token: true (если бэкенд проверяет acr)

Алгоритм:

  •     Копирует значение acr токенаиз аутентификационного контекста в токен
  •     Если в сессии нет явного acr — используется значение по умолчанию

Приложение может запросить конкретный acr, и keycloak можно настроить на изменение способа авторизации. В токен попадает итоговый реальный acr.

Реальные сценарии

Сценарий 1: Разные уровни доверия для разных операций

  • Просмотр баланса → достаточно acr = "1" (пароль)
  • Перевод до 1000$ → нужно acr = "2" (пароль + SMS)
  • Перевод от 1000$ → нужно acr = "3" (пароль + OTP + биометрия)

Сценарий 2: Соответствие стандартам (eIDAS, NIST)

Правительственные системы требуют определенные уровни аутентификации:

  •     NIST AAL1 (Low): пароль
  •     NIST AAL2 (Moderate): пароль + OTP
  •     NIST AAL3 (High): аппаратный токен + биометрия

Сценарий 3: Корпоративная безопасность

  • Доступ к обычным документам → acr = "password"
  • Доступ к финансовым отчетам → acr = "password+mfa"
  • Доступ к HR данным → acr = "hardware-key"

 

 

 

 

Authentication Method Reference (AMR) Добавьте к токену ссылку на метод аутентификации (AMR).
Claims parameter Token Утверждения, указанные в параметре Claims, преобразуются в токены.
Claims parameter with value ID Token Утверждения, указанные в параметре Claims со значением, преобразуются в токен ID.
Group Membership Сопоставьте членство в группе пользователей
Map user group membership Жестко запрограммированное утверждение
Hardcoded Role Жестко запрограммируйте роль в токене доступа.
Nonce backwards compatible Добавляет утверждение nonce в токен Access, Refresh и ID
Organization Membership Сопоставьте членство пользователя в организации
Map user Organization membership Попарный идентификатор субъекта
Pairwise subject identifier Вычисляет парный идентификатор субъекта, используя хэш-код sha-256, и добавляет его к заявке "sub". Смотрите спецификацию OpenID Connect для получения дополнительной информации о парных идентификаторах субъектов.
Role Name Mapper Сопоставьте назначенную роль с новым именем или позицией в токене.
Session State Добавьте запрос о состоянии сеанса (session_state)
Subject (sub) Добавьте запрос о предмете (под)
User Address Сопоставляет атрибуты адреса пользователя (улица, населенный пункт, регион, почтовый индекс и страна) с утверждением OpenID Connect "адрес".
User Attribute Сопоставляет пользовательский атрибут пользователя с утверждением токена.
User Client Role Сопоставьте роль пользователя-клиента с заявкой на токен.
User Property Сопоставьте встроенное свойство пользователя (адрес электронной почты, имя, фамилию) с заявкой на токен.
User Realm Role Сопоставьте роль области пользователя с заявкой на токен.
User Session Note Сопоставьте пользовательскую заметку о сеансе пользователя с заявкой на токен.
User's full name Сопоставляет имя и фамилию пользователя с заявкой OpenID Connect "имя". Формат <first> + ' ' + <last>