Данные детей пользователя


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

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

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

  • Области доступа: openid fullname kid_fullname kid_birthdate kid_snils (и другие kid_*)
  • Согласие: не требуется на стороне адаптера
  • Система зарегистрирована в ЕСИА, сертификат загружен (подключение).

Вызовы API ЕСИА

GET /rs/prns/{oid}/kids?embed=(elements); полные данные ребёнка с документами: /kids/{kid_id}?embed=(documents.elements,addresses.elements,contacts.elements)

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

Число детей; по ребёнку: firstName, lastName, birthDate, snils, 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": "kids",
    "m": "GET",
    "u": "{{.extidp_details.baseUrl}}/rs/prns/{{printf \"%.0f\" .id_token_claims.sub}}/kids",
    "h": {
      "Authorization": "Bearer {{.token.access_token}}"
    },
    "d": {
      "embed": "(elements)"
    }
  }
]

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

{
  "kids_count": "kids && kids.elements ? kids.elements.length : 0;"
}

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

{
  "fn": "{{.person.firstName}}",
  "ln": "{{.person.lastName}}",
  "mn": "{{.person.middleName}}",
  "un": "",
  "e": "",
  "p": "",
  "l": "",
  "xid": "",
  "rid": "state",
  "x": {
    "kids_count": "{{.kids_count}}"
  }
}

Данные первого ребёнка с документами

Полные данные ребёнка (документы, адреса, контакты) запрашиваются одним вызовом со встраиванием:

[
  { "t": 2, "d": { "kid_id": "kids && kids.elements && kids.elements.length ? String(kids.elements[0].id) : \"\";" } },
  { "t": 0, "r": "kid", "m": "GET",
    "u": "{{.extidp_details.baseUrl}}/rs/prns/{{printf \"%.0f\" .id_token_claims.sub}}/kids/{{.kid_id}}",
    "h": { "Authorization": "Bearer {{.token.access_token}}" },
    "d": { "embed": "(documents.elements,addresses.elements,contacts.elements)" } },
  { "t": 2, "d": { "kid_docs_count": "kid && kid.documents && kid.documents.elements ? kid.documents.elements.length : 0;" } }
]

Документы ребёнка описываются так же, как документы пользователя (раздел 8.2); для ребёнка применимы области kid_id_doc, kid_birth_cert_doc, kid_medical_doc, kid_snils, kid_inn, kid_email, kid_mobile.

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

  • Данные несовершеннолетних требуют повышенной защиты; рекомендуется передавать минимально необходимый набор значений.

Проверка

  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": "case9-children",
    "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 kid_fullname kid_birthdate kid_snils\",\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": "kids",
        "m": "GET",
        "u": "{{.extidp_details.baseUrl}}/rs/prns/{{printf \"%.0f\" .id_token_claims.sub}}/kids",
        "h": {
          "Authorization": "Bearer {{.token.access_token}}"
        },
        "d": {
          "embed": "(elements)"
        }
      },
      {
        "t": 2,
        "d": {
          "kids_count": "kids && kids.elements ? kids.elements.length : 0;"
        }
      }
    ],
    "attrMapping": {
      "fn": "{{.person.firstName}}",
      "ln": "{{.person.lastName}}",
      "mn": "{{.person.middleName}}",
      "un": "",
      "e": "",
      "p": "",
      "l": "",
      "xid": "",
      "rid": "state",
      "x": {
        "kids_count": "{{.kids_count}}"
      }
    },
    "isActive": false
  }
]

Обсуждение