//Автор: Владимир Недорослев// ====== Протокол работы ЕСИ ====== //Все запросы в данном руководстве разделены построчно для удобства чтения.// Данный документ описывает конфигурацию работы ЕСИ. ====== Discovery document ====== Discovery document – это документ в формате JSON, показывающий информацию об ЕСИ - адреса Endpoint’ов, поддерживаемые scope, какую информацию о пользователе можно выдать и пр. Его можно найти по адресу: https://{Базовый адрес ЕСИ}/.well-known/openid-configuration Содержит следующие поля: * **issuer** – URL идентификатор ЕСИ. ЕСИ использует его для сравнения с полем **iss** в **ID Token**’е. * **jwks_uri** – URL JWK документа. Содержит ключи, которые клиенты должны использовать при проверке подписей ЕСИ. * **authorization_endpoint** – URL адрес Authorization Endpoint’а. * **token_endpoint** – URL адрес Token Endpoint’а. * **userinfo_endpoint** - URL адрес UserInfo Endpoint’а. * **end_session_endpoint**- endpoint закрытия сессии. * **revocation_endpoint** - endpoint отзыва **Access Token**'а. * **scopes_supported** – JSON массив, содержащий список всех значений scope, поддерживаемых ЕСИ: * **openid** – обязательный scope для всех запросов. Означает, что используется технология OpenID Connect. * **profile** – включает в себя: * **pin** - персональный номер. * **citizenship** - гражданство. Для граждан Кыргызстана ''KGZ''. * **name** - полное имя (ФИО). * **family_name** - фамилия. * **given_name** - имя. * **middle_name** - отчество. * **gender** - пол (''male'' или ''female''). * **birthdate** - дата рождения в формате ''YYYY-MM-DD''. (Например, ''1990-11-23''). * **email** – включает в себя **email** и **email_verified**. * **phone** – включает в себя **phone** и **phone_verified**. * **offline_access** – нужно ли получение Refresh Token'а. * **device_api** - доступ к API для мобильного приложения. * **IdentityServerAPI** - обязательный параметр для доступа к ЕСИ посредствам API (Обязателен для мобильных приложений, и сервисов которые используют API "ЕСИ"). * **PEP_disable** - позволяет отключить вход через обычную электронную подпись клиенту. * **signature_api** - доступ к подписанию. * **TouchIdApi** - доступ к API для верификации человека по отпечатку пальца. * **FaceIdApi** - доступ к API для верификации человека по лицу. * **PersonalCabinetApi** - доступ к API личного кабинета мобильному приложению. * **oepRegister** - доступ к API для удаленного получения ОЭП. * **pki_srs_kg_create_user** - API необходимое для создание и синхронизированния пользователей. * **organization_tin** – ИНН юридического лица, от имени которого заходит пользователь. * **position_name** – должность пользователя в юридическом лице, указанного в organization_tin. * **QRdisable** - значение отключает возможность авторизации в системе через QR-код (при включенной функции не отображается вкладка «QR» в ЕСИ) * **CdsOtpApi** - доступ к api для получения otp с ОЭП * **IisUserInfo** - доступ к информации о пользователе еси api * **response_types_supported** – JSON массив, содержащий список значений **response_type**, поддерживаемых ЕСИ. * **response_modes_supported** – JSON массив, содержащий список поддерживаемх **response_mode**, основанный на OAuth 2.0. * **claims_supported** – JSON массив, содержащий список данных, которые может выдавать ЕСИ: * **sub** – идентификатор пользователя ЕСИ. * **pin** – персональный номер. * **family_name** – Фамилия. * **given_name** – Имя. * **middle_name** – Отчество. * **name** – Полное имя (ФИО). * **citizenship** – гражданство. Для граждан Кыргызстана – **KGZ**. * **gender** – Пол (''male'' или ''female''). * **birthdate** – Дата рождения. * **email** – электронная почта. * **email_verified** – подтверждена ли почта (true/false). * **phone_number** – мобильный телефон в формате **E164**. * **phone_number_verified** – подтвержден ли телефон (true/false). * **organization_tin** – ИНН юридического лица, от имени которого заходит пользователь. * **position_name** – должность пользователя в юридическом лице, указанного в organization_tin. ====== ID Token ====== Для аутентификации пользователя OpenID Connect вводит понятие **ID Token**’а. Это структура данных, которая хранит информацию об аутентификации пользователя ЕСИ. Представляется в виде JSON Web Token (JWT). ЕСИ выдаёт **ID Token** со следующими полями: * **iss** – (Issuer) идентификатор ЕСИ. Это регистрозависимый URL, использующий https, содержащий схему и хост. Может содержать номер порта, путь. Не содержит параметров запроса. * **sub** – (Subject) идентификатор пользователя. Уникальный, никогда неменяющийся идентификатор пользователя, который хранит ЕСИ. Например, ''24400320''. Является регистрозавсимой строкой, длиной не более 255 ASCII символов. * **aud** – (Audience) массив строк, содержащий идентификаторы клиентов (client_id), которым предназначен данный ID Token. * **exp** – (Expiration) срок действия **ID Token**'a. Время, после которого **ID Token** не является валидным и не должен обрабатываться. При валидации **ID Token**'а текущее время должно быть до времени, указанного в этом поле. Значение представляется в виде JSON числа, показывающий количество секунд прошедших с 1970-01-01T0:0:0Z согласно UTC стандарту (см. [[https://tools.ietf.org/html/rfc3339|RFC3339]] для подробной информации насчёт времени в UTC). * **iat** – (Issued at) время, когда **ID Token** был создан. Значение представляется в виде JSON числа, показывающий количество секунд прошедших с 1970-01-01T0:0:0Z согласно UTC стандарту. * **auth_time** – (Authentication time) время, когда была произведена аутентификация пользователя. Значение представляется в виде JSON числа, показывающий количество секунд прошедших с 1970-01-01T0:0:0Z согласно UTC стандарту. * **nonce** – регистрозависимая строка, ассоциирующая сессию клиента с **ID Token**’ом, чтобы избежать атак повторного воспроизведения (replay attacks). Клиенты должны проверять, чтобы это значение было равно значению **nonce** в authentication request'e. * **amr** – (Authentication methods references) JSON массив строк, хранящий методы аутентификации, использованные при аутентификации пользователя. Например, может покаазть, что при аутентификации был использован пароль и OTP. Возможные значения: **sc** (Паспорт), **pwd** (Логин/пароль), **cds** (Облачная ЭП). * **at_hash** – хэш Access Token’а. Хэш вычисляется по левой половине хэшей октетов Access Token“a в формате ASCII. Примененный хэш-алгоритм указан в головной части (header) **ID Token**’a в значении ''alg''. ====== Authentication request ====== **Authentication request** (Запрос на аутентификацию) — это GET или POST запрос клиента в Authorization Endpoint, который требует, чтобы пользователь аутентифицировался в ЕСИ. В случае GET запроса, параметры запроса указывается непосредственно в самом запросе.\\ В случае POST запроса, параметры запроса указываются в теле запроса (''Content-Type: application/x-www-form-urlencoded'') ===== Параметры запроса ===== * **scope** (Обязательный) – разделенные пробелом значения, показывающие, какие данные необходимы клиенту. **Обязательно должно содержать значение ''openid''.** * **response_type** (Обязательный) – определяет способ процесса авторизации. В случае с ЕСИ используется значение ''**code**''. * **client_id** (Обязательный) – идентификатор клиента, выданный ЕСИ. * **redirect_uri** (Обязательный) – URI, куда будет отправлен ответ ЕСИ после того, как пользователь пройдёт аутентификацию в ЕСИ. Он задаётся вместе с ''client_id'' при регистрации клиента. * **nonce** (Опциональный) – регистрозависимая строка ассоциирующая сессию клиента с **ID Token**’ом, чтобы избежать атак повторного воспроизведения (replay attacks). * **state** (Опциональный) – значение для сохраниния состояние между запросом и callback’ом. Обычно для борьбы с CSRF это значение криптографически привязывается к куки браузера. ===== PKCE ===== Так как ЕСИ использует **Authorization Code flow with PKCE** ([[https://tools.ietf.org/html/rfc7636|RFC7636]]), при запросе на аутентификацию необходимо дополнительно сделать следующее: * Сформировать и сохранить **code_verifier** (он ещё понадобится при запросе на получение токенов) - криптографически случайная строка размером от 43 до 128 символов, и состоящая из символов ''a-z A-Z 0-9 -._~'' * Сформировать **code_challenge** следующим образом: base64url(sha256(code_verifier)) * Добавить в запрос следующие параметры: &code_challenge={сформированный code_challenge} code_challenge_method=S256 ===== Валидация запроса ===== При получении запроса на аутентификацию ЕСИ проверит, чтобы все параметры запроса соответствовали стандарту OAuth 2.0 и OpenId Connect. Подразумевается наличие обязательных параметров **scope**, **response_type**, **client_id**, **redirect_uri**. В случае, если запрос составлен не верно, ЕСИ вернёт ошибку на указанный **redirect_uri**. Информация об ошибке будет представлена в виде параметров. Возможны следующие параметры: * **error** (Обязательный) – название ошибки. Может принимать следующие значения: * **invalid_request** – неправильный запрос. Возникает, если в запросе отсутствует обязательный параметр, запрос содержит неправильное значение параметра, включает в себя одинаковый параметр более одного раза и пр. * **unauthorized_client** – неавторизованный клиент. Клиент не авторизован и не может получить авторизационный код. * **access_denied** – отказано в доступе. Пользователь либо ЕСИ отколнили запрос. * **unsupported_response_type** – неподдерживаемый возвращаемый тип. ЕСИ не поддерживает получение авторизационного кода указанным методом. * **invalid_scope** – неправильный scope. Запрашиваемое(-ые) значения scope неверны, неизвестны, либо составлены неправильно. * **server_error** – ошибка сервера. Ошибка со стороны ЕСИ, которая не позволяет обработать запрос. * **temporarily_unavailable** – временно недоступно. В данный момент ЕСИ не может обработать запрос из-за перегрузки либо технических работах на сервере. * **invalid_request_uri** – неправильный **redirect_uri**. **redirect_uri** возвращает ошибку при перенаправлении либо содержит неправильные данные. * **error_description** (Опциональный) – описание ошибки понятное человеку. * **error_uri** (Опциональный) – URI веб-страницы, на которой находится дополнительная информация об ошибке. * **state** (Опциональный) – состояние согласно OAuth 2.0. Ставится значение, полученное от клиента. ===== Примеры ===== Примеры значений scope: scope=openid profile email scope=openid profile notification_api phone Пример ответа клиента, который перенаправляет браузер пользователя к Authorization endpoint для запроса на аутентификацию: 302 Found Location: https://{базовый-адрес-ЕСИ}/connect/authorize? response_type=code &scope=openid%20profile%20email &client_id=s6BhdRkqt3 &state=af0ifjsldkj &redirect_uri=https://client.example.org/cb &code_challenge=UTGRtxSHqyGSX1z54iTOBB6Z3WdAMeFv7iYSC6yqx0w &code_challenge_method=S256 Пример запроса, который отправит браузер пользователя к ЕСИ в ответ на redirect ответ клиента: GET https://{базовый-адрес-ЕСИ}/connect/authorize? response_type=code &scope=openid%20profile%20email &client_id=s6BhdRkqt3 &state=af0ifjsldkj &redirect_uri=https://client.example.org/cb &code_challenge=UTGRtxSHqyGSX1z54iTOBB6Z3WdAMeFv7iYSC6yqx0w &code_challenge_method=S256 Пример ошибки: HTTP/1.1 302 Found Location: https://client.example.org/cb? error=invalid_request &error_description=Unsupported%20response_type%20value &state=af0ifjsldkj Пример ответа ЕСИ при успешной валидации запроса на аутентификацию: HTTP/1.1 302 Found Location: https://client.example.org/cb? code=SplxlOBeZQQYbYS6WxSbIA &state=af0ifjsldkj ====== Token request ====== Для получения ID Token’а и Access Token’а клиент должен отправить POST запрос в Token Endpoint ЕСИ, предоставив полученный авторизационный код (**code**), свой **client_id**, **client_secret** и **redirect_uri**. **client_id** и **client_secret** должны находится в заголовке запроса **Authorization** типа **Basic** в качестве имени пользователя и пароля соответственно. В теле запроса должны находится следующие параметры (''Content-Type: application/x-www-form-urlencoded''): grant_type=authorization_code &code={ваш авторизационный код} &redirect_uri={ваш redirect_uri} &code_verifier={code_verifier, который вы сгенерировали на этапе Authentication request} ===== Пример запроса к Token Endpoint ===== POST https://{базовый-адрес-ЕСИ}/connect/token Content-Type: application/x-www-form-urlencoded Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA &redirect_uri=https://client.example.org/cb &code_verifier=oV0CFtgI2ywyqV6y5Z2gy49stoKn0T2daY_nLvXDeWRNqQeJ ===== Валидация запроса ===== ЕСИ валидирует запрос следующим образом: - Аутентифицирует клиента по полученным **client_id** и **client_secret**. - Проверит авторизационный код. Действительно ли он был выдан данному клиенту. Правильный ли он, не был ли он использован до этого, был ли выдан в результате запроса на аутентификацию. - Сравнит полученный **redirect_uri** со значением **redirect_uri** полученном при запроса на аутентификацию. - Сформирует **code_challenge** из полученнго **code_verifier** и сравнит с ранее полученным **code_challenge** при запросе на аутентификацию. При успешной валидации ЕСИ вернёт **Id Token** и **Access Token** в ответ в формате ''application/json'', а также следующие поля: * **token_type** – по стандарту всегда имеет значение ''Bearer''. * **refresh_token** – токен, позволяющий получить новый access token после того, как он истёк. Выдаётся в случае, если было указано значение scope ''offline_access'' во время authentication request. * **expires_in** – значение в секундах, показывающее через сколько access token станет недействительным. При возникновении ошибки, информация о ней вернётся в том же формате, что и при запросе на аутентификацию. Пример успешного ответа ЕСИ с **ID Token**'ом и **Access Token**'ом: HTTP/1.1 200 OK Content-Type: application/json Cache-Control: no-store Pragma: no-cache { "access_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6ImNhZzBDYk1UYzh4d293MDVaVDRpRFEiLCJ0eXAiOiJhdCtqd3QifQ.eyJuYmYiOjE1ODQ0NDExNTAsImV4cCI6MTU4NDQ0NDc1MCwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo3MDAwIiwiYXVkIjoibm90aWZpY2F0aW9uX2FwaSIsImNsaWVudF9pZCI6Im12Yy1jbGllbnQiLCJzdWIiOiIxMDAwMDAwIiwiYXV0aF90aW1lIjoxNTg0NDQxMTQ4LCJpZHAiOiJsb2NhbCIsInNjb3BlIjpbInByb2ZpbGUiLCJvcGVuaWQiLCJlbWFpbCIsInBob25lIiwibm90aWZpY2F0aW9uX2FwaSJdLCJhbXIiOlsibWZhIl19.KC-_Ua4k3rbRAVEkWF_NffnlGqInfSsSiD-zEk5dmLn5a7PAiiptDNqGpmSJbMhl2rbyFqTrBtUDxmkSbXALDWTAvuRqagTkEvlu2uUpJSQ9c_aYLsW4QdUdR2uAKliRlNAQFygWkN4xsXPSaB6E0B71YKmQcYutssBBcP3tBu_E4EDMW30Vky_osQeB8moHkPthMxXkpBxLSKieY77FOQDJzbaVmzrm9wmIor7OuwFtp0b9yKdpAsg9cGbgcmqW5pYNSa2GdhEzYrf6tkfF7tyuw-LYbJjCklfPwIQNk-RT1TicSwdZzsBWoAS8pWiBfwN4MAaA6TP4O6LA7hcdZw", "token_type": "Bearer", "refresh_token": "8xLOxBtZp8", "expires_in": 3600, "id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6ImNhZzBDYk1UYzh4d293MDVaVDRpRFEiLCJ0eXAiOiJKV1QifQ.eyJuYmYiOjE1ODQ0NDExNTAsImV4cCI6MTU4NDQ0MTQ1MCwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo3MDAwIiwiYXVkIjoibXZjLWNsaWVudCIsIm5vbmNlIjoiNjM3MjAwMzc5MzYyNjEzMzE0LlkyTmtZVGN3T0dFdFl6Y3pNaTAwTURreUxUbGhPVFF0T0RJd1pXVmxPVGMxTURVeFpXUTJNemd3TXpJdE9XTmlZaTAwWVROakxUZzFaREV0WkdFMU1XVTJNbVJsWVRFNCIsImlhdCI6MTU4NDQ0MTE1MCwiYXRfaGFzaCI6ImhpOWItMURYanVtSWRCeFJ1VGRlb3ciLCJzX2hhc2giOiJFNXdOMzRlMFZkVl9jSVJQREZGS0h3Iiwic2lkIjoiMjc1SEtZRE1QS0EtRGt1NXJWdjNJdyIsInN1YiI6IjEwMDAwMDAiLCJhdXRoX3RpbWUiOjE1ODQ0NDExNDgsImlkcCI6ImxvY2FsIiwiYW1yIjpbIm1mYSJdfQ.dkcyzcRHrTZAGATA2p3Q8x7PMeQRQdWYq9h0ptMD0lx_2LvKNc9ItEncBFm1Kjjtq8oY2wiIGILJTBwiyO7IGCePHCLLXTy2csxcd-gL83nXL5aosIOxOjTiIc7Wo4mCVIsnk-fHdroHmfPmB-gAEAnoEpOTbN5IZ-lpAJl_WhB-utbiNVyEVrzm03usHppbu2-ev-9vWOuiQy6Ryg-GBWTkv0Hi3Q5mnG1FBA-ac-13JXBIhSEPtKWFw6Ck3-OVi4IoTzh5uz-IPFv_1H12ervVE7Qf9tv6sEbrGj2q1x3KbAkAEeaGpJWduJfHLNthxwk99Gbl8JL710cZGGCpdQ" } ====== UserInfo request ====== Для получения информации о пользователе, клиенту необходимо отправить GET или POST запрос в UserInfo Endpoint с заголовком **Authorization** типа **Bearer** со значением полученного **Access Token**'а. Рекомендуется использовать GET запрос. Пример запроса: GET https://{базовый-адрес-ЕСИ}connect/userinfo Host: server.example.com Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6ImNhZzBDYk1UYzh4d293MDVaVDRpRFEiLCJ0eXAiOiJhdCtqd3QifQ.eyJuYmYiOjE1ODQ0NDExNTAsImV4cCI6MTU4NDQ0NDc1MCwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo3MDAwIiwiYXVkIjoibm90aWZpY2F0aW9uX2FwaSIsImNsaWVudF9pZCI6Im12Yy1jbGllbnQiLCJzdWIiOiIxMDAwMDAwIiwiYXV0aF90aW1lIjoxNTg0NDQxMTQ4LCJpZHAiOiJsb2NhbCIsInNjb3BlIjpbInByb2ZpbGUiLCJvcGVuaWQiLCJlbWFpbCIsInBob25lIiwibm90aWZpY2F0aW9uX2FwaSJdLCJhbXIiOlsibWZhIl19.KC-_Ua4k3rbRAVEkWF_NffnlGqInfSsSiD-zEk5dmLn5a7PAiiptDNqGpmSJbMhl2rbyFqTrBtUDxmkSbXALDWTAvuRqagTkEvlu2uUpJSQ9c_aYLsW4QdUdR2uAKliRlNAQFygWkN4xsXPSaB6E0B71YKmQcYutssBBcP3tBu_E4EDMW30Vky_osQeB8moHkPthMxXkpBxLSKieY77FOQDJzbaVmzrm9wmIor7OuwFtp0b9yKdpAsg9cGbgcmqW5pYNSa2GdhEzYrf6tkfF7tyuw-LYbJjCklfPwIQNk-RT1TicSwdZzsBWoAS8pWiBfwN4MAaA6TP4O6LA7hcdZw Ответ от ЕСИ: Если пользователь заходит как физическое лицо: HTTP/1.1 200 OK Content-type: application/json { "sub":"1000000", "pin":"20101199012345", "citizenship":"KGZ", "family_name":"Андреев", "given_name":"Андрей", "middle_name":"Андреевич", "name":"Андреев Андрей Андреевич", "gender":"male", "birthdate":"1990-01-01", "email":"some.email@gmail.com", "email_verified":"True", "phone_number":"+996000123456", "phone_number_verified":"True" } Если пользователь заходит как юридическое лицо: HTTP/1.1 200 OK Content-Type: application/json { "sub": "1000000", "organization_tin": "ОсОО \"/Компания\"", "position_name": "Директор", "pin":"20101199012345", "citizenship":"KGZ", "family_name":"Андреев", "given_name":"Андрей", "middle_name":"Андреевич", "name":"Андреев Андрей Андреевич", "gender":"male", "birthdate":"1990-01-01", "email":"some.email@gmail.com", "email_verified":"True", "phone_number":"+996000123456", "phone_number_verified":"True" }