Как спроектировать сервис авторизации для микросервисной архитектуры с выдачей токенов
Что проверяют
Различаете ли вы аутентификацию и авторизацию и умеете ли выстроить доверие между сервисами так, чтобы каждый запрос не ходил в центральный сервис. Следом почти всегда спрашивают, как отозвать токен до истечения срока и что делать при утечке ключа подписи.
Как отвечать
Начните с разделения ролей: сервис авторизации (identity provider) выпускает токены, остальные сервисы только проверяют их локально. Каркас:
- протокол: OAuth 2.0 для выдачи доступа и OpenID Connect поверх него для входа пользователя. Для браузера и мобильных клиентов Authorization Code с PKCE, для вызовов между сервисами client credentials;
- access token: короткий JWT, живёт от 5 до 15 минут, содержит
sub,aud,exp,scopeили роли. Подпись асимметричная (RS256илиEdDSA): закрытый ключ есть только у сервиса авторизации, публичные раздаются через JWKS-эндпоинт, сервисы кешируют их и проверяют подпись без сетевого вызова; - refresh token: длинный, непрозрачный, хранится в базе и ротируется при каждом использовании. Повторное предъявление старого refresh означает кражу, и вся цепочка отзывается;
- отзыв: раз access живёт минуты, чёрный список всех токенов не нужен. Достаточно отзывать refresh и держать в Redis
jtiскомпрометированных токенов до ихexp; - ротация ключей: в заголовке JWT есть
kid, новый ключ публикуется в JWKS заранее, старый убирается, когда истекут выданные им токены.
На Python это FastAPI или Django с authlib, проверка в сервисах через PyJWT и его PyJWKClient. Если команда не хочет писать криптографическую обвязку, берут готовый Keycloak. Проверку удобно вынести в API Gateway, но сервисы всё равно сверяют aud, чтобы токен для одного сервиса не открывал другой.
Где спотыкаются
Подписывают токены общим секретом HS256 и раздают его всем сервисам. Тогда любой взломанный сервис выпускает токены от чужого имени.
Кладут в JWT персональные данные, забывая, что payload закодирован в base64, а не зашифрован, и что токен едет в каждом заголовке.
Делают access-токен на сутки и сверяют каждый запрос с чёрным списком. Это возвращает сетевой вызов, от которого JWT должен был избавить.