При выполнении очередного госзаказа наша команда столкнулась с проблемой интеграции сайта с ЕСИА. Инструкции по решению этой задачи в сети нет, кроме информации в официальных документах МинКомСвязи (примерно 300 страниц в трех регламентах). Также есть компании, которые оказывают платные услуги по интеграции ЕСИА. Мы реализовали, описали процесс интеграции и решили поделиться с сообществом habrahabr.
- Что такое ЕСИА
- Зачем нам ЕСИА?
- Стек и схема интеграции
- Формирование подписи
- Создание ссылки для редиректа на страницу ЕСИА
- Получение авторизационного токена ЕСИА
- Запрос токена идентификации
- Получение данных о пользователе
- Полезные материалы по теме
- Что же может получить коммерческая организация из ЕСИА?
- Как подключиться?
- Аутентификация пользователя в системе
- Подключаться или нет?
Что такое ЕСИА
Единая Система Идентификации и Аутентификации — российская информациия система, обеспечивающая доступ (регистрация, аутентификация) на сайты государтсвенных структур и некоторых коммерческих организаций. Подробнее на википедии
В процессе интеграции ЕСИА, система сможет отправлять запрос на ЕСИА и при успешной авторизации получать в качестве ответа данные пользователя
Сценарий авторизации выглядит примерно так:
- Пользователь на сайте, на котором внедряется ЕСИА, в личном кабинете нажимает на кнопку «Войти через ГосУслуги»
- Система переадресует на сайт ЕСИА
- Пользователь вводит свои логин и пароль на сайте ЕСИА
- При успешной авторизации ЕСИА возвращает пользователя обратно на сайт и по защищенному протоколу передает его личные данные
Привет, Хабр! В одном из постов блога мой коллега Иван писал о нашем блокчейн-сервисе для онлайн-голосований WE.Vote. Он подробно разобрал, как работает WE.Vote с точки зрения технологий. Но чтобы сервисы удаленного голосования можно было использовать для принятия официальных решений юрлиц, не хватает еще одного важного компонента — достоверной верификации участников. В России для этого можно провести интеграцию с ЕСИА (Единой Системой Идентификации и Аутентификации) — проще говоря, с Госуслугами. Интеграция эта заметно отличается от интеграции с другими OAuth2-сервисами, как, например, Google или VK. В этом посте мы постараемся помочь тем, кто захочет интегрировать ЕСИА в свой сервис через стек, подобный нашему, а также дадим несколько полезных ссылок по ЕСИА в принципе.

Зачем нам ЕСИА?
Согласно Федеральному закону № 225-ФЗ от 28.06.2021 «О внесении изменений в часть первую Гражданского кодекса Российской Федерации», многие организаций в РФ получили право проводить официальные собрания и голосования по корпоративным вопросам дистанционно.
Ранее решения с юридической силой требовали очных собраний или голосований по почте. Голосования по почте не отличаются надежностью, а собрать много руководителей со всей России в одном месте — это кошмар с точки зрения затрат.
Чтобы проводить мероприятия принятия решения дистанционно в соответствии с новым федеральным законом, необходимо предоставить возможность достоверного установления личности участников. В России это возможно через проверку доступа к верифицированному аккаунту на Госуслугах.
Стек и схема интеграции
Для интеграции мы используем:
Typescript, ReactJS, NestJS
КриптоПро CSP 4

Формирование подписи
Прежде чем разбирать все по порядку, кое о чем стоит подумать заранее. В отличие от других интеграций, запросы к ЕСИА должны сопровождаться подписью ГОСТ Р 34.10/11-2012, а не просто API key. Создать такую подпись можно с помощью утилиты КриптоПро CSP. Для нас основная задача здесь — правильно обернуть эту утилиту в Docker, чтобы с ней можно было работать как с отдельным сервисом в рамках нашей инфраструктуры. Получившийся сервис мы выложили в открытый доступ на гитхабе. Инструкция по запуску есть в README.md.
В процесс сборки Docker образа сервиса с утилитой КриптоПро мы встроили:
Установку утилиты КриптоПро СSP 4 из .deb пакета
Установку лицензии КриптоПро
Загрузку корневого сертификата тестовой или основной среды ЕСИА
Загрузку пользовательского сертификата с PIN кодом
REST-сервер с методом, позволяющим создавать подписи
Таким образом вся криптография собрана в отдельном самостоятельном компоненте, который можно использовать, когда необходимо что-нибудь подписать. Вот как это выглядит на бэкенде:
private async signParams(params: Record<string, string>) {
const time = moment().format('YYYY.MM.DD HH:mm:ss ZZ')
const state = uuid()
const clientId = this.clientId
const scope = this.scope
const { data: { result: clientSecret } } = await axios.post<{ result: string }>(
`${this.cryptoProServiceAddress}/cryptopro/sign`,
{ text: [scope, time, clientId, state].join('') },
)
return {
...params,
timestamp: time,
client_id: clientId,
scope,
state,
client_secret: clientSecret.replace(/\n/g, ''),
}
}
С созданием подписей разобрались, теперь последовательно разберем, как реализовать схему выше.
Создание ссылки для редиректа на страницу ЕСИА
Все начинается с того, что пользователь решает пройти авторизацию через ЕСИА. Создаем на бэкенде ссылку для перехода с использованием нашего инструмента формирования подписей.
async getAuthLink(redirectLink: string) {
const params = await this.signParams({
redirect_uri: redirectLink,
response_type: 'code',
access_type: 'offline',
})
const authQuery = new URLSearchParams(params)
const authURL = `${this.esiaHost}/aas/oauth2/ac`
return `${authURL}?${authQuery}`
}
В redirectLink необходимо указать адрес страницы, на которую ЕСИА перенаправит пользователя после успешной аутентификации. Созданную ссылку возвращаем на фронтенд и перенаправляем на нее пользователя.
Получение авторизационного токена ЕСИА
Запрос токена идентификации
После успешной аутентификации на странице ЕСИА пользователь возвращается на фронтенд приложения по указанному нами адресу. ЕСИА передаёт авторизационный токен в виде get-параметра code. Этот токен необходимо передать на бэкенд и запросить с его помощью идентификационный токен пользователя.
async getTokens(code: string) {
try {
const params = await this.signParams({
grant_type: 'authorization_code',
token_type: 'Bearer',
redirect_uri: 'no',
code,
})
const authURL = `${this.host}/aas/oauth2/te`
const authQuery = new URLSearchParams(params)
const { data: tokens } = await axios.post(`${authURL}?${authQuery}`)
return {
idToken: tokens.id_token,
accessToken: tokens.access_token,
refreshToken: tokens.refresh_token,
}
} catch (e) {
const status = e.response ? e.response.status : 500
const message = e.response ? e.response.data.error_description : e.message
throw new HttpException('Failed to get auth tokens: ' + message, status)
}
}
Получение данных о пользователе
Идентификационный токен пользователя необходимо проверить с помощью публичного RSA ключа от ЕСИА и получить из него id пользователя. С помощью этого id и accessToken, который мы получили в предыдущем шаге, мы уже наконец можем запросить персональные данные пользователя.
getUserIdFromToken(idToken: string) {
const decodedIdToken = verify(idToken, this.esiaPublicKey, {
algorithms: ['RS256'],
audience: 'WE_VOTE',
}) as EsiaParsedToken
return decodedIdToken['urn:esia:sbj']['urn:esia:sbj:oid']
}
async getUserInfo(tokens: EsiaTokens) {
const { idToken, accessToken } = tokens
const oId = this.getUserIdFromToken(idToken)
const [{ data: mainInfo }, { data: contactsInfo }] = await Promise.all([
axios.get(`${this.esiaHost}/rs/prns/${oId}`, {
headers: {
Authorization: `Bearer ${accessToken}`,
},
}),
axios.get(`${this.esiaHost}/rs/prns/${oId}/ctts?embed=(elements)`, {
headers: {
Authorization: `Bearer ${accessToken}`,
},
}),
])
const email = contactsInfo.elements.find(({ type }: { type: string }) => type === 'EML')
return {
id: oId,
firstName: mainInfo.firstName,
lastName: mainInfo.lastName,
surName: mainInfo.middleName,
trusted: mainInfo.trusted,
email: email ? {
value: email.value.toLowerCase(),
verified: email.vrfStu === 'VERIFIED',
} : null,
}
}
На этом шаге мы уже имеем все необходимые данные о пользователе. Остается только занести их в свою систему и закончить авторизацию.
Полный код интеграционного модуля на бэкенде
import { HttpException } from '@nestjs/common'
import * as moment from 'moment'
import { v4 as uuid } from 'uuid'
import { URLSearchParams } from 'url'
import axios from 'axios'
import { verify } from 'jsonwebtoken'
type EsiaTokens = {
idToken: string,
accessToken: string,
}
type EsiaParsedToken = {
'urn:esia:sbj': {
'urn:esia:sbj:oid': string,
},
}
export class EsiaApiService {
private readonly clientId = 'WE_VOTE'
private readonly scope = ['openid', 'email', 'fullname'].join(' ')
constructor(
private readonly esiaHost: string, // 'https://esia-portal1.test.gosuslugi.ru' или 'https://esia.gosuslugi.ru'
private readonly esiaPublicKey: string, // можно взять из http://esia.gosuslugi.ru/public/esia.zip
private readonly cryptoProServiceAddress: string, // адрес сервиса по созданию подписей e.g 'http://127.0.0.1:3037'
) {
}
async getAuthLink(redirectLink: string) {
const params = await this.signParams({
redirect_uri: redirectLink,
response_type: 'code',
access_type: 'offline',
})
const authQuery = new URLSearchParams(params)
const authURL = `${this.esiaHost}/aas/oauth2/ac`
return `${authURL}?${authQuery}`
}
private async signParams(params: Record<string, string>) {
const time = moment().format('YYYY.MM.DD HH:mm:ss ZZ')
const state = uuid()
const clientId = this.clientId
const scope = this.scope
const { data: { result: clientSecret } } = await axios.post<{ result: string }>(
`${this.cryptoProServiceAddress}/cryptopro/sign`,
{ text: [scope, time, clientId, state].join('') },
)
return {
...params,
timestamp: time,
client_id: clientId,
scope,
state,
client_secret: clientSecret.replace(/\n/g, ''),
}
}
async getTokens(code: string) {
try {
const params = await this.signParams({
grant_type: 'authorization_code',
token_type: 'Bearer',
redirect_uri: 'no',
code,
})
const authURL = `${this.esiaHost}/aas/oauth2/te`
const authQuery = new URLSearchParams(params)
const { data: tokens } = await axios.post(`${authURL}?${authQuery}`)
return {
idToken: tokens.id_token,
accessToken: tokens.access_token,
refreshToken: tokens.refresh_token,
}
} catch (e) {
const status = e.response ? e.response.status : 500
const message = e.response ? e.response.data.error_description : e.message
throw new HttpException('Failed to get auth tokens: ' + message, status)
}
}
getUserIdFromToken(idToken: string) {
const decodedIdToken = verify(idToken, this.esiaPublicKey, {
algorithms: ['RS256'],
audience: this.clientId,
}) as EsiaParsedToken
return decodedIdToken['urn:esia:sbj']['urn:esia:sbj:oid']
}
async getUserInfo(tokens: EsiaTokens) {
const { idToken, accessToken } = tokens
const oId = this.getUserIdFromToken(idToken)
const [{ data: mainInfo }, { data: contactsInfo }] = await Promise.all([
axios.get(`${this.esiaHost}/rs/prns/${oId}`, {
headers: {
Authorization: `Bearer ${accessToken}`,
},
}),
axios.get(`${this.esiaHost}/rs/prns/${oId}/ctts?embed=(elements)`, {
headers: {
Authorization: `Bearer ${accessToken}`,
},
}),
])
const email = contactsInfo.elements.find(({ type }: { type: string }) => type === 'EML')
return {
id: oId,
firstName: mainInfo.firstName,
lastName: mainInfo.lastName,
surName: mainInfo.middleName,
trusted: mainInfo.trusted,
email: email ? {
value: email.value.toLowerCase(),
verified: email.vrfStu === 'VERIFIED',
} : null,
}
}
}
Надеюсь, статья оказалась для вас полезной. Желаю, чтобы у вас все получилось без особых проблем!
Полезные материалы по теме
Изменения в законодательстве, начинающие действовать с начала 2018 года и включающие в себя самые разнообразные области нашей с вами жизнедеятельности (закон о мессенджерах, о телемедицине и т.д.) объединяет одно – все большее проникновение информационных сервисов в нашу жизнь. Естественным является факт, что как и в реальной жизни, для получения человеком какой-либо услуги ему требуется пройти идентификацию. В офф-лайновой жизни средством авторизации является паспорт гражданина, а в он-лайн сфере таковым средством правительство решило признать ЕСИА — единая система идентификации и аутентификации.
Вот о ней и хотелось бы поговорить. Это ознакомительная статья, можно сказать ликбез. Для знакомства людей, которые еще не знают, что при необходимости можно использовать ЕСИА у себя в проектах и идти в ногу со временем вместе с государством. И так, что же это за зверь и как его рассматривает правительство.
Минкомсвязь России в рамках инфраструктуры электронного правительства создала и развивает Единую систему идентификации и аутентификации (ФГИС ЕСИА), цель которой — упорядочить и централизовать процессы регистрации, идентификации, аутентификации и авторизации пользователей.
1. Предоставляет информационным системам решение по достоверной идентификации пользователей (физических и юридических лиц, органов государственной власти).
Достоверность достигается за счет того, что:
- регистрация лица в ЕСИА сопряжена с проверкой значимых для удостоверения личности критериев;
- ЕСИА обеспечивает защиту размещённой в ней информации в соответствии с законодательством Российской Федерации.
2. Является ориентированной на пользователя и предоставляет возможности:
- идентификации и аутентификации с использованием единой учетной записи и широкого спектра поддерживаемых методов аутентификации при доступе к различным информационным системам органов государственной власти;
- управления своими персональными данными, размещенными в ЕСИА, и контроля над их предоставлением в информационные системы органов государственной власти.
Основные функциональные возможности ЕСИА:
- идентификация и аутентификация пользователей, в том числе:
- однократная аутентификация, которая дает пользователям ЕСИА следующее преимущество: пройдя процедуру идентификации и аутентификации в ЕСИА, пользователь может в течение одного сеанса работы обращаться к любым информационным системам, использующим ЕСИА, при этом повторная идентификация и аутентификация не требуется;
- поддержка различных методов аутентификации: по паролю, по электронной подписи, а также двухфакторная аутентификация (по постоянному паролю и одноразовому паролю, высылаемому в виде sms-сообщения);
- поддержка уровней достоверности идентификации пользователя (упрощённая учетная запись, стандартная учетная запись, подтвержденная учетная запись).
- ведение идентификационных данных, а именно – ведение регистров физических, юридических лиц, органов и организаций, должностных лиц органов и организаций и информационных систем;
- авторизация уполномоченных лиц органов государственной власти при доступе к следующим функциям ЕСИА:
- ведение регистра должностных лиц органов власти в ЕСИА;
- ведение справочника полномочий в отношении информационной системы и предоставление пользователям ЕСИА (зарегистрированным в ЕСИА как должностные лица) полномочий по доступу к ресурсам систем, зарегистрированным в ЕСИА;
- делегирование вышеуказанных полномочий уполномоченным лицам нижестоящих органов государственной власти.
- ведение и предоставление информации о полномочиях пользователей в отношении информационных систем, зарегистрированных в ЕСИА.
В данный момент подключить систему к ЕСИА может любая государственная организация, а также отдельные виды коммерческих организаций: страховые компании, кредитные организации (банки), профессиональные участники рынка ценных бумаг, негосударственные пенсионные фонды, микрофинансовые и микрокредитные организации, а также операторы связи.
Законодательство со временем корректируется, и вместе с ним расширяется перечень организаций, которым разрешено подключиться к ЕСИА.
Люди, не знакомые с ситуацией, при слове «государственная» сразу представляют себе каналы связи, которые необходимо защитить при помощи отечественных криптоалгоритмов со всеми вытекающими затратами, лицензиями и оборудованием. Но, как бы смешно это не было (или печально), главная площадка для идентификации в стране – работает с иностранной криптографией (а куда деваться).
Поэтому, если вы хотите воспользоваться услугами данной платформы, то можете размещать свои ресурсы где у годно в нашей огромной стране, в том числе и в нашей инфраструктуре Cloud4Y.
Что же может получить коммерческая организация из ЕСИА?
Перечень доступных к получению сведений зависит от:
- Категории организации, подключающейся к ЕСИА
- Использованного способа подключения к ЕСИА
Минкомсвязь ограничивает перечень данных, доступных коммерческим организациям. Обычно разрешают получать только сведения о ФИО, реквизитах паспорта (серия и номер, кем и когда выдан), гражданстве, а также признака «подтвержденности» аккаунта и идентификатора аккаунта в ЕСИА.
Государственные организации могут получать из ЕСИА полный набор данных о пользователе и его организациях. Это следующие сведения:
- личные данные (ФИО, пол, дата и место рождения, гражданство)
- данные идентификационных документов (СНИЛС, ИНН, общегражданский и заграничный паспорт, свидетельство о рождении, водительское удостоверение, военный билет, полис ОМС)
- контактная информация (email, мобильный и домашний телефон, адреса регистрации и проживания)
- сведения о детях (личные данные и документы)
- сведения о транспортных средствах (номер и свидетельство о регистрации)
- сведения об организациях и ИП (наименование, ОГРН, ИНН/КПП, организационно-правовая форма, юридический адрес, контакты, филиалы, списки сотрудников, полномочия сотрудников, транспортные средства организации)
- данные учетной записи (идентификатор аккаунта в ЕСИА, признак «подтвержденности» аккаунта)
Сведения предоставляются в том объеме, в каком они заполнены пользователем в ЕСИА, а также при условии согласия пользователя на предоставления этих сведений.
Как подключиться?
Чтобы подключить сайт своей организации, нужно пройти несколько довольно несложных процедур.
В общих чертах для подключения к ЕСИА нужно:
- Убедиться, что вашей организации можно подключать свои системы к ЕСИА.
- Директору организации с помощью веб-приложения «Профиль ЕСИА» зарегистрировать организацию в ЕСИА.
- Ему также нужно прикрепить к учетной записи организации ответственного сотрудника и назначить ему право доступа в специальное приложение — Технологический портал ЕСИА. Если директор не планирует делегировать дальнейшие операции своему сотруднику, то тогда он все равно должен явно предоставить доступ себе к Технологическому порталу ЕСИА.
Назначенному ответственному сотруднику организации нужно с помощью веб-приложения «Технологический портал ЕСИА»:
- Зарегистрировать учетную запись системы в ЕСИА. Мнемонику для системы придумать, либо использовать существующую мнемонику точки подключения к СМЭВ, если подключаемая к ЕСИА система раньше уже была подключена к СМЭВ.
- Загрузить в карточку системы ее сертификат.
Ответственному сотруднику организации нужно:
- Поочередно подать по электронной почте заполненные в соответствии с регламентом заявки на использование программных интерфейсов ЕСИА в тестовой и промышленной среде.
Разработчикам подключаемой системы:
- Доработать систему для подключения к ЕСИА, самостоятельно разработав код взаимодействия с ЕСИА в соответствии с действующим документом «Методические рекомендации по использованию ЕСИА» или использовать готовые решения, благо такие на рынке есть.
- Отладить взаимодействие в тестовой и промышленной среде ЕСИА.
Здесь нужно заметить, что с 01.01.2018 г. взаимодействие по протоколу SAML 2.0 больше не будет разрешено (только для действующих систем). Для подключения к ЕСИА необходимо будет использовать протокол OAuth 2.0 / OpenID Connect (сейчас доступны оба варианта).
Аутентификация пользователя в системе
Рекомендуемый сценарий аутентификации пользователя при интеграции по OpenID Connect 1.0 в его базовом виде происходит по следующему сценарию:
- Пользователь нажимает на веб-странице системы-клиента кнопку «Войти через ЕСИА».
- Система-клиент формирует и отправляет в ЕСИА запрос на аутентификацию и перенаправляет браузер пользователя на специальную страницу предоставления доступа.
- ЕСИА осуществляет аутентификацию пользователя одним из доступных способов. Если пользователь ещё не зарегистрирован в ЕСИА, то он может перейти к процессу регистрации.
- Когда пользователь аутентифицирован, ЕСИА сообщает пользователю, что система-клиент запрашивает данные о нем в целях проведения идентификации и аутентификации, предоставляя перечень запрашиваемых системой-клиентом сведений.
- Если пользователь дает разрешение на проведение аутентификации системой-клиентом, то ЕСИА выдает системе-клиенту специальный авторизационный код.
- Система-клиент формирует в адрес ЕСИА запрос на получение маркера идентификации, включая в запрос полученный ранее авторизационный код.
- ЕСИА проверяет корректность запроса (например, что система-клиент зарегистрирована в ЕСИА) и авторизационного кода и передает системе-клиенту маркер идентификации.
- Система-клиент извлекает идентификатор пользователя из маркера идентификации. Если идентификатор получен, а маркер проверен, то система-клиент считает пользователя аутентифицированным. После получения маркера идентификации система-клиент использует REST-сервисы ЕСИА для получения дополнительных данных о пользователе, предварительно получив соответствующий маркер доступа.
Подключаться или нет?
За операторов, в связи со вступлением в действие закона о мессенджерах данный вопрос практически решен.
Напомним, в соответствии с Федеральным законом №245 «О внесении изменений в Федеральный закон «О связи» от 29 июля 2017 года, операторы связи обязаны проверять достоверность сведений об абоненте. В законе закреплен перечень способов проверки, одним из которых является использование Единого портала государственных и муниципальных услуг или информационных систем госорганов при наличии подключения к ним у операторов через СМЭВ.
Поправки в ФЗ «О связи» вступают в силу 1 июня 2018. До этого времени операторы связи смогут протестировать работу своих систем со СМЭВ и ЕСИА.
Становится ли чебурнет всё ближе? Официальных заявлений о планах сделать выход в Интернет возможным только через ЕСИА нами не найдено. На данный момент, по официальным данным, в ЕСИА зарегистрировано около 50 миллионов пользователей (физических лиц) и около 300 000 организаций.