Назначение сценария
Определить, сотрудником каких организаций является пользователь, и получить сведения об организации: реквизиты, филиалы, сотрудников и группы доступа. Сценарий применяется при входе от имени юридического лица, проверке полномочий и заполнении реквизитов.
Сценарий состоит из двух частей, которые можно использовать независимо:
- Организации пользователя — список организаций, в которых пользователь состоит, и их идентификаторы (
oid). - Данные организации — карточка, филиалы, сотрудники и группы организации с известным
oid.
Требования к настройке ЕСИА
| Часть | Области доступа | Согласие |
|---|---|---|
| Организации пользователя | openid fullname usr_org; для работы от имени организации — scope_org в запросе | не требуется |
| Данные организации | http://esia.gosuslugi.ru/org_inf?org_oid=<OID_ОРГАНИЗАЦИИ>, http://esia.gosuslugi.ru/org_emps?org_oid=<OID_ОРГАНИЗАЦИИ>, http://esia.gosuslugi.ru/org_grps?org_oid=<OID_ОРГАНИЗАЦИИ>; состав областей определяется полномочиями системы в ЕСИА | не требуется |
Система зарегистрирована в ЕСИА, сертификат загружен (подключение).
Вызовы API ЕСИА
Организации пользователя: GET /rs/prns/{oid}/orgs?embed=(elements).
Данные организации: GET /rs/orgs/{orgOid} — карточка организации; /brhs — филиалы; /emps — сотрудники; /grps — группы; дополнительно /ctts, /addrs, /vhls (контакты, адреса, транспорт организации).
Данные, передаваемые приложению
- Организации пользователя: количество и названия организаций;
oidкаждой организации. - Данные организации:
shortName,fullName,type(LEGAL— юрлицо,AGENCY— орган власти,FOREIGN— филиал иностранного юрлица),ogrn,inn,kpp,leg. Списки филиалов, сотрудников и групп — в массивеelements.
Конфигурация провайдера
Фрагменты конфигурации внешнего провайдера; полный пример — в конце страницы. Значения в <...> задаются при настройке: это параметры конкретной установки.
Организации пользователя
Шаги конвейера после проверки токена и 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": "orgs",
"m": "GET",
"u": "{{.extidp_details.baseUrl}}/rs/prns/{{printf \"%.0f\" .id_token_claims.sub}}/orgs",
"h": {
"Authorization": "Bearer {{.token.access_token}}"
},
"d": {
"embed": "(elements)"
}
}
]
Обработка ответа (JavaScript-шаг):
{
"orgs_count": "orgs && orgs.elements ? orgs.elements.length : 0;",
"orgs_names": "r = \"\"; if (orgs && orgs.elements) { for (i = 0; i < orgs.elements.length; i++) { n = orgs.elements[i].shortName || orgs.elements[i].fullName; if (n) { r += (r ? \"; \" : \"\") + n; } } } r;"
}
Данные организации
Идентификатор организации и области доступа задаются в typeDetails. Области org_inf, org_emps и org_grps содержат oid организации, поэтому он должен быть известен до запроса авторизации:
"orgOid": "<OID_ОРГАНИЗАЦИИ>", "scope": "openid fullname usr_org http://esia.gosuslugi.ru/org_inf?org_oid=<OID_ОРГАНИЗАЦИИ> http://esia.gosuslugi.ru/org_emps?org_oid=<OID_ОРГАНИЗАЦИИ> http://esia.gosuslugi.ru/org_grps?org_oid=<OID_ОРГАНИЗАЦИИ>"
Шаги конвейера выполняются после шага получения данных пользователя:
[
{
"t": 0,
"r": "org",
"m": "GET",
"u": "{{.extidp_details.baseUrl}}/rs/orgs/{{.extidp_details.orgOid}}",
"h": {
"Authorization": "Bearer {{.token.access_token}}"
}
},
{
"t": 0,
"r": "brhs",
"m": "GET",
"u": "{{.extidp_details.baseUrl}}/rs/orgs/{{.extidp_details.orgOid}}/brhs",
"h": {
"Authorization": "Bearer {{.token.access_token}}"
},
"d": {
"embed": "(elements)"
}
},
{
"t": 0,
"r": "emps",
"m": "GET",
"u": "{{.extidp_details.baseUrl}}/rs/orgs/{{.extidp_details.orgOid}}/emps",
"h": {
"Authorization": "Bearer {{.token.access_token}}"
},
"d": {
"embed": "(elements)"
}
},
{
"t": 0,
"r": "grps",
"m": "GET",
"u": "{{.extidp_details.baseUrl}}/rs/orgs/{{.extidp_details.orgOid}}/grps",
"h": {
"Authorization": "Bearer {{.token.access_token}}"
},
"d": {
"embed": "(elements)"
}
}
]
Обработка ответа (JavaScript-шаг):
{
"org_name": "org && (org.shortName || org.fullName) ? (org.shortName || org.fullName) : \"\";",
"org_inn": "org && org.inn ? String(org.inn) : \"\";",
"org_type": "org && org.type ? org.type : \"\";",
"branches_count": "brhs && brhs.elements ? brhs.elements.length : 0;",
"employees_count": "emps && emps.elements ? emps.elements.length : 0;",
"groups_count": "grps && grps.elements ? grps.elements.length : 0;"
}
Отображение в атрибуты
Для сценария из двух частей:
{
"fn": "{{.person.firstName}}",
"ln": "{{.person.lastName}}",
"mn": "{{.person.middleName}}",
"un": "",
"e": "",
"p": "",
"l": "",
"xid": "",
"rid": "state",
"x": {
"orgs_count": "{{.orgs_count}}",
"orgs": "{{.orgs_names}}",
"org_name": "{{.org_name}}",
"org_inn": "{{.org_inn}}",
"org_type": "{{.org_type}}",
"branches_count": "{{.branches_count}}",
"employees_count": "{{.employees_count}}",
"groups_count": "{{.groups_count}}"
}
}
Ограничения и особенности
- Список организаций пользователя возвращается без ограничения по
oidи доступен при областиusr_org. Данные организации запрашиваются отдельной областью доступа сoidв её составе, поэтомуoidвыбирается до входа: он не может быть получен из результата/orgsв том же входе. - Идентификатор организации (
orgOid) задаётся в конфигурации провайдера. Вход приложений направляется на провайдер сsynonym, равнымesia, поэтому в одной установке адаптера работает одна конфигурация; для другой организации используется отдельный экземпляр адаптера или другое значениеorgOid. Список организаций пользователя позволяет определить, какое значение задавать. - Роли и полномочия организации получаются отдельными методами раздела А.4–А.6 методических рекомендаций.
- Методы управления данными организации (раздел А.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": "case-organizations",
"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 usr_org http://esia.gosuslugi.ru/org_inf?org_oid=<OID_ОРГАНИЗАЦИИ> http://esia.gosuslugi.ru/org_emps?org_oid=<OID_ОРГАНИЗАЦИИ> http://esia.gosuslugi.ru/org_grps?org_oid=<OID_ОРГАНИЗАЦИИ>\",\n \"scopeOrg\": \"\",\n \"orgOid\": \"<OID_ОРГАНИЗАЦИИ>\"\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": "orgs",
"m": "GET",
"u": "{{.extidp_details.baseUrl}}/rs/prns/{{printf \"%.0f\" .id_token_claims.sub}}/orgs",
"h": {
"Authorization": "Bearer {{.token.access_token}}"
},
"d": {
"embed": "(elements)"
}
},
{
"t": 0,
"r": "org",
"m": "GET",
"u": "{{.extidp_details.baseUrl}}/rs/orgs/{{.extidp_details.orgOid}}",
"h": {
"Authorization": "Bearer {{.token.access_token}}"
}
},
{
"t": 0,
"r": "brhs",
"m": "GET",
"u": "{{.extidp_details.baseUrl}}/rs/orgs/{{.extidp_details.orgOid}}/brhs",
"h": {
"Authorization": "Bearer {{.token.access_token}}"
},
"d": {
"embed": "(elements)"
}
},
{
"t": 0,
"r": "emps",
"m": "GET",
"u": "{{.extidp_details.baseUrl}}/rs/orgs/{{.extidp_details.orgOid}}/emps",
"h": {
"Authorization": "Bearer {{.token.access_token}}"
},
"d": {
"embed": "(elements)"
}
},
{
"t": 0,
"r": "grps",
"m": "GET",
"u": "{{.extidp_details.baseUrl}}/rs/orgs/{{.extidp_details.orgOid}}/grps",
"h": {
"Authorization": "Bearer {{.token.access_token}}"
},
"d": {
"embed": "(elements)"
}
},
{
"t": 2,
"d": {
"orgs_count": "orgs && orgs.elements ? orgs.elements.length : 0;",
"orgs_names": "r = \"\"; if (orgs && orgs.elements) { for (i = 0; i < orgs.elements.length; i++) { n = orgs.elements[i].shortName || orgs.elements[i].fullName; if (n) { r += (r ? \"; \" : \"\") + n; } } } r;",
"org_name": "org && (org.shortName || org.fullName) ? (org.shortName || org.fullName) : \"\";",
"org_inn": "org && org.inn ? String(org.inn) : \"\";",
"org_type": "org && org.type ? org.type : \"\";",
"branches_count": "brhs && brhs.elements ? brhs.elements.length : 0;",
"employees_count": "emps && emps.elements ? emps.elements.length : 0;",
"groups_count": "grps && grps.elements ? grps.elements.length : 0;"
}
}
],
"attrMapping": {
"fn": "{{.person.firstName}}",
"ln": "{{.person.lastName}}",
"mn": "{{.person.middleName}}",
"un": "",
"e": "",
"p": "",
"l": "",
"xid": "",
"rid": "state",
"x": {
"orgs_count": "{{.orgs_count}}",
"orgs": "{{.orgs_names}}",
"org_name": "{{.org_name}}",
"org_inn": "{{.org_inn}}",
"org_type": "{{.org_type}}",
"branches_count": "{{.branches_count}}",
"employees_count": "{{.employees_count}}",
"groups_count": "{{.groups_count}}"
}
},
"isActive": false
}
]