Персональные данные физического лица

ESIA-Adapter : 8.1. Персональные данные физического лица


Назначение сценария

Получить ФИО, СНИЛС, ИНН, дату и место рождения, пол, подтверждённые email и телефон пользователя и признак подтверждённой учётной записи.

Требования к настройке ЕСИА

  • Области доступа: openid fullname birthdate birthplace gender snils inn email mobile
  • Согласие: не требуется. Если ЕСИА ограничивает область доступа для информационной системы, применяется согласие (см. раздел 8.7)
  • Система зарегистрирована в ЕСИА, сертификат загружен (подключение).

Вызовы API ЕСИА

GET /rs/prns/{oid} — данные о пользователе; GET /rs/prns/{oid}/ctts?embed=(elements) — контакты, тип EML/MBT, берутся только со статусом vrfStu = VERIFIED

Данные, передаваемые приложению

fn, ln, mn, e, p и в x: snils, inn, birthdate, birthplace, gender, trusted

Конфигурация провайдера

Фрагменты конфигурации внешнего провайдера; полный пример — в конце страницы. Значения в <...> задаются при настройке: это параметры конкретной установки.

Шаги конвейера после проверки токена и 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": "contacts",
    "m": "GET",
    "u": "{{.extidp_details.baseUrl}}/rs/prns/{{printf \"%.0f\" .id_token_claims.sub}}/ctts",
    "h": {
      "Authorization": "Bearer {{.token.access_token}}"
    },
    "d": {
      "embed": "(elements)"
    }
  }
]

Обработка ответа (JavaScript-шаг):

{
  "main_email": "r = \"\"; if (contacts && contacts.elements) { for (i = 0; i < contacts.elements.length; i++) { if (contacts.elements[i].type === \"EML\" && contacts.elements[i].vrfStu === \"VERIFIED\") { r = contacts.elements[i].value; } } } r;",
  "main_phone": "r = \"\"; if (contacts && contacts.elements) { for (i = 0; i < contacts.elements.length; i++) { if (contacts.elements[i].type === \"MBT\" && contacts.elements[i].vrfStu === \"VERIFIED\") { r = contacts.elements[i].value; } } } r;"
}

Отображение в атрибуты:

{
  "fn": "{{.person.firstName}}",
  "ln": "{{.person.lastName}}",
  "mn": "{{.person.middleName}}",
  "un": "",
  "e": "{{.main_email}}",
  "p": "{{.main_phone}}",
  "l": "",
  "xid": "",
  "rid": "state",
  "x": {
    "snils": "{{.person.snils}}",
    "inn": "{{.person.inn}}",
    "birthdate": "{{.person.birthDate}}",
    "birthplace": "{{.person.birthPlace}}",
    "gender": "{{.person.gender}}",
    "trusted": "{{.person.trusted}}"
  }
}

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

  • Адаптер прерывает вход, если учётная запись не подтверждена (urn:esia:sbj:is_tru). Если вход неподтверждённых пользователей допустим, шаг проверки исключается из конвейера.
  • trusted отдаёт ЕСИА как признак подтверждённости УЗ: true/false.
  • Статус VERIFIED возможен только у контактов типа MBT и EML.

Проверка

  1. Провайдер включить (isActive: true) на тестовом стенде.
  2. Пройти вход тестовой учётной записью ЕСИА.
  3. Проверить /health/ready и атрибуты пользователя в приложении.
  4. При ошибке смотреть журнал 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": "case1-personal-data",
    "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 birthdate birthplace gender snils inn email mobile\",\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": "contacts",
        "m": "GET",
        "u": "{{.extidp_details.baseUrl}}/rs/prns/{{printf \"%.0f\" .id_token_claims.sub}}/ctts",
        "h": {
          "Authorization": "Bearer {{.token.access_token}}"
        },
        "d": {
          "embed": "(elements)"
        }
      },
      {
        "t": 2,
        "d": {
          "main_email": "r = \"\"; if (contacts && contacts.elements) { for (i = 0; i < contacts.elements.length; i++) { if (contacts.elements[i].type === \"EML\" && contacts.elements[i].vrfStu === \"VERIFIED\") { r = contacts.elements[i].value; } } } r;",
          "main_phone": "r = \"\"; if (contacts && contacts.elements) { for (i = 0; i < contacts.elements.length; i++) { if (contacts.elements[i].type === \"MBT\" && contacts.elements[i].vrfStu === \"VERIFIED\") { r = contacts.elements[i].value; } } } r;"
        }
      }
    ],
    "attrMapping": {
      "fn": "{{.person.firstName}}",
      "ln": "{{.person.lastName}}",
      "mn": "{{.person.middleName}}",
      "un": "",
      "e": "{{.main_email}}",
      "p": "{{.main_phone}}",
      "l": "",
      "xid": "",
      "rid": "state",
      "x": {
        "snils": "{{.person.snils}}",
        "inn": "{{.person.inn}}",
        "birthdate": "{{.person.birthDate}}",
        "birthplace": "{{.person.birthPlace}}",
        "gender": "{{.person.gender}}",
        "trusted": "{{.person.trusted}}"
      }
    },
    "isActive": true
  }
]

Обсуждение