Аутентификация и IdentityProvider



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



• Для событий телеграм идентификация происходит на основе данных из события. Аутентификация пользователя не производится - мы только проверяем безопасность соединения с сервером

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

• Для API в микросервисной среде мы можем использовать JWT-токены, содержащие айди пользователя, которые проверяются на основе подписи.

• В некоторых сервисах мы можем полагаться на пользовательские TLS-сертификаты, заверенные сертифицирующем сервисом

• Проверка токена или сертификата может делаться как в коде приложения, так и на реверс прокси.

• При разработке или тестировании может использоваться фиксированный пользователь с определенными правами.



Множество вариантов реализации усложняется тем, что они могут использоваться одновременно с одной и той же бизнес логикой. Это приводит к необходимости выделения интерфейса (IdentityProvider), скрывающего эти детали. Обращаю так же внимание, что такой объект не должен возвращать данные, относящиеся к текущему контексту приложения. Грубо, его можно свести к чему-то такому:

class IdentityProvider(Protocol):

def get_current_user_id(self) -> int: ...

def get_current_user_roles(self) -> list[Role]: ...


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



Таким образом, связывая бизнес логику и логику аутентификации через протокол IdentityProvider мы:



• Скрываем всю работу с аутентификацией и идентификацией за простым интерфейсом

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

• Не обращаемся к внешним ресурсам самостоятельно из слоя представления

• Разделяем входные данные интерактора и контекст вызова



Дополнительные материалы:

https://www.keycloak.org/docs/latest/authorization_services/index.html

https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html

https://ru.wikipedia.org/wiki/Компоновщик_(шаблон_проектирования)