Назначение
Цифровой профиль физического лица — сведения о гражданине из государственных информационных систем (МВД, ЗАГС, СФР, ФНС, Росреестр и других), доступные через ЕСИА по согласию пользователя. ESIA Adapter получает эти сведения во время входа и передаёт их приложению в атрибутах пользователя.
Для получения сведений через адаптер в файле конфигурации внешнего провайдера (extidp.json) задаются три вещи:
- параметр
permissionsвredirectForm— какие согласия пользователь даёт информационной системе; - шаги запросов к REST API Цифрового профиля в
userInfoPipeline; - правила передачи полученных значений в атрибуты пользователя в
attrMapping.
Изменений в самом адаптере не требуется. Готовые примеры по видам сведений — на страницах источников (8.7.1–8.7.5) и в подробных кейсах.
Принцип работы
- Адаптер направляет пользователя в ЕСИА на
/aas/oauth2/v2/acсоscope=openidи параметромpermissions— JSON-массив согласий в URL-safe Base64. - Пользователь подтверждает согласие в ЕСИА.
- Адаптер обменивает авторизационный код на токен на
/aas/oauth2/v3/te(безpermissions). Маркер доступа содержит информацию о согласии. - Адаптер выполняет шаги конвейера: запрашивает сведения методом
GET /digital/api/public/{version}/pso/{oid}/doc/{doc_type}с маркером в заголовкеAuthorization. - Адаптер преобразует ответы в атрибуты пользователя и возвращает их приложению.
Для API ЕСИА v2 используется шаблон extidp.esia.v2.pkce.example.json из комплекта поставки, провайдер криптографии — cprocsp_raw.
Параметр permissions
Перед кодированием — JSON-массив:
[
{
"sysname": "<ТИП_СОГЛАСИЯ>",
"scopes": [ { "sysname": "history_passport_doc" } ],
"purposes": [ { "sysname": "<ЦЕЛЬ>" } ],
"actions": [ { "sysname": "ALL_ACTIONS_TO_DATA" } ],
"responsibleObject": "<ФИО_ИЛИ_ПОДРАЗДЕЛЕНИЕ>",
"expire": 262800
}
]
| Поле | Значение |
|---|---|
sysname | мнемоника типа согласия из реестров платформы согласий |
scopes | области доступа, запрашиваемые в рамках согласия |
purposes | цели из реестра целей |
actions | допустим только ALL_ACTIONS_TO_DATA |
expire | срок действия в минутах (262800 — 182,5 суток), не больше максимума для типа согласия; предельное значение для всех типов — 26 297 460 минут |
responsibleObject | ответственное лицо или иные сведения (необязательно) |
ogrn | ОГРН организации, от имени которой запрашивается согласие, целое число (необязательно) |
terms, sites | условия обработки и ресурсы оператора; применяются только к согласию на распространение данных (необязательно) |
В запросе авторизации в scope остаётся только openid. В файле конфигурации значение можно задать шаблоном: функция base64 адаптера кодирует URL-safe без символов =:
"permissions": "{{ $perm := printf `[{\"sysname\":\"<ТИП_СОГЛАСИЯ>\",\"scopes\":[{\"sysname\":\"history_passport_doc\"}],\"purposes\":[{\"sysname\":\"<ЦЕЛЬ>\"}],\"actions\":[{\"sysname\":\"ALL_ACTIONS_TO_DATA\"}],\"expire\":262800}]` }}{{ $perm | base64 }}"
Согласие запрашивается только у пользователей с подтверждённой учётной записью и, по умолчанию, старше 18 лет (возраст задаётся в настройках типа согласия). Если пользователь не подходит под условия, ЕСИА выдаёт токен с областью openid без запроса согласия; запросы сведений для такого пользователя возвращают NO_CONSENT. Пользователь может отклонить необязательные области доступа типа согласия: сведения по ним недоступны. Если согласие с теми же типом, целями, действиями и областями уже выдано и не истекло, экран согласий не показывается. При обмене кода на токен в scope также указывается только openid.
Шаг запроса сведений
{ "t": 0, "r": "gibdd", "m": "GET",
"u": "{{.extidp_details.baseUrl}}/digital/api/public/v1/pso/{{printf \"%.0f\" .id_token_claims.sub}}/doc/GIBDD_DRIVER_LICENSE",
"h": { "Authorization": "Bearer {{.token.access_token}}" } }
Для ответов-массивов параметр r (имя переменной результата) обязателен.
Вариант с идентификатором согласия
Коммерческие методы /digital/api/public/commercial/{version}/pso/{oid}/doc/{doc_type} принимают идентификатор согласия в параметре permissionId. Идентификатор согласия (id) можно получить из списка согласий, выданных организации: поле permissions_url маркера доступа содержит адрес метода /esia-rs/api/public/{version}/prns/{oid}/issued/permissions. Для каждого согласия в ответе указаны id, status (A — выдано, D — отозвано), expiredOn и scopes; в примере выбирается первое действующее согласие (status = A). Идентификатор также передаётся в уведомлениях платформы согласий (issPrmId).
{ "t": 2, "d": { "at_claims": "jwt(token.access_token)" } },
{ "t": 0, "r": "consents", "m": "GET", "u": "{{.at_claims.permissions_url}}",
"h": { "Authorization": "Bearer {{.token.access_token}}" } },
{ "t": 2, "d": { "permission_id": "r = \"\"; if (consents && consents.elements) { for (i = 0; i < consents.elements.length; i++) { c = consents.elements[i]; if (c.status === \"A\") { r = String(c.id); break; } } } r;" } },
{ "t": 0, "r": "gibdd", "m": "GET",
"u": "{{.extidp_details.baseUrl}}/digital/api/public/commercial/v1/pso/{{printf \"%.0f\" .id_token_claims.sub}}/doc/GIBDD_DRIVER_LICENSE/raw",
"d": { "permissionId": "{{.permission_id}}" },
"h": { "Authorization": "Bearer {{.token.access_token}}" } }
Состояние ответа
Ведомственные сведения запрашиваются асинхронно, поэтому ответ может не содержать данных. Примеры определяют состояние JavaScript-шагом и передают его приложению в атрибуте:
| Состояние | Признак в ответе | Действие приложения |
|---|---|---|
yes | есть status | использовать сведения |
pending | только oid и requestId | запрос ушёл в ведомство; повторить позже |
NO_CONSENT | statusDoc: NO_CONSENT | запросить согласие пользователя |
error | поле error | данных нет или ошибка ведомства |
no | иное | сведений нет |
Особенности
- Любой ответ, кроме 2xx (например, 403 «доступ к REST API ограничен»), прерывает вход пользователя. Заявку на снятие ограничения подаёт организация.
- Адаптер выполняет конвейер один раз за вход и не ожидает ответа ведомства: данные по
requestIdприложение получает при следующем входе. - Ответ с пустым телом или с пробельным символом перед
[обрабатывается как ошибка; ответ сContent-Type, отличным отapplication/json, сохраняется строкой. - В атрибуты (
attrMapping.x) передаются только строки. Содержимое документов (XML, PDF) в атрибуты не передаётся; рекомендуется передавать признак наличия, год или количество. - Если значения нет, шаблон
{{.person.firstName}}вернёт<no value>, а при отсутствии переменнойpersonвход прервётся. Для безопасного обращения применяется{{ with .person.firstName }}{{ . }}{{ end }}или, в JavaScript,|| "". - JavaScript-шаги выполняются движком ES5 (без
let, стрелочных функций, шаблонных строк) не дольше 10 секунд. - Каждый запрос документа добавляет один HTTP-запрос к ЕСИА на вход. Данные пользователя кэшируются на время
USERINFO_TTL.
Источники сведений
Виды сведений сгруппированы по ведомствам; на каждой странице — таблица видов с doc_type и областями доступа, права, шаги запросов, обработка ответа и атрибуты.
| Источник | Виды сведений | Страница |
|---|---|---|
| МВД | паспорт РФ, паспорта детей, загранпаспорт, документ иностранного гражданина, история паспортов, водительское удостоверение, выписка о ТС | 8.7.1 |
| ЗАГС | свидетельства о рождении (РФ, СССР, другая страна, ребёнок), о браке, расторжении брака, перемене имени, отцовстве | 8.7.2 |
| СФР | ИЛС, пенсии и выплаты, меры соцзащиты, предпенсионный возраст, трудовая деятельность, СНИЛС детей, свидетельство пенсионера, инвалидность, многодетная семья, материнский капитал | 8.7.3 |
| ФНС | 2-НДФЛ, доходы и взносы, ЕГРИП, ЕГРЮЛ, самозанятые, НПД, ИНН детей | 8.7.4 |
| Росреестр и другие | недвижимость, полис ОМС (в том числе ребёнка), студенческий билет, удостоверение ГГС, сведения об учителе | 8.7.5 |
Базовые сведения
Базовые сведения (GET /digital/api/public/{version}/pso/{oid}) передаются по областям доступа:
| Сведение | Атрибут | Область доступа |
|---|---|---|
| ФИО | firstName, middleName, lastName | fullname |
| СНИЛС | snils | snils |
| ИНН | inn | inn |
| Пол, дата и место рождения | gender, birthDate, birthPlace | gender, birthdate, birthplace |
| Гражданство | citizenship | citizenship |
| Email, телефон | email, mobilePhone | email, mobile |
| Адреса регистрации и проживания | registrationAddress, homeAddress | addresses |
Версии методов меняются: ИЛС и трудовая деятельность — с 10.10.2026, пенсии и предпенсионный возраст — с 01.12.2026. Актуальная версия метода указывается на странице источника.
Добавление нового вида сведений
- Описание вида сведений находится на info.gosuslugi.ru, в статье «получить сведения … через REST API с идентификатором согласия»: в ней указаны
doc_type, версия API и формат ответа. - Область доступа добавляется в
scopesпараметраpermissions. - Добавляется шаг запроса (см. «Шаг запроса сведений»).
- Добавляется JavaScript-шаг состояния ответа по образцу страниц источников.
- Нужные поля добавляются в
attrMapping.x. - Вход проверяется на тестовом контуре ЕСИА (
https://esia-portal1.test.gosuslugi.ru); для методов, где он предусмотрен (например,PASSPORT_HISTORY), тестовые ответы запрашиваются параметромtest=N.
Полный пример конфигурации
Файл конфигурации внешнего провайдера (extidp.json) целиком на примере сведений МВД. Значение typeDetails — строка с JSON; значения в <...> задаются при настройке. Для других источников добавляются области доступа и шаги со страниц 8.7.1–8.7.5.
[
{
"id": "<UUID_ПРОВАЙДЕРА>",
"redirectMethod": 0,
"authorizeUri": "{{.extidp_details.baseUrl}}/aas/oauth2/v2/ac",
"logoutRedirectMethod": 0,
"logoutUri": "{{.extidp_details.baseUrl}}/aas/oauth2/api/logout",
"logoutForm": {
"redirect_url": "https://<адрес-после-выхода>",
"client_id": "{{ .extidp_details.clientID }}"
},
"type": "esia",
"name": "dp-mvd",
"synonym": "esia",
"typeDetails": "{\n \"baseUrl\": \"https://esia-portal1.test.gosuslugi.ru\",\n \"clientCertHash\": \"<ХЕШ_СЕРТИФИКАТА_ГОСТ_Р_34.11-2012_256>\",\n \"clientID\": \"<MNEMONIC_ИС_В_ЕСИА>\",\n \"cryptoProvider\": {\n \"name\": \"cprocsp_raw\",\n \"details\": {\n \"certPath\": \"/opt/esia-adapter/certs/<сертификат_ИС>.cer\",\n \"containerName\": \"<ИМЯ_КОНТЕЙНЕРА>\",\n \"containerPIN\": \"<PIN_КОНТЕЙНЕРА>\",\n \"sha1Thumbprint\": \"<SHA1_ОТПЕЧАТОК>\",\n \"hashAlg\": \"GOST3411_2012_256\"\n }\n },\n \"scope\": \"openid\",\n \"scopeOrg\": \"\"\n}",
"redirectForm": {
"response_type": "code",
"client_id": "{{ .extidp_details.clientID }}",
"redirect_uri": "{{ .extidp_request.CallbackURI }}",
"scope": "{{ .extidp_details.scope }}",
"client_secret": "{{ $ts := timeFormat \"2006.01.02 15:04:05 Z0700\" .process_started }}{{ $clientSecretRaw := join .extidp_details.clientID .extidp_details.scope $ts .extidp_request.ID .extidp_request.CallbackURI }}{{ cryptoSign .context $clientSecretRaw .extidp_details.cryptoProvider | base64 }}",
"client_certificate_hash": "{{ .extidp_details.clientCertHash }}",
"timestamp": "{{ timeFormat \"2006.01.02 15:04:05 Z0700\" .process_started }}",
"state": "{{ .extidp_request.ID }}",
"access_type": "offline",
"code_challenge_method": "{{ .extidp_details.cryptoProvider.details.hashAlg }}",
"code_challenge": "{{ cryptoHashSum .context .code_verifier .extidp_details.cryptoProvider }}",
"permissions": "{{ $perm := printf `[{\"sysname\":\"<ТИП_СОГЛАСИЯ>\",\"scopes\":[{\"sysname\":\"id_doc\"},{\"sysname\":\"kid_id_doc\"},{\"sysname\":\"foreign_passport_doc\"},{\"sysname\":\"history_passport_doc\"},{\"sysname\":\"drivers_licence_doc\"},{\"sysname\":\"vehicle_reg_cert_doc\"}],\"purposes\":[{\"sysname\":\"<ЦЕЛЬ>\"}],\"actions\":[{\"sysname\":\"ALL_ACTIONS_TO_DATA\"}],\"responsibleObject\":\"<ФИО_ИЛИ_ПОДРАЗДЕЛЕНИЕ>\",\"expire\":<СРОК_В_МИНУТАХ>}]` }}{{ $perm | base64 }}"
},
"userInfoPipeline": [
{
"t": 1,
"d": {
"state": "{{ guid }}"
}
},
{
"t": 0,
"r": "token",
"m": "POST",
"u": "{{.extidp_details.baseUrl}}/aas/oauth2/v3/te",
"d": {
"client_certificate_hash": "{{.extidp_details.clientCertHash}}",
"client_id": "{{.extidp_details.clientID}}",
"client_secret": "{{$ts := timeFormat \"2006.01.02 15:04:05 Z0700\" .process_started}}{{$clientSecretRaw := join .extidp_details.clientID .extidp_details.scope $ts .state .extidp_request.CallbackURI .code}}{{ cryptoSign .context $clientSecretRaw .extidp_details.cryptoProvider | base64 }}",
"code": "{{.code}}",
"grant_type": "authorization_code",
"redirect_uri": "{{.extidp_request.CallbackURI}}",
"scope": "{{.extidp_details.scope}}",
"state": "{{.state}}",
"timestamp": "{{timeFormat \"2006.01.02 15:04:05 Z0700\" .process_started}}",
"token_type": "Bearer",
"code_verifier": "{{ .code_verifier }}"
}
},
{
"t": 1,
"d": {
"verify_id_token": "{{ cryptoVerifyJWT .context .token.id_token .extidp_details.cryptoProvider }}"
}
},
{
"t": 2,
"d": {
"id_token_claims": "jwt(token.id_token)"
}
},
{
"t": 1,
"d": {
"is_trusted": "{{ with index .id_token_claims \"urn:esia:sbj\" \"urn:esia:sbj:is_tru\" }}{{ if not . }}{{ fail \"user is not trusted\" }}{{ end }}{{ end }}"
}
},
{
"t": 0,
"r": "person",
"m": "GET",
"u": "{{.extidp_details.baseUrl}}/digital/api/public/v1/pso/{{printf \"%.0f\" .id_token_claims.sub}}",
"h": {
"Authorization": "Bearer {{.token.access_token}}"
}
},
{
"t": 1,
"d": {
"person_ok": "{{ with .person.oid }}{{ else }}{{ fail \"cp: pso response has no oid\" }}{{ end }}"
}
},
{
"t": 0,
"r": "rf_passport",
"m": "GET",
"u": "{{.extidp_details.baseUrl}}/digital/api/public/v1/pso/{{printf \"%.0f\" .id_token_claims.sub}}/doc/RF_PASSPORT",
"h": {
"Authorization": "Bearer {{.token.access_token}}"
}
},
{
"t": 0,
"r": "kid_rf_passport",
"m": "GET",
"u": "{{.extidp_details.baseUrl}}/digital/api/public/v1/pso/{{printf \"%.0f\" .id_token_claims.sub}}/doc/KID_RF_PASSPORT",
"h": {
"Authorization": "Bearer {{.token.access_token}}"
}
},
{
"t": 0,
"r": "frgn_pass",
"m": "GET",
"u": "{{.extidp_details.baseUrl}}/digital/api/public/v1/pso/{{printf \"%.0f\" .id_token_claims.sub}}/doc/FRGN_PASS",
"h": {
"Authorization": "Bearer {{.token.access_token}}"
}
},
{
"t": 0,
"r": "fid_doc",
"m": "GET",
"u": "{{.extidp_details.baseUrl}}/digital/api/public/v1/pso/{{printf \"%.0f\" .id_token_claims.sub}}/doc/FID_DOC",
"h": {
"Authorization": "Bearer {{.token.access_token}}"
}
},
{
"t": 0,
"r": "passport_history",
"m": "GET",
"u": "{{.extidp_details.baseUrl}}/digital/api/public/v2/pso/{{printf \"%.0f\" .id_token_claims.sub}}/doc/PASSPORT_HISTORY",
"h": {
"Authorization": "Bearer {{.token.access_token}}"
}
},
{
"t": 0,
"r": "gibdd_driver_license",
"m": "GET",
"u": "{{.extidp_details.baseUrl}}/digital/api/public/v1/pso/{{printf \"%.0f\" .id_token_claims.sub}}/doc/GIBDD_DRIVER_LICENSE",
"h": {
"Authorization": "Bearer {{.token.access_token}}"
}
},
{
"t": 0,
"r": "vehicle_info",
"m": "GET",
"u": "{{.extidp_details.baseUrl}}/digital/api/public/v1/pso/{{printf \"%.0f\" .id_token_claims.sub}}/doc/VEHICLE_INFO",
"h": {
"Authorization": "Bearer {{.token.access_token}}"
}
},
{
"t": 2,
"d": {
"rf_passport_status": "r = \"no\"; d = rf_passport; if (Array.isArray(d)) { if (d.length && d[0].error) { r = \"error\"; } else if (d.length && d[0].status) { r = \"yes\"; } else if (d.length && d[0].requestId) { r = \"pending\"; } else if (d.length && d[0].statusDoc) { r = d[0].statusDoc; } } else if (d) { if (d.statusDoc) { r = d.statusDoc; } else if (d.requestId) { r = \"pending\"; } else if (d.error) { r = \"error\"; } else if (d.status) { r = \"yes\"; } } r;",
"kid_rf_passport_status": "r = \"no\"; d = kid_rf_passport; if (Array.isArray(d)) { if (d.length && d[0].error) { r = \"error\"; } else if (d.length && d[0].status) { r = \"yes\"; } else if (d.length && d[0].requestId) { r = \"pending\"; } else if (d.length && d[0].statusDoc) { r = d[0].statusDoc; } } else if (d) { if (d.statusDoc) { r = d.statusDoc; } else if (d.requestId) { r = \"pending\"; } else if (d.error) { r = \"error\"; } else if (d.status) { r = \"yes\"; } } r;",
"frgn_pass_status": "r = \"no\"; d = frgn_pass; if (Array.isArray(d)) { if (d.length && d[0].error) { r = \"error\"; } else if (d.length && d[0].status) { r = \"yes\"; } else if (d.length && d[0].requestId) { r = \"pending\"; } else if (d.length && d[0].statusDoc) { r = d[0].statusDoc; } } else if (d) { if (d.statusDoc) { r = d.statusDoc; } else if (d.requestId) { r = \"pending\"; } else if (d.error) { r = \"error\"; } else if (d.status) { r = \"yes\"; } } r;",
"fid_doc_status": "r = \"no\"; d = fid_doc; if (Array.isArray(d)) { if (d.length && d[0].error) { r = \"error\"; } else if (d.length && d[0].status) { r = \"yes\"; } else if (d.length && d[0].requestId) { r = \"pending\"; } else if (d.length && d[0].statusDoc) { r = d[0].statusDoc; } } else if (d) { if (d.statusDoc) { r = d.statusDoc; } else if (d.requestId) { r = \"pending\"; } else if (d.error) { r = \"error\"; } else if (d.status) { r = \"yes\"; } } r;",
"passport_history_status": "r = \"no\"; d = passport_history; if (Array.isArray(d)) { if (d.length && d[0].error) { r = \"error\"; } else if (d.length && d[0].status) { r = \"yes\"; } else if (d.length && d[0].requestId) { r = \"pending\"; } else if (d.length && d[0].statusDoc) { r = d[0].statusDoc; } } else if (d) { if (d.statusDoc) { r = d.statusDoc; } else if (d.requestId) { r = \"pending\"; } else if (d.error) { r = \"error\"; } else if (d.status) { r = \"yes\"; } } r;",
"gibdd_driver_license_status": "r = \"no\"; d = gibdd_driver_license; if (Array.isArray(d)) { if (d.length && d[0].error) { r = \"error\"; } else if (d.length && d[0].status) { r = \"yes\"; } else if (d.length && d[0].requestId) { r = \"pending\"; } else if (d.length && d[0].statusDoc) { r = d[0].statusDoc; } } else if (d) { if (d.statusDoc) { r = d.statusDoc; } else if (d.requestId) { r = \"pending\"; } else if (d.error) { r = \"error\"; } else if (d.status) { r = \"yes\"; } } r;",
"vehicle_info_status": "r = \"no\"; d = vehicle_info; if (Array.isArray(d)) { if (d.length && d[0].error) { r = \"error\"; } else if (d.length && d[0].status) { r = \"yes\"; } else if (d.length && d[0].requestId) { r = \"pending\"; } else if (d.length && d[0].statusDoc) { r = d[0].statusDoc; } } else if (d) { if (d.statusDoc) { r = d.statusDoc; } else if (d.requestId) { r = \"pending\"; } else if (d.error) { r = \"error\"; } else if (d.status) { r = \"yes\"; } } r;"
}
}
],
"attrMapping": {
"fn": "{{.person.firstName}}",
"ln": "{{.person.lastName}}",
"mn": "{{.person.middleName}}",
"un": "",
"e": "",
"p": "",
"l": "",
"xid": "",
"rid": "state",
"x": {
"rf_passport_status": "{{.rf_passport_status}}",
"kid_rf_passport_status": "{{.kid_rf_passport_status}}",
"frgn_pass_status": "{{.frgn_pass_status}}",
"fid_doc_status": "{{.fid_doc_status}}",
"passport_history_status": "{{.passport_history_status}}",
"gibdd_driver_license_status": "{{.gibdd_driver_license_status}}",
"vehicle_info_status": "{{.vehicle_info_status}}"
}
},
"isActive": false
}
]