Аутентификация по протоколу WebAuthn на основе биометрических данных
К настоящему времени некоторые мобильные приложения удалены из каталогов приложений. В связи с этим необходимо обеспечить пользователям взаимодействие с веб-страницами сервисов, сопоставимое по удобству с использованием приложений для мобильных устройств.
RooX UIDM позволяет выполнять аутентификацию пользователей с использованием современного протокола Web Authentification API (далее — WebAuthN), в том числе на основе биометрических данных — распознавания лица, отпечатка пальца и других сведений об уникальных биологических характеристиках пользователя. В соответствии с протоколом WebAuthN биометрические данные используются только на стороне клиентского устройства (аутентификатора) и по сети не передаются.
Использование этого протокола позволяет выполнять безопасную аутентификацию через браузер на мобильном устройстве с распознаванием на устройстве лица или отпечатка пальца, что сопоставимо по удобству с мобильными приложениями.
В основе протокола WebAuthN лежит надёжная асимметричная криптография (криптография с открытым ключом).
Создание ключей и аутентификация по протоколу WebAuthN
Для регистрации открытого ключа пользователя в RooX UIDM пользовательским устройством создаётся закрытый (приватный) ключ и открытый (публичный) ключ, которые вместе называются ключевой парой.
Закрытый (приватный) ключ используется пользователем для подписания направляемых им сообщений.
Открытый (публичный) ключ используется сервером для проверки подписи полученных сообщений. Успешная проверка подписи сообщения от пользователя доказывает, что полученные сервером сообщения направлены владельцем закрытого ключа (пользователем).
Закрытый ключ сохраняется на пользовательском устройстве в защищённом хранилище аутентификатора (программного компонента или устройства).
Открытый ключ вместе со идентификатором (логином) пользователя направляется на сервер и сохраняется на нём[1].
Аутентификация пользователя на защищаемом ресурсе по протоколу WebAuthN означает доказывание того, что авторизующийся пользователь владеет закрытым ключом, соответствующим ранее направленному на сервер открытому ключу.
Аутентификатор (программное обеспечение или устройство) при выполнении процедуры аутентификации должен проверить физическое присутствие пользователя и его согласие на совершение операции. Аутентификатор может выполнять распознавание биометрических данных.
Аутентификация (доказывание владения закрытым ключом) выполняется следующим образом:
-
сервер направляет стороне, запрашивающей аутентификацию, challenge (случайное число);
-
запрашивающая аутентификацию сторона (пользователь) подписывает это число закрытым ключом из ключевой пары, созданной для аутентификации на этом ресурсе;
-
сервер, получив ответ запрашивающей стороны, проверяет его подпись при помощи хранящегося на сервере открытого ключа пользователя, которым сервер располагает после выполнения процедуры регистрации открытого ключа;
-
успешная проверка подписи при помощи открытого ключа подтверждает владение стороны, запрашивающей аутентификацию, закрытым ключом этого пользователя; этим подтверждается подлинность пользователя.
При аутентификации по протоколу WebAuthN может предоставляться сертификат ключевой пары, выданный третьей доверенной (удостоверяющей) стороной. Открытый ключ пользователя, направляемый на сервер, подписывается удостоверяющей стороной (закрытым ключом). Сервер, используя открытый ключ удостоверяющей стороны, проверяет подпись удостоверяющей стороны.
Сценарии с использованием протокола WebAuthN
Регистрация устройства для аутентификации на основе биометрических данных
-
Выполнена аутентификация пользователя (в браузере сохранён токен доступа).
-
Устройство поддерживает технологию WebAuthN.
-
Пользователь переходит на страницу управления способами аутентификации.
-
Браузер отображает элемент пользовательского интерфейса (кнопку), указывающий на возможность привязать устройство.
-
При нажатии на кнопку выполняется запрос API: Инициализация сценария привязки для получения параметров электронной подписи. Текущий токен доступа передается браузером автоматически.
-
Сервер генерирует случайное число (
nonce
) и возвращает его в ответе. -
Клиентский код вызывает метод
credentials.create()
. -
Пользователь разрешает генерацию ключевой пары и выполняет другие действия, которые ему предлагает операционная система (например, прикладывает палец к сканеру отпечатка пальца или выполняет распознавание лица). Серверу возвращаются объекты
clientJson
,attestation
. -
Браузер выполняет запрос API: Привязка нового ключа на регистрацию открытого ключа. Передаются
execution
,clientData
,attestation
. -
Сервер выполняет проверки
clientData
,attestation
в соответствии со спецификацией WebAuthN. -
Сервер создает запись в таблице CertificateCredentials, в которой открытый ключ ассоциируется с логином пользователя.
Аутентификация на ранее зарегистрированном устройстве
-
Пользователь выполнил регистрацию на этом устройстве.
-
Пользователь переходит на страницу выбора способа аутентификации.
-
Браузер отображает элемент пользовательского интерфейса (кнопку), указывающую на возможность аутентификации по протоколу WebAuthN.
-
При нажатии выполняется запрос API: Инициализация сценария аутентификации на получение параметров подписания. Текущий токен доступа передается браузером автоматически.
-
Сервер генерирует случайное число (
nonce
) и возвращает его в ответе. -
Клиентский код вызывает метод
credentials.get()
. -
Пользователь разрешает использование закрытого ключа (генерацию электронной подписи) и выполняет другие действия, которые ему предлагает операционная система (например, прикладывает палец к сканеру отпечатка пальца или выполняет распознавание лица). Возвращается объект аутентификатора.
-
Браузер выполняет запрос API: Аутентификация с открытым ключом из ключевой пары на аутентификацию с использованием протокола WebAuthN. Передаются
execution
,credentialId
,authenticatorData
,clientData
,userHandle
,signature
. -
Сервер выполняет проверки переданных данных в соответствии со спецификацией WebAuthN.
-
Сервер выполняет поиск в таблице CertificateCredentials. Если открытый ключ зарегистрирован, аутентификация считается успешной.
-
Выполняется запись события аутентификации в БД аудита.
-
Выпускаются и возвращаются токен доступа, токен обновления доступа.
Спецификация API
Создание ключевой пары и привязка открытого ключа к идентификатору пользователя
Инициализация сценария привязки
POST {host}/customer-webapi-1.0/webauthn/addInitiate
Content-Type: application/json
Accept: application/json
Запрос выполняется с предоставлением клиентского токена доступа. Токен может быть передан или в заголовке Authorization, или в Cookie. Имя cookie настраивается, см. Установка и настройка функциональности
Тело запроса отсутствует.
HTTP 200 OK
{
"continuationKey": "b409234f-25b5-4e7f-b632...",
"approvalInfo": {
"serverNonce": "1234",
"rpId": "uidm.ru"
},
"form": { ... },
"status": "approval_required"
}
Переменная | Описание | Комментарий |
---|---|---|
|
Токен поддержания сценария |
Должен передаваться на сервер при следующем обращении в рамках сценария. Является полным аналогом параметра |
|
Атрибуты сценария для использования на стороне клиентского приложения. |
Содержит различные параметры, необходимые клиентскому приложению для работы в рамках сценария. На данном шаге важны параметры |
|
Машинное описание формы ввода для данного шага сценария. |
Аналогично атрибуту |
|
Статус ответа. |
На данный момент для этого запроса всегда принимает значение |
Отсутствие ошибок в form.errors
означает, что можно перейти к шагу API: Привязка нового ключа.
Из ответа на запрос необходимо сохранить значение атрибута continuationKey
.
Также из ответа необходимо сохранить значения полей rpId
и serverNonce
и передать их в качестве значений для параметров rpId
и challenge
методу
create()
WebAuthN.
В зависимости от типа и причины ошибки, информация о ней может вернуться как в виде соответствующего HTTP Status, так и в виде значения параметра form.errors
при HTTP Status=200
Код возврата | Описание | Вероятные причины и поведение клиентского приложения |
---|---|---|
400 |
Некорректный запрос |
Ошибка реализации протокола. Пользователю необходимо сообщить о невозможности выполнения операции. |
401 |
Пользователь неавторизован |
Не передан токен доступа или передан невалидный токен доступа. Необходимо инициировать повторную аутентификацию пользователя |
403 |
Операция запрещена |
Передан валидный токен, но текущие политики не позволяют выполнить данную операцию. Необходимо проверить настройку политик и выполнение их условий |
500 |
Ошибка обработки запроса на стороне сервера |
Непредвиденная ошибка на стороне сервера. Необходимо выполнить диагностику проблему по логам компонентов |
Код ошибки | Описание | Вероятные причины и поведение клиентского приложения |
---|---|---|
|
Поддержка webauthn выключена в настройках сервера. |
Необходимо включить поддержку webauthn в настройках. Продолжение сценария невозможно. |
Привязка нового ключа
POST {host}/customer-webapi-1.0/webauthn/add
Content-Type: application/json
Accept: application/json
{
"continuationKey": "...",
"attestation": "...",
"clientData": "..."
}
Переменная | Описание | Комментарий |
---|---|---|
|
Токен поддержания сценария |
Передается последний полученный от сервера |
|
Base64-кодированная строка |
|
|
Base64-кодированная строка |
HTTP 200 OK
{
"status": "done"
}
Наличие в ответе поля status
со значением done
означает, что публичный
ключ успешно привязан к идентификатору пользователя.
Наличие в ответе поля status
со значением error
означает, что при выполнении запроса произошла логическая ошибка. Формат ответа и возможные ошибки приведены ниже.
В зависимости от типа и причины ошибки, информация о ней может вернуться как в виде соответствующего HTTP Status, так и в виде значения параметра form.errors
при HTTP Status=200. В последнем случае ответ от сервера будет иметь следующий формат:
{
"continuationKey": "b409234f-25b5-4e7f-b632...",
"approvalInfo": {
"serverNonce": "1234",
"rpId": "uidm.ru"
},
"form": { ... },
"status": "approval_required"
}
Переменная | Описание | Комментарий |
---|---|---|
|
Токен поддержания сценария |
Должен передаваться на сервер при следующем обращении в рамках сценария. Является полным аналогом параметра |
|
Атрибуты сценария для использования на стороне клиентского приложения. |
Содержит различные параметры, необходимые клиентскому приложению для работы в рамках сценария. На данном шаге важны параметры Примечание: значение |
|
Машинное описание формы ввода для данного шага сценария. |
Аналогично атрибуту |
|
Статус ответа. |
На данный момент для этого запроса может принимать значения |
Код возврата | Описание | Вероятные причины и поведение клиентского приложения |
---|---|---|
400 |
Некорректный запрос |
Ошибка реализации протокола. Пользователю необходимо сообщить о невозможности выполнения операции. |
401 |
Пользователь неавторизован |
Не передан токен доступа или передан невалидный токен доступа. Необходимо инициировать повторную аутентификацию пользователя |
403 |
Операция запрещена |
Передан валидный токен, но текущие политики не позволяют выполнить данную операцию. Необходимо проверить настройку политик и выполнение их условий |
500 |
Ошибка обработки запроса на стороне сервера |
Непредвиденная ошибка на стороне сервера. Необходимо выполнить диагностику проблему по логам компонентов |
Код ошибки | Описание | Вероятные причины и поведение клиентского приложения |
---|---|---|
|
Проверка attestation завершилась ошибкой. |
Необходимо предложить пользователю повторить операцию. |
|
Предъявленный сертификат клиента уже используется для какой-то учетной записи. |
Необходимо сообщить пользователю о том, что устройство уже привязано и при необходимости, сначала отвязать устройство, а потом привязать его заново. |
Аутентификация на основе протокола WebAuthN
Инициализация сценария аутентификации
POST {host}/sso/oauth2/access_token
Content-Type: application/x-www-form-urlencoded
Accept: application/json
client_id=oauth_front_m2m&
client_secret=password&
realm=/customer&
grant_type=urn:roox:params:oauth:grant-type:m2m&
service=login-by-webauthn
Запрос выполняется с предоставлением клиентского токена доступа.
Переменная | Описание | Комментарий |
---|---|---|
|
Идентификатор приложения |
|
|
Секрет приложения |
Передается, если сейчас работает m2m-протокол. Если |
HTTP 200 OK
{
"execution": "b409234f-25b5-4e7f-b632...",
"view": {
"serverNonce": "1234",
"rpId": "uidm.ru",
},
"form": { … },
"step": "webauthn-assertion"
}
Наличие в ответе поля step
со значением webauthn-assertion
означает, что
можно перейти к шагу API: Аутентификация с открытым ключом из ключевой пары.
Из ответа на запрос необходимо сохранить значение атрибута execution
.
Также из ответа необходимо сохранить значения полей rpId
и serverNonce
и передать их в качестве значений для параметров rpId
и challenge
методу
create()
WebAuthN.
Аутентификация с открытым ключом из ключевой пары
POST {host}/sso/oauth2/access_token
Content-Type: application/x-www-form-urlencoded
Accept: application/json
client_id=oauth_front_m2m&
client_secret=password&
realm=/customer&
grant_type=urn:roox:params:oauth:grant-type:m2m&
execution=<1234>&
_eventId=next&
credentialId=<ABCD>&
authenticatorData=<ABCD>&
clientData=<ABCD>&
userHandle=<sso_1>&
signature=<ABCD>
Значения параметров execution
, credentialId
, authenticatorData
, clientData
, userHandle
, signature
даны условно.
Переменная | Описание | Комментарий |
---|---|---|
|
Идентификатор приложения |
|
|
Секрет приложения |
Передается, если сейчас работает m2m протокол. Если |
|
Токен поддержания сценария |
Передается последнее значение |
|
Идентификатор |
|
|
Base64-кодированная строка |
|
|
Base64-кодированная строка |
|
|
UTF-8 cтрока |
Пример преобразования: var decoder = new TextDecoder("utf-8"); function ab2str(buf) { return decoder.decode(new Uint8Array(buf)); } Кодировка Base64 здесь не используется, поскольку это строка в кодировке UTF-8. |
|
Base64-кодированная строка |
HTTP 200 OK
{
"access_token": "..."
}
Наличие токена доступа (access_token
) в теле ответа означает, что
аутентификация выполнена успешно.
Установка и настройка функциональности
Используемые компоненты
Для работы функциональности требуется использование компонента customer-webapi. Запросы, адресованные на URL вида {host}/customer-webapi-1.0/webauthn должны перенаправляться на данный компонент
Настройки функциональности
Для работы функциональности необходимо выполнить следующие настройки:
Добавить следующие свойства для работы WebAuthN в файл с настройками:
# описание: выключатель функциональности входа по webauthn
# тип данных: boolean
# единицы измерения: нет
com.rooxteam.sso.login-by-webauthn.enabled=true
# описание: выключатель функциональности входа по webauthn для указанного {realm}
# тип данных: boolean
# единицы измерения: нет
com.rooxteam.sso.{realm}.webauthn.enabled=true
# описание: значение Relying Party Identifier согласно спецификации WebAuthn. Содержит домен сервиса UIDM
# тип данных: string
# единицы измерения: нет
# ограничения: валидный домен
com.rooxteam.sso.{realm}.webauthn.rpId={sso.public.host}
# описание: список origins, согласно спецификации WebAuthn
# тип данных: массив строк, разделенных запятой
# единицы измерения: нет
# ограничения: список валидных URL
com.rooxteam.sso.{realm}.webauthn.origins={sso.public.protocol}://{sso.public.host}
# описание: список поддерживаемых алгоритмов для публичных ключей, согласно спецификации WebAuthn
# тип данных: массив строк
# единицы измерения: нет
com.rooxteam.sso.{realm}.webauthn.pubKeyAlgs=-7,-257
Параметр | Описание | Пример |
---|---|---|
|
Название реалма, для которого выполняются настройки. |
|
|
Публичный домен сервиса UIDM |
|
|
Протокол для обращения к UIDM из публичной сети. |
|
Убедиться, что уже настроены и при необходимости настроить следующие общие настройки взаимодействия customer-webapi и sso-server:
# описание: Внутренний (для обращения внутри сети между компонентами) URL sso
# тип данных: url
# единицы измерения: нет
# ограничения: валидный url
com.rooxteam.aal.sso.endpoint={sso.private.url}
# описание: Имя клиента для аутентификации запросов к sso-server.
# тип данных: string
# единицы измерения: нет
# ограничения: нет
com.rooxteam.aal.auth.client={client_id}
# описание: Пароль клиента для аутентификации запросов к sso-server
# тип данных: string
# единицы измерения: нет
# ограничения: нет
com.rooxteam.aal.auth.password={client_password}
# описание: Пароль клиента с указанным именем для аутентификации обращений к sso-server.
# тип данных: string
# единицы измерения: нет
# ограничения: нет
com.rooxteam.aal.auth.client.{client_id}.password={client_password}
# описание: имя куки, в которой передается токен доступа
# тип данных: string
# единицы измерения: нет
# ограничения: валидное имя куки
com.rooxteam.aal.sso.token.cookie.name={cookie_name}
Параметр | Описание | Пример |
---|---|---|
|
Внутренний домен сервиса UIDM. Может совпадать или отличаться от публичного, в зависимости от того, как устроена маршрутизация запросов внутри решения |
|
|
Имя oauth2-агента, для аутентификации запросов к sso |
|
|
Пароль (secret) oauth2-агента, для аутентификации запросов к sso |
|
|
Имя cookie, в которой передается токен доступа |
|
Добавить следующие политики в настройках sso-server:
<Policy name="webauthn-add" referralPolicy="false" active="true">
<Rule name="urlScopeRule">
<ServiceName name="iPlanetAMWebAgentService"/>
<ResourceName name="/webauthn/add"/>
<AttributeValuePair>
<Attribute name="POST"/>
<Value>allow</Value>
</AttributeValuePair>
</Rule>
<Subjects>
<Subject name="AuthUsers" type="AuthenticatedUsers"/>
</Subjects>
<Conditions>
<Condition name="authLevelCondition-customer" type="AuthLevelCondition">
<AttributeValuePair>
<Attribute name="AuthLevel"/>
<Value>/customer:5</Value>
</AttributeValuePair>
</Condition>
</Conditions>
</Policy>
<Policy name="certificateCredentials" referralPolicy="false" active="true">
<Rule name="urlScopeRule">
<ServiceName name="iPlanetAMWebAgentService"/>
<ResourceName name="/certificateCredentials"/>
<AttributeValuePair>
<Attribute name="DELETE"/>
<Value>allow</Value>
</AttributeValuePair>
</Rule>
<Subjects>
<Subject name="AuthUsers" type="AuthenticatedUsers"/>
</Subjects>
<Conditions>
<Condition name="authLevelCondition-customer" type="AuthLevelCondition">
<AttributeValuePair>
<Attribute name="AuthLevel"/>
<Value>/customer:0</Value>
</AttributeValuePair>
</Condition>
<Condition name="multi-spelTokenCondition" type="SpelTokenCondition">
<AttributeValuePair>
<Attribute name="allow-if"/>
<Value>
(token['claims'].containsKey('roles')
and (token['claims']['roles'].contains('ROLE_SYSTEM')))
</Value>
</AttributeValuePair>
<AttributeValuePair>
<Attribute name="denied-advice"/>
<Value>Wrong user</Value>
</AttributeValuePair>
</Condition>
</Conditions>
</Policy>