Разработчикам · OpenID Connect

Войти через КБП.
Подключение вашего проекта

Одна учётная запись КБП. Свой профиль, данные и сессия в каждом приложении. Пошаговый контракт для разработчика игры и других сервисов.

Версия 1.0 · 19 октября 2026 · Публично, без закрытых ключей

Это публичная документация для разработчиков сторонних приложений: игр, сайтов и других самостоятельных сервисов. Она описывает порядок подключения и требования к интеграции. Документация не содержит действующих закрытых ключей, паролей, пользовательских данных или сведений о зарегистрированных проектах.

1. Что делает КБП, а что — приложение

КБП на https://lk.kbp.kz — единственный источник учётной записи и пароля. Используем OpenID Connect (OIDC), Authorization Code Flow с PKCE S256. Центральный компонент — oidc-provider 9.12.2; пример приложения — openid-client 6.8.8 и jose 6.2.10. Протокол самостоятельно не переписывайте: используйте поддерживаемую OIDC-библиотеку своего языка.

Игра получает подтверждение личности и создаёт собственный профиль, прогресс, права и сессию. Пароль вводится только на сайте КБП. Приложение не получает административные права КБП, рекламные балансы, документы, БИН, телефон или дату рождения. SSO-токен не подходит для вызовов обычного API кабинета или админки.

Готовый центр и пример не означают, что ваша игра уже подключена. Для рабочего подключения нужны её точные адреса, открытые ключи, настройка в реестре и проверенная реализация отзыва на её сервере.

2. Как получить подключение

  1. Подготовьте серверное приложение на HTTPS. SPA или мобильный интерфейс использует собственный backend/BFF; закрытый ключ и токены не размещаются в JavaScript браузера, localStorage, мобильном бинарном файле или на Тильде.
  2. Создайте отдельную RSA-пару 3072 бит (допустимы 2048–4096), алгоритм RS256, уникальный kid. В примере: node keys.mjs. Файл private/client-key.json остаётся на сервере приложения, режим 0600. Файл private/session-key также закрытый.
  3. Передайте главному администратору КБП только client-public-jwks.json, название проекта, запрашиваемые scopes и точные URL из таблицы ниже. Открытый JWKS не секретен. Поля d, p, q, dp, dq, qi в передаваемом ключе недопустимы.
  4. Главный администратор с действующей 2FA открывает https://admin.kbp.kz/sso-admin → «Единый вход → Проекты», создаёт подключение и сообщает client_id. client_id — публичный идентификатор, не пароль.
  5. Настройте сервер приложения, проведите проверки из раздела 13. После проверки администратор включает проект.

Вместо общего client_secret используется private_key_jwt: сервер приложения подписывает короткое одноразовое утверждение своим закрытым ключом. КБП проверяет его по зарегистрированному открытому JWKS. При таком методе КБП не выдаёт и не хранит секрет приложения; пароль аккаунта КБП для интеграции не нужен. Изображения экрана с ключами и содержимое закрытых файлов никому не отправляйте.

Настройка проектаПример, заменить своим точным адресом
Возврат после входа (redirect_uri)https://game.example.com/auth/kbp/callback
После выхода (post_logout_redirect_uri)https://game.example.com/logged-out
Серверный отзыв (backchannel_logout_uri)https://game.example.com/auth/kbp/backchannel-logout

Все URL регистрируются целиком. Разный путь, query, регистр пути, завершающий слеш — другой адрес. Wildcard, префиксы, фрагменты, внутренние IP, localhost и динамические перенаправления в production запрещены. Адреса выхода относятся к origin зарегистрированных адресов входа. КБП проверяет публичный DNS серверного адреса отзыва и закрепляет проверенный IP на время запроса.

3. Адреса центра

Issuer: `https://lk.kbp.kz/api/sso/oidc` — сравнивается точно, без завершающего /.

Discovery: https://lk.kbp.kz/api/sso/oidc/.well-known/openid-configuration

Загружайте metadata из Discovery библиотекой, а не собирайте конечные адреса догадками. Для этой версии:

НазначениеАдресВызов
Начало входаhttps://lk.kbp.kz/api/sso/oidc/authGET, браузер
Обмен кода / refreshhttps://lk.kbp.kz/api/sso/oidc/tokenPOST form, сервер приложения
Открытые ключи центраhttps://lk.kbp.kz/api/sso/oidc/jwksGET
Данные личностиhttps://lk.kbp.kz/api/sso/oidc/meGET, сервер с access token
Актуальность токенаhttps://lk.kbp.kz/api/sso/oidc/token/introspectionPOST form, private_key_jwt
Отзыв токенаhttps://lk.kbp.kz/api/sso/oidc/token/revocationPOST form, private_key_jwt
Завершение текущей центральной сессииhttps://lk.kbp.kz/api/sso/oidc/session/endБраузерный OIDC RP-Initiated Logout

Публичная самостоятельная регистрация OAuth-клиентов, implicit/password/client_credentials grant и plain PKCE не поддерживаются. API регистрации/редактирования проекта требует административной сессии КБП с 2FA; игровой токен туда не подходит.

4. Пошаговый вход

  1. Кнопка «Войти через КБП» ведёт на свой сервер /login.
  2. Сервер создаёт криптографически случайные state, nonce, code_verifier (не меньше 32 байт случайности). Генерирует S256 challenge. Хранит значения только на сервере в одноразовом контексте, привязанном к HttpOnly cookie браузера, максимум 15 минут.
  3. Сервер перенаправляет браузер на authorization_endpoint с client_id, точным redirect_uri, response_type=code, scope, state, nonce, code_challenge, code_challenge_method=S256. Для offline_access обязательно prompt=consent. Для языка: ui_locales=ru или kk. Вход идёт в обычной вкладке, без iframe и сторонних cookie.
  4. При действующей сессии КБП повторный пароль не требуется. Иначе пользователь входит обычным способом. Новый пользователь регистрируется и подтверждает email. После подтверждения в том же браузере может продолжить вход в проект; просроченный/открытый на другом устройстве контекст требует снова начать вход из приложения. Даже старый аккаунт без подтверждённого email не получает SSO-доступ до подтверждения.
  5. КБП показывает название проекта и запрашиваемые данные. Пользователь продолжает или отказывается.
  6. Callback приложения однократно забирает и удаляет свой серверный контекст, сравнивает state и обменивает код на сервере, передавая исходный verifier и private_key_jwt. При несовпадении, ошибке/отказе или отсутствии cookie сессию не создаёт.
  7. OIDC-библиотека проверяет подпись ID Token по JWKS, iss, aud своего client_id, exp, iat, nonce, ожидаемый code flow и ответ. Включите проверку подписи ID Token и при прямом HTTPS-обмене; в примере это enableNonRepudiationChecks.
  8. При необходимости сервер запрашивает UserInfo и проверяет, что его sub совпадает с ID Token. В примере, запрашивающем email, дополнительно проверяется email_verified === true.
  9. В транзакции создаётся/находится собственный профиль по (issuer,sub) и создаётся новая случайная локальная сессия. Cookie: host-only, HttpOnly, Secure, SameSite=Lax, без Domain=.kbp.kz. Обычный callback не принимает пользовательский returnTo на произвольный сайт.
  10. Сразу уберите код из адресной строки редиректом на свою обычную страницу. Не сохраняйте код, токен или полную строку callback в access/error/analytics-логах.

Минимальное начало входа на Node.js (полный работающий код — в архиве):

js
import * as oidc from 'openid-client';
import { importJWK } from 'jose';
const key = await importJWK(privateJwk, 'RS256'); // закрытый файл на сервере
const config = await oidc.discovery(
  new URL('https://lk.kbp.kz/api/sso/oidc'), clientId,
  { token_endpoint_auth_method: 'private_key_jwt', id_token_signed_response_alg: 'RS256' },
  oidc.PrivateKeyJwt({ key, kid: privateJwk.kid }),
  { execute: [oidc.enableNonRepudiationChecks], timeout: 5 }
);
const verifier = oidc.randomPKCECodeVerifier();
const state = oidc.randomState(), nonce = oidc.randomNonce();
// Сохранить verifier/state/nonce на сервере и связать с одноразовой cookie.
const url = oidc.buildAuthorizationUrl(config, {
  redirect_uri: registeredCallback,
  response_type: 'code', scope: 'openid profile email offline_access', prompt: 'consent',
  state, nonce, code_challenge: await oidc.calculatePKCECodeChallenge(verifier),
  code_challenge_method: 'S256', ui_locales: 'ru'
});
// Ответ своего сервера: redirect 303 на url.href.

Обработка ответа библиотекой:

js
const tokens = await oidc.authorizationCodeGrant(config, callbackUrl, {
  pkceCodeVerifier: saved.verifier, expectedState: saved.state,
  expectedNonce: saved.nonce, idTokenExpected: true
});
const identity = tokens.claims();
const info = await oidc.fetchUserInfo(config, tokens.access_token, identity.sub);
// Проверки должны закончиться ДО INSERT профиля и сессии.

Не заменяйте проверку подписи простым JSON.parse JWT. ID Token — подтверждение входа, а не способ бесконечно считать аккаунт активным.

5. Данные и scopes

ScopeДанные / действие
openid (обязателен)sub: постоянный непрозрачный идентификатор пользователя
profilename: имя из профиля КБП
emailemail, email_verified: true
offline_accessРазрешает серверное обновление через refresh token

Клиент может запросить только scopes, разрешённые в его карточке. Лишний scope отклоняется. Технические поля OIDC (iss, aud, iat, exp, nonce, sid) обеспечивают проверку, не являются пользовательскими правами. sid связывает центральную сессию с уведомлением о выходе; сохраните его в локальной сессии.

Для минимальной идентификации достаточно openid; имя и email запрашивайте по необходимости. Предлагаемый пример использует все четыре scopes, поэтому администратор должен разрешить их именно этому примеру. В локальном приложении email и name — обновляемые атрибуты, а не ключ личности. Нельзя автоматически объединять разные (issuer,sub) по совпавшей почте.

sql
CREATE UNIQUE INDEX game_profiles_identity ON game_profiles(issuer,subject);
-- Атомарный INSERT ... ON CONFLICT(issuer,subject) ... RETURNING id.

Смена email не создаёт второго профиля и не переносит прогресс. sub не меняется. КБП не принимает игровые пароли и не управляет игровой базой.

6. Сроки и хранение

ОбъектМаксимальный срок
Контекст входа / подтверждение перехода15 минут, связан с браузером
Authorization code60 секунд, однократно
Access Token5 минут
ID Token5 минут
Refresh Token / разрешение проекта30 дней от первоначальной выдачи, без бесконечного продления
Центральная OIDC-сессия14 дней
Локальная сессия примера8 часов, собственная cookie
Кэш успешной introspection в приложенииНе более 30 секунд от начала проверки
Предельная потеря доступа при отзыве60 секунд для корректно подключённого проекта

Обычная сессия кабинета КБП остаётся прежней: это отдельная cookie и прежние правила входа, 2FA и выхода. Истечение локальной сессии требует нового входа; refresh не должен восстанавливать уже отозванную локальную сессию.

На сервере приложения храните токены зашифрованными, отдельным ключом вне базы/репозитория. В примере AES-256-GCM с отдельным случайным nonce на запись и проверкой AAD. Cookie содержит только случайный идентификатор; в БД — его hash. Не помещайте токены в URL, HTML, письма, Telegram, логи, аналитику или localStorage.

7. Проверка актуальности — обязательная часть

Подписи ID Token недостаточно для выполнения игровых действий. Перед каждым защищённым запросом приложение проверяет собственную неотозванную сессию и результат introspection. Успешный ответ можно кэшировать максимум 30 секунд, считая время с начала запроса. Проверяйте active === true, client_id === ваш client_id, sub === sub сессии.

js
const status = await oidc.tokenIntrospection(config, accessToken);
if (status.active !== true || status.client_id !== clientId || status.sub !== session.subject) {
  // Отозвать локальную сессию, запретить защищённое действие, предложить новый вход.
}

При блокировке аккаунта, удалении, сбросе/смене пароля, потере подтверждения email, общем выходе или отключении проекта центральная проверка сразу перестаёт подтверждать старый доступ. Разблокировка не оживляет старые токены и сессии.

Если КБП недоступен, сетевой таймаут или ответ не проверен, после окончания допустимого кэша действие запрещается. Нельзя продлевать кэш при ошибке или продолжать игру по одному ID Token. В примере сессия при такой ошибке завершается; проект может вместо этого временно закрыть защищённые действия, сохранив данные, но не разрешать их без актуальной проверки.

Именно приложение обеспечивает этот предел для своих API, WebSocket-сообщений и фоновых пользовательских действий. Для долгого WebSocket проверяйте доступ периодически не реже 30 секунд и перед изменением защищённых данных. Центр не может закрыть произвольную сессию игры, которая игнорирует отзыв.

8. Серверный выход Back-Channel Logout

Зарегистрированный HTTPS endpoint приложения принимает POST application/x-www-form-urlencoded, поле logout_token. Cookie браузера и Origin здесь не нужны: это подписанное серверное уведомление. Исключение Origin ограничьте только этим endpoint, не всем API.

Используйте JWT-библиотеку. Проверяются RS256, подпись по JWKS КБП, точный issuer, audience своего клиента, обязательные iat, jti, events, событие http://schemas.openid.net/event/backchannel-logout, отсутствие nonce, наличие sid и/или sub. Пример принимает токен возрастом до 120 секунд с допуском часов 5 секунд. Часы серверов должны быть синхронизированы.

В одной транзакции зарегистрируйте уникальный (issuer,jti) и отзовите совпадающие локальные сессии по issuer + sid, при наличии sub также проверяя его. Если sid отсутствует, завершите локальные сессии этого sub. Повторная доставка того же токена должна вернуть успешный 200/204 и не удалять профиль/прогресс. Недействительная подпись или чужой audience — 400/401 без изменений.

КБП хранит очередь и историю доставки. Первая попытка обычно в течение 5 секунд; сетевой таймаут 5 секунд; до 12 попыток с интервалами 5,10,20,40,80,160, затем 300 секунд. Каждая попытка получает свежий подписанный токен. История доступна главному администратору в карточке проекта; завершённые записи технической доставки хранятся 90 дней, общий безопасный аудит — по политике КБП. Очередь не заменяет introspection: при потере всех уведомлений предел отзыва сохраняется через проверку актуальности.

9. Три разных действия выхода

  • «Выйти» в игре: завершить только текущую локальную игровую сессию (POST с Origin + CSRF), очистить свою cookie. Другие проекты и кабинет продолжают работать. Не вызывайте центральный общий выход и не отзывайте общий grant при каждом локальном выходе.
  • RP-Initiated Logout: стандартный end_session_endpoint из Discovery с проверенным id_token_hint, зарегистрированным post_logout_redirect_uri и state, который проверяется приложением при возврате. КБП запрашивает подтверждение. Завершается текущая центральная OIDC-сессия и связанные разрешения; это не команда «все устройства». Обычная cookie кабинета не переопределяется этим протоколом.
  • «Выйти из всех сервисов на всех устройствах» в профиле КБП: отдельная явно подтверждаемая команда; отзывает все сессии кабинета и центральные разрешения этого пользователя на всех устройствах, ставит уведомления проектам. Игра должна обработать их и/или закрыть доступ по introspection. Данные сохраняются.

Обычная кнопка выхода из кабинета КБП сохраняет прежний смысл и не превращается в незаметный общий выход.

10. Refresh, отзыв и ошибки

offline_access нужен только если серверу действительно требуется refresh. Access token можно обновить через oidc.refreshTokenGrant(config, refreshToken). Refresh token меняется при каждом обмене; сохраняйте новую пару атомарно, сериализуйте обновление одной локальной сессии. Пример удерживает блокировку строки локальной сессии на время ограниченного 5-секундным таймаутом обмена; используйте отдельную БД приложения и разумные таймауты блокировок.

Повтор старого refresh token отзывает всю семью разрешения, включая выданные при ротации токены. invalid_grant → закрыть локальную сессию и начать новый вход. Не повторяйте бесконечно обмен старого кода/refresh. 30 дней отсчитываются от первоначальной выдачи, а не от каждого refresh.

Для отдельного явного отключения доступа приложения используйте token_revocation_endpoint библиотекой (oidc.tokenRevocation), затем завершите локальную сессию. Это отличается от обычного локального выхода.

СитуацияДействие приложения
access_deniedПоказать отказ и кнопку повторного входа, не создавать профиль/сессию
Нет контекста / неверные state или nonceОтклонить callback; новый вход из приложения
invalid_grantКод/refresh больше не использовать, войти заново
invalid_clientЗакрыть операцию, проверить client_id/JWKS/kid/подпись/часы на сервере
invalid_scopeИсправить запрос под разрешённые scopes
Introspection active:falseОтозвать локальную сессию и запретить действие
Недоступность/429/5xxБез обхода проверки, без продления кэша, без вывода токенов в ошибку

11. Смена ключей

Ключи приложения: создаёт и хранит приложение. Новый открытый JWKS администратор сохраняет в карточке проекта. Изменение подключения (в том числе ключей/адресов/scopes) отзывает старый доступ только этого проекта; пользователи входят заново. Закрытый ключ не передаётся. Отключение проекта не блокирует пользователя и другие приложения.

Ключи подписи центра: kid может меняться. Используйте Discovery/JWKS и стандартное обновление ключей при неизвестном kid. Обычная ротация двухэтапная: новый ключ сначала появляется в JWKS, прежний продолжает подписывать; после минимум 6 минут (окно обновления кэша JWKS до 5 минут) новый становится активным. Приложение должно обновлять кэш JWKS не реже 5 минут. Центр сохраняет прежний открытый ключ минимум 24 часа; закрытый старый ключ не используется для новых токенов. Действующие токены 5 минут продолжают проверяться, сами refresh-семьи не продлеваются. Не закрепляйте единственный ключ навсегда и не отключайте проверку подписи при смене kid.

При компрометации ключа действует аварийная процедура: отзыв затронутого доступа и удаление скомпрометированного ключа, без обещания совместимости старых токенов. Это отдельная административная операция, не автоматическое действие подключённой игры. Ключ шифрования хранилища и cookie-подписи не заменяйте файлом другой среды.

12. Запуск рабочего примера

Архив: https://lk.kbp.kz/docs/kbp-sso-client.zip Markdown этой инструкции: https://lk.kbp.kz/docs/kbp-sso-integration.md

Состав: app.mjs, server.mjs, keys.mjs, schema.sql, .env.example, package.json, lockfile и README. Пример не содержит игры, платежей и готовых действующих ключей. Это серверное приложение с отдельными PostgreSQL-профилями и сессиями.

  1. Node.js 22.23+; PostgreSQL для собственной БД приложения. Установите зависимости pnpm install --frozen-lockfile (либо ваш проверенный менеджер).
  2. Выполните schema.sql в своей БД, не в базе аккаунтов КБП.
  3. node keys.mjs; настройте закрытый .env по .env.example; выдайте секретным файлам 0600, каталогу private — 0700.
  4. Передайте открытый JWKS и точные URL администратору, получите свой client_id и разрешённые scopes.
  5. Запустите pnpm start за HTTPS reverse proxy своего домена. Не публикуйте private/.env/логи callback. Cookie принадлежат только своему host.
  6. Откройте /login, пройдите вход; /me показывает отдельный профиль. Локальный выход не завершает другие сервисы.
  7. Проверьте блокировку/потерю уведомления/отзыв/дубли на искусственном тестовом аккаунте в согласованной среде. В production не создавайте фиктивные игровые платежи или массовые аккаунты.

Локальный HTTP разрешён только при явном SSO_LOCAL_TEST=true, NODE_ENV != production, обоих origin на http://127.0.0.1:<port> и отдельном локальном центре с отдельными ключами/БД. Рабочий центр КБП не регистрирует localhost. Не включайте insecure-режим для production-issuer или реальных аккаунтов.

Пример — основа интеграции, не готовая игровая платформа: добавьте свои игровые правила, права, API, ограничения частоты, обработку инфраструктурных ошибок, резервирование и очистку истёкших локальных контекстов/сессий/jti. Не удаляйте профили и прогресс при обычном выходе или блокировке.

13. Приёмка приложения разработчиком

Перед подключением должны проходить: проверенный вход; повторный вход без второго профиля; регистрация/email и восстановление с возвратом; отказ неподтверждённому/блокированному аккаунту; точные redirect; неверные state/nonce/PKCE; истёкший и повторный код; чужой client_id/audience; смена email без потери профиля; refresh rotation/replay; подпись и дубли logout; блокировка открытого приложения ≤60 секунд; потеря back-channel с тем же пределом; недоступность центра после 30-секундного кэша; локальный выход без выхода других проектов; общий выход без удаления прогресса; отключение одного клиента; ротация JWKS; мобильный интерфейс и язык.

Проверка центра и эталонного клиента не заменяет приёмку будущей игры. Разработчик игры отвечает за охват всех защищённых действий актуальной проверкой, собственные роли, профиль, прогресс, покупки, резервные копии, логи и безопасность своего сервера.

14. Источники стандартов