OAuth 2.0 простым и понятным языком

На хабре уже писали про OAuth 1.0, но понятного объяснения того, что такое OAuth 2.0 не было. Ниже я расскажу, в чем отличия и преимущества OAuth 2.0 и, как его лучше использовать на сайтах, в мобильных и desktop-приложениях.
Что такое OAuth 2.0
OAuth 2.0 — протокол авторизации, позволяющий выдать одному сервису (приложению) права на доступ к ресурсам пользователя на другом сервисе. Протокол избавляет от необходимости доверять приложению логин и пароль, а также позволяет выдавать ограниченный набор прав, а не все сразу.
Чем отличаются OpenID и OAuth
Не смотря на то, что объяснений на эту тему уже было много, она по-прежнему вызывает некоторое непонимание.
OpenID предназначен для аутентификации — то есть для того, чтобы понять, что этот конкретный пользователь является тем, кем представляется. Например, с помощью OpenID некий сервис Ололо может понять, что зашедший туда пользователь, это именно Рома Новиков с Mail.Ru. При следующей аутентификации Ололо сможет его опять узнать и понять, что, это тот же Рома, что и в прошлый раз.
OAuth же является протоколом авторизации, то есть позволяет выдать права на действия, которые сам Ололо сможет производить в Mail.Ru от лица Ромы. При этом Рома после авторизации может вообще не участвовать в процессе выполнения действий, например, Ололо сможет самостоятельно заливать фотографии на Ромин аккаунт.
Как работает OAuth 2.0
Как и первая версия, OAuth 2.0 основан на использовании базовых веб-технологий: HTTP-запросах, редиректах и т. п. Поэтому использование OAuth возможно на любой платформе с доступом к интернету и браузеру: на сайтах, в мобильных и desktop-приложениях, плагинах для браузеров…
Ключевое отличие от OAuth 1.0 — простота. В новой версии нет громоздких схем подписи, сокращено количество запросов, необходимых для авторизации.
- получение авторизации
- обращение к защищенным ресурсам
- авторизация для приложений, имеющих серверную часть (чаще всего, это сайты и веб-приложения)
- авторизация для полностью клиентских приложений (мобильные и desktop-приложения)
- авторизация по логину и паролю
- восстановление предыдущей авторизации
Авторизация для приложений, имеющих серверную часть

- Редирект на страницу авторизации
- На странице авторизации у пользователя запрашивается подтверждение выдачи прав
- В случае согласия пользователя, браузер редиректится на URL, указанный при открытии страницы авторизации, с добавлением в GET-параметры специального ключа — authorization code
- Сервер приложения выполняет POST-запрос с полученным authorization code в качестве параметра. В результате этого запроса возвращается access token
Это самый сложный вариант авторизации, но только он позволяет сервису однозначно установить приложение, обращающееся за авторизацией (это происходит при коммуникации между серверами на последнем шаге). Во всех остальных вариантах авторизация происходит полностью на клиенте и по понятным причинам возможна маскировка одного приложения под другое. Это стоит учитывать при внедрении OAuth-аутентификации в API сервисов.
Пример
Здесь и далее примеры приводятся для API Mail.Ru, но логика одинаковая для всех сервисов, меняются только адреса страниц авторизации. Обратите внимание, что запросы надо делать по HTTPS.
Редиректим браузер пользователя на страницу авторизации:
> GET /oauth/authorize?response_type=code&client_id=464119& redirect_uri=http%3A%2F%2Fexample.com%2Fcb%2F123 HTTP/1.1 > Host: connect.mail.ru
Здесь и далее, client_id и client_secret — значения, полученные при регистрации приложения на платформе.
После того, как пользователь выдаст права, происходит редирект на указанный redirect_uri:
Обратите внимание, если вы реализуете логин на сайте с помощью OAuth, то рекомендуется в redirect_uri добавлять уникальный для каждого пользователя идентификатор для предотвращения CSRF-атак (в примере это 123). При получении кода надо проверить, что этот идентификатор не изменился и соответствует текущему пользователю.
Используем полученный code для получения access_token, выполняя запрос с сервера:
> POST /oauth/token HTTP/1.1 > Host: connect.mail.ru > Content-Type: application/x-www-form-urlencoded > > grant_type=authorization_code&client_id=464119&client_secret=deadbeef&code=DoRieb0y& redirect_uri=http%3A%2F%2Fexample.com%2Fcb%2F123 < HTTP/1.1 200 OK < Content-Type: application/json
Обратите внимание, что в последнем запросе используется client_secret, который в данном случае хранится на сервере приложения, и подтверждает, что запрос не подделан.
В результате последнего запроса получаем сам ключ доступа (access_token), время его «протухания» (expires_in), тип ключа, определяющий как его надо использовать, (token_type) и refresh_token о котором будет подробнее сказано ниже. Дальше, полученные данные можно использовать для доступа к защищенным ресурсам, например, API Mail.Ru:
> GET /platform/api?oauth_token=SlAV32hkKG&client_id=464119&format=json&method=users.getInfo& sig=. HTTP/1.1 > Host: appsmail.ru
Авторизация полностью клиентских приложений

- Открытие встроенного браузера со страницей авторизации
- У пользователя запрашивается подтверждение выдачи прав
- В случае согласия пользователя, браузер редиректится на страницу-заглушку во фрагменте (после #) URL которой добавляется access token
- Приложение перехватывает редирект и получает access token из адреса страницы
Этот вариант требует поднятия в приложении окна браузера, но не требует серверной части и дополнительного вызова сервер-сервер для обмена authorization code на access token.
Пример
Открываем браузер со страницей авторизации:
> GET /oauth/authorize?response_type=token&client_id=464119 HTTP/1.1 > Host: connect.mail.ru
После того, как пользователь выдаст права, происходит редирект на стандартную страницу-заглушку, для Mail.Ru это connect.mail.ru/oauth/success.html:
Приложение должно перехватить последний редирект, получить из адреса acess_token и использовать его для обращения к защищенным ресурсам.
Авторизация по логину и паролю
Авторизация по логину и паролю представляет простой POST-запрос, в результате которого возвращается access token. Такая схема не представляет из себя ничего нового, но вставлена в стандарт для общности и рекомендуется к применению только, когда другие варианты авторизации не доступны.
Пример
> POST /oauth/token HTTP/1.1 > Host: connect.mail.ru > Content-Type: application/x-www-form-urlencoded > > grant_type=password&client_id=31337&client_secret=deadbeef&username=api@corp.mail.ru& password=qwerty < HTTP/1.1 200 OK < Content-Type: application/json
Восстановление предыдущей авторизации
Обычно, access token имеет ограниченный срок годности. Это может быть полезно, например, если он передается по открытым каналам. Чтобы не заставлять пользователя проходить авторизацию после истечения срока действия access token‘а, во всех перечисленных выше вариантах, в дополнение к access token‘у может возвращаться еще refresh token. По нему можно получить access token с помощью HTTP-запроса, аналогично авторизации по логину и паролю.
Пример
> POST /oauth/token HTTP/1.1 > Host: connect.mail.ru > Content-Type: application/x-www-form-urlencoded > > grant_type=refresh_token&client_id=31337&client_secret=deadbeef&refresh_token=8xLOxBtZp8 < HTTP/1.1 200 OK < Content-Type: application/json
Минусы OAuth 2.0
Во всей этой красоте есть и ложка дегтя, куда без нее?
OAuth 2.0 — развивающийся стандарт. Это значит, что спецификация еще не устоялась и постоянно меняется, иногда довольно заметно. Так, что если вы решили поддержать стандарт прямо сейчас, приготовьтесь к тому, что его поддержку придется подпиливать по мере изменения спецификации. С другой стороны, это также значит, что вы можете поучаствовать в процессе написания стандарта и внести в него свои идеи.
Безопасность OAuth 2.0 во многом основана на SSL. Это сильно упрощает жизнь разработчикам, но требует дополнительных вычислительных ресурсов и администрирования. Это может быть существенным вопросом в высоко нагруженных проектах.
Заключение
OAuth — простой стандарт авторизации, основанный на базовых принципах интернета, что делает возможным применение авторизации практически на любой платформе. Стандарт имеет поддержку крупнейших площадок и очевидно, что его популярность будет только расти. Если вы задумались об API для вашего сервиса, то авторизация с использованием OAuth 2.0 — хороший выбор.
Со своей стороны, мы внедрили OAuth 2.0 в API Mail.Ru и, теперь, вы можете использовать возможности протокола для реализации любых клиентов и сервисов, интегрированных с Mail.Ru.
Ссылки
- Текущая версия драфта стандарта OAuth 2.0
- Официальный сайт OAuth
- Рабочая группа по выработке стандарта (архивы)
- Документация по реализации OAuth 2.0 в Mail.Ru
Все еще работаете с access token на клиенте? Тогда мы идем к вам
Организация работы с токенами в клиентской части веб-приложений — тема, которая на слуху давно и многократно обсуждалась, однако далеко не для всех современных веб-приложений все еще оцениваются риски в этой части, что приводит к уязвимым реализациям.
В статье рассмотрим причины необходимости работы с токеном на клиенте веб-приложений, узнаем ,что лучше для хранения токена: localStorage, sessionStorage или cookie без флага HttpOnly (спойлер, ничего из этого), а также посмотрим на меры воздействия, которые можно использовать для снижения риска утечки токена посредством различных уязвимостей.
Надеюсь, что данная статья будет полезна разработчикам, тестировщикам, аналитикам и архитекторам, в ней постарался рассмотреть вопрос с разных точек зрения и собрать широкую картину.

Введение
Аутентификация и авторизация в веб-приложениях и их правильное приготовления не перестают быть обсуждаемыми и дискутируемыми, даже несмотря на, казалось бы, популяризацию многих типовых подходов.
Тема статьи на самом деле далеко не нова и известна. Различные вариации работы с токенами используются давно. Однако в повседневной жизни я до сих пор встречаю веб-приложения, имеющие риски реализации, которые могут привести к увеличению критичности уязвимостей.
Некорректная работа с токенами в клиентской части веб-приложений или непонимание
их [веб-приложений] особенностей и необходимости с ними считаться ведет к созданию уязвимых продуктов, когда, казалось бы, как никогда должна быть актуальна тенденция повышения информационной безопасности. Статьей хочу привлечь внимание к проблеме, пояснить ее значимость, а также разобрать механизмы повышения безопасности при работе с токенами на клиенте.
Access token (токеном доступа) здесь и далее я буду называть некую строку, используемою для доступа к защищенному ресурсу (protected resource). Сам токен может иметь совершенно разные форматы и имплементации.
Также в статье будем оперировать понятиями XSS и CSRF .
Проблематика
Неподходящая для конкретного веб-приложения реализация аутентификации и работы с токенами на клиенте приводит к повышению уровня критичностей уязвимостей в нем. Самый простой пример: если у нас access token для доступа к ресурсам доступен в клиентской части, то любая XSS автоматически дает кражу учетной записи пользователя, поскольку внедряя и выполняя код на клиенте в контексте страницы мы можем получить доступ к тем же данным, к которым обращаются легитимные скрипты.
Такая проблема актуальна с начала прошлого десятилетия, когда были распространены случаи хранения сессий в не-HttpOnly cookies, но, кажется, мы стали снова про нее забывать.
Кто-то скажет здесь:
Ну так это пример в вакууме, мы делаем наши приложения без XSS-уязвимостей, у нас есть %framework_name% или %library_name%, там это все учтено. Зачем нам что-то додумывать, если мы сфокусируемся на том, чтобы не допускать такие уязвимости вообще?
Звучит резонно, не правда ли? Однако реальность, к сожалению, не так проста. Практика и подходы показывают нам, что гарантию такую дать в большинстве случаев почти нереально. К тому же запросто может быть случай, где угрозы безопасности нашим веб-приложениям приходят совсем из других мест: например, токен мы положили в какую-нибудь wildcard и не-HttpOnly-cookie, обезопасили свое приложение от XSS до зубов, но вот незадача, на одном из поддоменов обнаружился старый дырявый проект, который свел на нет все наши старания.
Чем опасен украденный access token? С ним злоумышленник сможет выполнять запросы к ресурсам API от нашего лица или же попросту имперсонализироваться под нашей учетной записью у себя в браузере.
Примеры (или набор вредных советов)
Далее как раз рассмотрим проблему на конкретных примерах, специфичных для веб-приложений, поскольку они, в отличие от других видов приложений, не предоставляют такого однозначного места для конфиденциального хранения информации. Примеры эти реальны, их я собрал, посещая различные сайты, однако все они здесь обезличены.
Access token в localStorage
В данном приложении access token после получения с бэкенда помещается в локальное хранилище localStorage.

В таком случае при наличии XSS-уязвимости токен может быть получен злоумышленником. Однако у токена указан срок жизни — 15 минут. Допустим ли риск того, что в течение N (N<=15) минут токеном пользователя может воспользоваться злоумышленник, - вопрос над которым при применении такого решения следует подумать.
Пара access и refresh token в localStorage
В этом примере приложение помещает в localStorage пару: access token и refresh token. Access token имеет срок жизни — 60 минут. Refresh token — при исследовании был валиден и спустя более чем 12 часов.

Здесь проблема из первого примера расширяется. Access token имеет уже куда больший срок жизни — целый час. Но также мы имеем и долгоживущий refresh token. Refresh token — строка, используемая для получения access token. В то время как access token используется для доступа к защищенному ресурсу refresh token позволяет обратиться за получением нового (или дополнительного) access token.
Таким образом, здесь, как и в примере ранее, в случае XSS злоумышленник может получить доступ к access token (который уже живет дольше), а также и к совсем долгоживущему refresh token.
Отмечу, что для повышения безопасности использования refresh-токенов существует набор мер, таких как ротация токенов, защита от переиспользования, грамотный подбор времени жизни и другие. Однако данными мерами следует скорее дополнять, но не заменять защиту токена от утечки при XSS.
Access token в wildcard не-HttpOnly cookie
Напоследок рассмотрим самый интересный вариант.

В примере мы видим, что токен помещается в сookie со сроком жизни — 1 год. Сама cookie при этом не имеет установленного значения флагов HttpOnly, Secure, SameSite. Кроме этого, видно, что домен начинается с точки: .%sitename%.com . Значит, данная cookie является wildcard, то есть будет доступна на всех поддоменах, удовлетворяющих такому условию.
- Во-первых, сам по себе access token, живущий год и при этом доступный на клиенте, является серьезной угрозой. При краже злоумышленником токена посредством XSS он будет действовать еще очень долго.
- Во-вторых, здесь токен помещен в cookie, которая доступна на всех поддоменах сайта. Это означает, что даже если в самом основном приложении XSS злоумышленник и не найдет, то среди содержимого наших поддоменов запросто может оказаться уязвимый сервис. Поиск поддоменов — один из типовых приемов, используемых при пентесте. На них может найтись много интересного: например, какой-то старый сайт или тестовый проект — потенциально уязвимый сервис. Также встречаются и атаки вида subdomain takeover (захват поддомена). В этом случае тоже злоумышленник сможет произвести кражу токена аналогичным способом.
Примечательно также, что подобную реализацию я встречал не единожды и, как видно, она несет наибольшие риски из всех перечисленных. Возможно, такое использование wildcard сookie где-то предполагалось для «бесшовной» аутентификации между поддоменами, однако хочется предостеречь от бездумного использования такого подхода.
Почему так, зачем нужно работать с токеном на клиенте?
Логичный вопрос: а зачем вообще нам тогда делать токен доступным на клиенте и работать с ним там? Почему бы не положить его просто в сессионную hardened-cookie? Кроме допущенных недостатков проектирования, я вижу для этого несколько предпосылок.
1. Использование stateless-токенов
Такой подход еще иногда называют «token-based authentication» (что на мой взгляд не совсем корректно, ведь токен у нас может быть и вполне stateful). Когда мы используем слово «сессия», мы скорее всего подразумеваем stateful-вариант. То есть на сервере хранится какая-то информация о нашей сессии, которая проверяется при обращении ресурсу.
В случае со stateless-токенами картина иная. Данный токен самодостаточен сам по себе и предполагает содержание в себе всей информации, необходимой для авторизации. Обычно для этого используют JWT-токены, которые имеют стандартную структуру.

За счет наличия блока signature токен может подписывать свое содержимое, и валидность подписи может быть проверена при получении запроса с данным токеном: приватным ключом при симметричном или публичным ключом при ассиметричном шифровании. Так валидация JWT может происходить без участия выдавшего его сервера и, соответственно, без необходимости обращения в БД для каждой проверки.
Подход позволяет как раз уйти от хранения токенов на сервере (по крайней мере, в его «ванильной» имплементации), отдавать такой JWT-токен после аутентификации и хранить его уже на том, что для нас является клиентом.
2. Использование OAuth 2.0/OIDC для получения access token в своем же приложении
Довольно популярная вещь — использование, например, Authorization code grant flow в OAuth2.0 или OpenID Connect (OIDC).
Не будем путать OpenID Connect с OAuth 2.0. OAuth 2.0 является протоколом для делегирования пользователем доступа к определенным ресурсам конкретному приложению. По своей спецификации он не предполагает стандартного способа получения identity пользователя, то есть пользователь дал нам доступ на выполнение ряда действий, а вот кто он – не сказал. OIDC расширяет его возможности и предоставляет такую возможность через получение ID token или использование userinfo-эндпоинта. Отличие в том, что в случае OIDC Authorization Server играет роль также и Resource server, но только для identity пользователя.
Причину я вижу в том, что by default реализации эндпоинта получения токена (token endpoint) для того же Authorization code flow (который продвигается как более безопасная реализация, нежели чем Implicit flow) в OAuth 2.0 или OIDC возвращают токен в application/json теле ответа с типом Bearer. И если в качестве Client используется public, а не confidential client (про них будет упоминаться ниже), то мы часто может встретить отправку запроса к token endpoint прямиком с клиента. Пример ответа вызова такого метода какой-нибудь типовой имплементации:
< "access_token": "", "token_type": "Bearer", "expires_in": 3600, "scope": "openid operations" >
Тип токена Bearer предполагается к использованию и самой спецификацией OIDC. Следствие кроется в самом названии типа. Bearer-токен — токен на предъявителя.
Соответственно, кто его предъявит, тот и является авторизованным пользователем. Поскольку токен доступен на клиенте, это и используется: обычно клиент отправляет к серверу запросы с заголовком Authorization: Bearer .
Ну и поскольку мы получаем токен в JSON-ответе от сервера, то на клиенте у нас не так много опций, куда этот токен можно безопасно положить.
Пару слов про OIDC и PKCE
Отдельное внимание хочу уделить использованию механизма PKCE в authorization code flow. PKCE рекомендован к использованию для public clients — клиентов, которые не имеют возможности конфиденциального хранения client secret, таким как SPA , однако применяется также и для confidential clients. Важно понимать, что PKCE не предоставляет абсолютно никакой защиты для токена на клиенте, он вообще не про это. Корректно реализованный PKCE дает возможность убедиться, что за получением токена обращается тот же субъект, который обращался за получением authorization code и все. Помните про это и не смешивайте эти понятия.
3. Использование Single-page applications (SPA) без бэкенда
Также это касается и использования Single-page applications (SPA) без бэкенда. Не используя бэкенд, разработчики вынуждены искать способы работы с токеном на клиенте.
Что можно сделать, варианты мер и решений
В интернете раньше были популярны споры, где лучше хранить токен для работы с ним: в localStorage, sessionStorage или сookies (естественно, без HttpOnly). На самом деле, с точки зрения безопасности разницы практически нет. И вот почему:
localStorage
sessionStorage
Cookies без флага HttpOnly
Доступно с клиента
Привязка к конкретному домену
Да (и шире, см. wildcard-случаи)
Контекст
Синхронизируется между вкладками
Ограничен пределами вкладки
Синхронизируется между вкладками
Персистентность
Сохраняет состояние после закрытия браузера
Сохраняет состояние при обновлении вкладки, теряет при ее закрытии
Сохраняет состояние после закрытия браузера
А как же IndexedDB?
С рассматриваемых в сравнении точек зрения IndexedDB не отличается от localStorage, за исключением одного нюанса: доступ к ней есть также и у service workers.
Как видно из сравнения выше, все три приведенных способа одинаково уязвимы при возможности выполнения вредоносного кода на клиенте. Что доступно из кода разработчику, доступно из кода и злоумышленнику.
А что же тогда делать? Как и везде, серебряной пули не существует, разные подходы имеют свои особенности, свои pros и cons — достоинства и недостатки. И здесь мы попробуем рассмотреть такие подходы независимо, тогда как выбор конкретного — индивидуальная задача, которую необходимо решить при проектировании.
Подход 1. Не работать с токеном на клиенте вообще
Именно так, первый и самый простой способ избежать рисков — отказаться от работы с токеном на клиенте. Использовать stateful-подход к аутентификации вместо stateless. Тогда мы можем использовать сессионную cookie для хранения нашего токена. Важно помнить, что для такой cookie потребуется корректная установка атрибутов HttpOnly , Secure , SameSite , Path . Также подчеркну, что не рекомендуется бездумно использовать wildcard cookies (причины были рассмотрены выше), поэтому внимание стоить уделить и атрибуту Domain .
Однако есть нюанс. В таком случае, поскольку данная cookie будет отправляться на сервер в запросах к указанным Domain и Path , необходимо предусмотреть защиту от CSRF-атаки. Сделать это можно, например, согласно рекомендациям OWASP.
Такой подход лишает нас возможностей, предоставляемых stateless-токенами, однако он делает наши риски более прозрачными и может быть проще в реализации.
Подход 2. Проксирующий бэкенд
А что если мы хотим оставить возможность работы со stateless-токенами? Здесь возможно использование middleware-слоя, который будет обеспечивать безопасность хранения токена.
Наглядно это можно представить на схеме. Сначала посмотрим, как у нас выглядит упрощенный процесс аутентификации и обращения к ресурсу без проксирующего бэкенда:

Пару слов про Authorization server
По RFC 6749 под Authorization server понимается сервер, выпускающий access-токены для клиента после успешной аутентификации. Поэтому в данном случае Authorization server выполняет аутентификацию.
Authorization server и Resource server могут быть различными сервисами или одним и тем же, их реализация зависит от выбранной архитектуры приложения.
Имеем все те проблемы, о которых упоминали выше. Теперь добавим наш промежуточный слой:

Выглядит сложнее, давайте разбираться. Здесь Backend proxy уже выступает не public-, а confidential-клиентом (RFC 6749, п. 2.1), и должен выполнять обращение за получением токена со своими client id и client secret.
В процессе аутентификации и получения токена мы обращаемся не напрямую к Authorization server, а через Backend proxy, что показано в [1]. Соответственно и access token также будет получать он ([4]). Затем сервер генерирует некую cookie (подробнее рассмотрим ниже) c флагом HttpOnly, которую и отправляет на клиент ([6]). Клиент далее в [7] обращается к API ресурса, но делает это также не напрямую (поскольку он не владеет токеном доступа), а через наш Backend proxy. Здесь происходит аутентификация клиента и «размен» значения из cookie на легитимный access_token, с которым идет обращение к Resource server ([9]), а полученный ответ проксируется обратно на клиент ([12]).
Встает вопрос: какую cookie может выдавать наш Backend proxy? Здесь вижу несколько подходов.
- Обыкновенная сессионная cookie, которая идет в пару полученному токену. В таком случае в нашем компоненте нам придется реализовать работу с сессиями и хранить их. Однако так мы заодно получим способ управления инвалидацией доступа.
- Шифрование полученных от Authorization server значений. В таком случае мы избегаем необходимости хранить сессию у себя. При получении запроса на обращение к ресурсу мы производим расшифровку значения из cookie (ключ для этого у нас есть) и проксируем запрос далее.
Сроком действия данных cookie также необходимо управлять самостоятельно, также необходимо помнить про установку значений других их атрибутов. Отдельно отмечу, что такая реализация несет еще ряд тонкостей, таких как подбор времени жизни access_token, организация его кэширования в Backend proxy, механизм обновления access_token и т.д. Вследствие наличия и так большого объема информации, оставим эти вопросы за рамками данной статьи.
Подобный подход может быть применим и к SPA, поскольку, делая Single-page application, совсем не обязательно полностью отказываться от бэкенда, такая тонкая прослойка может быть вполне используема. В микросервисной архитектуре роль Backend proxy может выполнять API gateway или Backend-for-frontend (BFF).
Итак, данный подход предоставляет нам возможность защититься от кражи токена через XSS, однако делает возможной CSRF-атаку, поэтому меры, по защите от нее также должны быть применены.
Подход 3. Добавление пользовательского контекста в токен
До этого мы использовали подходы только с HttpOnly cookies, но что если есть и иной путь? Добавление пользовательского контекста в токен предполагает использование вместе как HttpOnly cookie, так и части, доступной на клиенте. Метод проще всего объясним на примере JWT-токена.
Пользовательский контекст может состоять из следующей информации:
- Случайная строка, сгенерированная сервисом в процессе аутентификации. Передается на клиент в HttpOnly cookie (помним и про другие атрибуты).
- SHA256-хэш от случайной строки, помещенный в payload JWT-токена.
Очень упрощенно это можно изобразить так:

Таким образом для авторизации, помимо проверки JWT-токена, необходимо еще и сравнение хэша от случайной строки, полученной в hardened-cookie, с хэшированным значением в самом токене. Токен при этом может быть сохранен как в сookies, так и в localStorage или sessionStorage — сути это не меняет, поскольку токен сам по себе становится недостаточным для доступа к Resource server.

Существует также еще интересная вариация, которую я тоже отнесу к данному подходу из-за схожести исполнения — Two Cookie JWT Approach. Здесь мы аналогично используем HttpOnly и не-HttpOnly cookie, получаемые с сервера, но принцип разделения информации несколько отличается.
В HttpOnly cookie здесь мы помещаем подпись JWT-токена, в то время как header и payload находятся в доступной на клиенте cookie. Тогда наше обращение к API будет выглядеть так:

При этом важно не забыть о грамотном выставлении атрибута Max-Age у cookies. Таким образом, авторизацию мы все еще выполняем на основе проверки JWT-токена, однако полная его «версия» может быть получена только путем совмещения двух частей, что снижает последствия эксплуатации XSS-уязвимости, поскольку подпись нам с клиента недоступна. При использовании одних только cookies здесь, как и ранее, тоже следует принять меры защиты от CSRF-атак.
Подход 4. Использование service worker
Service worker — скрипт, который браузер запускает в фоновом режиме, выполняющий роль прокси-сервера для взаимодействия между веб-приложением, браузером и сетью. Service worker запускается в отдельном контексте, работает в отдельном потоке, не имеет доступа к DOM, и соответственно клиент также не имеет доступа к service worker и хранимым там данным. Этой его особенностью мы и воспользуемся, чтобы обезопасить access token от утечки при XSS.
В этом случае service worker отвечает за получение токена от Authorization server и выполнение запросов к Request server. Запросы с клиента в данном случае проксируются service worker, он как бы перехватывает их. Следовательно вызов метода получения токена и сам токен полностью изолированы, поскольку контекст service worker недоступен для прочих JavaScript-контекстов.

Напоминает рассмотренную ранее схему с проксирующим бэкендом, не правда ли? Однако здесь средний слой, выступающий в качестве прокси, мы реализуем не на сервере, а на клиенте.
У использования service worker существует еще одна важная деталь: его регистрация должна происходить в самом начале загрузки клиентской части приложения, в противном случае при эксплуатации XSS злоумышленник может инициировать новый flow аутентификации (до регистрации service worker) и получить токен в обход него.
В данном случае CSRF-атака неприменима, поскольку токен доступен только для service worker, а также исключается возможность кражи токена посредством XSS, однако реализация и эксплуатация такой схемы будут сложнее. Технология поддерживается современными браузерами, но, если требуется поддержка Internet Explorer, то такой подход не подойдет.
Подход 5. Хранение токена в памяти
JavaScript предоставляет возможность хранения полученного значения токена в памяти. Для этого используется имитация приватного свойства класса через «closure variable» — локальную переменную внутри замыкания. Тогда мы можем создать некий token-manager-class, который будет хранить значение токена и выполнять самостоятельно все обращения к Resource server, не допуская доступности токена снаружи.
Такой подход возможен, однако имеет ряд особенностей. Во-первых, значение токена не будет сохраняться после перезагрузки страницы и быть доступным из других вкладок. Во-вторых, злоумышленник может перехватить запрос на клиенте уже после его формирования, например, использовав monkey patching для метода fetch.
Таким образом мы рассмотрели различные способы снижения рисков утечки нашего токена с клиента. Все они могут подходить для использования в каких-то конкретных случаях, все имеют свои особенности. Также вижу возможной и комбинацию нескольких подходов: например, фоллбэк с использования service worker при неудачной его регистрации на другой механизм.
Подчеркну, что мы говорили здесь только про кражу токена, в большинстве случая при наличии XSS злоумышленник все еще сможет отправить легитимные запросы к вашему API непосредственно из браузера жертвы, однако меры для предовтращения завладения токеном все равно важны.
Заключение
Тема безопасности аутентификации и авторизации достаточно обширна, и ее не охватить одной статьей. Здесь постарался рассмотреть вопрос хранения и работы с access token в клиентской части веб-приложений, какие риски он может нести и какие меры для их снижения существуют. Я намеренно использую слово «риски» — поскольку степень угрозы для каждого приложения может быть своя, но понимать ее важно.
Также для интересующихся привожу список статей по теме.
Список релевантного чтива
- OAuth 2.0 Security Best Current Practice [Internet-Draft]
- OAuth 2.0 for Browser-Based Apps [Internet-Draft]
- OWASP JSON Web Token Cheat Sheet for Java
- auth0.com — Token Storage
- Authentication in SPA (ReactJS and VueJS) the right way — Part 1
- Getting Token Authentication Right in a Stateless Single Page Application
- Best OAuth Security Practices for Single Page Applications
- OAuth and Single Page JavaScript Web-Apps
Помните о безопасности, проверяйте свои приложения и оценивайте риски.
API Token
В начале работы с API VMware Cloud Director необходимо пройти аутентификацию , в результате которой вы указываете данные своей учетной записи и взамен получаете Access Token. Вместо учетных данных можно указать API Token, который принадлежит пользователю тенанта. API Token удобно использовать для работы различных API-клиентов, в т. ч. скриптов для автоматизации управления.
Идентификатор, выпущенный пользователем. Позволяет изменить способ аутентификации и получить Access Token для авторизации дальнейших вызовов API.
API Token создается и удаляется в VMware Cloud Director.
API Token бессрочный, но может быть отозван пользователем.
Идентификатор сессии, который используется для авторизации при вызовах API.
Access Token создается при аутентификации с помощью стандартного способа аутентификации (с использованием логина и пароля) или API Token (с помощью вызова метода API, описанного ниже).
Срок жизни Access Token определяется сроком жизни сессии, которым управляет провайдер Cloud.ru.
- Ограничения API Token
- Генерация API Token
- Получение доступа с помощью API Token
- Проверка получения доступа
- Отзыв API Token
Ограничения API Token
При аутентификации с помощью API Token пропадают следующие возможности:
- изменение паролей пользователей;
- управление пользователями (создание, удаление, изменение);
- создание других API Token;
- просмотр и отзыв других API Token.
Генерация API Token
Убедитесь , что для роли администратора организации включено право ACCESS CONTROL → User → Manage user’s own API token (для управления своим API Token) и право Manage all user’s API tokens (для управления API Token других пользователей тенанта). Они необходимы для генерации и управления API Token, и без указанных прав блок Access Tokens будет недоступен в интерфейсе VMware Cloud Director.
Чтобы сгенерировать API Token для текущего пользователя, выполните следующие шаги в VMware Cloud Director:
- Справа сверху раскройте меню пользователя и нажмите User preferences .
- В блоке Access Tokens нажмите NEW .
- Укажите название API Token и нажмите CREATE .
- Скопируйте сгенерированный API Token.
Важно API Token можно скопировать только на этом шаге.
Далее проверьте доступ с помощью полученного API Token.
Получение доступа с помощью API Token
API Token нужен для получения Access Token, который далее используется во всех запросах сессии и помогает выполнять различные операции с доступной инфраструктурой.
Чтобы получить Access Token необходимо знать:
- — сгенерированный API Token.
- — зависит от региона, в котором размещается ваш виртуальный ЦОД. Он отображается в ссылке на VMware Cloud Director https:///tenant/my-tenant/ . Ее мы отправляем при подключении услуги. Например, для региона PD01 параметр принимает значение vcd.sbercloud.ru , для PD11 — vcd11.msk.sbercloud.ru .
- — название тенанта , которое можно посмотреть в URL-адресе для входа в VMware Cloud Director https:///tenant/ .
cURL (format) cURL (sample) Postman (sample)
curl -k --header "Accept: application/json" --header "Content-Type: application/x-www-form-urlencoded" --header "Content-Length: 71" --data "grant_type=refresh_token&refresh_token=" --request POST "https:///oauth/tenant//token"
curl -k --header "Accept: application/json" --header "Content-Type: application/x-www-form-urlencoded" --header "Content-Length: 71" --data "grant_type=refresh_token&refresh_token=pkAtl. " --request POST "https://vcd.sbercloud.ru/oauth/tenant/my-tenant/token"
POST https://vcd.sbercloud.ru/oauth/tenant/my-tenant/token Headers: - KEY: Accept - VALUE: application/json - KEY: Content-Type - VALUE: application/x-www-form-urlencoded - KEY: Content-Length - VALUE: 71 Body: grant_type=refresh_token&refresh_token=pkAtl.
Вы получите ответ с Access Token:
200 ОК "access_token": "eyJhbG. ", "token_type": "Bearer", . >
API Token действителен даже после выхода пользователя из системы. После истечения срока действия Access Token приложение может получить новый Access Token с помощью API Token.
Проверка получения доступа
Отправьте любой запрос, используя Access Token, и убедитесь, что он выполнился корректно. Например, запросите список доступных тенантов.
cURL (format) cURL (sample) Postman (sample)
curl -k --header "Accept: application/*;version=36.1" --header "Authorization: Bearer " --request GET "https:///api/org/"
curl -k --header "Accept: application/*;version=36.1" --header "Authorization: Bearer eyJhbG. " --request GET "https://vcd.sbercloud.ru/api/org/"
GET https://vcd.sbercloud.ru/api/org/ Headers: - KEY: Accept - VALUE: application/*;version=36.1 Authorization: - Type: Bearer Token - Token: eyJhbG.
Вы получите ответ вида:
href="https:///api/org/" name=""/> .
Access Token, Refresh Token
Ресурс /v2/oauth/token используется для получения Access Token.
Перед использованием ресурса /v2/oauth/token необходимо получить сертификаты и настроить TLS соединение.
Host и полный url для вызова ресурса на тестовом и промышленном контурах см. в разделе URL для вызова API СберБизнес ID.
Ресурс /v2/oauth/token позволяет получать Access Token путем обмена кода авторизации (см. получение кода авторизации в разделе Authorization code или токена обмена (Refresh Token), который предоставляется вместе с Access Token.
Направления взаимодействия, описанные в разделе, выделены на схеме красным.
При запросе ресурса /v2/oauth/token используются параметры, полученные при регистрации приложения Партнера.
Параметры запроса Access Token
Параметры, которые необходимо передавать при вызове ресурса /v2/oauth/token при запросе Access Token.
| Параметр | Описание |
|---|---|
| Заголовок | |
| Content-Type | Обязательный параметр. Должен содержать значение application/x-www-form-urlencoded |
| Accept | Необязательный параметр. |
| Если ответ не требуется в зашифрованном виде, то может быть не указан или передан со значением application/json. | |
| Если необходимо получить ответ на запрос токена в зашифрованном виде, то необходимо передать параметр со значением application/jose. | |
| Параметры запроса (entity body) | |
| grant_type | Обязательный параметр. Значение должно быть authorization_code |
| code | Обязательный параметр. В параметре необходимо использовать значение code полученное Приложением партнера на адрес redirect_uri . Каждый code может быть использован только один раз. Например, если при запросе access_token по коду авторизации была допущена ошибка в каком либо из параметров и передано значение code , то повторное использование данного значения code уже будет недопустимо. |
| client_id | Обязательный параметр. Уникальный идентификатор Приложения Партнера, полученный при регистрации приложения. |
| redirect_uri | Обязательный параметр. Должен в точности совпадать со значением переданным ранее при вызове ресурса /v2/oauth/authorize. Пример: Если в redirect_uri было указано https://example.ru/auth/login/register , то точно такое же значение должно быть указано при запросе /v2/oauth/token, даже если зарегистрирован redirect_uri = https://example.ru/auth/login |
| client_secret | Обязательный параметр. Секрет Приложения Партнера, полученный при регистрации приложения. |
| code_verifier | Необязательный параметр. Параметр в соответствии с надстройкой PKCE над протоколом OAuth 2.0. Раскодированное значение code_challenge переданное ранее при вызове ресурса /authorize . См. правила формирования параметра в PKCE. Становится обязательным, если при регистрации приложения настройка PKCE была подключена или при вызове ресурса /authorize параметр code_challenge был передан. |
Вызов ресурса должен осуществляться только с использованием экранирования специальных символов.
Пример запроса без экранирования
POST /ic/sso/api/v2/oauth/token Host: edupirfintech.sberbank.ru:9443 Content-Type: application/x-www-form-urlencoded Accept: application/json grant_type=authorization_code&code=f710576d-7263-4ec6-a01b-8404aca2850d-1&client_id=999999&client_secret=da658d9360604e41a5bac9611d0f1620&redirect_uri=https://example.ru/login?return_to=settings
Пример запроса с экранированием
POST /ic/sso/api/v2/oauth/token Host: edupirfintech.sberbank.ru:9443 Content-Type: application/x-www-form-urlencoded Accept: application/json grant_type=authorization_code&code=f710576d-7263-4ec6-a01b-8404aca2850d-1&client_id=999999&client_secret=da658d9360604e41a5bac9611d0f1620&redirect_uri=https%3A%2F%2Fexample.ru%2Flogin%3Freturn_to%3Dsettings
Содержание ответа на запрос Access Token
В теле ответа на запрос Access Token будут содержаться следующие параметры:
| Параметр | Описание |
|---|---|
| Заголовок ответа | |
| Content-Type | Если в запросе: — не был передан заголовок Accept или передан в значении application/json, то в ответе параметр будет содержать значение application/json — был передан заголовок Accept в значении application/jose, то в ответе будет указано application/jose и полученное тело ответа потребуется расшифровать. Более подробно о расшифровке ответа, см. в разделе Access Token, Refresh Token |
| Параметры ответа | |
| access_token | Авторизационный токен |
| token_type | Тип токена. Всегда возвращается значение «Bearer» |
| expires_in | Срок жизни токена в секундах. Срок жизни access_token составляет 60 минут. |
| refresh_token | Токен обновления. Используется для обновления Access Token. Подробнее про применение значения refresh_token см. в Параметры запроса на обновление Access Token Срок жизни refresh_token составляет 180 дней. |
| scope | Область сведений (разрешения), до которых разрешен доступ Приложению Партнера. |
| id_token | Закодированный в Base64URL набор атрибутов клиента, необходимых для идентификации пользователя. Атрибуты разделены символами «.», каждый необходимо декодировать отдельно. Подробнее о декодировании и параметрах id_token см. в ID Token |
Пример ответа
HTTP/1.1 200 OK Content-Type: application/json "scope": "openid PAYROLL name", "access_token": "c76fb018-27c9-43f7-a751-62646eda7e1a-1", "token_type": "Bearer", "expires_in": 3600, "refresh_token": "03e0be32-e72e-47ec-b740-a00b333a8ac4-1", "id_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJnb3N0MzQuMTAtMjAxMiJ9.eyJzdWIiOiI1OGMzMzE2ZTBmMDgyYzk1ZjZlZDE1ZGZlNzdmNzZkOTljNjI2ZGVhMmQ4ZTJmZWYyNDcyMzM5Y2QyNzMzNWQzIiwiYXVkIjoiNjMyMjk0IiwiYWNyIjoibG9hLTMiLCJhenAiOiI2MzIyOTQiLCJhdXRoX3RpbWUiOjE2MzE1MzQ1NjIsImFtciI6Intwd2QsIG1jYSwgbWZhLCBvdHAsIHNtc30iLCJpc3MiOiJodHRwOi8vc2J0LW9hZnMtNjM4OjkwODAvaWNkayIsImV4cCI6MTYzMTUzNDkxMSwiaWF0IjoxNjMxNTM0NjExLCJub25jZSI6IjdiZTY2YWM5LWQwN2MtNDk2Ny1hZGVkLWNhMjcwYTI3ZTllOCJ9.B46H281g1HKLnTOxhitBI25zgmEseNyGLp1hFQGPWnZWlv5xAjJhAJSQPTbiseLmX_MVKhaT9Dq3QF2F0fa7Vg" >
Параметры запроса на обновление Access Token
При обмене кода авторизации на токен, СберБизнес ID возвращает пару access_token и refresh_token .
Полученный refresh_token применяется для обновления access_token после истечения срока его действия или в случае его компрометации.
При использовании refresh_token формируется новая пара access_token и refresh_token .
При этом использованный refresh_token переводится в статус резервного на 2 часа с момента выпуска новой пары ключей ( access_token/refresh_token ).
Если по каким-то причинам сформированная пара не была получена от банка, то рекомендуется повторно отправить запрос на актуализацию ключей в течение 1 часа от момента отправки первой попытки, используя тот же refresh_token .
Для последующих обновлений ключей доступа необходимо использовать refresh_token из новой пары ключей ( access_token/refresh_token ).
Для обновления access_token по refresh_token , необходимо вызывать ресурс /v2/oauth/token со следующими параметрами:
| Параметр | Описание |
|---|---|
| Заголовок | |
| Content-Type | Обязательный параметр. Должен содержать значение application/x-www-form-urlencoded |
| Accept | Необязательный параметр. |
| Если ответ не требуется в зашифрованном виде, то может быть не указан или передан со значением application/json. | |
| Если необходимо получить ответ на запрос токена в зашифрованном виде, то необходимо передать параметр со значением application/jose. | |
| Параметры запроса (entity body) | |
| grant_type | Обязательный параметр. Значение должно быть refresh_token |
| client_id | Обязательный параметр. Уникальный идентификатор Приложения Партнера, полученный при регистрации приложения. |
| refresh_token | Обязательный параметр. Значение refresh_token полученное при обмене кода авторизации на access_token |
| client_secret | Обязательный параметр. Секрет Приложения Партнера, полученный при регистрации приложения. |
Пример запроса
POST /ic/sso/api/v2/oauth/token Host: edupirfintech.sberbank.ru:9443 grant_type=refresh_token &refresh_token=03e0be32-e72e-47ec-b740-a00b333a8ac4-1 &client_id=999999 &client_secret=da658d9360604e41a5bac9611d0f1620
Содержание ответа на запрос обновления Access Token
Параметры ответа на запрос access_token по refresh_token аналогичны ответу на запрос Access Token по коду авторизации за исключением отсутствия параметра id_token .
Пример ответа
HTTP/1.1 200 OK Content-Type: application/json "scope": "openid PAYROLL name", "access_token": "c76fb018-27c9-43f7-a751-62646eda7e1a-1", "token_type": "Bearer", "expires_in": 3600, "refresh_token": "03e0be32-e72e-47ec-b740-a00b333a8ac4-1" >
Коды возврата
В случае возникновения ошибок при вызове ресурса /v2/oauth/token в ответ будут возвращены следующие параметры:
| Параметр | Описание |
|---|---|
| error | Код возникшей ошибки |
| error_description | Описание возникшей ошибки |
Перечень исключений, которые могут быть возвращены при вызове ресурса /v2/oauth/token :
| error | error_description | Описание причины |
|---|---|---|
| SSOREQUESTED_FORMAT_NOT_ACCEPTABLE_EXCEPTION | В соответствии с текущими настройками сервиса с clientId= необходимо запрашивать ответ в формате JSON | Для Приложения Партнера установлен запрет на прием запросов с http-заголовком Accept со значением application/jose |
| invalid_grant | Missing grant_type parameter value | Не заполнен обязательный параметр grant_type |
| invalid_grant | One of the params ( code , refresh_token ) is required at request | Не заполнен один из обязательных параметров code или refresh_token |
| unsupported_grant_type | Grant type » is not supported» | В параметре grant_type указано значение отличное от authorization_code или refresh_token |
| invalid_request | Missing parameters: code | В параметре grant_type указано значение authorization_code , но отсутствует значение в параметре code |
| invalid_grant | Failed to extract shoulder ID from | В параметре code указано некорректное значение |
| invalid_request | Missing parameters: refresh_token | В параметре grant_type указано значение refresh_token , но отсутствует значение в параметре refresh_token |
| invalid_grant | Failed to extract shoulder ID from | В параметре refresh_token указано некорректное значение |
| unauthorized_client | Unknown client_id = » | Переданный client_id не зарегистрирован |
| invalid_grant | Unknown code = » | Не найдено переданное значение code. Например, потому что срок действия кода истек или по данному коду уже осуществлялся вызов. |
| invalid_grant | Unknown refresh token = » Не найдено переданное значение refresh_token . Например, потому что срок действия токена истек или по данному токену уже был осуществлен обмен на access_token . | |
| invalid_grant | Ext service for authz code » is blocked | Приложение Партнера зарегистрированное по указанному client_id — заблокировано. |
| unauthorized_client | Client » is blocked | Приложение Партнера зарегистрированное по указанному client_id — заблокировано. |
| invalid_grant | Invalid credentials for authz code » | При запросе access_token по коду авторизации указан неверный c lient_secret` |
| invalid_grant | Invalid credentials for refresh_token » | При запросе access_token по refresh_token указан неверный client_secret |
| invalid_request | Missing parameters: | Отсутствует параметр redirect_uri |
| invalid_grant | Redirect uri » is invalid | Переданный параметр redirect_uri не совпадает со значением полученным ранее при запросе кода авторизации |
| invalid_request | client secret expired | Истек срок действия client_secret |
| invalid_request | Code verifier required | При запросе кода авторизации был указан code_challenge , но в параметрах запроса токена отсутствует параметр code_verifier |
| invalid_request | Invalid code verifier | Значение code_verifier не отвечает параметрам протокола PKCE |
| invalid_grant | Failed to verify code verifier | Хешированное значение code_challenge , полученное в запросе /authorize не совпадает со значением полученным в параметре code_verifier |
Также могут быть получены следующие параметры в ответе при обращении к серверу:
| Параметр | Описание |
|---|---|
| errorCode | Код возникшей ошибки |
| errorMsg | Описание возникшей ошибки |
Данный тип параметров возвращается, если на сервере возникли следующие исключения:
| http-code | errorCode/errorMsg | Описание причины |
|---|---|---|
| 403 | requestForbidden |
ID Token
- Является основным расширением, которое спецификация OpenID Connect накладывает на протокол OAuth 2.0 и добавляется в ответ на запрос Access Token, если в состав scope включено значение openid;
- Является токеном безопасности и содержит информацию об аутентификации пользователя с использованием СберБизнес ID для входа в Приложение Партнера;
- Представляет собой JSON Web Token (JWT).
В соответствии со спецификации JSON Web Token (JWT) ID Token представлен структурой вида:
- Алгоритм подписи и тип токена (Header);
- Полезная нагрузка (Payload);
- Электронная подпись (Signature).
Каждая часть ответа, разделенная точкой должна декодироваться отдельно. Для декодирования ID Token следует воспользоваться алгоритмом Base64URL Encoding. Для проверки подписи в поле id_token на стороне Приложения Партнера, необходимо вычислить подпись публичным ключом Банка, декодировав блок Header и Payload по Base64URL (содержимое между двумя точками). Далее необходимо сравнить полученное значение c блоком Электронная подпись (содержимое после второй точки), декодированным по Base64URL.
Пример ID Token
eyJ0eXAiOiJKV1QiLCJhbGciOiJ nb3N0MzQuMTAtMjAxMiJ9.eyJzd WIiOiJmYWQ0YjYzMThiNjYyZDIx YjJlMTk2ZWJlYjBmMDhYWI3NDJj NGYzYmFhMmUxZTE1ZGNhNjkwZmM YjJkYTkwIiwiYXVkIjoiMTExMjk 2IiwiYWNyIjoibG9hLTMiLCJhen AiOiIxMTEyOTYiLCJhdXRoX3Rpb WUiOjE2MzExNjcxNjgsImFtciI6 Intwd2QsIG1jYSwgbWZhLCBvdHA sIHNtc30iLCJpc3MiOiJodHRwO 8vc2J0LW9hZnMtNjM4OjkwODAva WNkayIsImV4cCI6MTYzMTE2NzUy NywiaWF0IjoxNjMxMTY3MjI3LCJ b25jZSI6IjdiZTY2YWM5LWQwN2M tNDk2Ny1hZGVkLWNhMjcwYTI3ZT llOCJ9.NnGurMyyyket3i9TkyRx LRbllZTxfqNJfrDhNXy7N_B5BbE fj3kdxKxaDMChug2MXflrbzn7WY SbSPV6H_Kjug
Заголовок. Алгоритм и тип токена
< "sub": "fad4b6318b662d21b2e196ebeb0f02aab742c4f3baa2e1e15dca690fc3b2da90", "aud": "111296", "acr": "loa-3", "azp": "111296", "auth_time": 1631167168, "amr": "", "iss": "http://sbt-oafs-638:9080/icdk", "exp": 1631167527, "iat": 1631167227, "nonce": "7be66ac9-d07c-4967-aded-ca270a27e9e8" >
По умолчанию в ID Token включены следующие параметры:
| Параметр | Описание |
|---|---|
| Заголовок (Header) | |
| typ | Тип токена |
| alg | Алгоритм шифрования |
| Полезная нагрузка (Payload) | |
| acr | Уровень аутентификации пользователя: — acr=loa-2 (пользователь аутентифицируется с помощью устройства защиты) — acr=loa-3 (пользователь использует подтверждение по одноразовому SMS-паролю для аутентификации) |
| amr | Методы аутентификации: — amr=pwd — пользователь аутентифицируется с помощью устройства защиты; — amr= — пользователь использует подтверждение по одноразовому SMS-паролю для аутентификации |
| aud | Идентификатор Приложения Партнера, для которого сформирован ID Token |
| auth_time | Время аутентификации пользователя в СберБизнес ID. Формат поля Unix time |
| azp | Идентификатор Приложения Партнера, для которого сформирован ID Token |
| exp | Время, после которого ID Token не принимается для обработки. Формат поля Unix time |
| iat | Время формирования ID Token. Формат поля Unix time |
| iss | URL сервиса, сформировавшего ID Token |
| nonce | Значение параметра nonce, полученного при запросе авторизации |
| sub | Уникальный идентификатор пользователя |
| Электронная подпись (Signature) | |
| Массив байт электронной подписи | Проверка электронной подписи, полученной в ID Token (содержимое ответа ID Tokena после второй точки), выполняется, используя алгоритм, указанный в параметре заголовка JWT alg. |
Если при формировании ID Token, какой-либо параметр не может быть заполнен, то такой параметр не будет включен в ответ.
ПАО Сбербанк использует cookie для персонализации сервисов и удобства пользователей.
Вы можете запретить сохранение cookie в настройках своего браузера.