Про OAuth 2.0 уже написано немало статей, в том числе на Хабре, но у этих статей есть особенность - в то время как после основного RFC 6749 вышло несколько дополнительных RFC, расширяющих, объясняющих и дополняющих основной RFC, обычно описывают только базовую спецификацию. Это не говорит, что статьи плохие - наоборот, они полезны. Но хотелось бы сделать обзор вообще всего, что можно отнести к OAuth 2.0, более того часть из них рекомендована к использованию. Поэтому в данной статье попробую кратко описать большинство спецификаций, которые относятся к OAuth 2.0.
The OAuth 2.0 Authorization Framework
Ссылка на спецификацию: RFC 6749
Основной RFC, в котором описана основа, базовые понятия. Все последующие RFC дополняют, расширяют и объясняют различные моменты именно этого документа. Поэтому вкратце хотелось бы пройти по базовым понятиям, так как мы будем неоднократно возвращаться к ним.
Базовая концепция
OAuth 2.0 в отличие от SAML2 или Kerberos вводит понятие клиента - приложение или целый класс приложений, которые предоставляют доступ пользователя к ресурсу. Причём доступ выдаётся при следующих условиях:
клиенту выдаём доступа без передачи пароля или прочих секретов, который предоставляют неограниченный доступ;
клиенту выдаём гранулированный доступ, то есть доступ можно выдать минимально необходимый доступ. Клиент может только запросить что ему нужно, но конечное решение за пользователем (если бы кто-то ещё читал, куда идёт запрос доступа).
При этом конечная цель авторизации - выдача токена (или нескольких токенов), которые предоставляют предъявителю конкретный доступ. При этом в RFC 6749 что Access Token, что Refresh Token - это просто некая строка, которая выдаётся клиенту. Да, да, ни слова про JWT.
Клиенты
Клиент - это приложение или целый класс приложений, которые предоставляют доступ пользователя к ресурсам, для чего надо получить доступ. Определяются через параметр запроса client_id. Могут быть публичными и приватными. Отличие только в том, что для приватного клиента требуется аутентификация, которая выполняется через передачу параметра client_secret.
Процессы авторизации
Кратко все flow (процессы) авторизации можно изобразить по схеме:
+--------+ +---------------+
| |--(A)- Authorization Request ->| Resource |
| | | Owner |
| |<-(B)-- Authorization Grant ---| |
| | +---------------+
| |
| | +---------------+
| |--(C)-- Authorization Grant -->| Authorization |
| Client | | Server |
| |<-(D)----- Access Token -------| |
| | +---------------+
| |
| | +---------------+
| |--(E)----- Access Token ------>| Resource |
| | | Server |
| |<-(F)--- Protected Resource ---| |
+--------+ +---------------+
Вначале клиент инициирует авторизацию запросом Authorization Request и отправляет пользователя договариваться с сервером авторизации. Сам клиент никак не участвует в процессе общения пользователя и сервера авторизации, ему главное получить в случае успешного завершения от пользователя некий код, он же Authorization Grant. Этот код не даёт прав на доступ к ресурсу, лучше, он даёт право получить доступ к ресурсу, для этого клиент отправляет код в запросе Access Token Request в сервер авторизации. Сервер авторизации выполняет проверки и отдаёт либо один токен Access Token, либо пару токенов Access Token / Refresh Token. С этими токенами уже можно делать запрос к ресурсу.
Базовые процессы авторизации:
Authorization Code - базовый и основной процесс. Все прочие процессы, которые добавляются в RFC 6749 и других дополняют и корректируют данный процесс под частные случаи и сценарии. Когда говорят про OAuth 2.0, то как правило имеют в виду именно Authorization Code. На Authorization Request указываем
response_type=code
и на AccessToken Requestgrant_type=authorization_code
.Implicit - упрощения Authorization Code, когда вместо Authorization Grant-а сразу выдаётся Access Token. Может применяться для публичных клиентов, так как выдача кода не имеет смысла. Не рекомендуется к использованию (об этом ниже). На Authorization Request указываем
response_type=token
, Access Token Request отсутствует.Resource Owner Password Credentials - упрощение Authorization Code для сценария, когда клиент получает пароль от пользователя и по нему получает Access Token. Не рекомендуется к использованию и исключен из черновика OAuth 2.1. Authorization Request отсутствует, на Access Token Request указываем
grant_type=password
.Client Credentials - вариант Authorization Code для сценария, когда надо авторизовать информационную систему, а не конечного пользователя. Authorization Request отсутствует, на Access Token Request указываем
grant_type=client_credentials
.
Отдельно надо отметить обновление Access Token по Refresh Token, хоть он и имеет свой отдельный grant_type=refresh_token
- это не процесс авторизации и Authorization Request отсутствует.
Процессы авторизации и обновление токена можно представить в таблице, чтобы понимать где какой параметр указываем.
Процесс (flow) | Authorization Request | Access Token Request |
---|---|---|
Authorization Code | response_type=code | grant_type=authorization_code |
Implicit | response_type=token | |
Resource Owner Password Credentials | grant_type=password | |
Client Credentials | grant_type=client_credentials | |
* Refresh Token | grant_type=refresh_token |
Итак, попробую кратко описать все последующий RFC, которые относятся к OAuth 2.0.
The OAuth 2.0 Authorization Framework: Bearer Token Usage
Ссылка на спецификацию: RFC 6750
Описывается каким образом можно передать Access Token в запросах к ресурсу. Предлагается три варианта:
заголовок запроса - в этом случае заголовок имеет вид
Authorization: Bearer mF_9.B5f-4.1JqM
. Скорее всего все используют именно этот вариант.в составе формы - на POST операциях к ресурсу (GET нельзя использовать) с
Content-Type: application/x-www-form-urlencoded
помимо данных добавляем отдельное поле для Access Token-а. Я не видел, чтобы так кто-то делал.через параметр запроса - к нашему url-у запроса добавляем параметр access_token=. Помимо этого надо отправлять заголовок
Cache-Control: no-store
, чтобы данный url не закэшировался. Данный метод не рекомендуется к использованию, так как сложно обеспечить конфиденциальность параметров запроса (который к тому же видят все).
Также данное RFC описывает, что в случае отсутствия данных для аутентификации, сервер должен в ответе обязательно вернуть заголовок WWW-Authenticate
в соответствии с rfc2617, то есть должен иметь вид: WWW-Authenticate: Bearer <прочие данные>
, ключевое слово Bearer обязательно, это сигнал, что у нас OAuth 2.0.
An IETF URN Sub-Namespace for OAuth (Информационный)
Ссылка на спецификацию: RFC 6755
Регистрирует пространство имён urn:ietf:params:oauth:
, которое используется другими RFC для своих нужд. Например, свой дополнительный процесс авторизации можно описывать как urn:ietf:params:oauth:grant-type:my_flow
. Используется другими RFC.
OAuth 2.0 Threat Model and Security Considerations (Информационный)
Ссылка на спецификацию: RFC 6819
Описывает модели атак, угроз и соображения безопасности OAuth 2.0. Рекомендуется к прочтению специалистам по безопасности.
Можно обратить внимание на пункт 5.1.5, где предлагается использовать короткоживущие токены и ограничивать кол-во использований токенов. А так же пункт 4.1.5, где описывается тип атаки "Open Redirect" через манипуляции с параметром redirect_uri
.
OAuth 2.0 Token Revocation
Ссылка на спецификацию: RFC 7009
Описывается процесс отзыва токенов. Причём любой сервер авторизации обязан предоставлять реализацию отзыва Refresh Token-а. Для Access Token-а сказано, что желательно также иметь возможность отзыва, но требование менее строгое.
JSON Web Token (JWT)
Ссылка на спецификацию: RFC 7519
Вот мы и добрались до JWT. В данном RFC описывается, что для Access Token-ов и Refresh Token-ов. Также описываются стандартные поля:
Атрибут | Имя | Обязательное | Описание |
---|---|---|---|
iss | issuer | Нет | Тот, кто выдал токен |
sub | subject | Нет | Тот, кого представляет токен |
aud | audience | Нет | Тот, кому предназначен токен |
exp | expiration | Нет | Дата и время, когда истекает срок действия токена |
nbf | not before | Нет | Дата и время, до которого токен не актуален |
iat | issued at | Нет | Дата и время создания токена |
jti | JWT ID | Нет | Идентификатор токена |
Также возможно использовать дополнительные атрибуты, никаких ограничений нет.
Assertion Framework for OAuth 2.0 Client Authentication and Authorization Grants
Ссылка на спецификацию: RFC 7521
Описывается платформу (framework над framework-ом) для работы с утверждениями (assertions) для аутентификации клиента и авторизации. Утверждение (assertion) - это просто некие данные, которые можно передавать между доменами безопасности. Как правило assertion подписываются или имеют иной вид защиты целостности.
Использование assertion в качестве авторизации
На Access Token Request клиент передаёт следующие атрибуты:
grant_type (обязательно) - формат assertion;
assertion (обязательно) - собственно само assertion;
scope (опционально) - набор ресурсов, куда надо получить доступ.
Использование assertion в качестве аутентификации клиента
Можно использовать везде, где требуется аутентификация клиента, клиент передаёт следующие атрибуты:
client_assertion_type (обязательно) - формат assertion;
client_assertion (обязательно) - собственно само assertion;
client_id (опционально) - идентификатор клиент.
В данном RFC описывается сам механизм передачи assertion-ов, без конкретики. Возможные форматы описаны в двух последующих RFC.
Security Assertion Markup Language (SAML) 2.0 Profile for OAuth 2.0 Client Authentication and Authorization Grants RFC 7522
Ссылка на спецификацию: RFC 7522
Переиспользуем assertion-ы из стандарта Security Assertion Markup Language (SAML) 2.0.
Для авторизации указываем grant_type=urn:ietf:params:oauth:grant-type:saml2-bearer
, для аутентификации клиента - client_assertion_type=urn:ietf:params:oauth:client-assertion-type:saml2-bearer
(вот для чего нужен RFC 6755). Также в RFC описан формат assertion (который представлен в xml) и поведение клиента с серверов авторизации.
JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and Authorization Grants
Ссылка на спецификацию: RFC 7523
Описывает использование формата JWT в качестве assertion вместо xml-я из предыдущего RFC.
Для авторизации указываем grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer
, для аутентификации клиента client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer
. В качестве самого assertion передаётся всегда один JWT. К примеру, на регистрации клиента генерируется пара ключей, приватный ключ храним на клиенте, публичный сохраняем на сервере авторизации. И теперь вместо передачи client_secret
клиент формирует JWT, подписывает своим приватным ключом и передаёт на сервер авторизации. Сервер авторизации, проверяя подпись и содержимое (например, JWT может иметь ограниченный срок жизни), может аутентифицировать клиент.
OAuth 2.0 Dynamic Client Registration Protocol
Ссылка на спецификацию: RFC 7591
Описывает протокол динамической регистрации клиент на сервере авторизации.
Общая схема выглядит следующим образом:
+--------(A)- Initial Access Token (OPTIONAL)
|
| +----(B)- Software Statement (OPTIONAL)
| |
v v
+-----------+ +---------------+
| |--(C)- Client Registration Request -->| Client |
| Client or | | Registration |
| Developer |<-(D)- Client Information Response ---| Endpoint |
| | or Client Error Response +---------------+
+-----------+
Опционально надо иметь либо токен, либо некое утверждение (сертификат, ...), с помощью которых клиент может зарегистрировать самостоятельно клиента.
OAuth 2.0 Dynamic Client Registration Management Protocol (экспериментальный)
Ссылка на спецификацию: RFC 7592
Дополняет предыдущее RFC методами обновления и удаления динамически зарегистрированных клиентов. Метод обновления похож на метод регистрации, просто выполняется через PUT-запрос. Больше особо нет смысла писать, динамическая регистрация редко используется, а данный RFC вообще в экспериментальном статусе.
Proof Key for Code Exchange by OAuth Public Clients
Ссылка на спецификацию: RFC 7636
Описывает использование механизма PKCE
(произносится как "пикси"), который на сегодня рекомендуют и советуют практически все. Как уже выше было сказано, против OAuth 2.0 часто используют атаки на перенаправлении пользователя в клиент с кодом для последующего получения токенов. Данное RFC предлагает механизм подтверждения, что инициатор авторизации и отправитель запроса получения токенов по коду является один и тем же действующим лицом. Клиент перед началом авторизации формирует некую случайную строку code_verifier (желательно устойчивой к перебору), функцию хэширования (либо SHA 256, либо plain, то есть ничего), получает хэш случайной строки и в запросе на начало авторизации передаёт дополнительно два атрибута, code_challenge
, в котором передаётся хэш и code_challenge_method
, в котором указывает функцию хэширования (S256 или PLAIN) . Далее на Access Token Request помимо кода передаёт code_verifier
, в котором передаёт исходную строку. Сервер авторизации проверяет, что code_verifier
передан, далее хэширует и сравнивает хэш со значением, которое было передано на старте авторизации. Если хэши совпадают, то всё хорошо, иначе возвращаем ошибку.
Данный механизм позволяет использовать процесс Authorization Code
с публичными клиентами, делая Implicit
не нужным.
OAuth 2.0 Token Introspection
Ссылка на спецификацию: RFC 7662
Токены в RFC 6749 представляют собой некую строку, которую клиент использует для получения доступа к ресурсам, при этом ничего не знаю про структуру и содержание токена. Кроме этого к токену можно соотнести некую метаинформацию, которая может быть полезна клиенту. Данную задачу отчасти решает RFC 7519, когда токен имеет формат JWT, но не всегда есть возможность передавать данные внутри токена (например, есть требования безопасности, что внутри токена нельзя передавать персональные данные, email, роли и прочее).
Данная спецификация определяет протокол, с помощью которого клиент может получить различные сведения о токене. В частности, вводится Introspection Endpoint
- адрес, через который клиент может получить данные о токене, и Introspection Request
- запрос, который надо отправлять. Introspection Endpoint
должен закрываться TLS и требовать аутентификацию (например, клиента), а Introspection Request должен выполняться через POST-запрос с Content-Type: application/x-www-form-urlencoded
.
В ответе возвращается обязательно статус токена, активен или нет, опционально можно вернуть что угодно, в частности можно вернуть параметры токена как будто это JWT.
Proof-of-Possession Key Semantics for JSON Web Tokens (JWTs)
Ссылка на спецификацию: RFC 7800
Описывает механизм подтверждения владения токена в формате JWT с помощью симметричных или асимметричных ключей. Возможны случаи, когда JWT необходимо подписать не только серверов авторизации, но и неким сторонним доверенным лицом. В этом случае в JWT добавляется атрибут cnf
, содержащий список членов, которые могут подтвердить владение.
Authentication Method Reference Values
Ссылка на спецификацию: RFC 8176
Описывает атрибут amr
(Authentication Methods References) в JWT, содержащий массив методов аутентификации, который использовались в аутентификации. Может содержать следующие методы (список не полный, просто демонстрация возможных значений):
pwd - аутентификация через пароль
tel - подтверждение через телефонный звонок
sms - подтверждение через смс
pin - pin-код
otp - использование одноразового кода
face - аутентификация через биометрию и распознавание лица
и другие
Атрибут очень похож на acr
(Authentication Context Class Reference), отличие в том, что amr - просто перечисление методов аутентификации, которые использовались на авторизации, а acr - это уровень бизнес-правил, которым удовлетворяет данный токен.
OAuth 2.0 for Native Apps (Best Current Practice)
Ссылка на спецификацию: RFC 8252
Процессы авторизации, описанные в RFC 6749, в основном завязаны на активную работу браузера и приложений в браузере, включая перенаправления. Однако OAuth 2.0 можно использовать и для авторизации в толстых (язык не поворачивается называть нативных) и мобильных клиентах. В данной спецификации приведены примеры организации процесса авторизации, рекомендации и пояснения тонкостей работы в толстых и мобильных клиентах.
В частности авторизация в мобильном приложении через встроенный браузер - это как правило реализация данного RFC (когда пользователю на входе открывается браузер, предлагая аутентифицироваться, затем возвращая обратно в приложение). Предлагается использовать процесс Authorization code
для публичного клиента (на клиенте нет смысла хранить client_secret
), включения PKCE
(RFC 7636), использовать параметр state
, чтобы исключить CSRF атак и другие. Также даётся пояснения по поводу использования нестандартный User-Agent-ов. Отдельно описывается процесс работы с перенаправлением после успешной аутентификации (атрибут redirect_uri
). Так как весь процесс выполняется на мобильном приложении, то перенаправлять куда-то на внешние ресурсы нет смысла, то можно во-первых, использовать протокол, а во-вторых, перенаправлять на localhost. И для встроенного webview указывать redirect_uri
как адрес для завершения webview.
RFC стоит почитать, он важный и рекомендован к реализации.
OAuth 2.0 Authorization Server Metadata
Ссылка на спецификацию: RFC 8414
Описывает Service Discovery для протокола OAuth 2.0, то есть способ и механизм (.well-known URI) для получения метаданных о доступных процессах и параметрах авторизации. Вдохновлён спецификацией OpenID Connect Service Discovery, и отдельно никем и нигде не используется. Фактически описывает и "стандартизирует" атрибуты, относящиеся к авторизации в рамках OpenID Connect.
Описываются атрибуты:
issuer - эмитент;
authorization_endpoint - endpoint для старта авторизации;
token_endpoint - endpoint для получения токенов;
jwks_uri - URI для получения данных о сертификатах, которыми подписываются токены;
scopes_supported - доступные scope для авторизации
grant_types_supported - доступные grant_type-ы И многие другие.
Метаданные можно получить по адресу /.well-known/oauth-authorization-server
, но как уже писал, фактически никто не использует, так как всё то же самое есть в .well-known/openid-configuration
.
OAuth 2.0 Device Authorization Grant
Ссылка на спецификацию: RFC 8628
Добавляет к платформе OAuth 2.0 новый процесс авторизации с использованием дополнительного фактора. Все существующие процессы авторизации могут использовать дополнительные факторы на аутентификации. Данный процесс можно использовать в сценарии, когда доступ к браузеру ограничен (смарт ТВ, принтеры, консоли и пр.).
Процесс авторизации можно изобразить схемой:
+----------+ +----------------+
| |>---(A)-- Client Identifier --->| |
| | | |
| |<---(B)-- Device Code, ---<| |
| | User Code, | |
| Device | & Verification URI | |
| Client | | |
| | [polling] | |
| |>---(E)-- Device Code --->| |
| | & Client Identifier | |
| | | Authorization |
| |<---(F)-- Access Token ---<| Server |
+----------+ (& Optional Refresh Token) | |
v | |
: | |
(C) User Code & Verification URI | |
: | |
v | |
+----------+ | |
| End User | | |
| at |<---(D)-- End user reviews --->| |
| Browser | authorization request | |
+----------+ +----------------+
Условием для работы процесса является наличие некоего устройство, которое может выполнять исходящие запросы. Спецификация добавляет новый endpoint (отличный от authorization_endpoint
) для выполнения запроса Device Authorization Request (на диаграмме A), для старта процесса авторизации. В ответ (B) получаем атрибуты, среди которых:
device_code - код проверки устройства;
user_code - код проверки пользователя;
verification_uri - адрес для подтверждения. По возможности должен быть коротким, чтобы пользователь мог вручную ввести его;
expires_in - время жизни процесса. И другие атрибуты.
Получив ответ запускается два параллельных процесса:
устройство начинает периодически опрашивать сервер авторизации, отправляя client_id, device_code и grant_type=urn:ietf:params:oauth:grant-type:device_code на endpoint получения токена. Пока подтверждения от пользователя нет, сервер авторизации может ответить ошибкой со следующими дополнительными кодами ошибки:
- authorization_pending - ждём подтверждение от пользователя, устройство может продолжать опрашивать сервер авторизации;
- slow_down - механизм back pressure, по-прежнему ждём подтверждение от пользователя, но время до следующего запроса надо увеличить на 5 секунд;
- access_denied - процесс авторизации запрещён;
- expired_token - у device_code истёк срок действия (пользователь долго запрашивал подтверждение)
Если подтверждение от пользователя получено, то сервер авторизации возвращает токены.параллельно с этим устройство перенаправляет пользователя по адресу verification_uri или показывает этот адрес пользователя, предлагая вручную перейти по нему. На этом адресе надо ввести код подтверждения из user_code, который является подтверждением авторизации.
Также описываются момент, на которые надо обратить внимание (возможный перебор device_code и user_code, фишинг и другие).
OAuth 2.0 Token Exchange
Ссылка на спецификацию: RFC 8693
Описывает протокол Security Token Service (STS) для обмена токенов для делегирования и имперсонализации на базе HTTP и JSON (аналог WS-Trust
для веб-сервисов). Иногда бывают случаи, когда надо токен обменять на другой токен, например, клиент сделал запрос на один ресурс с токеном, дающим права на этот доступ, но ресурсу надо сделать запрос на другой ресурс для получения дополнительных данных. Он, конечно, можно пробросить токен от клиента (скорее всего так и делают), но может сходить к серверу авторизации и обменять токен клиента на новый токен, который даёт доступ первому ресурсу ко второму ресурсу. Либо в ситуации, когда мы хотим понизить права токена (например, пришёл токен админа, но нам дальше не нужен админ, а достаточно обычного пользователя), тогда в целях безопасности можно обменять токен, и использовать уже новый токен.
Обмен токенов выполняется запросом на endpoint получения токенов со следующими параметрами:
grant_type=urn:ietf:params:oauth:grant-type:token-exchange;
subject_token=<токен, который надо обменять>
;и другие необязательные параметры. В ответ в случае успеха получаем новый токен, с которым можно выполнять новые запросы.
OAuth 2.0 Mutual-TLS Client Authentication and Certificate-Bound Access Tokens
Ссылка на спецификацию: RFC 8705
Описывается возможность использовать mTLS (mutual TLS, взаимный TLS) для
аутентификация клиента - вместо использования хранения секретов клиента
client_secret
можно клиенту выдать собственный сертификат, который используется для формирования TLS-соединения. Так как у каждого клиента свой уникальный (по возможности с использование надёжной криптографией) сертификат, то сервер авторизации может аутентифицировать клиентов по сертификату;привязка токена к сертификату клиента - при выдаче токена сервер авторизации может привязать токен к сертификату клиента, таким образом на всех запросах помимо токена клиент обязан организовать соединение к ресурсу также через mTLS с помощью своего сертификата, подтверждая владение токенов.
Resource Indicators for OAuth 2.0
Ссылка на спецификацию: RFC 8707
Описывается расширение к OAuth 2.0, с помощью которого клиент может на старте авторизации указать серверу авторизации, к какому конкретному ресурсу нужно получить доступ. Очень похоже на параметр
scope
, ноscope
как правило описывает группу ресурсу, в то время как в данном RFC добавляется возможность указать конкретный URL, куда будут отправляться запросы. RFC добавляет к запросу на старт авторизации параметрresource
, в который указывается URL, для которого предназначен токен. Сервер авторизации в зависимости от значений параметра resource может, например, вернуть opaque или JWT токен, использовать различный алгоритмы подписи JWT-токена и так далее.JSON Web Token Best Current Practices (Best Current Practice)
Ссылка на спецификацию: RFC 8725
Очередной (и не последний) набор рекомендованный практик. В данном случае акцент делается на использовании JWT. Приводятся уязвимости, слабости и угрозы использования JWT, а также набор рекомендаций, включая обязательной валидации подписи токенов, валидации атрибутов токенов (
iss
,sub
,acr
,aud
) использовании UTF-8, стойких криптографических алгоритмов и другие. Рекомендуется к ознакомлению.JSON Web Token (JWT) Profile for OAuth 2.0 Access Tokens
Ссылка на спецификацию: RFC 9068
Продолжаем мучить JWT. На первый взгляд может показать, что данный RFC похож JSON Web Token (JWT) RFC 7519. И это так. Но с момента выхода RFC 7519 вышло несколько дополнительных RFC, которые расширяли список атрибутов JWT. Более того, существует несколько спецификаций, которые не относятся напрямую в OAuth 2.0, но тем не менее активно используют OAuth 2.0 (речь, конечно же, про OpenID Connect), которые добавляют новые атрибуты в JWT. Соответственно, в данной спецификации собрано полное описание всех атрибутов, которые можно использовать в JWT, с описанием что за атрибут и в какой спецификации объявлен с разбивкой на логические группы.
The OAuth 2.0 Authorization Framework: JWT-Secured Authorization Request (JAR)
Ссылка на спецификацию: RFC 9101
В запросе на старт авторизации необходимо передавать параметры запроса (например, client_id, redirect_uri, response_type и другие), которые являются частью запроса. В данной спецификации предлагается вместо передачи кучи параметров собрать эти параметры в один JWT, подписать и отправить на сервер авторизации. Например, вместо запроса
GET /authorize?response_type=code&client_id=s6BhdRkqt3&state=xyz&redirect_uri=https%3A %2F%2Fclient%2Eexample%2Ecom
Формируем JWT
{ "response_type": "code", "client_id": "s6BhdRkqt3", "state": "xyz", "redirect_uri": "https://client.example.com" }
Преобразуем в base64:
eyJhbGciOiJSUzI1NiIsImtpZCI6ImsyYmRjIn0.ewogICJyZXNwb25zZV90eXBlIjogImNvZGUiLAogICJjbG llbnRfaWQiOiAiczZCaGRSa3F0MyIsCiAgInN0YXRlIjogInh5eiIsCiAgInJlZGlyZWN0X3VyaSI6ICJodHRw czovL2NsaWVudC5leGFtcGxlLmNvbSIKfQ.Nsxa_18VUElVaPjqW_ToI1yrEJ67BgKb5xsuZRVqzGkfKrOIX7B Cx0biSxYGmjK9KJPctH1OC0iQJwXu5YVY-vnW0_PLJb1C2HG-ztVzcnKZC2gE4i0vgQcpkUOCpW3SEYXnyWnKz uKzqSb1wAZALo5f89B_p6QA6j6JwBSRvdVsDPdulW8lKxGTbH82czCaQ50rLAg3EYLYaCb4ik4I1zGXE4fvim9 FIMs8OCMmzwIB5S-ujFfzwFjoyuPEV4hJnoVUmXR_W9typPf846lGwA8h9G9oNTIuX8Ft2jfpnZdFmLg3_wr3W a5q3a-lfbgF3S9H_8nN3j1i7tLR_5Nz-g
И тогда запрос на авторизацию будет иметь вид:GET /authorize?client_id=s6BhdRkqt3&request=`eyJhbGciOiJSUzI1NiI...LR_5Nz-g`
где в параметре request передаётся JWT. Также вместо самого JWT в параметре request можно передать параметр адрес
request_uri
, откуда сервер авторизации может самостоятельно забрать запрос для старта авторизации.OAuth 2.0 Pushed Authorization Requests
Ссылка на спецификацию: RFC 9126
В базовой спецификации OAuth 2.0 RFC 6749 сказано, что сервер авторизации для старта авторизации обязан поддерживать GET-запросы и опционально может поддерживать POST-запросы. Но как правило все выполняют именно GET-запросы, у которых есть ряд ограничений, а именно:
ограничение длины запроса - так как все параметры являются частью URI, а прокси и веб-серверы могут обрезать (и обрезают) длинные запросы;
нет механизма для проверки целостности запроса (включая криптографические средства защиты);
нет возможности обеспечить конфиденциальность запроса, все будут видеть URI, а следовательно и все параметры.
Предыдущая спецификация (RFC 9101) предоставляет механизм для обеспечения целостности, в данной же спецификации предлагается передавать данные в теле POST-запроса вместо GET-запроса, убирая остальные ограничения (тело запроса может быть любой длины, кроме того тело запроса уже зашифровано TLS).
OAuth 2.0 Authorization Server Issuer Identification
Ссылка на спецификацию: RFC 9207
В спецификации добавляется новый атрибут iss в ответе от сервера авторизации, в котором указывается эмитент токена. Это позволяет клиенту провести валидацию того, что токен выдан тем, у кого запрашивался. Данное актуально в первую очередь для клиентов, которые работают с несколькими серверами авторизации и позволяет избежать атаки, когда инициируют авторизацию для одного эмитента, а затем подменяют ответ, как будто он пришёл от другого сервера авторизации. Для клиентов, работающих только с одним сервером авторизации, данный тип атак не актуален, просто дополнительный атрибут для валидации.
JWK Thumbprint URI
Ссылка на спецификацию: RFC 9278
Узкоспециализированное дополнение, которое регистрирует специальный префикс urn:ietf:params:oauth:jwk-thumbprint для отпечатка JWK. Используется в спецификации Self-Issued OpenID Provider v2.
OAuth 2.0 Rich Authorization Requests
Ссылка на спецификацию: RFC 9396
Описывает дополнительный параметр
authorization_details
для запроса на старт авторизации, чтобы клиент мог передать дополнительные данные серверу авторизации, чтобы помочь серверу авторизации принять решение. Также спецификация предлагает общие атрибуты для JSON. Например, параметр может иметь следующий вид:{ "type": "customer_information", "locations": [ "https://example.com/customers" ], "actions": [ "read", "write" ], "datatypes": [ "contacts", "photos" ] }
Данный атрибут может использоваться вместе с scope и resource (RFC 8707).
OAuth 2.0 Demonstrating Proof of Possession (DPoP)
Ссылка на спецификацию: RFC 9449
Описывает механизм, с помощью которого клиент может подтвердить владение токенов с помощью асимметричной криптографии. Может использоваться для защиты от использования утёкших токенов.
Клиент формирует пару сертификатов (генерирует на лету, получает от сервера авторизации или иным другим способом) и на запросе токена вместе с запросом передаёт заголовок
DPoP
, в котором указывает JWT вида:{ "typ":"dpop+jwt", "alg":"ES256", "jwk": { "kty":"EC", "x":"l8tFrhx-34tV3hRICRDY9zCkDlpBhF42UQUfWVAWBFs", "y":"9VE4jf_Ok_o64zbTTlcuNJajHmt6v9TDVrU0CdvGRDA", "crv":"P-256" } } . { "jti":"-BwC3ESc6acc2lTc", "htm":"POST", "htu":"https://server.example.com/token", "iat":1562262616 } . { ... }
Сервер авторизации привязывает токен к публичному ключу и, возвращая токен, указывает тип токена как
DPoP
вместо bearer, показывая, чтобы помимо самого токена надо подтвердить владение токеном. И на всех запросах помимо самого токена, который передаётся в заголовкеAuthorization: Bearer
необходимо передать заголовокDPoP
, в котором JWT, подтверждающий, что клиент владеет токеном.OAuth 2.0 Step Up Authentication Challenge Protocol
Ссылка на спецификацию: RFC 9470
Иногда серверу авторизации достаточно по scope принять решение о выдаче токена, но это не всегда. Например, в электронной коммерции, принимать решение о выдаче токена надо в зависимости от уровня пользователя (KYC, лимиты и пр.). В данной спецификации добавляется новый тип ошибки
insufficient_user_authentication
, который говорит о том, что уровень пользователя недостаточен. Также в заголовокWWW-Authenticate
добавляется два параметраacr_values
иmax_age
, в которых указывается необходимый уровень пользователя. Используя эти данные, клиент может на старте авторизации запросить необходимый уровень доступа (и сервер аутентификации, например, запросит дополнительные данные о пользователе).Best Current Practice for OAuth 2.0 Security (Best Current Practice)
Ссылка на спецификацию: RFC 9700
Вишенка среди всех спецификаций, относящихся к OAuth 2.0, дополняет RFC 6819 и другие описывает возможные атаки и слабые места, содержит набор рекомендаций к архитектуре и безопасности OAuth 2.0 (начиная с CSRF, PKCE, и заканчивая параметром nonce из OpenID Connect для защиты от атак через повторный запрос токена по коду). Фактически для освоения OAuth 2.0, достаточно прочитать базовую RFC 6749 и текущую спецификацию.
Настоятельно советую к ознакомлению.
JSON Web Token (JWT) Response for OAuth Token Introspection
Ссылка на спецификацию: RFC 9701
Описывает формат ответа на запрос получения данных о токене на сервере авторизации. Дополняет RFC OAuth 2.0 Token Introspection, предлагая отдавать ответ в формате JWT, который содержит данные о токене.
Заключение
Это все опубликованные спецификации. Ещё есть черновики, которых 20 штук. Среди них отдельно могу порекомендовать OAuth 2.1 и OAuth 2.0 for Browser-Based Applications. Первый описывает следующую версию платформы для авторизации, второй вводит архитектурный шаблон BFF (Backend For Frontend).
В следующей статье попробую описать как должен выглядеть фреймворк авторизации OAuth 2.0 с учётом всех рекомендаций и дополнений.