Платформа 3V/Инструкция по работе с платформой/Принципы работы платформы 3V с аутентификацией и авторизацией

Материал из 3v-wiki
Перейти к навигации Перейти к поиску

Введение

Для аутентификации и авторизации в платформе используется протокол 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, в частности:

И так далее.

Стоит упомянуть также, что именно аутентификация, по-большому счету, может происходить совершенно произвольным способом, особенно если вместо стандартного веб-интерфейса платформы используется какой-то пользовательский. А вот уже авторизация на стороне бэкенда работает с access-токенами, которые больше относятся именно к OAuth 2.0. По-большому счету, весь процесс авторизации проходит в рамках чистого OAuth 2.0, из OIDC там лишь опционально используется механизм получения метаданных сервера авторизации (который, впрочем, по стандарту тоже относится к OAuth 2.0 - https://datatracker.ietf.org/doc/html/rfc8414).

Соответственно, для того, чтобы подключить любую стороннюю авторизацию к платформе, достаточно на стороне веба получать любой JWT access-токен, который сможет отвалидироваться бэкендом. Применительно к произвольному конкретному серверу авторизации есть два варианта:

  1. Этот сервер умеет работать по протоколу OIDC в его общепринятой стандартной реализации на базе JWT. Тогда достаточно использовать его соответствующее API, задать нужные разделы в файлах конфигурации - и все заработает.
  2. Этот сервер не умеет работать по протоколу OIDC в его общепринятой стандартной реализации. Тогда между платформой и этим сервером можно реализовать и поставить некоторый транслирующий сервер авторизации, который OIDC-совместимые запросы будет преобразовывать в те запросы, которые понимает имеющийся сервер, а ответы имеющегося сервера преобразовывать в OIDC-совместимые запросы.

Принципы авторизации

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

Пример минимального приложения, реализующего oidc сервер авторизации

AuthController.cs


Настройки файлов конфигурации сервисов бэкенда

В конфигурационном файле каждого сервиса (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 }