Вы когда‑нибудь логинились на сайте, используя аккаунт Google или Facebook? Или подключали приложение, требующее доступа к GitHub? Если да, то вы уже сталкивались с OAuth2, зная того или нет.
OAuth2 — наиболее популярный и расширяемый фреймворк авторизации. Он позволяет интегрировать различные системы, делегируя доступ к вашим данным одного сервиса другому. Но фишка в том, что большая часть людей понятия не имеет, как именно OAuth2 на самом деле работает.
Лично мне приходилось реализовывать несколько приложений, использующих OAuth2. Это было настолько прямолинейно, что мне даже не пришлось задумываться о том, как работает сам протокол. И это не случайно. OAuth2 сделан таким образом, чтобы его было легко добавить в приложение, а не воевать со сложными механизмами авторизации.
Но если мы немного остановимся и начнем копать глубже, то найдем для себя много нового с точки зрения дизайна ПО.
В этой статье мы раскроем причины, по которым были приняты те или иные решения в процессе дизайна протокола OAuth2 и разберем наиболее часто встречаемые гранты авторизации.
Бэкграунд
Будет полезно начать с некой исторической справки на тему того, какая проблема стояла при создании OAuth2 и какие альтернативы существовали бы без него.
Предположим, мы хотим создать user‑friendly платформу для разработки наподобие Vercel или Fly.io. И сразу же сталкиваемся с основной проблемой: как наши клиенты будут импортировать свой код на нашу платформу?
В наше время почти все используют Git. Мы могли бы попробовать добавить функционал Git‑хостинга прямо в нашу платформу, но это достаточно крупная задача, в то время как основная задача нашего бизнеса — управление ресурсами, автоскейлинг, распределение нагрузки и тому подобное. Более того, большая часть наших клиентов скорее всего уже используют одну из уже существующих платформ наподобие GitHub, GitLab или Bitbucket. К сожалению, у нас нет способа убедить эти платформы интегрироваться с нами.
Какие у нас есть варианты? Как бы мы могли обеспечить клиентам доступ к Git‑репозиториям на сторонних сервисах?
Обмен учетными данными пользователя
Наши клиенты используют свои данные для входа в свои платформы Git‑хостинга. Не могут ли они просто поделиться этими данными с нами?
Мы могли бы безопасно хранить эти данные и при необходимости авторизовываться в сервисе Git за них, использовать их куки сессий и получать нужные репозитории.

На первый взгляд звучит как достаточно очевидный способ — позволить нашей платформе работать с данными клиентов, даже когда они не в сети.
Но становится понятно, что у такого подхода куча проблем:
Отсутствие контроля доступа. У платформы есть полный доступ ко всему, что доступно нашим клиентам без возможности ограничить или управлять этим, даже если нам просто нужен доступ к репозиториям.
Отсутствие контроля сессий. Сложно определить, какая сессия была создана пользователем, а какая платформой. Если процесс авторизации одинаков для обоих видов, сложно реализовать такие вещи как мультифакторную авторизацию.
Сложно отозвать. После получения, доступы могут быть кэшированы и слиты неожиданным способом, даже если они не появляются в интерфейсе платформы. Единственный способ отозвать доступ — сменить пароль.
Хрупкость. Смена пароля по сути сломает доступ платформы к данным.
Риски безопасности. Платформе придется безопасно хранить полученные данные, а это достаточно серьезная ответственность. Если с ними обращаться неосторожно, может произойти утечка и аккаунт клиента будет под угрозой.
Использование одного набора данных с полным доступом для абсолютно разных целей это проблема. Что же нам делать?
Personal Access Tokens
Оказывается, если мы хотим поступить лучше, нам надо избежать использования основных данных авторизации. Вместо этого мы могли бы создать альтернативный вид данных исключительно для использования в интеграциях приложений.
Назовем их Personal Access Tokens (PATs). Представьте себе статическую секретную строку с относительно долгим временем жизни. Технически, у каждого PAT может быть кастомный набор прав, ограничивая тем самым, что именно платформа может делать с соответствующими данными.
При интеграции Git‑репозитория с новым сервисом, как например с нашей платформой, генерировался бы новый токен с необходимыми правами и передавался сервису.

Подобный подход это значительное улучшение по сравнению с передачей обычных данных авторизации, поскольку решает основные проблемы того подхода. Но все еще остаются некоторые детали, которые стоит учитывать:
Отслеживание дат протухания и замена токенов быстро надоедает по мере увеличения их количества.
Чтобы облегчить прошлую задачу, можно увеличить время жизни токена (на месяцы или даже годы). Но в таком случае, к сожалению, если токен скомпрометирован, у злоумышленников будет полно времени чтобы наворотить дел.
Но нужно ли клиентам управлять своими токенами для каждого используемого сервиса и интеграции? Можем ли мы упростить это еще сильнее, чтобы клиентам нужно было делать как можно меньше для подключения новых интеграций, а сам процесс был автоматизирован той стороной, которой это нужно?
PAT все еще актуальны
Токены персонального доступа все еще широко используются в индустрии.
Они упрощают жизнь пользователям, давая возможность быстро сгенерировать несколько токенов в UI и запихнуть их в клиент сервера ресурса для автоматизации некоторых задач. Это гораздо проще, чем вручную проходить процесс OAuth2.
В свою очередь, процесс OAuth2 создан для запроса доступа у пользователя приложениями.
Вот для этого нам и нужен фреймворк наподобие OAuth.
Что такое OAuth2?
OAuth2 это фреймворк, который определяет, как доступ или разрешения запрашиваются или делегируются одной сущностью (например, пользователем) сторонним приложениям.
Авторизация или делегирование?
Термин «авторизация» сильно перегружен в контексте OAuth2, так что давайте проясним.
Суть OAuth2 в том, чтобы позволить владельцу ресурса авторизовать (дать согласие) другое приложение на получение части ваших прав доступа. По сути это процесс делегирования полномочий.
Основная идея OAuth2 в том, чтобы дать пользователям возможность решать, какие приложения (помимо тех, что нативно поддерживаются сервером ресурса) имеют доступ к их данным. Это обеспечивает управляемый и удобный доступ, позволяя таким приложениям использовать ваши данные по мере необходимости без вашего прямого участия, чтобы расширить базовый функционал сервера ресурса. Давайте немного разберем это.
Без таких механизмов как OAuth2, сервер ресурса по сути управляет тем, какие приложения имеют доступ к вашим данным. Я предполагаю, что это происходит путем партнерских соглашений, где две организации договариваются об интеграции в сервисы друг друга (зачастую очень кастомным и нестандартным способом).
Подобный подход централизован, поскольку:
Могут участвовать только приложения разрешенных партнеров.
Все остальное по сути блокируется.
OAuth2 вносит компромисс, позволяя сторонним приложениям использовать сервер ресурса, до тех пор пока пользователи готовы предоставить право на использование их данных или функционала приложения. При использовании такой модели сервер ресурса ничего не решает за конечного пользователя (если только дело не касается блокирования вредоносных приложений для защиты пользователей).
Это создает мощную форму децентрализации, которая:
Позволяет владельцу ресурса расширить функционал сервера в несколько кликов.
Помогает построить экосистему инструментов и приложений вокруг ресурса.
Роли
OAuth2 определяет три основных роли для организации процесса делегирования:
Сервер Ресурса. Это сервис, к которому нужен доступ приложению клиента, либо от имени самого приложения, либо от имени пользователя. В нашем примере это Git‑хостинг GitHub.
Владелец Ресурса (пользователь, если это человек). Сущность, которая имеет права на использование сервера ресурса и может выдавать права доступа приложению клиента.
Сервер Авторизации. Этот сервис выдает токены доступа к ресурсу приложению клиента в обмен на различные способы авторизации.
Приложение Клиента (или клиент, OAuth‑приложение). Приложение или сервис, обращающееся к защищенному серверу ресурса, обычно от имени владельца ресурса.

Общий воркфлоу
OAuth2 добавляет Сервер Авторизации, выступая в качестве посредника между Владельцем Ресурса (у которого есть полномочия) и Приложением Клиента (которому нужна часть этих полномочий). Сервер Ресурса, предоставляющий некий функционал на основе полномочий, доверяет Серверу Авторизации.
Приложение Клиента запрашивает у владельца ресурса определенный набор прав для доступа к Серверу Ресурса.
Шифрование при передаче
Модель безопасности OAuth2 полагается на существование безопасного, TLS‑зашифрованного соединения между Сервером Авторизации и Приложением Клиента. Они обязаны оба использовать HTTPS. Без этого было бы легко перехватить флоу авторизации и украсть данные клиента, коды авторизации, а также токены доступа и обновления.
Владелец Ресурса рассматривает запрос прав и выдает согласие на выдачу доступа приложению клиента посредством сервера авторизации.
Доверие при первом использовании
Сервер Авторизации обычно запоминает согласие, чтобы не спрашивать Владельца Ресурса снова и снова.
После чего Сервер Авторизации выдает право авторизации Приложению Клиента, не показывая экран согласия (так делает GitHub и Bitbucket, в то время как GitLab отдает страницу редиректа).
Обычно Сервер Авторизации просит Владельца Ресурса пройти процесс выдачи прав только если Приложение Клиента изменило перечень запрашиваемых разрешений или прошлое согласие было отозвано.
Подобный подход называется TOFU (trust on first use), он часто используется провайдерами OAuth2 для упрощения флоу выдачи прав.
В зависимости от флоу авторизации, приложение клиента получает право авторизации в той или иной форме и использует их для обмена на токен доступа (или их пару) у Сервера Авторизации.
И наконец, Приложение Клиента использует токен доступа для доступа к Серверу Ресурса от имени Владельца Ресурса.
Сервер Ресурса знает, как проводить валидацию токенов доступа, выданных Сервером Авторизации, обычно за счет внутреннего запроса к Серверу Авторизации.
Клиенты
Путешествие в мир OAuth2 начинается с Клиентских Приложений. Существует два типа Клиентских Приложений, классифицируемых по их способности хранить секреты:
Публичные приложения, такие как JS‑приложения в браузере, десктопные или мобильные приложения. Любые секреты, встроенные в этот тип приложений, могут быть обратно спроектированы и извлечены, даже если вы попытаетесь обфусцировать их дистрибутивы или зашифровать.
Частные (или конфиденциальные) приложения, которые обычно представляют собой веб‑приложения с фронтендом и, что наиболее важно, бэкендом. Бэкенд способен безопасно хранить секреты и устанавливать защищенное взаимодействие с сервером авторизации.
OAuth2 предполагает, что Клиентских Приложений гораздо больше, чем Серверов Авторизации и Ресурсов, поэтому он стремится максимально упростить сторону Клиентского Приложения. Это не только уменьшает объем работы, необходимой для реализации Клиентского Приложения, но и ограничивает возможность создания небезопасных Клиентов.
Основная нагрузка по обеспечению безопасности рабочего процесса OAuth2 ложится на Серверы Авторизации.
Регистрация клиента
Чтобы подключить наши Клиентские Приложения к OAuth2, их сначала необходимо зарегистрировать на Сервере Авторизации.
OAuth2 не делает никаких предположений о том, как должен работать процесс регистрации, но обычно это часть настроек веб‑сайта провайдера OAuth2, например, настройки функционала для создания и управления OAuth‑приложениями.

Форма регистрации обычно включает:
Redirect URL(s) — список разрешенных URL‑адресов для редиректов в интерактивных флоу авторизации, таких как флоу с кодом авторизации или неявный флоу.
Scopes — список делегированного доступа к функционалу Сервера Ресурсов, например, чтение Git‑репозиториев, создание задач и т. д.
Прочая информация, такая как название приложения, иконка, URL‑адреса политики конфиденциальности и условий использования и т. д.
Существуют и другие, менее популярные подходы к регистрации клиентов. Например, я видел:
Регистрацию через внутренние административные запросы к Серверу Авторизации, как в ORY Hydra.
Декларативную регистрацию путем создания Пользовательских Ресурсов Kubernetes в кластере с использованием ORY Hydra Maester.
Динамическая регистрация клиентов
Статическая регистрация клиентов хорошо подходит для конфиденциальных клиентов, но она не является удачным решением для публичных клиентов, поскольку небезопасно размещать общие учетные данные клиента в таких приложениях.
Каждый публичный клиент представляет собой отдельный экземпляр приложения, поэтому было бы удобно назначать им уникальные учетные данные. Таким образом, даже если кто‑то извлечет динамически сгенерированные учетные данные, скоуп будет ограничена конкретной делегацией пользователя.
К счастью, существуют RFC7591 и RFC7592, которые расширяют оригинальный протокол OAuth2, добавляя эндпоинты для динамической регистрации и управления публичными клиентами. Эти стандарты также предоставляют метод для безопасной подписи важной информации об экземпляре OAuth‑приложения, чтобы предотвратить подделку с вредоносными намерениями.
Учетные данные клиента
В конце регистрации вы обычно получаете учетные данные клиента:
Client ID (также известный как App ID) — публичный, несекретный идентификатор вашего Клиентского Приложения.
Client Secret — секретный пароль, который Клиентское Приложение хранит в тайне.
Учетные данные клиента используются для:
аутентификации запросов Клиентского Приложения к серверу авторизации.
привязки конкретного флоу авторизации к Клиентскому Приложению, которое его инициировало. Это гарантирует, что завершить этот флоу с помощью совершенно другого Клиентского Приложения невозможно.
добавления дополнительного уровня защиты к флоу авторизации и полученным токенам обновления, поскольку их нельзя использовать без правильных учетных данных клиента.
Клиентское Приложение отправляет учетные данные в виде заголовка Basic HTTP Authorization.
Basic Authentication
Basic authentication — это очень простая схема аутентификации, встроенная в протокол HTTP:
Authorization: Basic base64(client_id:client_secret)
Base64 — это формат кодирования, а не шифрование, поэтому учетные данные не защищены.
Это нормально, если соединение зашифровано с помощью TLS, а обмен данными происходит через backend‑канал, который невозможно легко перехватить.
Client ID привязан к грантам на авторизацию и токенам обновления, поэтому крайне важно, чтобы он оставался неизменным. Его изменение приведет к аннулированию всех ранее полученных авторизаций (например, токенов обновления).
С другой стороны, секрет клиента можно и нужно периодически менять. Изменение секрета приведет к «ротации» всех токенов обновления, полученных Клиентским Приложением, хотя сами токены не будут затронуты. Это связано с тем, что если учетные данные клиента были скомпрометированы вместе с некоторыми токенами обновления, злоумышленники не смогут получить новые токены доступа, используя старый секрет клиента после его ротации.
Это значительно упрощает процесс ротации секретов, так как вам нужно сменить только один секрет вместо ротации тысяч токенов обновления для каждого конечного пользователя, который когда‑либо авторизовал ваше Клиентское Приложение.
Помимо секретов клиента
OAuth 2.1 указывает, что секрет клиента является необязательным методом аутентификации клиента и предлагает несколько дополнительных вариантов.
В целом, OAuth2 имеет целый реестр, в котором перечислены утвержденные, стандартные и поддерживаемые методы аутентификации токенов (и не только). В настоящее время в список включены следующие методы:
Секрет клиента, передаваемый в заголовке Basic Auth или в теле запроса.
Аутентификация на основе TLS.
Серверы AuthZ
Сервер Авторизации управляет процессом делегирования через множество флоу авторизации (включая потенциально нестандартные) единообразно и в результате выдает специальный токен доступа (или пару токенов). Для этого сервер авторизации должен предоставить как минимум следующие эндпоинты, требуемые протоколом OAuth2:
Эндпоинт авторизации запускает интерактивные флоу, такие как флоу с кодом авторизации или неявный флоу.
Эндпоинт токенов генерирует токены доступа и обновления и используется практически во всех других флоу авторизации.
Эндпоинт устройства, если вы хотите поддерживать флоу устройств.
На практике вам также могут понадобиться другие эндпоинты, которые не определены напрямую в OAuth2:
Эндпоинт интроспекции токенов доступа (имеет собственный RFC) — возвращает метаданные, связанные с данным токеном доступа. Может использоваться серверами ресурсов для проверки входящих токенов доступа.
Эндпоинт отзыва грантов на авторизацию позволяет отозвать все гранты на авторизацию.
Эндпоинт отзыва токенов позволяет отозвать выданные токены доступа и обновления.
и множество других эндпоинтов, представленных в последующих RFC и черновиках, если они вам необходимы.
Обнаружение эндпоинтов
Исторически сложилось так, что эндпоинты сервера авторизации OAuth2 не были фиксированными, и не существовало способа их автоматического обнаружения. Эндпоинты извлекались из документации провайдера и жестко прописывались в библиотеках OAuth2 или клиентских приложениях (вот пример из библиотеки Goth).
Обнаружение метаданных авторизации
Позже люди поняли, что обнаружение метаданных авторизации на самом деле является полезной функцией. Поэтому они обобщили подход к обнаружению из спецификации OpenID Connect в RFC8414, который добавляет новый маршрут для метаданных по подпути.well‑known.
Этот маршрут содержит URL‑адреса эндпоинтов авторизации и токенов, поддерживаемые методы аутентификации токенов, URL‑адрес пользователя OpenID и т. д.
Безопасность
Сервер Авторизации концептуально представлен как отдельный компонент, но протокол не предъявляет требований к тому, как он должен быть реализован внутри. Это может быть как отдельный микросервис, так и часть Ресурсного Сервера.
Одно важное предположение, которое протокол OAuth2 делает неявно, заключается в том, что один Сервер Авторизации потенциально может обрабатывать авторизацию для нескольких Ресурсных Серверов. Это означает, что среди всех компонентов OAuth2 Серверы Авторизации встречаются реже всего. Именно поэтому они отвечают за обработку множества нюансов безопасности, связанных с реализацией Серверов Авторизации. Ваша система OAuth2 по сути настолько безопасна, насколько безопасен ваш Сервер Авторизации.
Токены доступа
Сервер Авторизации генерирует токены доступа в результате успешного завершения процесса авторизации.
Токены доступа — это специальные учетные данные, которые служат альтернативным методом аутентификации для Ресурсного Сервера. Их также можно рассматривать как абстракцию вокруг конкретного процесса авторизации. Сервер Авторизации может поддерживать несколько процессов авторизации, но все они будут приводить к созданию токенов доступа одинакового формата. Это упрощает их проверку для Ресурсного Сервера, которому не нужно знать слишком много информации о том, как именно был получен конкретный токен.

Скоупы токенов доступа
Концепция токена доступа также важна, потому что мы можем генерировать несколько токенов доступа с разными уменьшенными подмножествами изначально запрошенных скоупов. Если бы не было токенов доступа как отдельного типа учетных данных, и мы использовали бы, скажем, код авторизации для этой цели, он бы содержал все скоупы разрешений, запрошенные клиентским приложением на этапе прохождения процесса авторизации.
Типы токенов
OAuth2 не определяет, как должны выглядеть токены доступа. Для Клиентских Приложений (и, вероятно, для Ресурсных Серверов тоже) они представляют собой непрозрачные строки.
Кроме того, когда токен доступа генерируется, Сервер Авторизации указывает, какой тип токена был выдан.
Bearer Токены
Самый распространенный тип токена доступа называется Bearer token.
Bearer Токены
Тип Bearer указывает на то, что клиентское приложение должно использовать токены и передавать их «как есть» в составе заголовка Authorization:
Authorization:Bearer {{ACCESS_TOKEN}}
На практике Серверы Авторизации могут выдавать bearer токены в виде:
Уникальной случайной строки. Эта строка должна быть непредсказуемой и не должна быть возможной для генерации за пределами Сервера Авторизации.
Самостоятельного JWT‑токена, который включает в себя подписанную метаинформацию.
Префиксы Токенов
Если ваши токены представляют собой просто случайные строки, добавление префикса — это разумный шаг. Возьмем, к примеру, GitHub:
Они используют
gho_
для токенов доступа, сгенерированных приложением GitHub OAuth.Они используют
gha_
для токенов доступа, сгенерированных приложением GitHub.Они используют
ghr_
для всех токенов обновления.Префиксы помогают разработчикам клиентских приложений избежать путаницы с различными типами токенов.
Они также помогают GitHub с такими вещами, как автоматическая инвалидация токенов, если кто‑то случайно опубликовал токен в исходном коде. Без префиксов реализация подобной защиты на масштабе GitHub была бы значительно сложнее, особенно если бы токены были просто случайными, неструктурированными строками.
Теоретически возможны и другие типы токенов, но лично я никогда не встречал их на практике.
MAC Tokens
Существует RFC, который определяет токены с Кодом Аутентификации Сообщений (Message Authentication Code, MAC). MAC‑токены были добавлены для решения ситуаций, когда нет простого способа обеспечить SSL‑соединение, и поэтому токены‑носители могут быть перехвачены.
В 2013 году это имело смысл, но в наше время проще использовать токены‑носители и обеспечить шифрование TLS.
Время жизни токена
OAuth2 требует, чтобы Серверы Авторизации генерировали только токены доступа. В таком случае сгенерированный токен считается долгоживущим, и это не очень хорошо по двум причинам:
Токены доступа связаны с клиентским приложением, но они обычно передаются на сервер ресурсов без какого‑либо дополнительного подтверждения владения токеном. Следовательно, если они будут скомпрометированы, злоумышленники получат достаточно времени для их использования.
Токен доступа связан с исходными скоупами, и нет возможности сгенерировать новый токен доступа с подмножеством скоупов без повторного прохождения всего процесса авторизации.
Чтобы решить эти проблемы, лучшей практикой является использование краткосрочных токенов доступа. Вместе с этим можно генерировать отдельный долгоживущий токен, который будет выдавать свежие токены доступа по мере необходимости. Такой тип токена называется токеном обновления.
Proof of Possession, DPoP
Короткий срок жизни и использование TLS — это практически единственные меры защиты, которые есть у токенов доступа. В большинстве случаев этого достаточно, но всё же могут возникать непредвиденные проблемы с реализацией, которые приводят к потенциальной утечке токенов доступа (например, Cloudbleed, утечка токенов OAuth на GitHub). Если вы оказались в такой ситуации, злоумышленники могут попытаться украсть ваши токены доступа и использовать их. Ничто другое не помешает атакующим запрашивать защищенную информацию, поскольку токенов доступа достаточно для доступа к Ресурсному Серверу.
Должно быть что‑то еще, что доказывает, что пользователь токена доступа — это Клиентское Приложение, которое изначально сгенерировало токен.
RFC9449 представил токены JWT с доказательством владения (DPoP), которые могут подтвердить, что текущий токен доступа используется действительным Клиентским Приложением, которое его получило. Клиент подписывает токен DPoP JWT с использованием закрытого ключа, который известен только авторизованному Клиентскому Приложению. Токен DPoP JWT содержит информацию о публичной части ключа, хэше токена доступа, методе запроса ресурса и URL, времени генерации и т. д. Сервер Авторизации привязывает сгенерированный токен доступа к публичному ключу, а Ресурсный Сервер отклоняет все попытки использования токена доступа без валидного токена DPoP JWT, подписанного этим закрытым ключом.
Другой способ доказать владение токеном доступа — это привязка их к сертификату mTLS Клиентского Приложения, как предложено в RFC8705.
Скоупы
Скоупы авторизации — это набор функциональных возможностей, которые Владелец Ресурса делегирует клиентскому Приложению, позволяя клиенту получать доступ к ресурсам так, как если бы он был их первоначальным владельцем.
Политики авторизации vs Скоупы
Владелец Ресурса может делегировать доступ только к тем ресурсам, к которым у него самого есть разрешения. Это важно понимать, поскольку скоупы не имеют ничего общего с политиками авторизации (например, правами пользователя), которые определяют, может ли пользователь вообще получить доступ к конкретному ресурсу.
Другими словами, скоупы применяются только к тем ресурсам, которые остаются доступными после применения политик авторизации. Скоупы не переопределяют и не заменяют права пользователя. Они добавляют дополнительный уровень контроля над тем, к чему может получить доступ Клиентское Приложение, гарантируя, что не всё, к чему имеет доступ Владелец Ресурса, автоматически передается клиенту.

Скоупы представляют собой просто список строк, разделенных пробелами, где каждая строка указывает определенный тип доступа. Формат областей не определен в протоколе OAuth2, но обычно они структурированы следующим образом: {ресурс}_{уровень доступа}
.
Например:
user_read
может разрешить Клиенту читать информацию о профиле текущего пользователя (например, Владельца Ресурса).repo_write
может разрешить Клиентскому Приложению вносить изменения в репозитории, к которым у владельца ресурса есть доступ.
Как видите, области довольно грубо детализированы: они не предоставляют доступ к конкретным ресурсам, а работают на уровне типов ресурсов и уровней доступа (например, чтение/запись/администрирование).
Области являются аддитивными, то есть при запросе нескольких областей они объединяются, расширяя разрешения Клиентского Приложения или токена доступа.
Флоу OAuth2
Флоу авторизации, также известный как гранты авторизации это то, как права делегируются Приложениям Клиента. Вне зависимости от используемого флоу, конечный результат это набор токенов доступа, которые дают Приложению Клиента прямой доступ к Серверу Ресурса.
Основные различия между флоу следующие:
Интерактивность или ее отсутствие.
Количество участников.
Безопасность использования публичными и конфиденциальными клиентами.
OAuth2 Legs
В зависимости от количества участников во флоу авторизации, он называется либо 2-leg, либо 3-leg.
2-leg флоу включает только Приложение Клиента и Сервер Авторизации.
3-leg флоу включает все три компонента: Приложение Клиента, Сервер авторизации и Владельца Ресурса
В OAuth1.0a существовал zero‑leg флоу, который более невозможен в OAuth2. В нем Приложения Клиентов использовали свои данные для подписи запросов к нужным ресурсам. Затем Серверы Ресурсов валидировали эти запросы, используя общий секрет или публичный ключ. Этот подход был заменен более безопасным (без общих или долгоживущих секретов), менее зависимым и более стандартизированным client credentials flow.
Код авторизации
Начнем с наиболее каноничного и безопасного флоу OAuth2 — флоу кода авторизации.
Этот флоу интерактивный и работает для Приложений Клиентов, которые способны хранить секреты и выполнять редиректы в браузере, что типично для веб‑сервисов с бэкэндом.
Флоу состоит из двух этапов:
Запрос авторизации — редирект на сервер авторизации.
Обмен кодом авторизации. Выполняется на callback URL клиента.

Флоу может быть разделен на две основных части:
Взаимодействия, которые происходят косвенно между Сервером Авторизации и Приложением Клиента, используя браузер в качестве посредника. Они выполняются на фронтэнде и могут в теории быть перехвачены или манипулированы в процессе (например, вредоносным расширением браузера, подслушивающим параметры кода).
Взаимодействия, происходящие напрямую между Сервером Авторизации и клиентом по доверенному каналу на бэкэнде.
Флоу кода авторизации создан таким образом, чтобы было невозможно получить делегирование доступа, используя только информацию с фронтэнда.
Запрос авторизации
Чтобы начать флоу OAuth2, приложению клиента необходимо запросить авторизацию с нужными правами от Владельца Ресурса. Это происходит посредством редиректа владельца ресурса к эндпоинту authorize
сервера авторизации.
URL авторизации обычно содержит следующие параметры:
HTTP/1.1 302 Found
Location: https://auth.example.com/authorize?response_type=code
&client_id=Iv23lilfdg920cAzhcxA
&redirect_uri=https://www.clientapp.com/callback/
&scope=read_user%20write_repo%20read_repo
&state=wSDOBWf0PAC9C7AENIRoCfHnDDSbr-XAbzFLG937m5_u12DkEjQvfj4UsOkVg0uVHZMVcXWcWFr6iQ7XLFopkw==
response_type
определяет, какой интерактивный флоу будет выполняться. Для флоу кода авторизации это всегдаcode
(илиtoken
для неявного флоу).client_id
необходим, чтобы избежать фишинга флоу другого приложения — код авторизации привязывается к приложению клиента, начавшего флоу.redirect_uri
— URL страницы колбэка приложения клиента, куда будет передан код авторизации в случае получения согласия на авторизацию. Этот URL должен быть задан в настройках регистрации клиента.
State vs. CSRF атаки
Параметр
state
необязателен, но крайне рекомендуется.Он обеспечивает CSRF‑защиту, которая позволяет убедиться, что флоу авторизации был начат пользователем, а не, к примеру, вредоносным JS‑скриптом, выполняющим автоматическое действие с использованием сессии пользователя.
На практике, состояние это просто случайная неугадываемая строка, сохраненная в сессии приложения клиента (например, с помощью безопасной куки — gorilla/session или подобных) и передаваемая серверу авторизации в запросе
authorize
. Затем состояние передается обратно на callback URL и сравнивается с изначальным.
В браузере Владельца Ресурса уже должна быть пользовательская сессия (например, кука сессии) с сервером авторизации (либо производится авторизация), что используется редиректом для показа экрана запроса согласия.

Стоит заметить, что приложение клиента общается с сервером авторизации неявно посредством HTTP‑редиректов и браузера Владельца Ресурса. Таким образом Приложению Клиента не нужно знать данные или сессию Владельца Ресурса, что и было основной проблемой, стоявшей перед протоколом OAuth2.
В связи с этим на странице запроса согласия на авторизацию не должно быть специальных настроек конфигурации CORS. Это справедливо для всех флоу OAuth2.
Обмен кодом
Когда Владелец Ресурса подтвердит делегирование доступа Приложению Клиента, Сервер Авторизации перенаправляет Владельца Ресурса обратно на callback URL Приложения Клиента, заданного во время запроса на авторизацию.
Редирект выглядит следующим образом:
HTTP/1.1 302 Found
Location: https://www.clientapp.com/callback/?code=AUTHORIZATION_CODE
&state=wSDOBWf0PAC9C7AENIRoCfHnDDSbr
Параметр
code
называется кодом авторизации (от него и название флоу).state
возвращается, если было задано изначально для проверки целостности флоу.
Дополнительные параметры callback URL клиента
Также возможно передать дополнительные параметры в клиентском callback URL, чтобы они были переданы дальше на протяжении всего флоу OAuth2. К примеру, у вас может быть один callback URL, обрабатывающий авторизацию для нескольких провайдеров OAuth2 — тогда можно различать их по дополнительному параметру URL:
https://www.example.com/callback/?provider={{github
| gitlab | bitbucket | gitea}}
Но использование фрагментов URL не рекомендуется, поскольку это влечет потенциальные риски безопасности.
Код авторизации это одноразовый токен, отражающий согласие определенного Владельца Ресурса на выдачу доступа определенному приложению. Он привязан к ID клиента, получающего код, так что его невозможно использовать в другом приложении. В связи с этим даже в случае утечки кода, вам потребовались бы валидные данные клиента для его превращения в токены доступа.
И наконец, для завершения флоу, нам нужно обменять код авторизации на токены доступа. Это осуществляется посредством эндпоинта token
OAuth2:
POST /token HTTP/1.1
Host: auth.example.com
Authorization: Basic {{ base64(client_id:client_secret) }}
Accept: application/json
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA
&redirect_uri=https://www.clientapp.com/callback/
grant_type
определяет, какой флоу или грант мы хотим использовать для обмена на токены доступа. Это универсальный эндпоинт, используемый и в других флоу, но в данном случае это всегда будетauthorization_code
.code
— обязательный параметр при обмене кода авторизации.redirect_uri
это дополнительная защита в случае, если разрешено несколько URL редиректов, чтобы избежать инъекций кода авторизации.
Basic Client Auth — не единственный
У OAuth2 есть отдельный реестр, в котором указаны стандартные методы авторизации клиента, так что базовая аутентификация клиента — не единственный, хоть и популярный, будучи первым добавленным.
В ответе, в случае успеха, будет следующее:
{
"token_type":"bearer",
"access_token": "gho_16C7e42F292c6912E7710c838347Ae178B4a",
"scope":"read_user write_repo read_repo",
"expires_in": 3600,
"refresh_token": "ghr_16C7e42F292c6912E7710c838347Ae178",
}
Вот и все. Теперь нужно просто сохранить токены доступа и обновления и использовать их для доступа к Серверу Ресурса.
PKCE
Анализ реальных атак на процессы авторизации показал, что их можно дополнительно защитить. В частности, злоумышленники могут перехватить код авторизации или попытаться внедрить его в callback URL, чтобы выполнить обмен токенов через несанкционированные процессы. Эти векторы атак наиболее вероятны в публичных приложениях, таких как нативные приложения.
Чтобы устранить эту угрозу, протокол OAuth2 расширил процесс авторизации с использованием кода за счет расширения Proof Key for Code Exchange (PKCE, произносится как «пикси»).
PKCE — это простой способ доказать, что код авторизации был получен через легитимный запрос авторизации. Прелесть PKCE в том, что он лишь незначительно расширяет запросы авторизации и запросы токенов, не внося серьезных изменений в процесс.

Клиентские Приложения генерируют случайную строку, называемую
code_verifier
, а затем хэшируют её с использованием криптографически безопасного алгоритма, такого как SHA256. Хэшированное значение называетсяcode_challenge
.Клиентское Приложение сохраняет оригинальный
code_verifier
в секрете и передаетcode_challenge
и метод хэширования (например,code_challenge_method
) в качестве параметров запроса в запросе авторизации.Сервер Авторизации запоминает
code_challenge
иcode_challenge_method
. Никаких других изменений в существующие ответы сервера авторизации не требуется.Затем клиент отправляет
code_verifier
во время обмена токена. Сервер Авторизации вычисляет хэш этого значения и сравнивает его сcode_challenge
, переданным во время запроса авторизации.
PKCE поддерживает два метода хэширования:
S256
— алгоритм хэширования SHA256:
code_challenge = base64(SHA256(ASCII(code_verifier)))
plain
— метод простого текста. По сути, это простоcode_challenge
=code_verifier
. Методаplain
следует избегать, так как он не обеспечивает реальной защиты. Он может защитить только от атак, где злоумышленники могут перехватить ответы сервера авторизации.
Расширение PKCE позволяет публичным клиентам наконец безопасно использовать процесс авторизации с использованием кода. Однако сервер авторизации должен быть готов поддерживать PKCE для публичных клиентов, что сводится к тому, что от таких клиентов не требуется предоставление секрета клиента.
Токены обновления
Токен обновления — это дополнительный, необязательный, но крайне рекомендуемый токен, который может вернуть вам эндпоинт токенов OAuth2. В отличие от токена доступа, токен обновления предназначен для долгосрочного использования (либо без срока действия, либо с длительным сроком, например, полгода), и он отправляется только на сервер авторизации.
По сути, токен обновления представляет собой «внутренний» грант на авторизацию, так как он подразумевает авторизацию, предоставленную владельцем ресурса Клиентскому Приложению.
Токен обновления важен по двум причинам:
Он позволяет сохранять токены доступа краткосрочными, что минимизирует поверхность атаки в случае их утечки.
Он позволяет генерировать токены доступа с уменьшенным скоупом доступа, который более ограничен, чем скоупы, предоставленные Клиентскому Приложению во время авторизации. Это позволяет клиентам реализовать принцип минимальных привилегий на своей стороне.

Чтобы обновить токен доступа, вы отправляете запрос на эндпоинт токенов OAuth2 с параметром grant_type
, установленным в значение refresh_token
:
POST /token HTTP/1.1
Host: auth.example.com
Authorization: Basic {{ base64(client_id:client_secret) }}
grant_type=refresh_token&refresh_token=ghr_16C7e42F292c6912E7710c838347Ae178
Токен обновления связан с конкретными учетными данными клиента, поэтому его невозможно использовать с неавторизованным клиентом.
Запрос на обновление токена обычно возвращает тот же ответ, что и при обмене кода авторизации. Он содержит новый активный токен доступа, время его истечения и фактические скоупы доступа. В некоторых случаях (например, так делают GitHub и GitLab) запрос на обновление токена может также обновить ваш предыдущий токен обновления. Поэтому, если ответ содержит поле refresh_token, и оно отличается от вашего текущего токена обновления, это означает, что это ваш новый токен обновления, который нужно сохранить и использовать в дальнейшем.
Запрос на обновление токена обычно аннулирует все предыдущие токены доступа (и токены обновления).
Concurrent Token Refresh
Если в вашем приложении возможно одновременное выполнение запросов на обновление токена, это может привести к тому, что действительным останется только последний запрошенный токен доступа, в то время как предыдущие токены будут корректно аннулированы. Например, у вас может быть воркфлоу, который запрашивает токены доступа для клонирования репозиториев конечного пользователя, и этот процесс может вызываться несколько раз параллельно. Это может вызвать функциональные проблемы.
Чтобы обойти эту проблему и обеспечить разумное количество запросов к API токенов OAuth2, не превышающее лимиты частоты запросов, вам нужно реализовать две вещи в логике генерации токенов:
распределенная блокировка для обеспечения того, чтобы только один запрос на обновление токена обрабатывался в данный момент, а все остальные ждали. Это можно реализовать с помощью Redis и их подхода RedLock.
затем, когда токен доступа создан, можно попытаться сохранить его в распределенном кэше, например, в Redis, и возвращать его, пока он действителен. Все запросы, которые ожидали снятия блокировки, получат один и тот же токен доступа из кэша.
Неявный флоу
Мы уже говорили, что флоу кода авторизации разработан таким образом, чтобы сделать невозможным получение делегирования Владельца Ресурса, используя только информацию, передаваемую через фронтенд (например, код авторизации и идентификатор клиента). Чтобы добиться этого, флоу требует, чтобы у Клиентского Приложения был безопасный бэкенд. Но что, если приложение является публичным и ему негде хранить секрет?
Исходная спецификация OAuth2 представила упрощенную версию флоу кода авторизации, которая идет на значительный компромисс в области безопасности для поддержки публичных приложений, в первую очередь браузерных JS-приложений, таких как расширения для браузеров или одностраничные приложения (SPA) без бэкенда. Эта версия флоу называется неявным флоу.
Неявный флоу также является интерактивным флоу на основе перенаправления, но в нём отсутствует явный обмен кодом через бэкенд. Вместо этого обмен происходит неявно, и Клиентское Приложение просто получает токен доступа в callback URL.

Запрос авторизации похож на то, что мы видели во флоу кода авторизации, но в этот раз нам нужно указать response_type
как token
:
HTTP/1.1 302 Found
Location: https://auth.example.com/authorize?response_type=token
&client_id=Iv23lilfdg920cAzhcxA
&redirect_uri=https://www.clientapp.com/callback/
&scope=read_user%20write_repo%20read_repo
&state=wSDOBWf0PAC9C7AENIRoCfHnDDSbr
В JS‑приложениях есть несколько способов выполнить этот запрос:
Сделать полный редирект страницы на Сервер Авторизации.
Открыть отдельное всплывающее окно, выполнить редирект в нём, а затем закрыть его при получении callback URL.
Скрытые iframe для получения токена доступа
Раньше был и третий вариант — открытие страницы авторизации в iframe. Согласно принципу TOFU, такой подход позволял бы также незаметно аутентифицировать пользователей, которые уже дали согласие на делегирование, без видимых редиректов. Выглядит привлекательно, поскольку JS‑приложения не могут долго хранить токены доступа, что, в свою очередь, приводит к гораздо большему количеству запросов на авторизацию по сравнению с приложениями, использующими бэкенд.
Однако использование iframe требует передачи сторонних cookie, которые блокируются браузерами из‑за соображений конфиденциальности.
Если вы указываете параметр авторизации state
, лучшим местом для его временного хранения будет window.sessionStorage
.
После того как Владелец Ресурса одобрит делегирование, Сервер Авторизации перенаправит его обратно на callback URL клиента, который будет выглядеть следующим образом:
HTTP/1.1 302 Found
Location: https://www.clientapp.com/callback/#access_token=gho_16C7e42F292c6912E7710c838347Ae178B4a&state=wSDOBWf0PAC9C7AENIRoCfHnDDSbr
access_token
возвращается сразу в callback URL вместе с другими параметрами. Это обычный GET‑запрос, поэтому токен доступа становится частью URL и может быть потенциально перехвачен другими расширениями браузера, вредоносными скриптами, внедренными через XSS‑атаки, и т. д. Кроме того, весь callback URL, включая токен доступа, сохраняется в истории браузера. Именно поэтому неявный флоу считается небезопасным.
Все параметры возвращаются как фрагменты URL и предназначены только для использования браузерными клиентскими приложениями (например, не должны передаваться на серверы бэкенда).
Поскольку браузерные приложения не могут хранить секреты, возвращенный токен доступа имеет очень короткий срок действия (например, 1–2 часа). По той же причине OAuth2 не требует использования токенов обновления в неявном флоу.
Наконец, Клиентское Приложение может использовать полученный токен для доступа к Серверу Ресурсов так же, как и во флоу кода авторизации. Есть одно отличие: необходимо настроить политику CORS, чтобы Сервер Ресурсов был готов принимать эти запросы от браузерных приложений.
В итоге, можно выделить два ключевых элемента, которые помогают определить валидность клиента в неявном потоке:
Идентификатор клиента (Client ID)
URL перенаправления (Redirect URL)
Прекращение поддержки
Мы рассмотрели неявный флоу для полноты картины, но этот флоу объявлен устаревшим в OAuth 2.1 как небезопасный.
Рекомендуется использовать флоу кода авторизации с PKCE‑челленджем для публичных приложений.
Учетные данные клиента
Давайте продолжим размышления. Что если нет владельца ресурса, и Клиентское Приложение хочет действовать от своего собственного имени?
Вполне обычная ситуация, когда у вас есть несколько внутренних сервисов, которые должны общаться друг с другом, и вы хотите защитить это общение для создания среды с нулевым доверием.
В этом случае владельца ресурса нет, поэтому нет необходимости вовлекать весь фронтенд. Всё, что нам нужно, это заставить Сервер Авторизации принять учетные данные клиента как валидную причину для выдачи токенов доступа. Поэтому этот флоу называется флоу учетных данных клиента.

Флоу учетных данных клиента — это не интерактивный флоу, который позволяет конфиденциальным доверенным Клиентским Приложениям получить доступ к Серверу Ресурсов (или другим внутренним Клиентским Приложениям). Таким образом, единственный запрос, который нам здесь нужен, это запрос к эндпоинту токенов:
POST /token HTTP/1.1
Host: auth.example.com
Authorization: Basic {{ base64(client_id:client_secret) }}
Accept: application/json
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials&scope=read_user%20write_repo%20read_repo
Параметр
grant_type
устанавливается вclient_credentials
, для указания того, что валидность учетных данных клиента является основанием для получения токена доступа.Параметр
scopes
являются необязательными, но рекомендуется использовать его для обеспечения наименьших привилегий при доступе.
Вот и всё. Ответ аналогичен другим флоу. Нет особой необходимости выдавать токены обновления, так как учетные данные клиента выполняют ту же функцию, поэтому они обычно опускаются.
Другой момент — это область доступа. Поскольку Клиентское Приложение действует от своего собственного имени, оно может не быть ограничено ресурсами, доступными для конкретного Владельца Ресурса. Должен быть способ для Сервера Ресурсов отличать этот уровень доступа от обычного делегирования владельцем ресурса. Я видел два способа реализации этого:
использовать отдельный набор областей доступа для обозначения такого внутреннего, широкого доступа
добавить custom claims в JWT токен доступа и учитывать его при валидации токена доступа
Учетные данные Владельца Ресурса (ROC)
Самый парадоксальный флоу среди всех стандартных флоу OAuth2 — это флоу учетных данных владельца ресурса (ROC). Он парадоксален, потому что с первого дня существования протокола OAuth2 его использование было не рекомендовано, все говорили, что использовать его очень плохая идея, но тем не менее он всё равно оказался в спецификации. Почему так произошло?
Теоретически, могут быть ситуации, когда вам необходимо использовать ваше имя пользователя и пароль для доступа к некоторым ресурсам. Без флоу ROC вы были бы еще менее защищены, чем при его использовании. Это связано с тем, что этот поток ограничивает доступ к учетным данным по сети, что снижает вероятность утечек данных. Кроме того, он позволяет ограничить область доступа (вместо того чтобы давать клиенту абсолютно все доступы, которые у вас есть).
В этом потоке Владелец Ресурса передаёт свои учётные данные (например, имя пользователя и пароль) напрямую Клиентскому Приложению. Затем приложение использует эти учётные данные для получения пары токен доступа и токен обновления. Учётные данные владельца ресурса затем отбрасываются, и клиент использует только токены для доступа к защищенным ресурсам в дальнейшем.

Это флоу, использующийся только на бэкенде, поэтому Клиентское Приложение взаимодействует только с эндпоинтом токенов Сервера Авторизации:
POST /token HTTP/1.1
Host: auth.example.com
Authorization: Basic {{ base64(client_id:client_secret) }}
Accept: application/json
Content-Type: application/x-www-form-urlencoded
grant_type=password&username=dwight.shrut&password=bearbeetsbattlestargalactica&scope=org_admin
Параметр
grant_type
должен бытьpassword
, чтобы указать флоу ROC.Учётные данные, например, имя пользователя и пароль, передаются как часть запроса токена.
Также можно передать параметр
scope
, чтобы ограничить уровень делегированного доступа. В противном случае это будет полный доступ, который есть у Владельца Ресурса (что бы это ни означало для данного Сервера Ресурсов).
Прекращение поддержки
Мы рассмотрели флоу учетных данных владельца ресурса для полноты картины, но этот флоу объявлен устаревшим в черновике OAuth 2.1, так как он крайне небезопасен.
Как и в случае с неявным флоу, вам следует использовать флоу кода авторизации с PKCE-челленджем вместо этого.
Код устройства
Очевидно, что основная цель оригинальной спецификации OAuth2 заключалась в использовании в браузерных приложениях, но после того как OAuth2 приобрёл популярность, он нашёл применение и в других контекстах. Например, не в каждой среде есть возможность открыть браузер и использовать флоу на основе редиректа, такие как флоу кода авторизации. Вот несколько примеров:
Когда у вас появляется новый телевизор, и вы хотите смотреть Netflix на нём, вам нужно авторизовать это устройство для доступа к вашему аккаунту и подписке.
Когда вы хотите анализировать данные в Snowflake в контейнеризованном облачном Jupyter‑блокноте, может не быть возможности открыть браузер (headless Linux под капотом).
Когда вы пытаетесь подключиться к игровому порталу с консоли, на которой может быть браузер, но с ограниченными возможностями ввода (например, без полноценной клавиатуры).
К счастью, существует расширение оригинальной спецификации OAuth2, которое описывает так называемый флоу авторизации устройства.
Флоу авторизации устройства (или флоу кода устройства) — это особый тип интерактивного флоу, который не предполагает прямого взаимодействия между Клиентским Приложением на устройстве и браузером Владельца Ресурса.
Вместо этого Клиентское Приложение указывает, как Владелец Ресурса может авторизовать его через браузер косвенно, показывая URL для проверки, QR‑код для сканирования или просто предлагая открыть мобильное приложение провайдера.

Запрос авторизации устройства
Для реализации флоу авторизации устройства был введён новый эндпоинт для запуска флоу, называемый эндпоинтом авторизации устройства (поскольку он имеет совершенно другую семантику, чем стандартный, основанный на браузере эндпоинт authorize
):
POST /device_authorization HTTP/1.1
Host: auth.example.com
Accept: application/json
Content-Type: application/x-www-form-urlencoded
client_id=Iv23lilfdg920cAzhcxA&scope=read_user%20write_repo%20read_repo
client_id
необходим для идентификации Клиентского Приложения.Клиентский секрет не требуется, поскольку устройство‑клиент по своей сути схоже с публичными клиентами с точки зрения способности хранить секреты, например, любые встроенные секреты могут быть извлечены.
Эндпоинт авторизации устройства возвращает что‑то вроде этого:
{
"device_code": "GmRhmhcxhwAzkoEqiMEgDnyEysNkuNhszIySk9eS",
"user_code": "WDJB-MJHT",
"verification_uri": "https://auth.example.com/login/device",
"expires_in": 1800,
"interval": 5
}
verification_uri
— это URL, на который должен перейти конечный пользователь, чтобы ввестиuser_code
. URL должен быть достаточно коротким, чтобы его можно было ввести вручную. Альтернативно, Сервер Авторизации может предоставить другой URL, который, например, можно преобразовать в QR‑код. Этот URL обычно содержит код пользователя в качестве параметра запроса.device_code
— это то, что клиентское приложение устройства хранит секретно в памяти и использует в качестве гранта при опросе эндпоинта токенов.
Код устройства служит доказательством начала флоу авторизации. Если бы не было кода устройства, и клиент устройства имел бы только client ID в качестве идентификатора клиента, злоумышленники могли бы выяснить этот ID и попытаться отправить запросы на получение токенов доступа и токенов обновления до того, как это сделает реальное устройство, которое запросило их.
Владелец ресурса должен инициировать (или повторно инициировать, если предыдущий запрос истёк) флоу авторизации, но при этом мы не передаем информацию о пользователе при инициализации запроса на авторизацию. Сервер авторизации может сопоставить владельца ресурса с соответствующим client ID только после ввода кода пользователя на странице проверки. Честно говоря, мы также не передаем идентификатор владельца ресурса напрямую в других интерактивных флоу, но в случае редиректа авторизации используются cookie браузера, так что Сервер Авторизации может сразу идентифицировать конечного пользователя.
Коды пользователя
Коды пользователей требуют особого внимания. Они должны быть удобными для использования и сложными для подбора.
Например:
Использовать только цифры, такие как 123–456–408, разделенные дефисами или запятыми (так называемые незначащие символы). Должно быть хотя бы девять цифр, чтобы обеспечить достаточную энтропию. Этот подход хорош для мест, где не используется латинский алфавит (например, Израиль, Китай, Украина и т. д.).
Использовать заглавные латинские буквы, также разделенные дефисами. Можно использовать код из восьми символов, например, WDJB‑MJHT. В этом подходе больше нюансов. Стоит подумать об исключении гласных, чтобы избежать формирования слов (например, случайная генерация неприличных слов, ругательств, грамматически некорректных фраз и т. д.).
Если вы хотите использовать как цифры, так и буквы, стоит избегать использования похожих символов, таких как 1 и l, 0 и O, I и l и т. д.
Кроме того, необходимо ограничить количество попыток ввода кода, чтобы предотвратить атаки методом подбора (не более пяти попыток). RFC предполагает, что шанс угадать код с вероятностью 1 из 2^32 (~ 4 миллиарда) будет достаточно безопасным. Это невозможно без ограничения количества попыток.
Затем код пользователя каким‑то образом показывается Владельцу Ресурса. Обычно он просто выводится на экране устройства, чтобы пользователь мог ввести его вручную.
Опрос токена доступа
Процесс авторизации устройства ограничен по времени (время жизни указывается в поле expires_in
в ответе). Обычно продолжительность авторизации составляет около 15 минут.
Поскольку нет способа, которым Сервер Авторизации может сообщить клиенту устройства, когда авторизация была проведена(эту роль выполняет callback URL в других интерактивных флоу) и предполагается, что устройство не может принимать входящие запросы, протокол исходит из того, что устройство подключено к интернету и может делать исходящие запросы.
С учётом этих предположений, устройство может периодически опрашивать эндпоинт токенов, пока авторизация не будет проведена, запрос авторизации не истечет или не будет отклонен. Интервал опроса по умолчанию составляет 5 секунд.
Опрос осуществляется на эндпоинт токенов:
POST /token HTTP/1.1
Host: auth.example.com
Accept: application/json
Content-Type: application/x-www-form-urlencoded
grant_type=urn:ietf:params:oauth:grant-type:device_code&device_code=GmRhmhcxhwAzkoEqiMEgDnyEysNkuNhszIySk9eS&client_id=Iv23lilfdg920cAzhcxA
Параметр
grant_type
обычно указывает, какой флоу мы пытаемся завершить. В данном случае используется необычный код флоу, что означает, что имя флоу нестандартное (или пользовательское).Также отправляется
device_code
, чтобы подтвердить устройство, которое пытается получить токены.
Спецификация не требует аутентификации клиента при доступе к эндпоинту токенов, но это возможно, и некоторые провайдеры используют это (например, реализация флоу авторизации устройства от Google). В таком случае, вы все еще, не можете постоянно хранить секрет клиента на конечном устройстве и, вероятно, должны иметь какой‑то бэкэнд‑сервис для опроса эндпоинта токенов для устройства.
Очень вероятно, что клиенту устройства нужно будет несколько раз опросить эндпоинт токенов, прежде чем конечный пользователь фактически авторизует его. В этом случае эндпоинт токенов должен возвращать специальную ошибку, указывающую, что авторизация ещё не была предоставлена:
{
"error": "authorization_pending",
"error_description": "The authorization request is still pending as the end user hasn't yet authorized the device."
}
Если клиент слишком часто опрашивает эндпоинт, возвращается другая специальная ошибка, которая указывает на необходимость увеличить интервал опроса на 5 секунд.
{
"error": "slow_down",
"error_description": "The client should wait before polling the token endpoint again."
}
Когда авторизация наконец предоставлена, эндпоинт токенов должен вернуть обычный ответ с токеном, который мы видели во флоу авторизации с кодом.
Больше грантов
Что ещё можно использовать для получения токенов доступа и токенов обновления? Спецификация OAuth2 определяет способ расширения стандартных типов грантов с помощью пользовательского типа. Он называется расширением или third‑party assertion grant.
Assertion grant — это флоу, работающий только на стороне бэкенда, где Клиентское Приложение отправляет Серверу Авторизации специальное утверждение третьей стороны, которое подтверждает права клиента на доступ к защищенному Ресурсному Серверу.

Как и в случае с любым другим флоу, работающим только на стороне бэкенда, этот поток использует только эндпоинт токенов:
POST /token HTTP/1.1
Host: auth.example.com
Accept: application/json
Content-Type: application/x-www-form-urlencoded
grant_type=urn:roma-glushko:oauth:jwt&assertion=eyJhbGci[..omitted for brevity..]I6IkpXVCJ9.eyJzdWIiOi[..omitted for brevity..]MDIyfQ.SflK[..omitted for brevity..]adQssw5c
Тип гранта в этом случае представляет собой уникальную строку в формате URN, которая включает название организации и другую информацию о типе гранта. Например:
urn:organization:oauth:grant-type:custom-name
Важно, чтобы целевой Сервер Авторизации распознавал этот тип гранта и знал, как его проверить.
Утверждение обычно представляет собой автономный защищённый токен, который криптографически подписан поставщиком утверждения. На практике существует два типа утверждений, которые можно встретить на практике:
JWT‑утверждения (например, определённые как urn:ietf:params:oauth:grant‑type:jwt‑bearer)
SAML‑утверждения (например, определённые как urn:ietf:params:oauth:grant‑type:saml2-bearer)
Пользовательские утверждения, такие как urn:bitbucket:oauth2:jwt, которые, скорее всего, также основаны на JWT.
Аутентификация клиента может быть необязательной в этом случае (если это так, то токен обновления, скорее всего, не будет выдан, так как этот грант требует аутентификации клиента).
Какой флоу выбрать?
После того как мы рассмотрели все основные флоу OAuth2, какой из них вам следует выбрать для вашего конкретного приложения?
Я попытался составить следующее дерево решений, которое задает основные вопросы, чтобы помочь вам.

Всегда старайтесь использовать флоу кода авторизации с PKCE, если это возможно, независимо от того, является ли приложение публичным или конфиденциальным.
Если PKCE не поддерживается, то код авторизации подходит только для конфиденциальных клиентов, если не поддерживается динамическая регистрация клиента. Для публичных клиентов следует использовать флоу с явным получением токена и уделить внимание множеству аспектов безопасности.
Если ваше клиентское приложение не может открыть браузер с сессией владельца ресурса или ограничено по возможностям ввода, и пользователи не доверяют этому приложению, используйте флоу с кодом устройства.
Прежде чем прибегать к флоу с учетными данными владельца ресурса, попробуйте выяснить, могут ли API‑ключи помочь вам достичь той же цели.
Заключение
Размышления о том, почему протокол OAuth2 был разработан именно таким образом, оказались отличным упражнением по моделированию угроз с немедленными, простыми и практическими подходами к их смягчению. Эти подходы могут быть использованы для решения аналогичных проблем безопасности в других контекстах за пределами протокола OAuth, так что вы можете извлечь пользу из глубокого понимания протокола, даже если вы не являетесь экспертом по безопасности, которому нужно знать все нюансы OAuth2.
Помимо этого, OAuth2 — это настолько обширная область, что в этой статье мы смогли ответить только на основные вопросы «почему» и рассмотреть самые популярные механизмы делегирования.
Множество интересных расширений OAuth2 были упомянуты только вскользь, но вы не встретите их так часто в реальной жизни, так что было допустимо оставить их за скобками на данный момент.