OIDC интеграция

ℹ️ О документе

В этом разделе описана пошаговая интеграция внешних приложений с IDENTYX через протокол OpenID Connect. Руководство содержит практические примеры реализации и детальное описание всех этапов интеграции.

Обзор процесса интеграции

Интеграция приложения с IDENTYX через OIDC состоит из следующих этапов:

  1. Регистрация приложения в IDENTYX
  2. Получение конфигурации OIDC сервера
  3. Реализация Authorization Code Flow
  4. Обработка токенов и получение информации о пользователе
  5. Обновление токенов (Refresh Token)
  6. Завершение сессии

Шаг 1: Регистрация приложения

Перед началом интеграции необходимо зарегистрировать ваше приложение в IDENTYX:

  1. Перейдите в раздел OIDC приложения
  2. Нажмите кнопку Создать приложение
  3. Заполните обязательные поля:
    • Название приложения — понятное имя для пользователей
    • Client ID — уникальный идентификатор приложения
    • Client Secret — секретный ключ для аутентификации
    • URL приложения — адрес вашего приложения (если не используется IDENTYX Proxy)
  4. Укажите разрешенные URL для перенаправления (Redirect URIs)
  5. Сохраните приложение
⚠️ Важно

Сохраните значения client_id и client_secret — они понадобятся для настройки вашего приложения. Client Secret должен храниться в безопасности и никогда не передаваться на клиентскую сторону.

Шаг 2: Получение конфигурации OIDC

IDENTYX предоставляет эндпоинт автоматической конфигурации (Discovery), который содержит все необходимые URL для интеграции.

Well-Known Configuration Endpoint

Обратитесь к эндпоинту конфигурации:

GET https://ваш-identyx-сервер/.well-known/openid-configuration

Пример ответа:

{
  "issuer": "https://ваш-identyx-сервер",
  "authorization_endpoint": "https://ваш-identyx-сервер/login/oidc",
  "token_endpoint": "https://ваш-identyx-сервер/api/service/oidc/token",
  "userinfo_endpoint": "https://ваш-identyx-сервер/api/service/oidc/userinfo",
  "end_session_endpoint": "https://ваш-identyx-сервер/api/service/oidc/end-session",
  "scopes_supported": ["openid", "profile", "email", "permissions"],
  "claims_supported": ["name", "phone", "phone_verified", "email", "email_verified", "permissions"],
  "grant_types_supported": ["authorization_code"],
  "response_types_supported": ["code"],
  "response_modes_supported": ["query"],
  "token_endpoint_auth_methods_supported": ["client_secret_post"]
}
ℹ️ Примечание

Большинство OIDC-библиотек автоматически используют этот эндпоинт для получения конфигурации. Вам достаточно указать базовый URL вашего IDENTYX сервера.

Основные эндпоинты

Эндпоинт URL Назначение
Authorization /login/oidc Страница входа и получение кода авторизации
Token /api/service/oidc/token Обмен кода на токены и обновление токенов
UserInfo /api/service/oidc/userinfo Получение информации о пользователе
End Session /api/service/oidc/end-session Завершение сессии пользователя

Шаг 3: Authorization Code Flow

IDENTYX использует Authorization Code Flow — наиболее безопасный способ аутентификации для веб-приложений.

3.1 Перенаправление на страницу входа

Когда пользователь хочет войти в ваше приложение, перенаправьте его на Authorization Endpoint IDENTYX:

GET https://ваш-identyx-сервер/login/oidc?
  response_type=code&
  client_id=ваш_client_id&
  redirect_uri=https://ваше-приложение/callback&
  scope=openid profile email permissions&
  state=случайная_строка&
  nonce=случайная_строка

Параметры запроса:

Параметр Обязательный Описание
response_type Да Всегда code
client_id Да Идентификатор вашего приложения из IDENTYX
redirect_uri Да URL для возврата после входа (должен быть зарегистрирован в IDENTYX)
scope Да Запрашиваемые разрешения (через пробел): openid, profile, email, permissions
state Да Случайная строка для защиты от CSRF атак
nonce Рекомендуется Случайная строка для защиты от replay атак
⚠️ Безопасность

Обязательно генерируйте уникальный state для каждого запроса и проверяйте его при получении ответа. Это защищает от CSRF атак. Сохраните значение state в сессии вашего приложения.

Описание Scopes

  • openid (обязательный) — базовый scope для OIDC, возвращает идентификатор пользователя sub
  • profile — доступ к отображаемому имени (name) и телефону (phone) пользователя
  • email — доступ к email адресу пользователя
  • permissions — доступ к правам пользователя в вашем приложении

3.2 Пользователь проходит аутентификацию

IDENTYX отобразит страницу входа, где пользователь:

  1. Выберет метод аутентификации (локальная, LDAP, внешние провайдеры, и т.д.)
  2. Введет свои учетные данные
  3. При необходимости пройдет двухфакторную аутентификацию
  4. Примет условия использования приложения (если настроена оферта)

3.3 Получение кода авторизации

После успешной аутентификации IDENTYX перенаправит пользователя обратно на ваш redirect_uri с параметрами:

GET https://ваше-приложение/callback?
  code=полученный_код_авторизации&
  state=та_же_случайная_строка

Что нужно сделать:

  1. Проверьте, что значение state совпадает с тем, что вы отправили
  2. Извлеките значение code — это код авторизации
  3. Используйте код для получения токенов (следующий шаг)
🚫 Важно

Код авторизации действителен очень короткое время (несколько минут) и может быть использован только один раз. Немедленно обменяйте его на токены.

Шаг 4: Обмен кода на токены

Теперь нужно обменять полученный код авторизации на токены доступа. Этот запрос выполняется с серверной стороны вашего приложения.

Запрос токенов

POST https://ваш-identyx-сервер/api/service/oidc/token
Content-Type: application/json

{
  "grant_type": "authorization_code",
  "client_id": "ваш_client_id",
  "client_secret": "ваш_client_secret",
  "code": "полученный_код_авторизации",
  "redirect_uri": "https://ваше-приложение/callback"
}

Параметры запроса:

Параметр Описание
grant_type Всегда authorization_code
client_id Идентификатор вашего приложения
client_secret Секретный ключ вашего приложения
code Код авторизации, полученный на предыдущем шаге
redirect_uri Тот же redirect_uri, что использовался при запросе кода

Ответ с токенами

Успешный ответ:

{
  "token_type": "Bearer",
  "expires_in": 3600,
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refresh_token": "def50200a1b2c3d4e5f6...",
  "scope": "openid profile email permissions",
  "id_token": "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9..."
}

Описание полей:

Поле Описание
access_token Токен для доступа к защищенным ресурсам (UserInfo endpoint)
refresh_token Токен для обновления access_token после его истечения
id_token JWT токен с информацией о пользователе и аутентификации
token_type Тип токена, всегда Bearer
expires_in Время жизни access_token в секундах
scope Предоставленные разрешения
⚠️ Хранение токенов

Храните токены в безопасном месте на сервере (например, в зашифрованных cookie или в серверной сессии). Никогда не передавайте refresh_token на клиентскую сторону.

Проверка ID Token

ID Token — это JWT (JSON Web Token), содержащий информацию о пользователе. Он должен быть проверен перед использованием.

Структура ID Token:

{
  "iss": "https://ваш-identyx-сервер",
  "sub": "user123",
  "aud": "ваш_client_id",
  "exp": 1640995200,
  "iat": 1640991600,
  "auth_time": 1640991600,
  "nonce": "ваш_nonce",
  "sid": "session_id",
  "name": "Иван Иванов",
  "email": "ivan@example.com",
  "email_verified": false,
  "permissions": ["/app1:/read", "/app1:/write"]
}

Важные поля:

  • iss — издатель токена (ваш IDENTYX сервер)
  • sub — уникальный идентификатор пользователя
  • aud — для кого предназначен токен (должен быть ваш client_id)
  • exp — время истечения токена (Unix timestamp)
  • iat — время создания токена
  • nonce — должен совпадать с отправленным вами nonce
  • sid — идентификатор OIDC сессии

Проверки, которые необходимо выполнить:

  1. Проверьте подпись токена (используя client_secret и алгоритм HS512)
  2. Убедитесь, что iss соответствует вашему IDENTYX серверу
  3. Убедитесь, что aud соответствует вашему client_id
  4. Проверьте, что токен не истек (exp > current_time)
  5. Убедитесь, что nonce совпадает с отправленным
ℹ️ Библиотеки

Используйте проверенные OIDC библиотеки для вашего языка программирования — они автоматически выполнят все необходимые проверки токена.

Шаг 5: Получение информации о пользователе

После получения токенов вы можете запросить дополнительную информацию о пользователе через UserInfo endpoint.

Запрос UserInfo

GET https://ваш-identyx-сервер/api/service/oidc/userinfo
Authorization: Bearer ваш_access_token

Или отправьте POST запрос с access_token в теле:

POST https://ваш-identyx-сервер/api/service/oidc/userinfo
Content-Type: application/json

{
  "access_token": "ваш_access_token"
}

Ответ UserInfo

Пример ответа:

{
  "sub": "user123",
  "name": "Иван Иванов",
  "email": "ivan@example.com",
  "email_verified": false,
  "phone": "+79001234567",
  "phone_verified": false,
  "permissions": [
    "/ваше-приложение:/read",
    "/ваше-приложение:/write",
    "/ваше-приложение:/admin"
  ]
}

Возвращаемые поля зависят от запрошенных scopes:

Scope Возвращаемые поля
openid sub (идентификатор пользователя)
profile name, phone, phone_verified
email email, email_verified
permissions permissions (массив прав доступа)

Использование прав доступа (permissions)

Если вы запросили scope permissions, в ответе будет массив прав доступа пользователя в вашем приложении.

Формат прав:

/объект:/действие

Примеры:

  • /myapp:/read — право на чтение в приложении myapp
  • /myapp:/write — право на запись
  • /myapp:/admin — право администратора
  • /myapp/documents:/read — право на чтение документов

Используйте эти права для контроля доступа в вашем приложении.

Шаг 6: Обновление токенов

Access Token имеет ограниченный срок действия. Когда он истекает, используйте Refresh Token для получения нового Access Token без повторного входа пользователя.

Запрос обновления токена

POST https://ваш-identyx-сервер/api/service/oidc/token
Content-Type: application/json

{
  "grant_type": "refresh_token",
  "client_id": "ваш_client_id",
  "client_secret": "ваш_client_secret",
  "refresh_token": "ваш_refresh_token"
}

Ответ при обновлении

{
  "token_type": "Bearer",
  "expires_in": 3600,
  "access_token": "новый_access_token",
  "refresh_token": "новый_refresh_token"
}
ℹ️ Важно

При обновлении токенов вы получаете новый access_token И новый refresh_token. Старый refresh_token становится недействительным. Обязательно сохраните оба новых токена.

Обработка истечения токенов

Рекомендуемая стратегия:

  1. Отслеживайте поле expires_in при получении токенов
  2. Обновляйте токен за несколько минут до истечения (например, за 5 минут)
  3. Если запрос с access_token вернул ошибку 401, попробуйте обновить токен
  4. Если обновление не удалось, перенаправьте пользователя на страницу входа

Шаг 7: Завершение сессии

Когда пользователь выходит из вашего приложения, важно завершить его сессию в IDENTYX.

Завершение по ID Token

POST https://ваш-identyx-сервер/api/service/oidc/end-session
Content-Type: application/x-www-form-urlencoded

id_token_hint=ваш_id_token

Завершение по Access Token

POST https://ваш-identyx-сервер/api/service/oidc/end-session
Authorization: Bearer ваш_access_token

Что происходит при завершении сессии

  1. OIDC сессия в IDENTYX помечается как завершенная
  2. Токены становятся недействительными
  3. Все IAM сессии пользователя закрываются (опционально)
  4. Отправляется Backchannel Logout запрос в ваше приложение (если настроено)
⚠️ Важно

После завершения сессии в IDENTYX обязательно завершите сессию пользователя в вашем приложении (удалите токены, очистите cookie, и т.д.)

Backchannel Logout

Backchannel Logout позволяет IDENTYX уведомить ваше приложение о завершении сессии (например, когда пользователь вышел из другого приложения или администратор принудительно завершил сессию).

Настройка Backchannel Logout

При использовании IDENTYX Proxy функциональность Backchannel Logout работает автоматически. Proxy получает уведомления от IDENTYX и завершает сессии приложений.

Если вы не используете Proxy, вам нужно реализовать обработку Backchannel Logout в вашем приложении:

  1. Создайте эндпоинт для приема уведомлений (например, /backchannel-logout)
  2. Обрабатывайте POST запросы с параметром logout_token
  3. Проверьте подпись JWT токена
  4. Извлеките sid (session ID) из токена
  5. Завершите соответствующую сессию в вашем приложении

Пример Logout Token:

{
  "iss": "https://ваш-identyx-сервер",
  "sub": "user123",
  "aud": "ваш_client_id",
  "iat": 1640991600,
  "exp": 1640991780,
  "jti": "unique_id",
  "sid": "session_id",
  "events": {
    "http://schemas.openid.net/event/backchannel-logout": {}
  }
}

Примеры реализации

Пример на PHP

1. Перенаправление на страницу входа:

<?php
session_start();

$client_id = 'ваш_client_id';
$redirect_uri = 'https://ваше-приложение/callback';
$identyx_url = 'https://ваш-identyx-сервер';

// Генерируем и сохраняем state
$state = bin2hex(random_bytes(16));
$_SESSION['oauth_state'] = $state;

// Генерируем nonce
$nonce = bin2hex(random_bytes(16));
$_SESSION['oauth_nonce'] = $nonce;

// Формируем URL для авторизации
$auth_url = $identyx_url . '/login/oidc?' . http_build_query([
    'response_type' => 'code',
    'client_id' => $client_id,
    'redirect_uri' => $redirect_uri,
    'scope' => 'openid profile email permissions',
    'state' => $state,
    'nonce' => $nonce
]);

// Перенаправляем пользователя
header('Location: ' . $auth_url);
exit;
?>

2. Обработка callback и получение токенов:

<?php
session_start();

$client_id = 'ваш_client_id';
$client_secret = 'ваш_client_secret';
$redirect_uri = 'https://ваше-приложение/callback';
$identyx_url = 'https://ваш-identyx-сервер';

// Проверяем state
if (!isset($_GET['state']) || $_GET['state'] !== $_SESSION['oauth_state']) {
    die('Invalid state');
}

// Получаем код авторизации
$code = $_GET['code'] ?? null;
if (!$code) {
    die('No code provided');
}

// Обмениваем код на токены
$token_url = $identyx_url . '/api/service/oidc/token';
$post_data = [
    'grant_type' => 'authorization_code',
    'client_id' => $client_id,
    'client_secret' => $client_secret,
    'code' => $code,
    'redirect_uri' => $redirect_uri
];

$ch = curl_init($token_url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($post_data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);

$response = curl_exec($ch);
curl_close($ch);

$tokens = json_decode($response, true);

if (isset($tokens['access_token'])) {
    // Сохраняем токены в сессии
    $_SESSION['access_token'] = $tokens['access_token'];
    $_SESSION['refresh_token'] = $tokens['refresh_token'];
    $_SESSION['id_token'] = $tokens['id_token'];
    
    // Получаем информацию о пользователе
    $userinfo_url = $identyx_url . '/api/service/oidc/userinfo';
    $ch = curl_init($userinfo_url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
        'Authorization: Bearer ' . $tokens['access_token']
    ]);
    
    $userinfo_response = curl_exec($ch);
    curl_close($ch);
    
    $userinfo = json_decode($userinfo_response, true);
    $_SESSION['user'] = $userinfo;
    
    // Перенаправляем на главную страницу
    header('Location: /');
    exit;
} else {
    die('Failed to get tokens');
}
?>

3. Обновление токена:

<?php
function refreshAccessToken($refresh_token) {
    $client_id = 'ваш_client_id';
    $client_secret = 'ваш_client_secret';
    $identyx_url = 'https://ваш-identyx-сервер';
    
    $token_url = $identyx_url . '/api/service/oidc/token';
    $post_data = [
        'grant_type' => 'refresh_token',
        'client_id' => $client_id,
        'client_secret' => $client_secret,
        'refresh_token' => $refresh_token
    ];
    
    $ch = curl_init($token_url);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($post_data));
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
    
    $response = curl_exec($ch);
    curl_close($ch);
    
    return json_decode($response, true);
}

// Использование
session_start();
$tokens = refreshAccessToken($_SESSION['refresh_token']);

if (isset($tokens['access_token'])) {
    $_SESSION['access_token'] = $tokens['access_token'];
    $_SESSION['refresh_token'] = $tokens['refresh_token'];
}
?>

Пример на Node.js (Express)

Установка зависимостей:

npm install express express-session openid-client

Базовая интеграция:

const express = require('express');
const session = require('express-session');
const { Issuer } = require('openid-client');

const app = express();

app.use(session({
  secret: 'ваш_секретный_ключ',
  resave: false,
  saveUninitialized: false
}));

const IDENTYX_URL = 'https://ваш-identyx-сервер';
const CLIENT_ID = 'ваш_client_id';
const CLIENT_SECRET = 'ваш_client_secret';
const REDIRECT_URI = 'https://ваше-приложение/callback';

let client;

// Инициализация OIDC клиента
Issuer.discover(IDENTYX_URL).then(identyxIssuer => {
  client = new identyxIssuer.Client({
    client_id: CLIENT_ID,
    client_secret: CLIENT_SECRET,
    redirect_uris: [REDIRECT_URI],
    response_types: ['code']
  });
});

// Маршрут для входа
app.get('/login', (req, res) => {
  const authUrl = client.authorizationUrl({
    scope: 'openid profile email permissions',
    state: generateRandomString(),
    nonce: generateRandomString()
  });
  
  res.redirect(authUrl);
});

// Callback маршрут
app.get('/callback', async (req, res) => {
  try {
    const params = client.callbackParams(req);
    const tokenSet = await client.callback(REDIRECT_URI, params);
    
    // Сохраняем токены в сессии
    req.session.tokens = {
      access_token: tokenSet.access_token,
      refresh_token: tokenSet.refresh_token,
      id_token: tokenSet.id_token
    };
    
    // Получаем информацию о пользователе
    const userinfo = await client.userinfo(tokenSet.access_token);
    req.session.user = userinfo;
    
    res.redirect('/');
  } catch (err) {
    console.error(err);
    res.status(500).send('Authentication failed');
  }
});

// Защищенный маршрут
app.get('/', (req, res) => {
  if (!req.session.user) {
    return res.redirect('/login');
  }
  
  res.send(`Добро пожаловать, ${req.session.user.name}!`);
});

// Выход
app.get('/logout', async (req, res) => {
  if (req.session.tokens) {
    try {
      await client.revoke(req.session.tokens.access_token);
    } catch (err) {
      console.error('Logout error:', err);
    }
  }
  
  req.session.destroy();
  res.redirect('/login');
});

function generateRandomString() {
  return require('crypto').randomBytes(16).toString('hex');
}

app.listen(3000, () => {
  console.log('App running on http://localhost:3000');
});

Лучшие практики

🔐 Безопасность
  • Храните client_secret в безопасности, никогда не передавайте его на клиент
  • Всегда проверяйте параметр state для защиты от CSRF
  • Используйте HTTPS для всех запросов
  • Проверяйте подпись и срок действия ID Token
  • Храните токены в безопасном месте (httpOnly cookies, серверная сессия)
⚡ Производительность
  • Кешируйте well-known конфигурацию
  • Обновляйте токены заранее, не дожидаясь истечения
  • Используйте connection pooling для HTTP запросов
  • Не запрашивайте UserInfo на каждый запрос, кешируйте данные
🔄 Надежность
  • Обрабатывайте ошибки при получении и обновлении токенов
  • Реализуйте механизм retry при временных сбоях
  • Логируйте все важные события аутентификации
  • Корректно обрабатывайте Backchannel Logout
📚 Разработка
  • Используйте проверенные OIDC библиотеки для вашего языка
  • Тестируйте интеграцию в staging окружении
  • Документируйте процесс интеграции для вашей команды
  • Следите за обновлениями OIDC стандартов

Устранение проблем

Частые ошибки

Ошибка Причина Решение
invalid_request Неверные параметры запроса Проверьте правильность всех обязательных параметров
invalid_client Неверный client_id или client_secret Убедитесь, что используете правильные учетные данные из IDENTYX
invalid_grant Код авторизации истек или уже использован Используйте код немедленно после получения, каждый код можно использовать только один раз
redirect_uri_mismatch Redirect URI не совпадает с зарегистрированным Убедитесь, что redirect_uri точно совпадает с указанным в настройках приложения
Unauthorized (401) Access token истек или недействителен Обновите токен используя refresh_token

Советы по отладке

  • Проверьте, что все URL правильные и доступные
  • Убедитесь, что используете правильные client_id и client_secret
  • Проверьте, что redirect_uri зарегистрирован в IDENTYX
  • Включите подробное логирование HTTP запросов
  • Используйте инструменты разработчика браузера для отслеживания redirects
  • Проверьте журнал событий в IDENTYX для диагностики проблем

Тестирование интеграции

Перед запуском в production убедитесь, что протестировали следующие сценарии:

  1. Первичный вход — пользователь входит в приложение впервые
  2. Повторный вход — пользователь входит повторно (должен быть авторизован автоматически если есть активная сессия)
  3. Выход — пользователь выходит из приложения
  4. Истечение токена — обновление access_token через refresh_token
  5. Истечение сессии — что происходит когда истекает refresh_token
  6. Разные методы входа — локальная аутентификация, LDAP, внешние провайдеры
  7. 2FA — вход с двухфакторной аутентификацией
  8. Согласие с офертой — если настроено для вашего приложения
  9. Права доступа — проверка прав пользователя из scope permissions
  10. Принудительное завершение сессии — администратор завершает сессию пользователя

Полезные библиотеки

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

Язык Библиотека Описание
PHP jumbojett/openid-connect-php Легкая библиотека для OIDC
Node.js openid-client Сертифицированная OIDC библиотека
Python authlib Универсальная библиотека для OAuth и OIDC
Java Spring Security OAuth Интеграция с Spring Framework
.NET IdentityModel.OidcClient OIDC клиент для .NET
Go coreos/go-oidc OIDC клиент для Go
✅ Готовы продолжить?

После интеграции вашего приложения через OIDC, изучите раздел 55. Proxy интеграция для упрощенного способа подключения приложений через IDENTYX Proxy.