В этой статье попробуем кратко описать, чем отличаются разные типы grant flow.

Authorization code flow

Используется в мобильных и десктопных приложениях, чтобы получить доступ к ресурсам, таким как web API, выполняя sign in пользователя. Позволяет выполнять как аутентификация, так и авторизацию в SPA, web apps и native apps.

Этот поток позволяет безопасно получить access_tokens, который можно использовать для доступа к ресурсам, а также получать refresh tokens для продления access token и ID tokens для вошедшего пользователя.

Используются 2 эндпоинта: /authorize и /token. В запросе кода авторизации необходимо использовать следующие параметры (перечислены не все, но те, что характерны для данного потока):

  • response_type=code, response_type=id_token или response_type=token — показывает тип потока авторизации. id_token используется для hybrod flow.
  • prompt=loginnoneconsent, and select_account. Устанавливает тип взаимодействия с пользователем — интерактивный или неинтерактивный.
  • code_challenge — требуется для PKCE. Рекомендуется для всех приложений для повышения безопасности.
  • code_challenge_method=S256 или plain — метод шифрования, используемый в PKCE.
  • scope — список групп прав, которые разрешены

Гибридный поток авторизации

Это микс implicit grant и authorization code flow, который обычно используется веб приложениями и SPA, которые хотят отрисовать страницу для пользователя без блокировки исполнения кода, что уменьшает задержку.

Главное отличие от authorization code заключается в том, что используется ID token, для чего указывается новые scope, response_type и nonce.

Пример такого запроса авторизации:

https://login.microsoftonline.com/{tenant}/oauth2/v2.0/authorize?
client_id=6731de76-14a6-49ae-97bc-6eba6914391e
&response_type=code%20id_token
&redirect_uri=http%3A%2F%2Flocalhost%2Fmyapp%2F
&response_mode=fragment
&scope=openid%20offline_access%20https%3A%2F%2Fgraph.microsoft.com%2Fuser.read
&state=12345
&nonce=abcde
&code_challenge=YTFjNjI1OWYzMzA3MTI4ZDY2Njg5M2RkNmVjNDE5YmEyZGRhOGYyM2IzNjdmZWFhMTQ1ODg3NDcxY2Nl
&code_challenge_method=S256
response_typeДобавление id_token показывает серверу, что приложение хочет ID token в от вете от /authorize.
scopeДля ID tokens должны быть включены следующие скоупы — openid или опционально profile и email.
nonceЗначение, включенное в запрос, созданный приложением, который будет включен в результирующий id_token как клейм. Приложение может проверить это значение, чтобы уменьшить возможность атаки с повторным использованием токена. Значение обычно представляет собой случайную уникальную строку, которая может использоваться для определения источника запроса.
response_modeЗадает метод, который следует использовать для отправки полученного токена обратно в ваше приложение. По умолчанию используется query для получения кода авторизации, но используется fragment если запрос включает id_token в response_type.
Claims в гибридном потоке авторизации

Получение токена доступа

После получения кода авторизации для пользователя с его правами, можно использовать для получения access_token для доступа к нужному ресурсу. Это делается POST запросом к эндпоинту /token:

client_id=6731de76-14a6-49ae-97bc-6eba6914391e
&scope=https%3A%2F%2Fgraph.microsoft.com%2Fmail.read
&code=OAAABAAAAiL9Kn2Z27UubvWFPbm0gLWQJVzCTE9UkP3pSx1aXxUjq3n8b2JRLk4OxVXr...
&redirect_uri=http%3A%2F%2Flocalhost%2Fmyapp%2F
&grant_type=authorization_code
&code_verifier=ThisIsntRandomButItNeedsToBe43CharactersLong 
&client_secret=JqQX2PNo9bpM0uEihUPzyrh

Здесь ключевые параметры:

  • code = коду авторизации, полученный ранее
  • grant_type = authorization_code
  • client_secret = секретный ключ клиента (требуется для confidential web apps только). Ключ хранится на стороне сервера приложения. Нельзя использовать для native apps или SPA, где не обеспечивается безопасность хранения ключа на устройстве.
  • code_verifier = тот же код, который использовался для получения authorization_code, рандомная строка 43 символа.

В случае, если вместо секрета в виде ключа используется сертификат, то client_secret заменяется на client_assertion в виде JSON токена, который вы подписываете сертификатом, добавленным в зарегистрированное AAD приложение.

В ответ на такой запрос мы получаем JSON токен, содержащий access_token, refresh_token и id_token (в формате JWT). Приложение может декодировать id_token и получить из него информацию о пользователе, для которого он был выдан. Конфиденциальные клиенты могут использовать id_token для дальнейшей авторизации в ресурсе. Важно отметить, что id_token будет выдан, только если в запросе был указан scope = openid.

Получение токена обновления

Поскольку access_tokens имеют короткое время жизни, то для продления доступа используются refresh_token. Это такой же токен, который получается точно также POST-запросом к /token. Один дают такие же права, как и access_token, которые были указаны ранее в scope. В нашем примере scope=mail.read, то есть читать почтовый ящик пользователя, который авторизовался.

Время жизни токена обновления по умолчанию — 90 дней. Его можно сократить путем настройки политики. Но в SPA приложениях refresh token истечет через 24 часа.

Сlient credentials flow

Это второй по популярности поток авторизации. В отличие от предыдущего случае, где происходит аутентификация пользователя и выдача токена доступа согласно delegated permisions в приложении, здесь доступ к ресурсу выполняется с помощью identity самого приложения, то есть SPN, зарегистрированный для него в AAD.

Такой способ авторизации чаще всего используется для взаимодействия между серверами, где какая-то задача должна выполняться в фоновом режиме и что-то читать из или писать в Azure AD запросами со стороны внешнего сервера. Пользователь в процессе аутентификации не участвует. Такие типы приложений называют демонами или сервисными аккаунтами.

Стандарт OAuth 2.0, описывающий этот тип grant flow, позволяет web сервису (это конфиденциальный клиент) использовать свои собственные учетные данные вместо имперсонирования пользователя при обращении к другому веб сервису. Также, как и в прошлом случае, клиент может использовать секретный ключ или сертификат, причем последний рекомендуется как более безопасный вариант. Поскольку такой тип авторизации более опасный, он позволяет приложению самостоятельно делать любые действия согласно правам, ему выданным. Именно поэтому нельзя публиковать учетные данный в коде приложения или встраивать их во фронтенд страницы, и уж тем более встраивать в код публичного приложения.

Как было уже сказано, внешнее приложение выполняет действия в Azure AD напрямую с правами, ему выданными администратором тенанта. Естественно, администратор должен сделать выдать согласие (consent) на права заранее.

В этом потоке используются два эндпоинта — /token и /adminconsent. Причем, если администратор тенанта дал согласие заранее, то /adminconsent фактически не используется кодом приложения. Приложение напрямую получает токен через /token, предоставив client_secret или client_assertion в запросе.

Сам по себе запрос токена (access_token) ничем не отличается от предыдущих примеров, кроме одного — используется другое значение grant_type, которое равно client_credentials. Также не требуется реальный Redirect URI, так как токен не надо отправлять на какой-то другой web сервис или Web API, его получает само приложение его запросившее.

Приведу пример получения токена доступа с помощью curl в Linux:

curl -X POST -H "Content-Type: application/x-www-form-urlencoded" -d 'client_id=535fb089-9ff3-47b6-9bfb-4f1264799865&scope=https%3A%2F%2Fgraph.microsoft.com%2F.default&client_secret=qWgdYAmab0YSkuL1qKv5bPX&grant_type=client_credentials' 'https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token'

Теперь можно получить доступ к ресурсу, например, Graph API:

curl -X GET -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbG...." 'https://graph.microsoft.com/v1.0/me/messages'

Microsoft настоятельно рекомендует использовать библиотеку MSAL для такого взаимодействия.

Implicit grant flow

Такой поток описан в спецификации OAuth 2.0, но не является рекомендуемым. Дело в том, что в этом сценарии можно получить id_token и access_token напрямую от /authorize эндпоинта, а не от /token, что представляет собой угрозу безопасности.

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

Сейчас можно использовать данный метод в гибридном сценарии, описанном выше. В таком случае мы получает только id_token, но не access_token. Более-менее безопасно использовать id_token implicit grant в начальной стадии аутентификации пользователя через браузер. В этом случае чаще всего используется протокол OpenID Connect поверх OAuth для аутентификации пользователя.

Чтобы включить поддержку implicit grant или hybrid flow нужно в вашем App registration в разделе Authentication > Implicit grant and hybrid flows поставить галки напротив Access tokens и/или ID tokens (одну эту галку, если нужен hybrid flow). Тоже самое можно настроить через манифест файл регистрации.

Что касается самого OpenID Connect запроса, то в нем используются следующие параметры:

  • response_type=id_token
  • scope=openid
  • response_mode=fragment (опционально, если используется response_type=id_token)
  • nonce=678910 — рандомное значение, генерируемое приложением для проверки перехвата и подмены токена.

Если у вас полный implicit grant, response_type=token, что позволяет получить access token сразу.

Refresh токены в этой сценарии не используются, по истечении времени жизни текущего токена надо просто запросить новый.

Device authorization grant flow

Этот сценарий придуман для устройств, не имеющих браузера и возможности интерактивного логона, таких как Smart TV, IoT и принтеры. Чтобы активировать такой поток авторизации, устройство отправляет пользователя посетить определенную веб страницу в браузере пользователя на другом устройстве, после чего включается возможность получить access tokens и refresh tokens.

В этом потоке используются два других эндпоинта: /devicecode и /token.

Работает это так. Клиентское приложение сообщает /devicecode client_id и scope, а в ответ получает код для устройства и пользователя, а также URI, на который должен перейти пользователь для подтверждения. С этого момент у пользователя 15 минут, чтобы предоставить user_code логин-серверу.

Клиентское приложение показывает URL пользователю, который надо посетить. Device_code содержит строку, которая подтверждает сессию между устройством и сервером авторизации. С этим кодом клиентское приложение может получить access token. Как только пользователь подтвердил пользовательскую сессию, клиентское приложение запрашивает токен, используя device_code.

On-Behalf-Of flow (OBO)

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

Фактически, это расширение authorization code grant flow и других подобных сценариев с аутентификацией пользователя. Дополнительную часть работы делает промежуточное приложение или API. оно, используя имеющийся access token, client id и секрет запрашивает у AAD новый access token уже для второго конечного веб приложения. Информация о конечном токене первое приложение передает второму в хедере авторизации.

Отличия от предыдущих сценариев в списке claims:

  • grant_type = urn:ietf:params:oauth:grant-type:jwt-bearer
  • assertion = access token для первого приложения
  • requested_token_use = on_behalf_of

Пример POST запроса токена с использованием скоупа Graph API user.read:

grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer
&client_id=2846f71b-a7a4-4987-bab3-760035b2f389
&client_secret=BYyVnAt56JpLwUcyo47XODd
&assertion=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6InowMzl6ZHNGdWl6cEJmQlZLMVRuMjVRSFlPMCJ9.eyJhdWQiOiIyO{a lot of characters here}
&scope=https://graph.microsoft.com/user.read+offline_access
&requested_token_use=on_behalf_of

Пример использования токена для авторизации на конечном сервисе через authorization header:

GET /v1.0/me HTTP/1.1
Host: graph.microsoft.com
Authorization: Bearer eyJ0eXAiO ... 0X2tnSQLEANnSPHY0gKcgw

Примечательно, что данный сценарий позволяет использовать смешанные типы токенов, в частности позволяет отправлять SAML токены на конечный сервис, если тот его принимает в неинтерактивном режиме.

Для такого вариант регистрируется дополнительное SAML приложение в Azure AD, и его App ID URI используется в запросе на конечный токен.

В ответе промежуточное приложение получит от AAD SAML assertion в качестве access token для конечного сервиса.

Resource Owner Password Credentials (ROPC)

Этот сценарий является не безопасным, а потому не рекомендуется для публичного использования. В нем в качестве запроса на получение токена используется логин и пароль пользователя в открытом виде, передаваемые как параметры запроса напрямую к точке /token.

Конечно, это будет работать только для облачных аккаунтов и не пригодно для federated аккаунтов. В параметрах запроса также указывается grant_type=password.

Этот сценарий можно использовать в исключительных случаях, например, когда нет подходящего scope в Graph API (например, для запроса aplication proxy).

Также этот сценарий не поддерживает MFA, если он включен для пользователя — запрос будет заблокирован.

Насколько полезен этот пост?

Кликните на звезду, чтобы оценить!

Средний рейтинг 0 / 5. Количество голосов: 0

Еще нет голосов. Будь первым!

Поделиться:
Помечено %1$s , , , , , , , , , ,

Написано автором Александр Д.