Тонкости авторизации: обзор технологии OAuth 2.0 / Хабр

Что такое оauth2.0?

Разработку нового Auth мы решили начать с изучения доступных протоколов и технологий. Самый распространённый стандарт авторизации — фреймворк авторизации OAuth2.0. 

Стандарт был принят в 2022 году, и за 8 лет протокол меняли и дополняли. RFC стало настолько много, что авторы оригинального протокола решили написать OAuth 2.1, который объединит все текущие изменения по OAuth 2.0 в одном документе. Пока он на стадии черновика.

Актуальная версия OAuth описанна в RFC 6749. Именно его мы и разберем. 

OAuth 2.0 — это фреймворк авторизации.

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

Особенности:


Разберёмся подробнее в особенностях.

Что такое single sign on

Технология единого входа (Single Sign On) значительно упрощает жизнь пользователей. Для перехода между разными сервисами, количество которых растет день ото дня, достаточно одной аутентификации и авторизации. Сложность только в том, что Identity-провайдеров, которые поддерживают Single Sign On, существует много.

Представьте: вы приходите в театр на какой-нибудь спектакль и на входе показываете билет. Эту процедуру можно отождествить с тем, как вы проходите аутентификацию на сайте. Ваш билет проверили и пропустили, но дальше вам необходимо попасть на определенное место и занять его. И это уже можно сравнить с авторизацией.

Аутентификация на основе токенов

Аутентификация на основе токенов в последние годы стала очень популярна из-за распространения одностраничных приложений, веб-API и интернета вещей. Чаще всего в качестве токенов используются Json Web Tokens (JWT). Хотя реализации бывают разные, но токены JWT превратились в стандарт де-факто.

При аутентификации на основе токенов состояния не отслеживаются. Мы не будем хранить информацию о пользователе на сервере или в сессии и даже не будем хранить JWT, использованные для клиентов.

Процедура аутентификации на основе токенов:

Что такое grant?

Grant — это данные, которые представляют из себя успешную авторизацию клиента владельцем ресурса, используемые клиентом для получения access token.

Например, когда мы где-либо аутентифицируемся с помощью Google, перед глазами всплывает уведомление. В нём говорится, что такой-то сервис хочет получить доступ к данным о вас или к вашим ресурсам (выводятся запрашиваемые scope-token). Это уведомление называется «Consent Screen».

В момент, когда нажимаем «ОК», в базу данных попадает тот самый grant: записываются данные о том, что такой-то пользователь дал такие-то доступы такому-то сервису. Клиент получает какой-то идентификатор успешной аутентификации, например строку, которая ассоциируется с данными в базе данных.

Существует 4 1 способа получения grant — grant type:

Azure b2c

Этот Identity-провайдер поддерживает те же протоколы, что и IdentityServer 4, а также SAML и внешние провайдеры. По умолчанию у него предустановлены еще и пользовательские flow (Sign in, Sign up, Edit Profile, Password Reset), поддержка мультифакторной аутентификации и управление пользовательскими доступами.

Чтобы установить и настроить Azure B2C, потребуется подписка на Azure. Дальше мы создаем B2C Tenant ресурс, регистрируем API и клиентов с помощью App registrations. Чтобы все это подключить, понадобится один Nuget-пакет — Microsoft.Identity.Web, после чего у нас из кода все заведется.

На данном слайде можно оценить дизайн админки этого провайдера и функциональность App registrations. Для примера я создала одну API и два клиентских приложения. Для них формируется удобный список, где можно посмотреть разную информацию (как к ним подключиться, какие есть айдишники, скопы и т.д.). А еще можно добавить какую-то новую регистрацию.

Cookies

Наверное вы часто слышали термин Cookies – это маленький фрагмент данных, которые система (сервер) хранит у пользователя. Каждый раз, когда пользователь открывает сайт – серверу отправляются данные Cookies, которые сервер сохранил у пользователя на устройстве.

Вы можете проектировать свои интерфейсы как с сохранением сессий пользователей, так и без сохранения.

Keycloak

Поддерживает все те же протоколы, что и Azure B2C: OpenID Connect, OAuth 2.0 и SAML — как и тех же Identity-провайдеров. У него также есть предустановленные пользовательские flow и — что тоже важно — LDAP и Active Directory. С их помощью можно интегрировать пользователей из других систем. Также отмечу предустановленное управление пользователями и их доступами.

Сам Keycloak написан на Java. Но так как сейчас в основном используется кроссплатформенность, и .NET Core является ярким представителем таких технологий, почему бы не попробовать интегрироваться с другим языком программирования? Keycloak как сервис предоставляется с помощью разных площадок (Kubernetes, Podman, Openshift, Docker).

Чтобы базово настроить flow авторизации, нужно выполнить следующий сценарий:

  1. Создать realm. Это точно такое же понятие, как и Tenant в Azure B2C, — то есть некая область или группа, в которые мы будем добавлять наше приложение в дальнейшем.

  2. Добавить клиентов. Ими выступают наши приложения, которые должны быть в этой системе.

  3. Добавить конфигурацию для этих приложений.

  4. Создать скопы.

  5. Создать роли.

Ниже вы можете видеть админку Keycloak. В первую очередь здесь приводится список клиентов в системе. Базово для аккаунта у нас есть уже клиент, он создается как часть сервиса. Но я также создала и зарегистрировала свое демо-приложение.

После перехода в меню приложения перед вами открывается множество возможностей для его конфигурации. Здесь представлены Client-скоупы, то есть стандартные скоупы (тот же e-mail, профайл и роли), но можно добавить и свои и подключить их на приложения.

В меню слева также собрано много полезной информации: мы можем смотреть и роли, и Identity-провайдеров, и пользователей, и сессии.

Плюсы Keycloak:

Среди минусов Keycloak:

Напоследок хотелось бы суммировать всю информацию о сервисах для авторизации и аутентификации, чтобы вам было проще выбрать подходящий для вашего проекта:

  • Все сервисы поддерживают примерно одни и те же протоколы.

  • По стоимости проигрывает Azure B2C из-за оплаты подписки и пользовательской активности.

  • По части .NET Core проигрывает Keycloak. 

  • Насчет менеджмента есть интересный нюанс. В IdentityServer 4 мы можем все реализовать из кода сами (управление ролями, где их передавать и в каком виде). В Azure B2C весь роль-менеджмент проходит через Access Policy. Но роль-менеджмент в Azure-портале распространяется только на сам портал, а не на приложения, которые мы добавляем. А вот Keycloak содержит плюсы обоих Identity-провайдеров. Он позволяет использовать keycloak-роли, чтобы дать юзеру доступ к страничке о пользователе, которая хостится самим Keyclock. В то же время мы можем добавлять роли на ресурсы и пользователям. Таким образом настройка ролей и их добавление происходит более гибко, и в результате роль можно получить из того же токена.

  • По сложности настройки самый замысловатый Azure B2C. Если требуется какой-то базовый вариант, то все относительно нормально, на среднем уровне. Но чтобы что-то поменять, нужно сильно постараться.

В теме авторизации и аутентификации нельзя сказать: выбирайте только один flow, только один Identity-сервер или делайте только так. Более того, приведенные выше варианты — не единственные. Я советую сравнить разные готовые решения, взвесить их преимущества и недостатки и определить, что лучше всего подойдет именно в вашей ситуации.

Также делюсь полезными ссылками на официальную документацию перечисленных Identity-провайдеров:

Openid connect

Это привело нас к спецификации под названием OpenID Connect (OIDC).

OIDC это спецификация поверх OAuth 2.0, которая говорит, как аутентифицировать пользователей. Стандарты OIDC разрабатываются OpenID Foundation (OIDF).

OIDC — это слой идентификации для аутентификации пользователей при помощи сервера авторизации.

Вы помните, что сервер авторизации выпускает токены. Токены — это закодированные кусочки данных для передачи информации между разными сторонами (такими как сервер авторизации, приложение или API). В случае OIDC и аутентификации сервер авторизации выпускает ID-токены.

Абстрактный oauth 2.0. flow c применением access token

Мы рассмотрели роли, рассмотрели виды токенов, а также как выглядят scope. Посмотрим на flow предоставления доступа к сервису.

Ниже представлена абстрактная схема (или flow) взаимодействия между участниками. Все шаги на данной схеме выполняются строго сверху вниз. Разберём детальнее.


Клиент получает одобрение от resource owner, на основе которого ему выдаётся доступ к ресурсу. Всё просто. А будет ли так же просто, если мы добавим в эту схему работу с refresh token?

Аутентификация между микросервисами

Помимо трафика, идущего на север от пользователей и третьих сторон, существует также большой объем трафика между микросервисами с востока на запад. Этот трафик может находиться в одной локальной сети или в разных центрах обработки данных. На трафик между этими сервисами могут влиять третьи стороны.Опасность перехвата и атак требует контроля безопасности.

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

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

Аутентификация пользователя

Полное приложение микросервиса состоит из нескольких независимых процессов микросервиса, и для доступа к каждому микросервису требуется проверка подлинности пользователя. Если работа по аутентификации пользователя помещается в каждый микросервис, логика аутентификации приложения будет очень сложной.

Следовательно, необходимо рассмотреть решение SSO (единого входа), то есть пользователям нужно только войти в систему один раз, чтобы получить доступ ко всем услугам, предоставляемым микросервисами. Поскольку API-шлюз используется в качестве входа для внешних служб в микросервисной архитектуре, можно рассмотреть возможность предоставления единой аутентификации пользователя на API-шлюзе.

Аутентификация с использованием базы данных

Стандартный способ аутентификации и авторизации агентов и клиентов — это база данных.

Например, настройки аутентификации агентов, используя базу данных, выглядит так:

$Self->{'AuthModule'} = 'Kernel::System::Auth::DB';


Клиентов:

Беспарольная аутентификация

Первой реакцией на термин «беспарольная аутентификация» может быть «Как аутентифицировать кого-то без пароля? Разве такое возможно?»

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

Беспарольная аутентификация — это способ конфигурирования процедуры входа и аутентификации пользователей без ввода паролей. Идея такая:

Вместо ввода почты/имени и пароля пользователи вводят только свою почту. Ваше приложение отправляет на этот адрес одноразовую ссылку, пользователь по ней кликает и автоматически входит на ваш сайт / в приложение. При беспарольной аутентификации приложение считает, что в ваш ящик пришло письмо со ссылкой, если вы написали свой, а не чужой адрес.

Есть похожий метод, при котором вместо одноразовой ссылки по SMS отправляется код или одноразовый пароль. Но тогда придётся объединить ваше приложение с SMS-сервисом вроде twilio (и сервис не бесплатен). Код или одноразовый пароль тоже можно отправлять по почте.

И ещё один, менее (пока) популярный (и доступный только на устройствах Apple) метод беспарольной аутентификации: использовать Touch ID для аутентификации по отпечаткам пальцев. Подробнее о технологии.

Если вы пользуетесь Slack, то уже могли столкнуться с беспарольной аутентификацией.

Внедрить единый вход

Концепция единого входа проста, то есть пользователям нужно только один раз войти в приложение, чтобы получить доступ ко всем микросервисам в приложении. API Gateway предоставляет клиентам доступ к приложениям микросервисов, а Token реализует аутентификацию пользователей без сохранения состояния. Объединение этих двух технологий позволяет реализовать решение единой регистрации для приложений микросервисов.

Процесс аутентификации пользователя аналогичен базовому процессу аутентификации токена, разница в том, что API-шлюз добавляется как вход внешних запросов.

Двухфакторная аутентификация (2fa)

Двухфакторная аутентификация (2FA) улучшает безопасность доступа за счёт использования двух методов (также называемых факторами) проверки личности пользователя. Это разновидность многофакторной аутентификации. Наверное, вам не приходило в голову, но в банкоматах вы проходите двухфакторную аутентификацию: на вашей банковской карте должна быть записана правильная информация, и в дополнение к этому вы вводите PIN.

Если кто-то украдёт вашу карту, то без кода он не сможет ею воспользоваться. (Не факт! — Примеч. пер.) То есть в системе двухфакторной аутентификации пользователь получает доступ только после того, как предоставит несколько отдельных частей информации.

Делегирование с областью видимости

Как API узнает, какой уровень доступа нужно дать приложению? Это определяется путем установления области видимости (scopes).

Область видимости «ограничивает, что именно приложение может делать в интересах пользователя». Она не позволяет выдать права, которых у пользователя уже нет. Например, если пользователь MyCalApp не имеет права создавать новые корпоративные аккаунты, область видимости гарантирует, что HireMe123 тоже не позволит пользователю создавать новые корпоративные аккаунты.

Области видимости делегируют контроль доступа самому API или ресурсу. За соответствие областей видимости правам пользователя отвечает API.

Давайте рассмотрим это на примере.

Я использую приложение HireMe123. HireMe123 хочет получить доступ к стороннему API MyCalApp для создания события в календаре от моего имени. Приложение HireMe123 уже запросило токен доступа к MyCalApp на сервере авторизации MyCalApp. В этом токене содержится важная информация:

  • sub: (мой пользовательский ID в MyCalApp);
  • aud: MyCalAppAPI (этот токен создан для доступа к API MyCalApp);
  • scope: write:events (область видимости предполагает, что HireMe123 может использовать API для записи событий в моем календаре).

HireMe123 посылает запрос к API MyCalApp с токеном доступа в заголовке авторизации. Когда API MyCalApp получает этот запрос, он видит, что в нем установлена область видимости write:events.

Но на MyCalApp содержатся аккаунты с календарями сотен тысяч пользователей. Поэтому, чтобы убедиться, что этот запрос от HireMe123 будет касаться только моих прав создавать события в моем аккаунте, промежуточное ПО API MyCalApp должно проверить не только область видимости, но и sub — идентификатор субъекта.

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

Заголовок jwt

Первый сегмент токена это заголовок. Он может выглядеть примерно так:

eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9

Заголовок токена это JSON-объект, содержащий алгоритм подписи и тип токена. Он зашифрован при помощи алгоритма base64Url (бинарные данные, представленные в виде текста).

В расшифрованном виде это выглядит примерно так:

{
  "alg": "RS256",
  "typ": "JWT"
}

Задача auth

Проблема авторизации в десятках сервисов встречалась ещё несколько лет назад — в начале «

». Эту проблему решили новым сервисом, который назвали – Auth. Он помог реализовать бесшовную аутентификацию в различных сервисах и перенести данные о пользователях в отдельные базы данных. 

У сервиса Auth есть три основные задачи:

Крипто-сегмент

Последний сегмент — это подпись или данные шифрования. JWT подписываются, чтобы их нельзя было модифицировать при пересылке. Когда сервер авторизации выпускает токен, он подписывает его при помощи ключа.

Получая ID-токен, клиент проверяет подпись (тоже при помощи ключа).

(При использовании асимметричного алгоритма для подписи и проверки используются разные ключи. В таком случае только у сервера авторизации есть возможность подписывать токены).

Не переживайте, если все это кажется запутанным. Детали того, как все работает, не должны вас беспокоить или мешать эффективно использовать сервер авторизации с аутентификацией на основе токенов.

Отдельный контроль разрешений для каждого микросервиса

Если микросервис не строго следует спецификации REST для моделирования объекта доступа или приложению требуется настраиваемый контроль разрешений, разрешения пользователя необходимо оценивать и обрабатывать отдельно в микросервисе. В этом случае управление разрешениями микросервисов более гибкое, но каждая микрослужба должна отдельно хранить данные авторизации пользователей, что сложнее реализовать.

Права доступа

Права доступа выдаются клиенту в виде scope. Scope – это параметр, который состоит из разделённых пробелами строк — scope-token.

Каждый из scope-token представляет определённые права, выдающиеся клиенту. Например, scope-token doc_read может предоставлять доступ на чтение к какому-то документу на resource server, а employee — доступ к функционалу приложения только для работников фирмы. Итоговый scope может выглядеть так: email doc_read employee.

В OAuth 2.0 мы сами создаём scope-token, настраивая их под свои нужды. Имена scope-token ограничиваются только фантазией и двумя символами таблицы ASCII — ” и .

На этапе регистрации клиента, в настройках сервиса авторизации клиенту выдаётся стандартный scope по умолчанию. Но клиент может запросить у сервера авторизации scope, отличный от стандартного. В зависимости от политик на сервере авторизации и выбора владельца ресурса, итоговый набор scope может выглядеть совсем иначе.

Предисловие

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

Проблемы, с которыми сталкиваются микросервисная аутентификация и аутентификация

В микросервисной архитектуре приложение разбивается на несколько микросервисных процессов, и каждая микрослужба реализует бизнес-функцию модуля в исходном монолитном приложении. После разделения приложения каждый запрос доступа к микросервису должен пройти аутентификацию и аутентификацию. Если вы обратитесь к реализации одного приложения, вы столкнетесь со следующими проблемами:

  • Аутентификация и логика аутентификации должны обрабатываться в каждой микрослужбе, и эту часть общей логики необходимо многократно реализовывать в каждой микрослужбе. Хотя мы можем использовать базу кода для повторного использования части кода, это приведет к тому, что все микросервисы будут полагаться на конкретную базу кода и ее версию, что повлияет на гибкость выбора языка / инфраструктуры микросервисов.
  • Микросервисы должны следовать принципу единой ответственности. Микросервис обрабатывает только единственную бизнес-логику. Общая логика аутентификации и аутентификации не должна помещаться в реализацию микросервиса.
  • Чтобы в полной мере использовать преимущества архитектуры микросервисов и реализовать масштабируемость и отказоустойчивость микросервисов, лучше всего, чтобы микросервисы не имели состояния. Поэтому не рекомендуется использовать сеанс в качестве решения с отслеживанием состояния.
  • Аутентификация и проверка подлинности в микросервисной архитектуре включают более сложные сценарии, в которых пользователи получают доступ к приложениям микросервисов, сторонние приложения получают доступ к приложениям микросервисов, а также взаимный доступ между несколькими микросервисами в приложении. Каждый сценарий Для обеспечения гарантии необходимо рассмотреть следующие схемы проверки подлинности безопасность приложения.Тонкости авторизации: обзор технологии OAuth 2.0 / Хабр

Три сценария проверки подлинности микросервисов и проверки подлинности

Проблемы, связанные с использованием токенов доступа для аутентификации

Если HireMe123 полагает, что успешный вызов API MyCalApp при помощи токена доступа означает, что пользователь может считаться аутентифицированным в HireMe123, мы сталкиваемся с проблемами. Дело в том, что у нас нет возможности проверить, был ли токен доступа выпущен для данного человека.

Например:

  • Кто-нибудь мог украсть токен доступа другого пользователя.
  • Токен доступа мог быть получен от другого клиента (не HireMe123) и вставлен в HireMe123.

Эта проблема получила название confused deputy. HireMe123 не знает, откуда пришел токен и для кого он был выпущен. Давайте припомним: аутентификация — это проверка, действительно ли пользователь является тем, за кого себя выдает. То, что HireMe123 может использовать токен доступа для доступа к API, не дает ему оснований полагать, что пользователь действительно является тем, кем назвался.

Процессы аутентификации

Так как наши данные при регистрации в каком либо сервисе хранятся в базе данных – то к процессу аутентификации применяются базовые принципы работы с базами данных (далее БД) – это чтение, запись, обновление и удаление данных. При этом, во время каждого из действий с БД проверяется возможность совершения этих действий пользователем.

  1. Регистрация (CREATE) – создание в системе вашей личной учётной записи

  2. Авторизация (READ) – получение доступа к вашей личной учётной записи

  3. Восстановление доступа к учётной записи (UPDATE) – если вы на пример забыли пароль – то его можно сменить, подтвердив свою личность.

  4. Удаление (DELETE) – удаление вашей учётной записи из системы

Реализация одного приложения

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

После проверки того, что личность пользователя является законной, для пользователя создается сеанс (Session), и уникальный номер (Session Id) связывается с сеансом. . Сеанс – это небольшая структура памяти в приложении, в которой хранится информация о вошедшем в систему пользователе, такая как имя пользователя, роль, разрешение и т. Д.

Сервер возвращает идентификатор сеанса клиенту, а клиент записывает идентификатор сеанса с помощью файлов cookie или перезаписи URL-адреса и отправляет его приложению в последующих запросах, чтобы приложение могло использовать сеанс при получении клиентского доступа.

Примечание. Чтобы избежать перехвата и присвоения идентификатора сеанса третьей стороной, клиент и приложение должны использовать TLS для шифрования связи раньше, и для сеанса также будет установлен срок действия.


Схема последовательности аутентификации пользователя при входе в одно приложение

Реализация сервера авторизации oauth с помощью сервера авторизации spring

Тонкости авторизации: обзор технологии OAuth 2.0 / Хабр

Сервер авторизации в OAuth предназначен для выдачи маркера доступа, который позволяет клиентскому приложению использовать этот маркер доступа для запроса ресурса, который ему нужно получить. Сервер ресурсов будет подтверждать этот маркер доступа с помощью сервера авторизации каждый раз, когда клиентское приложение запрашивает ресурс, чтобы определить, следует ли разрешить клиентскому приложению доступ к этому ресурсу. Вы можете использовать множество различных открытых источников, таких как Keycloak, Spring Security OAuth (устаревший), или же новый проект Spring под названием Spring Authorization Server для реализации этого сервера авторизации. В этом руководстве я покажу вам, как использовать сервер авторизации Spring (Spring Authorization Server) для реализации сервера авторизации OAuth (OAuth Authorization Server)!

Сначала я создам новый проект Spring Boot с Web Starter, Security Starter:

Тонкости авторизации: обзор технологии OAuth 2.0 / Хабр

и сервер авторизации Spring:

<dependency>
    <groupId>org.springframework.security.experimental</groupId>
    <artifactId>spring-security-oauth2-authorization-server</artifactId>
    <version>0.1.2</version>
</dependency>

для примера.

Результат:

Тонкости авторизации: обзор технологии OAuth 2.0 / Хабр

Конфигурация сервера авторизации

Сначала я создам новый класс AuthorizationServerConfiguration для настройки сервера авторизации.

По умолчанию сервер авторизации Spring  поддерживает класс OAuth2AuthorizationServerConfiguration с конфигурациями по умолчанию для сервера авторизации. Если взглянуть на код этого класса, то можно увидеть, что он определяет метод applyDefaultSecurity(), который инициализирует объект OAuth2AuthorizationServerConfigurer с целью применения конфигураций по умолчанию, которые определяет этот класс OAuth2AuthorizationServerConfigurer:

public static void applyDefaultSecurity(HttpSecurity http) throws Exception {
    OAuth2AuthorizationServerConfigurer<HttpSecurity> authorizationServerConfigurer =
            new OAuth2AuthorizationServerConfigurer<>();
    RequestMatcher endpointsMatcher = authorizationServerConfigurer
            .getEndpointsMatcher();

    http
        .requestMatcher(endpointsMatcher)
        .authorizeRequests(authorizeRequests ->
            authorizeRequests.anyRequest().authenticated()
        )
        .csrf(csrf -> csrf.ignoringRequestMatchers(endpointsMatcher))
        .oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt)
        .apply(authorizationServerConfigurer);
}

Как вы можете видеть, метод applyDefaultSecurity() также  определяет безопасность для конечных точек сервера авторизации по умолчанию.

Класс OAuth2AuthorizationServerConfiguration также определяет бин (bean) для класса SecurityFilterChain, который вызывает метод applyDefaultSecurity() для регистрации этих конфигураций по умолчанию в Spring Security сервера авторизации.

Вы можете импортировать этот класс OAuth2AuthorizationServerConfiguration с помощью аннотации Spring @Import, чтобы использовать эти конфигурации по умолчанию:

package com.huongdanjava.springauthorizationserver;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.security.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;

@Configuration
@Import(OAuth2AuthorizationServerConfiguration.class)
public class AuthorizationServerConfiguration {

}

или если вы хотите добавить что-то из пользовательского кода, то давайте объявим бин для класса SecurityFilterChain и вызовем метод applyDefaultSecurity() следующим образом:

package com.huongdanjava.springauthorizationserver;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
public class AuthorizationServerConfiguration {

    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
        OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);

        return http.formLogin(Customizer.withDefaults()).build();
    }

}

Здесь я добавляю дополнительный код, чтобы в случае, если у пользователя нет разрешения на запрос к конечным точкам по умолчанию сервера авторизации, сервер перенаправит его на страницу входа в систему.

При использовании сервера авторизации нам важно определить веб-ключ JSON для проверки информации в маркере доступа, который пользователь запросил у сервера ресурсов, выданном сервером авторизации. Для завершения настройки этого сервера авторизации требуется бин JwtDecoder с объектом класса JWKSource. Мы можем определить бины для этих объектов следующим образом:

@Bean
public JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {
    return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);
}

@Bean
public JWKSource<SecurityContext> jwkSource() throws NoSuchAlgorithmException {
    RSAKey rsaKey = generateRsa();
    JWKSet jwkSet = new JWKSet(rsaKey);

    return (jwkSelector, securityContext) -> jwkSelector.select(jwkSet);
}

private static RSAKey generateRsa() throws NoSuchAlgorithmException {
    KeyPair keyPair = generateRsaKey();
    RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
    RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();

    return new RSAKey.Builder(publicKey)
        .privateKey(privateKey)
        .keyID(UUID.randomUUID().toString())
        .build();
}

private static KeyPair generateRsaKey() throws NoSuchAlgorithmException {
    KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
    keyPairGenerator.initialize(2048);

    return keyPairGenerator.generateKeyPair();
}

Конфигурация Spring Security

Когда сервер авторизации осуществляет перенаправление на страницу входа в систему, поскольку пользователь не прошел аутентификацию, нам следует определить еще один SecurityFilterChain для обработки этого запроса и всех остальных запросов сервера авторизации. Это необходимо, поскольку класс OAuth2AuthorizationServerConfiguration определяет безопасность только для конечных точек сервера авторизации по умолчанию.

Мы можем определить SecurityFilterChain следующим образом:

package com.huongdanjava.springauthorizationserver;

import org.springframework.context.annotation.Bean;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;

@EnableWebSecurity
public class SpringSecurityConfiguration {

    @Bean
    SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
        http
            .authorizeRequests(authorizeRequests ->
                authorizeRequests.anyRequest().authenticated()
            )
            .formLogin(Customizer.withDefaults());

        return http.build();
    }

}

На этом этапе страница входа будет отображаться, если пользователь не вошел в систему.

Регистрация клиента на сервере авторизации

Сервер авторизации Spring использует класс RegisteredClient для объявления информации клиента, зарегистрированного на сервере авторизации, и реализует интерфейс RegisteredClientRepository для хранения информации всех этих клиентов.

Мы можем объявить информацию о клиенте, используя память или соответствующую базу данных:

Тонкости авторизации: обзор технологии OAuth 2.0 / Хабр

Для простоты я буду использовать память следующим образом:

@Bean
public RegisteredClientRepository registeredClientRepository() {
    RegisteredClient registeredClient = RegisteredClient.withId(UUID.randomUUID().toString())
        .clientId("huongdanjava")
        .clientSecret("{noop}123456")
        .clientAuthenticationMethod(ClientAuthenticationMethod.POST)
        .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
        .redirectUri("https://oidcdebugger.com/debug")
        .scope(OidcScopes.OPENID)
        .build();

    return new InMemoryRegisteredClientRepository(registeredClient);
}

Есть несколько важных свойств, которыми должен обладать клиент: идентификатор клиента и тип гранта авторизации, активированный для этого идентификатора.

Что такое идентификатор клиента, объяснять не нужно! По поводу типа гранта авторизации, следует отметить, что сервер авторизации Spring поддерживает все типы грантов OAuth 2.

Секрет клиента зависит от типа клиента, который мы хотим определить, если наш клиент конфиденциальный, см. также Типы клиентов в OAuth 2.0, то секрет клиента обязателен. Здесь вам нужно объявить, как зашифровать клиентский секрет с помощью PasswordEncoder, если вы не хотите шифровать его в целях тестирования, то можно использовать NoOpPasswordEncoder, объявив “{noop}” в начале клиентского секрета, как я сделал выше. Помните, что это применяется только в тестовых целях!

Если клиент является конфиденциальным, нам также понадобится Метод Client Authentication для определения маркера доступа.

В зависимости от типа гранта клиента, который вы определяете, есть и другая необходимая информация, которую нам нужно объявить. Например, в моем случае я определяю клиента с типом разрешения authorization_code, поэтому мне нужно определить redirect_uri. Здесь я буду использовать инструмент https://oidcdebugger.com/ для получения кода авторизации, поэтому я определяю redirect_uri со значением https://oidcdebugger.com/debug, как это можно видеть в примере.

В зависимости от ваших потребностей, давайте определим информацию о клиенте соответствующим образом.

Регистрация пользователя на сервере авторизации

Для регистрации пользователя на сервере авторизации используется память со следующим объявлением:

@Bean
public UserDetailsService users() {
    UserDetails user = User.withDefaultPasswordEncoder()
        .username("admin")
        .password("password")
        .roles("ADMIN")
        .build();

    return new InMemoryUserDetailsManager(user);
}

Итак, на данном этапе мы завершили базовую конфигурацию для Authorization Server.

Чтобы проверить результаты, я воспользуюсь инструментом https://oidcdebugger.com/, как упоминалось выше, со следующим объявлением:

Тонкости авторизации: обзор технологии OAuth 2.0 / Хабр

Нажмите кнопку Send request (Отправить запрос) на этой странице, после чего вы откроете страницу входа на сервер авторизации, которая будет выглядеть следующим образом:

Тонкости авторизации: обзор технологии OAuth 2.0 / Хабр

Войдите в систему с информацией, которую мы объявили выше, и увидите следующие результаты:

Тонкости авторизации: обзор технологии OAuth 2.0 / Хабр

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

Тонкости авторизации: обзор технологии OAuth 2.0 / Хабр

Материал подготовлен в рамках курса «Разработчик на Spring Framework». Если вам интересно узнать подробнее о формате обучения и программе, познакомиться с преподавателем курса — приглашаем на день открытых дверей онлайн. Регистрация здесь.

Регистрация клиента

Способ регистрации клиента, например, ручной или service discovery, вы выбираете сами, в зависимости от

фантазии

конкретной реализации. Но при любом способе при регистрации, кроме ID клиента, должны быть обязательно указаны 2 параметра: redirection URI и client type.

Redirection URI — адрес, на который отправится владелец ресурса после успешной авторизации. Кроме авторизации, адрес используется для подтверждения, что сервис, который обратился за авторизацией, тот, за кого себя выдаёт.

Client type — тип клиента, от которого зависит способ взаимодействия с ним. Тип клиента определяется его возможностью безопасно хранить свои учётные данные для авторизации — токен. Поэтому существует всего 2 типа клиентов:

Токены

Токен в OAuth 2.0 — это строка, непрозрачная для клиента. Обычно строка выглядит как случайно сгенерированная — её формат не имеет значения для клиента. Токен — это ключ доступа к чему-либо, например, к защищённому ресурсу (access token) или к новому токену (refresh Token).

У каждого токена своё время жизни. Но у refresh token оно должно быть больше, т.к. он используется для получения access token. Например, если срок жизни access token около часа, то refresh token можно оставить жить на целую неделю. 

Refresh token опционален и доступен только для confedential клиентов. Пользуясь опциональностью токена, в некоторых реализациях время жизни access token сделано очень большим, а refresh token вообще не используется, чтобы не заморачиваться с обновлением.

За access token закреплён определённый набор прав доступа, который выдаётся клиенту во время авторизации. Давайте разберёмся, как выглядят права доступа в OAuth 2.0.

Токены доступа

Токены доступа используются для предоставления доступа к ресурсам. Благодаря токену доступа, выпущенному сервером авторизации MyCalApp, HireMe123 может получить доступ к API MyCalApp.

В отличие от ID-токенов, которые OIDC определяет как JSON веб-токены, токены доступа не имеют четко определенного формата. Они не обязательно являются JWT. Тем не менее, во многих решениях для токенов доступа все же используются JWT, потому что этот формат позволяет валидацию.

Токены доступа непрозрачны для клиента

Токены доступа предназначаются для API ресурса и важно, чтобы они были непрозрачны для клиента. Почему?

Токены доступа могут измениться в любой момент. У них должно быть короткое время устаревания, чтобы пользователь мог часто получать новые. Также они могут выпускаться повторно, чтобы дать возможность доступа к другим API или с другими правами. В клиентском приложении ни в коем случае не должно быть кода, полагающегося на содержимое токена доступа. Подобный код будет слишком хрупким и практически гарантированно сломается.

Упрощённое объяснение термина “сессия”

Сессия – это механизм сохранения состояния авторизации пользователя в системе на заданный срок. Сессий одного пользователя может быть много. Примером может служить сессии в социальных сетях – когда вы можете зайти в свой аккаунт социальной сети с нескольких устройств одновременно, без необходимости выходить из учётной записи на другом устройстве.

Для того что бы различать входы с различных устройств – каждой сессии присваивается свой уникальный для каждой авторизации номер (Токен), который хранится в Cookies. Это нужно для того, что бы при выходе (закрытии сессии) с одного из устройств вы не выходили из своей учёной записи во всех остальных устройствах.

Заключение

Как вы увидели, настройка аутентификации и синхронизации аккаунтов в OTRS — это очень просто, но все же надеюсь, что эта статья поможет тем, кто только начинает знакомиться с этой системой.

Что осталось за бортом этой статьи?


Советы и уточнения приветствуются комментариях, информация об опечатках — в ЛС.

Спасибо за внимание. Отдельное спасибо авторам, чьи материалы по OTRS я использовал много лет назад при знакомстве с этой системой: Turilion, supersuperoleg, wmlex

Вместо вывода

В этой статье я опустил много подробностей, чтобы максимально просто и доступно рассказать о самом важном. Например, типы запросов, как и в каком виде передавать параметры, какие символы допустимы в качестве значений для того. 

Если хотите погрузиться в тематику детальнее, то рекомендую в RFC 6749 (для OAuth 2.0) и RFC 8628 (для Device Flow). Кроме того, следить за актуальными версиями RFC можно на ресурсе, посвящённому OAuth.

Если статья была полезна и захотите подробностей — пишите в комментариях, и в следующих статьях расскажу о PKCE, о протоколе аутентификации OpenID Connect 1.0, о нашей реализации сервера аутентификации и многом другом.

Полезные ссылки:

Похожее:  Как создать пользовательскую страницу входа в WordPress 2021

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *