Аутентификация через OAuth 2.0

В этой статье:

Общие сведения

OAuth 2.0 - это протокол авторизации для предоставления доступа сторонним приложениям к защищенным ресурсам.

Протокол используется для аутентификации пользователей. Сторонним приложением является Universe MDM, защищенным ресурсом – информация о пользователе.

OAuth 2.0 предполагает разные способы получения токена доступа сторонним приложением. Самым безопасным и общепринятым стандартом для веб-приложений является Authorization Code Grant. Добавлена поддержка только этого способа.

Спецификация OAuth 2.0

Аутентифицированные пользователи создаются OAuth 2.0 провайдером (имплементацией интерфейса из SDK backend) после получения информации о пользователе из внешней системы (логин/email/фио/имена ролей/т.п.).

Авторизации OAuth 2.0 в Universe MDM

Пример добавления OAuth 2.0 провайдера вместе с инструкцией по использованию можно найти здесь.

Предусловие:

  • Присутствует зарегистрированный клиент (веб-приложение Universe MDM) на сервере авторизации.

  • Клиент указал корректный redirect URI (callback URI), который должен перенаправить запрос пользователя на GET http://your-universe-mdm.org/universe-backend/api/v2/core/authentication/login?source=oauth2

В Universe MDM на backend должен быть зарегистрирован OAuth 2.0 provider. Это может быть сделано двумя способами:

  • Вручную в коде из одного модулей, вызовом метода com.universe.mdm.rest.v1.oauth2.service.OAuth2Service#register.

  • Автоматически через загрузку библиотеки с классом, который реализует интерфейс com.universe.mdm.rest.v1.oauth2.type.OAuth2Provider, согласно контракту, который указан в Javadoc этого интерфейса (интерфейс находится в SDK).

    • Такие библиотеки на экране библиотек будут иметь "OAuth 2.0 провайдер" в колонке "Тип".

    • При удалении этой библиотеки этот OAuth 2.0 провайдер будет удален (т.е. аутентифицироваться через него будет нельзя).

    • При перезагрузке Universe MDM все имеющиеся библиотеки с типом "OAuth 2.0 провайдер" будут автоматически регистрировать свои провайдеры.

Интерфейс OAuth2Provider содержит следующие методы:

  • getId(): String - возвращает ID провайдера;

  • getClientId(): String - возвращает client ID веб-приложения на сервере авторизации;

  • getClientSecret(): String - возвращает client secret веб-приложения на сервере авторизации;

  • getScope(): String - возвращает список разрешений, требуемых веб-приложениям для получения информации о пользователе (необязательная часть, зависит от сервера авторизации);

  • isAuthRequest(HttpRequest): boolean - метод должен вернуть true, если запрос к backend является запросом, инициирующим авторизацию через этот провайдер;

  • getAuthorizationUri(): String - возвращает URI для авторизации, по которому будет перенаправлен пользователь для подтверждения своего желания предоставить свои данные веб-приложению и для получения кода авторизации;

  • getTokenUri(): String - возвращает URI для получения токена доступа веб-приложения, используя полученный код авторизации;

  • isBasicAuthorizationForTokenRequest(): boolean - определяет способ авторизации в запросе получения токена доступа (передавать client secret через Basic Authorization Scheme или через form);

  • getUserInfoUri(): String - возвращает URI для получения информации о пользователе с сервера ресурсов, используя полученный токен доступа (сервер ресурсов и сервер авторизации могут быть и разными серверами, и одним сервером);

  • extractUserInfo(HttpResponse): OAuth2UserInfo - конвертирует ответ запроса получения информации о пользователе в объект, с которым будет работать веб-приложение.

Схема работы

С точки зрения backend

../../../_images/oauth1.png

Исходник

  1. Для старта аутентификации через OAuth 2.0 должен быть послан на backend [любой запрос, который требует авторизации, или запрос логина] с параметрами sso=true и oauth2=<providerId> в query string.

  2. Для всех зарегистрированных провайдеров будет проверено равенство значения query параметра oauth2 и ID провайдера:

  • Если ID ни одного провайдера не равно значению query параметра oauth2, то будет прислан ответ об отсутствии токена авторизации.

  • Первый провайдер, ID которого равно значению query параметра oauth2, будет использован для ответа с перенаправлением (HTTP code 302) на сервер авторизации.

    • Перенаправление будет по адресу getAuthorizationUri() провайдера, с указанием client ID, scope, state (случайно сгенерированная строка) и redirect URI (из параметра com.universe.mdm.rest.v1.oauth2.redirect.address, если он там указан).

  1. Пользователь, перенаправленный на сервер авторизации, должен выдать разрешение на доступ к своим данным, в результате чего сервер авторизации перенаправит его обратно в веб-приложение:

  • Если пользователь однажды уже выдавал разрешение, то повторное разрешение может не требоваться (зависит от сервера авторизации).

  • В перенаправленном запросе должны присутствовать в query string параметры code (код авторизации) и state (равен state из предыдущего перенаправления).

  1. После этого веб-приложение отправляет на сервер авторизации запрос на получение токена доступа.

  • Для этого отправляется запрос по адресу POST getTokenUri() провайдера, используя client ID, client secret и code (в теле запроса в виде form) с заголовком Accept: application/json.

  1. Полученный токен доступа используется для получения информации о пользователе.

  • Посылается запрос GET getUserInfoUri() провайдера, используя токен доступа.

  • полученный ответ передаётся в extractUserInfo(HttpResponse) провайдера, где для платформы заполняется информация о пользователе (логин/email/имена ролей/т.п.).

  1. Полученный пользователь сохраняется в БД как внешний или обновляется, если уже существовал.

  2. Генерируется внутренний токен для авторизации и посылается в ответе пользователю.

  • Внутренний токен действителен согласно обычным параметрам (org.unidata.mdm.core.security.token.ttl).

  • Отзыв токена доступа к серверу авторизации никак не подействует на внутренний токен.

С точки зрения frontend

  1. Для старта аутентификации незалогиненный пользователь должен перейти на любую страницу приложения, в query-параметрах которой присутствуют sso=true, ssoType=oauth2 и oauth2ProviderId=<providerId>. Например: http://localhost:8082/?sso=true&ssoType=oauth2&oauth2ProviderId=<providerId>#/dashboard.

  2. На backend посылается запрос GET universe-backend/api/v2/core/authentication/login с query-параметрами sso=true, source=oauth2, oauth2=<providerId> и locale=<текущая локаль>.

  3. Пользователь перенаправляется на страницу логина внешнего сервиса.

  4. Пользователь выполняет вход на стороннем сервисе.

  5. Пользователь перенаправляется на callback-страницу приложения. В ней обязательно должны присутствовать query-параметры sso=true, ssoType=oauth2 и oauth2Callback=true:

  • Если также присутствуют параметры code и state, то на backend посылается GET universe-backend/api/v2/core/authentication/login c query-параметрами source=oauth2, code=<code> и state=<state>.

    • Если этот запрос вернул данные пользователя, то будет произведен вход и перенаправление на страницу из пункта 1.

    • Если этот запрос не пройдет, то будет отображена ошибка.

  • Если также присутствуют параметры error и error_description (например, если пользователь на пункте 4 запретил доступ к своим данным), то будет отображена ошибка.

  • Иначе пользователь будет перенаправлен на главную страницу приложения и будет отображена форма логина.

SDK

BE

Необходимые поля имплементации OAuth 2.0 провайдеров классы вынесены в SDK.

В этом SDK содержатся следующие классы:

  • com.universe.mdm.rest.v1.oauth2.type.OAuth2Provider

  • com.universe.mdm.rest.v1.oauth2.type.HttpResponse

  • com.universe.mdm.rest.v1.oauth2.type.OAuth2UserInfo

FE

Добавлен User-exit SsoAuthTypeManager, который позволяет добавлять поддержку различных типов SSO-авторизаций. User-exit активируется с присутствием query-параметра sso=true. Выбор между типами авторизации происходит исходя из присутствия и значения query-параметра ssoType.