Платформа 3V/Инструкция по работе с платформой/Принципы работы платформы 3V с аутентификацией и авторизацией
Содержание
- 1 Введение
- 2 Принципы авторизации
- 3 Пример минимального приложения, реализующего oidc сервер авторизации
- 4 Настройки файлов конфигурации сервисов бэкенда
- 5 Пример подключения сервера авторизации, работающего по протоколу oidc или по протоколу oauth 2.0 с поддержкой метаданных
- 6 Пример подключения сервера авторизации oauth 2.0 без поддержки метаданных, но реализующего стандартные эндпоинты, в том числе jwks_uri
- 7 Пример подключения сервера авторизации oauth 2.0 без поддержки метаданных, но реализующего стандартные эндпоинты, кроме jwks_uri
Введение
Для аутентификации и авторизации в платформе используется протокол OIDC (https://openid.net/connect/), реализованный общепринятым стандартным способом на основе JWT (https://datatracker.ietf.org/doc/html/rfc7519); кроме этого, есть возможность использовать протокол OAuth 2.0 (https://datatracker.ietf.org/doc/html/rfc6749), также реализованный общепринятым стандартным способом на основе JWT.
Почему важно упоминание об общепринятой реализации? Потому что, если вдумчиво вчитаться в вышеуказанные стандарты, то достаточно быстро становится ясно, что ощутимая их часть (особенно OAuth 2.0) описана как implementation specific и/или out of scope. Что это означает? Что протоколы (опять же, особенно OAuth 2.0) не вдаются в подробности деталей своей технической реализации, а больше описывают сам принцип своей работы. Если взять OAuth 2.0, то там неопределена бОльшая часть деталей; если взять OIDC - в нем достаточно детально описан путь аутентификации, но путь авторизации, опять же, отдается на откуп реализации (что неудивительно, поскольку сам по себе OIDC построен поверх OAuth 2.0).
К чему это потенциально приводит и реально приводило на практике? К тому, что каждый, кто делает поддержку этих протоколов (особенно это проявлялось в самом начале их, протоколов, становления, и, особенно, опять же, OAuth 2.0), приходил в итоге к какой-то своей частной/специфичной реализации, которая работала только у него. Соответственно, OAuth 2.0 сервер аутентификации Google, к примеру, мог работать только по спецификации того же Google с сервисами Google - ну или вам приходилось тоже поддерживать эту спецификацию (например, у Google токены изначально имели произвольный формат, не JWT; с одной стороны, конечно, можно предположить здесь использование reference/opaque-токенов, но с другой, стандарт на это (https://datatracker.ietf.org/doc/html/rfc7662), опять же, утвердился несколькими годами позже, и вполне вероятно, что как реакция на текущее положение дел).
Видя все это, комитет по стандартизации с течением времени разразился аж порядка 10 стандартов-дополнений к изначальному OAuth 2.0 (https://datatracker.ietf.org/doc/html/rfc8252, https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics, https://datatracker.ietf.org/doc/html/rfc6750 и т.д.), и в конечном счете все эти дополнения собрал в OAuth 2.1 (https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-02), впрочем, опять не фиксируя явно специфику реализации (того же формата токенов и/или конкретных используемых механизмов; спасибо и на том, что конкретика приводится хотя бы в качестве примеров со ссылками на соответствующие другие стандарты).
Видя все это, отрасль в целом постепенно пришла к некоторому единому пониманию использования и реализации этих протоколов, что вылилось в стандартные библиотеки для всех языков, которые можно подключить и сразу использовать, и в стандартные же реализации на стороне серверов авторизации.
В частности, на стороне веба платформа использует библиотеку angular-oauth2-oidc (https://github.com/manfredsteyer/angular-oauth2-oidc, https://www.npmjs.com/package/angular-oauth2-oidc) для самого процесса аутентификации, а для валидации и процессинга access-токена на стороне бэкэнда - стандартную реализацию .NET из пакета Microsoft.AspNetCore.Authentication.JwtBearer (https://www.nuget.org/packages/Microsoft.AspNetCore.Authentication.JwtBearer). Вкупе это все дает возможность использовать любой современный сервер авторизации, работающий по протоколу oidc/oauth2.0, в частности:
- Keycloak (https://www.keycloak.org/)
- IdentityServer4 (https://github.com/IdentityServer/IdentityServer4)
- Auth0 (https://auth0.com/)
- Active Directory Federation Services (https://docs.microsoft.com/ru-ru/windows-server/identity/active-directory-federation-services)
И так далее.
Стоит упомянуть также, что именно аутентификация, по-большому счету, может происходить совершенно произвольным способом, особенно если вместо стандартного веб-интерфейса платформы используется какой-то пользовательский. А вот уже авторизация на стороне бэкенда работает с access-токенами, которые больше относятся именно к OAuth 2.0. По-большому счету, весь процесс авторизации проходит в рамках чистого OAuth 2.0, из OIDC там лишь опционально используется механизм получения метаданных сервера авторизации (который, впрочем, по стандарту тоже относится к OAuth 2.0 - https://datatracker.ietf.org/doc/html/rfc8414).
Соответственно, для того, чтобы подключить любую стороннюю авторизацию к платформе, достаточно на стороне веба получать любой JWT access-токен, который сможет отвалидироваться бэкендом. Применительно к произвольному конкретному серверу авторизации есть два варианта:
- Этот сервер умеет работать по протоколу OIDC в его общепринятой стандартной реализации на базе JWT. Тогда достаточно использовать его соответствующее API, задать нужные разделы в файлах конфигурации - и все заработает.
- Этот сервер не умеет работать по протоколу OIDC в его общепринятой стандартной реализации. Тогда между платформой и этим сервером можно реализовать и поставить некоторый транслирующий сервер авторизации, который OIDC-совместимые запросы будет преобразовывать в те запросы, которые понимает имеющийся сервер, а ответы имеющегося сервера преобразовывать в OIDC-совместимые запросы.
Принципы авторизации
Авторизация в платформе происходит на основании ролей, указанных в пришедшем access-токене, а также на основании заданных уже в самой платформе доступов к операциям и/или объектам для соответствующих ролей. По сути просто происходит сопоставление тех ролей, которые указаны в токене, с теми, которые заданы в платформе. Если эти два множества имеют непустое пересечение - выдается максимальный доступ из пересечения. Если имеют пустое пересечение - доступ запрещается с кодом 403.
Пример минимального приложения, реализующего oidc сервер авторизации
Настройки файлов конфигурации сервисов бэкенда
В конфигурационном файле каждого сервиса (appsettings.json; или appsettings.Common.json, если общие настройки для всех сервисов вынесены в отдельный файл) есть раздел "Authorization":
1 "Authorization": {
2
3 "Enable": false, // признак включения/выключения обязательной аутентификации; при значении true неаутентифицированному пользователю будет отказано в произведении операции с кодом 401; при значении false неаутентифицированный пользователь считается администратором
4
5 "ClientId": "client-3d", // идентификатор клиента в терминах OAuth 2.0; значение этого свойства должно быть в атрибуте "aud" токена, чтобы токен считался валидным
6
7 "OIDCConfigurationFileName": "oidc.example.json", // файл, в котором задана конфигурация сервера авторизации, реализующего oidc/oauth2 протокол. Формат файла приведен ниже
8
9 "OIDCSigningKeysFileName": "keys.example.json", // файл, в котором указаны ключи шифрования для валидации токена в виде JWKS. Формат файла приведен ниже
10
11 "HS256Key": "BnZPK_poYt6nuwWMPg6DR....", // ключ шифрования для валидации токена по алгоритму HS256, в виде строки
12
13 "Authority": "https://auth.server.com/auth_root", // URL сервера авторизации, присоединив к которому справа строку "/.well-known/openid-configuration", можно получить метаданные сервера авторизации
14
15 "ExternalAuthority": "https://auth.server.com/external_auth_root", // то же, что и выше, но доступный во внешний мир, для обеспечения работы сваггера (Authority не всегда доступно наружу)
16
17 "CustomAuthorizationHeader": "MyAuthorizationHeader", // произвольная строка, указывающая на заголовок, в котором приходит токен доступа. По умолчанию (когда не задано), используется стандартный заголовок Authorization
18
19 "ValidIssuers": ["http://my.valid.issuer", "https://my.another.valid.issuer"], // набор строк, которые считаются допустимыми в качестве значения атрибута "iss" токена; на случай, если токен выдается не по тому URL, на который ведет Authority (ExternalAuthority при этом работает автоматически, явно его значение дублировать в ValidIssuers не нужно)
20
21 "FetchUserInfo": true/false, // признак необходимости получения утверждений пользователя (произвольных, в том числе ролей) через userinfo_endpoint; используется в случаях, когда в токене приходит минимально необходимая информация, чтобы размер токена не увеличивался
22
23 "AdminRole": "admin", // роль администратора
24
25 "RestrictedRole": "restricted", // ограниченная роль - пользователю с этой ролью недоступны некоторые операции, а также в стандартном веб-приложении не отображаются и/или недоступны некоторые возможности: '''дизаблит:''' кнопку сохранения в Методиках, строку формул над гридом; '''эта роль скрывает''': кнопку открытия конструктора (во всех типах вкладок), правый тулбар (со всякими редакторами джсон и остальным) во всех типах вкладок, шестеренку смены типа карточек в Карточках (старые/новые), шестеренку переключения сокетов в Отчетах/Справочниках/Показателях, в левом меню кнопки типов объектов (то есть нет фильтрации и поиска по типам объектов), в навигаторе выпадающая кнопка оздания разных типов объектов, в навигаторе кнопка копирования объектов, в навигаторе кнопка удаления объектов, в навигаторе кнопка создания обновлений, в навигаторе кнопка связанные объекты, в навигаторе в правом меню: редактор джсон, панель раздачи прав, панель создания обновлений
26
27 "ReadAllObjectsRole": "repo_read_all_objects", // роль, которая видит все объекты
28
29 "ChangePermissionsRole": "change_permissions", // роль, которая может раздавать права на объекты и операции в платформе
30
31 "ManageRightsRole": "manage_rights", // роль, которая может управлять списками ролей, пользователей, а также вхождением пользователей в роли
32
33 }
Пример подключения сервера авторизации, работающего по протоколу oidc или по протоколу oauth 2.0 с поддержкой метаданных
1 "Authorization": {
2
3 "Enable": false,
4
5 "ClientId": "client-3d",
6
7 "Authority": "https://auth.server.com/auth_root",
8
9 ...
10
11 }
Пример подключения сервера авторизации oauth 2.0 без поддержки метаданных, но реализующего стандартные эндпоинты, в том числе jwks_uri
1 "Authorization": {
2
3 "Enable": false,
4
5 "OIDCConfigurationFileName": "auth.config.json",
6
7 ...
8
9 }
auth.config.json:
1 { // объект, реализующий формат метаданных сервера авторизации (https://datatracker.ietf.org/doc/html/rfc8414). Для работоспособности как минимум должны быть заполнены issuer, authorization_endpoint, token_endpoint
2
3 "issuer": "http://my.auth.server/issuer", // должен совпадать с атрибутом "iss" токена
4
5 "authorization_endpoint": "http://my.auth.server/authorize",
6
7 "token_endpoint": "http://my.auth.server/token",
8
9 "jwks_uri": "http://my.auth.server/certs"
10
11 }
Пример подключения сервера авторизации oauth 2.0 без поддержки метаданных, но реализующего стандартные эндпоинты, кроме jwks_uri
1 "Authorization": {
2
3 "Enable": false,
4
5 "OIDCConfigurationFileName": "auth.config.json",
6
7 "OIDCSigningKeysFileName": "certs.json"
8
9 ...
10
11 }
auth.config.json:
1 {
2
3 "issuer": "http://my.auth.server/issuer",
4
5 "authorization_endpoint": "http://my.auth.server/authorize",
6
7 "token_endpoint": "http://my.auth.server/token"
8
9 }
auth.config.json:
1 {
2
3 "issuer": "http://my.auth.server/issuer",
4
5 "authorization_endpoint": "http://my.auth.server/authorize",
6
7 "token_endpoint": "http://my.auth.server/token"
8
9 }
certs.json:
1 {
2
3 "keys": [ // набор объектов, реализующих формат JWK (https://datatracker.ietf.org/doc/html/rfc7517)
4
5 {
6
7 "kid": "1",
8
9 "kty": "RSA",
10
11 "alg": "RS256",
12
13 "use": "sig",
14
15 "n": "qL72O...",
16
17 "e": "QWER",
18
19 "x5c": [
20
21 "MIICnT..E8pIg=="
22
23 ],
24
25 "x5t": "kfT...",
26
27 "x5t#S256": "PFf..."
28
29 }
30
31 ]
32
33 }
или, в случае наличия строки HS256-ключа
1 "Authorization": {
2
3 "Enable": false,
4
5 "OIDCConfigurationFileName": "auth.config.json",
6
7 "HS256Key": "BnZPK_poYt6nuwWMPg6DR...."
8
9 ...
10
11 }
auth.config.json:
1 {
2
3 "issuer": "http://my.auth.server/issuer",
4
5 "authorization_endpoint": "http://my.auth.server/authorize",
6
7 "token_endpoint": "http://my.auth.server/token"
8
9 }