Назначение сценария
Получить перечень документов, которые пользователь внёс в профиль ЕСИА (паспорт РФ, водительское удостоверение, загранпаспорт, полис ОМС, военный билет, свидетельства о рождении, документ иностранного гражданина), и их статус проверки.
Требования к настройке ЕСИА
- Области доступа:
openid fullname id_doc medical_doc military_doc foreign_passport_doc drivers_licence_doc birth_cert_doc residence_doc temporary_residence_doc(берутся только нужные) - Согласие: не требуется. Если ЕСИА ограничивает область доступа для информационной системы, применяются согласие и Цифровой профиль (раздел 8.7)
- Система зарегистрирована в ЕСИА, сертификат загружен (подключение).
Вызовы API ЕСИА
GET /rs/prns/{oid}/docs?embed=(elements) — все документы; отдельный документ — GET /rs/prns/{oid}/docs/{doc_id}; атрибуты иностранного документа — GET /rs/prns/{oid}/docs/{doc_id}/attrs
Данные, передаваемые приложению
Тип документа (type): RF_PASSPORT (паспорт РФ), FID_DOC (документ иностранного гражданина), RF_DRIVING_LICENSE (водительское удостоверение), MLTR_ID (военный билет), FRGN_PASS (загранпаспорт), MDCL_PLCY (полис ОМС), RF_BRTH_CERT, FID_BRTH_CERT, OLD_BRTH_CERT (свидетельства о рождении). Поля документа: series, number, issueDate, issueId, issuedBy, expiryDate, vrfStu (VERIFIED/NOT_VERIFIED), status (verified_by_request/unverified); для паспорта РФ — fmsValid, fmsState.
Конфигурация провайдера
Фрагменты конфигурации внешнего провайдера; полный пример — в конце страницы. Значения в <...> задаются при настройке: это параметры конкретной установки.
Шаги конвейера после проверки токена и is_trusted:
[
{
"t": 0,
"r": "person",
"m": "GET",
"u": "{{.extidp_details.baseUrl}}/rs/prns/{{printf \"%.0f\" .id_token_claims.sub}}",
"h": {
"Authorization": "Bearer {{.token.access_token}}"
}
},
{
"t": 0,
"r": "docs",
"m": "GET",
"u": "{{.extidp_details.baseUrl}}/rs/prns/{{printf \"%.0f\" .id_token_claims.sub}}/docs",
"h": {
"Authorization": "Bearer {{.token.access_token}}"
},
"d": {
"embed": "(elements)"
}
}
]
Обработка ответа (JavaScript-шаг):
{
"docs_types": "r = \"\"; if (docs && docs.elements) { for (i = 0; i < docs.elements.length; i++) { t = docs.elements[i].type; if (t) { r += (r ? \",\" : \"\") + t; } } } r;",
"passport_verified": "r = \"\"; if (docs && docs.elements) { for (i = 0; i < docs.elements.length; i++) { if (docs.elements[i].type === \"RF_PASSPORT\") { r = docs.elements[i].vrfStu || \"\"; break; } } } r;",
"driving_licence_expiry": "r = \"\"; if (docs && docs.elements) { for (i = 0; i < docs.elements.length; i++) { if (docs.elements[i].type === \"RF_DRIVING_LICENSE\") { r = docs.elements[i].expiryDate || \"\"; break; } } } r;",
"foreign_passport_expiry": "r = \"\"; if (docs && docs.elements) { for (i = 0; i < docs.elements.length; i++) { if (docs.elements[i].type === \"FRGN_PASS\") { r = docs.elements[i].expiryDate || \"\"; break; } } } r;",
"medical_policy_verified": "r = \"\"; if (docs && docs.elements) { for (i = 0; i < docs.elements.length; i++) { if (docs.elements[i].type === \"MDCL_PLCY\") { r = docs.elements[i].vrfStu || \"\"; break; } } } r;"
}
Отображение в атрибуты:
{
"fn": "{{.person.firstName}}",
"ln": "{{.person.lastName}}",
"mn": "{{.person.middleName}}",
"un": "",
"e": "",
"p": "",
"l": "",
"xid": "",
"rid": "state",
"x": {
"docs_types": "{{.docs_types}}",
"passport_verified": "{{.passport_verified}}",
"driving_licence_expiry": "{{.driving_licence_expiry}}",
"foreign_passport_expiry": "{{.foreign_passport_expiry}}",
"medical_policy_verified": "{{.medical_policy_verified}}"
}
}
Ограничения и особенности
- Реквизиты документов являются персональными данными. Рекомендуется передавать в атрибуты признак подтверждения, срок действия и перечень типов.
- Документы, проверенные в ведомстве, имеют
status = verified_by_request; введённые пользователем без проверки —unverified. - Сведения из ведомств (история паспортов, выписка из ГИБДД, свидетельства ЗАГС и др.) запрашиваются через Цифровой профиль — раздел 8.7.
Проверка
- Провайдер включить (
isActive: true) на тестовом стенде. - Пройти вход тестовой учётной записью ЕСИА.
- Проверить
/health/readyи атрибуты пользователя в приложении. - При ошибке смотреть журнал
LOG_LEVEL=debug(диагностика).
Полный пример конфигурации
Файл конфигурации внешнего провайдера (extidp.json) целиком. Значение typeDetails — строка с JSON; значения в <...> задаются при настройке.
[
{
"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": "case10-user-documents",
"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 fullname id_doc medical_doc military_doc foreign_passport_doc drivers_licence_doc birth_cert_doc residence_doc temporary_residence_doc\",\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 }}"
},
"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}}/rs/prns/{{printf \"%.0f\" .id_token_claims.sub}}",
"h": {
"Authorization": "Bearer {{.token.access_token}}"
}
},
{
"t": 0,
"r": "docs",
"m": "GET",
"u": "{{.extidp_details.baseUrl}}/rs/prns/{{printf \"%.0f\" .id_token_claims.sub}}/docs",
"h": {
"Authorization": "Bearer {{.token.access_token}}"
},
"d": {
"embed": "(elements)"
}
},
{
"t": 2,
"d": {
"docs_types": "r = \"\"; if (docs && docs.elements) { for (i = 0; i < docs.elements.length; i++) { t = docs.elements[i].type; if (t) { r += (r ? \",\" : \"\") + t; } } } r;",
"passport_verified": "r = \"\"; if (docs && docs.elements) { for (i = 0; i < docs.elements.length; i++) { if (docs.elements[i].type === \"RF_PASSPORT\") { r = docs.elements[i].vrfStu || \"\"; break; } } } r;",
"driving_licence_expiry": "r = \"\"; if (docs && docs.elements) { for (i = 0; i < docs.elements.length; i++) { if (docs.elements[i].type === \"RF_DRIVING_LICENSE\") { r = docs.elements[i].expiryDate || \"\"; break; } } } r;",
"foreign_passport_expiry": "r = \"\"; if (docs && docs.elements) { for (i = 0; i < docs.elements.length; i++) { if (docs.elements[i].type === \"FRGN_PASS\") { r = docs.elements[i].expiryDate || \"\"; break; } } } r;",
"medical_policy_verified": "r = \"\"; if (docs && docs.elements) { for (i = 0; i < docs.elements.length; i++) { if (docs.elements[i].type === \"MDCL_PLCY\") { r = docs.elements[i].vrfStu || \"\"; break; } } } r;"
}
}
],
"attrMapping": {
"fn": "{{.person.firstName}}",
"ln": "{{.person.lastName}}",
"mn": "{{.person.middleName}}",
"un": "",
"e": "",
"p": "",
"l": "",
"xid": "",
"rid": "state",
"x": {
"docs_types": "{{.docs_types}}",
"passport_verified": "{{.passport_verified}}",
"driving_licence_expiry": "{{.driving_licence_expiry}}",
"foreign_passport_expiry": "{{.foreign_passport_expiry}}",
"medical_policy_verified": "{{.medical_policy_verified}}"
}
},
"isActive": false
}
]