Интеграция с ЕСИА на базе oauth2-client (PHP) / Хабр

Что делать и как?

Сначала нам показалось, что в интеграции с ЕСИА нет ничего особенного с технической точки зрения — стандартная задача, связанная с получением данных посредством REST API. Однако, при ближайшем рассмотрении стало понятно, что не всё так просто. Например, выяснилось, что у нас нет представления о том, как работать с сертификатами, необходимыми для подписи нескольких параметров. Пришлось тратить время и разбираться. Но обо всем по порядку.

Для начала важно было наметить план действий. Наш план включал следующие основные шаги:

  1. зарегистрироваться на технологическом портале ЕСИА;
  2. подать заявки на использование программных интерфейсов ЕСИА в тестовой и промышленной среде;
  3. самостоятельно разработать механизм взаимодействия с ЕСИА (в соответствии с действующим документом «Методические рекомендации по использованию ЕСИА»);
  4. протестировать работу механизма в тестовой и промышленной среде ЕСИА.

Обычно мы разрабатываем наши проекты на Java. Поэтому для программной реализации выбрали:

Описание

Компонент для авторизации на портале “Госуслуги”.

Что теперь делать со всеми этими данными?

Мы можем сделать парсинг данных и получать объекты с требуемыми полями. Здесь каждый разработчик может оформлять классы как ему необходимо, в соответствии с техническим заданием.

Пример получения объекта с необходимыми полями:

final ObjectMapper objectMapper = new ObjectMapper()
	.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

String personDataEntityString = esiaPersonDataFetcher
	.apply(ESIA_REST_API_URL   "/prns/"   esiaAccountId);

EsiaPersonDto esiaPersonDto = objectMapper
	.readValue(personDataEntityString, EsiaPersonDto.class);


Заполняем объект esiaPersonDto необходимыми данными, например, контактами:

for (String contactUrl : esiaListDto.getElementUrls()) {
  String contactEntityString = esiaPersonDataFetcher.apply(contactUrl);
  EsiaContactDto esiaContactDto = objectMapper.readValue(contactEntityString, EsiaContactDto.class); // Десериализация контакта
  if (esiaContactDto.getType() == null) continue;
  switch (esiaContactDto.getType().toUpperCase()) {
    case EsiaContactDto.MBT: // Если это номер мобильного телефона, то заполним поле mobilePhone
      esiaPersonDto.setMobilePhone(esiaContactDto.getValue());
      break;
    case EsiaContactDto.EML: // Если это адрес электронной почты, то заполним поле email
      esiaPersonDto.setEmail(esiaContactDto.getValue());
  }
}

Класс EsiaPersonDto выглядит следующим образом:

@Data
@FieldNameConstants(prefix = "")
public class EsiaPersonDto {

  private String firstName;
  private String lastName;
  private String middleName;
  private String birthDate;
  private String birthPlace;
  private Boolean trusted;  // тип учетной записи - подтверждена (“true”) / не подтверждена (“false”)
  private String status;    // статус УЗ - Registered (зарегистрирована) /Deleted (удалена)
  // Назначение полей непонятно, но они есть при запросе /prns/{oid}
  private List<String> stateFacts;
  private String citizenship;
  private Long updatedOn;
  private Boolean verifying;
  @JsonProperty("rIdDoc")
  private Integer documentId;
  private Boolean containsUpCfmCode;
  @JsonProperty("eTag")
  private String tag;
  // ----------------------------------------
  private String mobilePhone;
  private String email;

  @javax.validation.constraints.Pattern(regexp = "(\d{2})\s(\d{2})")
  private String docSerial;

  @javax.validation.constraints.Pattern(regexp = "(\d{6})")
  private String docNumber;

  private String docIssueDate;

  @javax.validation.constraints.Pattern(regexp = "([0-9]{3})\-([0-9]{3})")
  private String docDepartmentCode;

  private String docDepartment;

  @javax.validation.constraints.Pattern(regexp = "\d{14}")
  @JsonProperty("snils")
  private String pensionFundCertificateNumber;

  @javax.validation.constraints.Pattern(regexp = "\d{12}")
  @JsonProperty("inn")
  private String taxPayerNumber;

  @JsonIgnore
  @javax.validation.constraints.Pattern(regexp = "\d{2}")
  private String taxPayerCertificateSeries;

  @JsonIgnore
  @javax.validation.constraints.Pattern(regexp = "\d{10}")
  private String taxPayerCertificateNumber;
}

Работа по усовершенствованию сервиса будет продолжаться, ведь ЕСИА не стоит на месте.

Авторизация через госуслуги

Везде идет речь о сертификате (1 файл), о закрытом ключе (1 файл) и пароле к закрытому ключу (строка)

Похожее:  [ETS2] Остальные вопросы [часть 2] - Страница 276 - [ETS2] Техническая поддержка - TRUCK-SIM.CLUB

Для обмена с ЕСИА потребуется шифрование, поэтому у вас должен быть свой RSA-ключ для подписи. Закрытый ключ находится у вас, публичный сертификат выгружается в кабинет ЕСИА.

Некие сертификаты, общедоступные по ссылке http://vhod-v-lichnyj-kabinet.ru/public/esia.zip

Поскольку ЕСИА тоже будет присылать вам шифрованную информацию, то вам потребуется публичный сертификат ЕСИА, чтобы расшифровать эту информацию.

Файлы от заказчика содержат один файл с расширением cer

Скорей всего – это публичный сертификат. Можете попробовать его открыть. В windows должно открыться стандартное окно с информацией о сертификате.

и папку с несколькими файлами с расширением key (header.key, masks.key, primary.key и тд)

А вот тут сказать сложно. Похоже, что клиент сгенерировал ключ на флешке средствами крипто-про или наподобие, а потом просто скопировал каталог. Вот тут он не прав. Надо выяснять конкретные требования заказчика по интеграции

Для авторизации через госуслуги можно использовать, например, самоподписанные сертификаты:

Сначала создать закрытый ключ и запрос на сертификат

openssl req -newkey rsa:2048 -nodes -keyout domain.key -out domain.csr -sha256

в процессе генерации будет запрос на воод пароля к закрытому ключу.

Потом выпустить публичный сертификат для него

openssl x509 -signkey domain.key -in domain.csr -req -days 1825 -out domain.crt -sha256

у вас будет два файла domain.key (закрытый ключ) и domain.crt (сертификат). Сертификат потом надо будет выгрузить в кабинет ЕСИА.

Как мне эти данные использовать?

Очень помог в интеграции с ЕСИА пакет https://vhod-v-lichnyj-kabinet.ru/ekapusta/oauth2-esia
Описание работы с ним https://vhod-v-lichnyj-kabinet.ru/ru/post/358834/

Кстати, если посмотрите тесты на гитхабе, то найдете там мнемонику для тестовой организации и готовые ключи для нее. Можно будет протестировать.

Аутентификация через есиа. есть реализация на c#?

Добрый день, ребята!

Есть ли у кого-то последовательный алгоритм аутентификации через ЕСИА?
Особая благодарность за код на C#!

Благодарим за внимание

Пакет заоперсорсен финтех-компанией в которой я работаю. Не тестировалось на животных.

Внимание!

Получив токен вы можете выполнять любые API запросы. Библиотека не поддерживает все существующие методы в API, а предоставляет только самые базовые. Основная цель библиотеки – получение токена.

Зачем нам нужна интеграция с есиа?

В связи с пандемией коронавируса количество офлайн сделок во многих направлениях кредитования начало сокращаться. Клиенты стали «уходить в онлайн», и для нас было жизненно важно укрепить своё онлайн-присутствие на рынке автокредитования. В процессе доработки сервиса «Автокредит» (на Хабре уже есть

) мы решили сделать интерфейс заведения кредитных заявок на сайте банка максимально удобным и простым. Интеграция с ЕСИА стала ключевым моментом в решении этой задачи, поскольку позволила автоматически получить персональные данные клиента.

Как обновить токен?

Стандартно, как описано в документации к oauth2-client

Как получить oid?

Если 2 способа:

  1. oid содержится в jwt токене, расшифровав его
  2. После получения токена oid сохраняется в config и получить можно так
Похожее:  Развитие сотрудников — СберБанк

Конфиг

clientId – ID вашего приложения.

redirectUrl – URL куда будет перенаправлен ответ с кодом.

Переиспользование токена

Дополнительно укажите токен и идентификатор в конфиге

Позволяет:

  • Сформировать ссылку для перехода на сайт ЕСИА с целью авторизации
  • Завершает процедуру авторизации обменивая временный код на access token
  • Опционально может производить JWT (JSON Web Token) валидацию ответа ЕСИА (при наличии публичного ключа ЕСИА)
  • Для формирования открепленной подписи запросов, в качестве бэкенда может использоваться
    модуль M2Crypto или openssl через системный вызов (указывается в настройках)
  • Выполнять информационные запросы к ЕСИА REST сервису для получения сведений о персоне:
    • Основаная информация
    • Адреса
    • Контактная информация
    • Документы
    • Дети
    • Транспортные средства

Получаем данные пользователя

Проверяя стейт и меняя код на аутентификационный токен.

Получение url для переадресации

Первый шаг ― это получение авторизационного кода. В нашем случае это делает отдельный сервис с переадресацией на страницу авторизации портала Госуслуг (расскажем об этом немного подробнее).

Сначала мы инициализируем переменные ESIA_AUTH_URL (адрес ЕСИА) и API_URL (адрес, на который происходит редирект в случае успешной авторизации). После этого создаем объект EsiaRequestParams, который содержит в своих полях параметры запроса к ЕСИА, и сформируем ссылку esiaAuthUri.

public Response loginByEsia() throws Exception {
  final String ESIA_AUTH_URL = dao.getEsiaAuthUrl(); // Адрес ЕСИА
  final String API_URL = dao.getApiUrl(); // Адрес, на который произойдет редирект с случае успешной авторизации
  EsiaRequestParams requestDto = new EsiaRequestParams(API_URL);
  URI esiaAuthUri = new URIBuilder(ESIA_AUTH_URL)
          .addParameters(Arrays.asList(
            new BasicNameValuePair(RequestEnum.CLIENT_ID.getParam(), requestDto.getClientId()),
            new BasicNameValuePair(RequestEnum.SCOPE.getParam(), requestDto.getScope()),
            new BasicNameValuePair(RequestEnum.RESPONSE_TYPE.getParam(), requestDto.getResponseType()),
            new BasicNameValuePair(RequestEnum.STATE.getParam(), requestDto.getState()),
            new BasicNameValuePair(RequestEnum.TIMESTAMP.getParam(), requestDto.getTimestamp()),
            new BasicNameValuePair(RequestEnum.ACCESS_TYPE.getParam(), requestDto.getAccessType()),
            new BasicNameValuePair(RequestEnum.REDIRECT_URI.getParam(), requestDto.getRedirectUri()),
            new BasicNameValuePair(RequestEnum.CLIENT_SECRET.getParam(), requestDto.getClientSecret())
          ))
          .build();
  return Response.temporaryRedirect(esiaAuthUri).build();
}

Для наглядности покажем, как может выглядеть класс EsiaRequestParams:

public class EsiaRequestParams {

  String clientId;
  String scope;
  String responseType;
  String state;
  String timestamp;
  String accessType;
  String redirectUri;
  String clientSecret;
  String code;
  String error;
  String grantType;
  String tokenType;

  public EsiaRequestParams(String apiUrl) throws Exception {
    this.clientId = CLIENT_ID;
    this.scope = Arrays.stream(ScopeEnum.values())
            .map(ScopeEnum::getName)
            .collect(Collectors.joining(" "));
    responseType = RESPONSE_TYPE;
    state = EsiaUtil.getState();
    timestamp = EsiaUtil.getUrlTimestamp();
    accessType = ACCESS_TYPE;
    redirectUri = apiUrl   RESOURCE_URL   "/"   AUTH_REQUEST_ESIA;
    clientSecret = EsiaUtil.generateClientSecret(String.join("", scope, timestamp, clientId, state));
    grantType = GRANT_TYPE;
    tokenType = TOKEN_TYPE;
  }
}

Получение данных пользователя


В нашем случае необходимо получить ФИО, дату рождения, паспортные данные и контакты.

Используем функциональный интерфейс, который поможет получать данные пользователя:

Получение токена

Следующий шаг ― получение маркера доступа (он же токен) в обмен на авторизационный код, который был получен в качестве параметра при успешной авторизации пользователя на портале Госуслуг.

Для получения каких-либо данных в ЕСИА нужно получить токен доступа. Для этого формируем запрос в ЕСИА. Основные поля запроса тут формируются аналогичным образом, в коде получается примерно следующее:

Похожее:  GitHub - mirceak/ApiServer-Unity-Laravel-Passport: A well detailed tutorial for a server based project made in Unity. This template provides you with every step you need to take to setup your own php server using laravel and having it communicate with unity.

Предварительные условия

Для работы требуется наличие публичного и приватного ключа в соответствии с методическими рекомендациями
по работе с ЕСИА. Допускается использование самоподписного сертифката, который можно сгенерировать
следующей командой:

Пример использования в django

Создайте конфигурационный файл esia.ini следующего содержания:

[esia]
### Внимание! Все пути указываются относительно данного файла.
# Базовый адрес сервиса ЕСИА, в данном случае указана тестовая среда
SERVICE_URL: https://esia-portal1.test.gosuslugi.ru

# Идентификатор информационной системы, указывается в заявке на подключение
CLIENT_ID: MYIS01

# Публичный ключ/сертфикат (необязателен, используется только для m2crypto или openssl)
CERT_FILE: keys/my_public_cert.crt

# Приватный ключ (необязателен, используется только для m2crypto или openssl)
PRIV_KEY_FILE: keys/my_private.key

# Публичный ключ сервиса ЕСИА, для валидации ответов (необязателен)
JWT_CHECK_KEY: keys/esia_test_pub.key

# Адрес страницы, на которую будет перенаправлен браузер после авторизации в ЕСИА
REDIRECT_URI: http://127.0.0.1:8000/esia/callback/

# Адрес страницы, на которую необходимо перенаправить браузер после логаута в ЕСИА (опционально)
LOGOUT_REDIRECT_URI: http://127.0.0.1:8000

# Список scope через пробел. Указывается в заявке, openid при авторизации обязателен
SCOPE: openid http://esia.gosuslugi.ru/usr_inf

# Используемый крипто бэкенд: m2crypto, openssl (системный вызов)
# или csp (системный вызов утилиты cryptcp из состава КриптоПРО CSP)
CRYPTO_BACKEND: m2crypto

# SHA1 отпечаток сертификата связанного с закрытым ключем, смотреть по выводу certmgr --list
# (необязателен, используется только для csp)
CSP_CERT_THUMBPRINT: 5c84a6a58bbeb6578ff7d26f4ea65b6de5f9f5b8

# Пароль (пин-код) контейнера с закрктым ключем
# (необязателен, используется только для csp)
CSP_CONTAINER_PWD: 12345678

В свой urls.py добавьте:

В свой views.py добавьте:

importjsonfromdjango.httpimportHttpResponseRedirect, HttpResponsefromdjango.contrib.auth.viewsimportlogoutfromesia.clientimportEsiaConfig, EsiaAuthESIA_SETTINGS=EsiaConfig('/full/path/to/esia.ini')

defesia_login(request):
    esia_auth=EsiaAuth(ESIA_SETTINGS)
    esia_login_url=esia_auth.get_auth_url()
    returnHttpResponseRedirect(esia_login_url)

defesia_logout(request):
    kwargs= {}
    esia_auth=EsiaAuth(ESIA_SETTINGS)
    kwargs['next_page'] =esia_auth.get_logout_url()
    returnlogout(request, **kwargs)

defesia_callback(request):
    esia_auth=EsiaAuth(ESIA_SETTINGS)
    ifrequest.GET.has_key('error'):
        data= {
            'error': request.GET['error'],
            'error_description': request.GET['error_description'],
        }
    else:
        data= []
        code=request.GET['code']
        state=request.GET['state']
        esia_client=esia_auth.complete_authorization(code, state)
        # Для отключения JWT валидации ответов ЕСИА, можно так:# esia_client = esia_auth.complete_authorization(code, state, validate_token=False)# Запрос информации о персонеmain_info=esia_client.get_person_main_info()
        pers_doc=esia_client.get_person_documents()
        pars_addr=esia_client.get_person_addresses()
        pers_contacts=esia_client.get_person_contacts()
        pers_kids=esia_client.get_person_kids()
        pers_trans=esia_client.get_person_transport()

        data.append(main_info)
        data.append(pers_doc)
        data.append(pars_addr)
        data.append(pers_contacts)
        data.append(pers_kids)
        data.append(pers_trans)

    # Просто выводим информацию. Здесь далее должна идти внутренняя логика авторизации# вашей информационной системы.returnHttpResponse(json.dumps(data, cls=json.JSONEncoder, ensure_ascii=False, indent=4),
        content_type='application/json')

Редиректим посетителя на есиа

Одновременно сохраняя стейт для последующей проверки.

Токен и oid

Токен – jwt токен которые вы получаете от ЕСИА для дальнейшего взаимодействия

oid – уникальный идентификатор владельца токена

Установка

При помощи composer:

Или добавьте в composer.json

1 Звезда2 Звезды3 Звезды4 Звезды5 Звезд (1 оценок, среднее: 5,00 из 5)
Загрузка...

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Adblock
detector