Эта статья будет интересна тем, кто особо не заморачивался, как устроена авторизация в бекенде, но хочет очень быстро въехать в тему. Мы не будем погружаться в самые недры реализации, но точно поймем как это работает и как прикрутить это на практике. Я буду использовать python и FastAPI, просто потому что так проще показать, но эти подходы работают и для других языков/фреймворков.
Это топ видов авторизации по API для бекенд сервисов. Конечно есть еще варианты, но эти - основа, которая встречается в 99% случаев.
Что будет в этой статье дальше
Basic Auth — быстро, просто, но почти всегда плохая идея
Session — подходит для серверных HTML-приложений
JWT — лучший выбор для REST API и SPA, если правильно реализовать
OAuth2 — must-have для входа через сторонние сервисы, делегирования прав
Keycloak — оптимален, если нужен SSO, MFA, LDAP, Google Login и централизованное управление пользователями
Все что будет описано ниже, я разобрал в видео формате для твоего удобства! Выбирай подходящий тебе вид контента и изучай!
Как работать с материалом в этой статье
Обязательно изучи материал по теме
Посмотри демо пример, это репозиторий в котором я реализовал для тебя этот вид авторизации
Сделай домашнее задание из файлика tasks.md в этой ветке, например tasks.md
Разбери возникшие вопросы с гуглом, или пиши мне в телегу
Итак для начала нам следует вспомнить некоторые понятия, которые помогут дальше говорить на одном языке.

Идентификация (Identification)
Идентификация — это процесс, при котором пользователь представляется системе, т.е. заявляет, кто он есть.
Ключевые черты:
Это заявление личности, а не проверка
Само по себе не гарантирует, что вы тот, за кого себя выдаёте
Всегда предшествует аутентификации
Используется в:
Формы логина (
username
илиemail
)JWT — поле
sub
(subject) — это идентификаторКакой то внешний идентификатор (например банковская карта, пропуск)
Аутентификация (Authentication)
Аутентификация — это процесс проверки личности пользователя. Система должна убедиться, что вы действительно тот, за кого себя выдаёте.
Ключевые методы:
Пароль / Pin code
Токен / сертификат
Одноразовый код (OTP)
Биометрия (лицо, палец)
Авторизация (Authorization)
Авторизация — это процесс определения, какие действия разрешены пользователю после успешной аутентификации.
Ключевые формы:
Роли (
admin
,user
,moderator
)Права (
read
,write
,delete
)Scopes (в OAuth2)
ACL (access control lists)
? Как легко запомнить:
Идентификация - это "покажи кто ты",
Аутентификация — это “докажи, что ты — это ты”,
Авторизация — “что тебе разрешено”
Base64
Еще одна штука нам потребуется, чтобы лучше друг друга понимать.
Base64 — это способ кодировать любые данные (текст или байты) в ASCII-символы, чтобы их можно было безопасно передавать по интернету (в заголовках, URL, JSON и т.п.).
Не защищает, не шифрует, просто делает байты пригодными для HTTP.
Как работает:
Преобразует бинарные данные в набор символов A-Z, a-z, 0-9, + и /.
Выход всегда строка, пригодная для HTTP и JSON.
Часто заканчивается на
=
(паддинг для выравнивания).
?? Примеры:
# Кодирование строки:
import base64
text = "hello:world"
encoded = base64.b64encode(text.encode()).decode()
print(encoded) # aGVsbG86d29ybGQ=
# Декодирование:
decoded = base64.b64decode(encoded).decode()
print(decoded) # hello:world
⚠️ Важно:
Base64 — не шифрование!
Любой может декодировать обратно. Это не безопасность, а удобство передачи.Для защиты (например, в JWT) используется подпись, а не base64.
HTTP
Все что нам достаточно тут знать - это то, что протокол HTTP работает по принципу клиент-сервер. Клиентское приложение формирует запрос и отправляет его на сервер, после чего сервер обрабатывает запрос, формирует ответ и передаёт его обратно клиенту.
У запросов есть заголовки (headers) и мы в них можем что то класть по принципу ключ-значение. Далее подробно мы посмотрим как и что мы отправляем.
Basic auth
Basic Auth — это самый простой способ аутентификации, при котором логин и пароль передаются в каждом HTTP-запросе в виде base64-кодированной строки.
Идеально, когда нужно просто и быстро, но никогда не используйте его в публичных приложениях без HTTPS.
Basic Auth — это минимализм в чистом виде.
Это не решение “на годы” — это способ “воткнуть логин за 30 секунд”.

Как работает:
Пользователь вводит логин и пароль
Эти данные кодируются в base64:
username:password
Base64 не шифрует, а просто кодирует → нужен HTTPS-
В каждом запросе отправляется заголовок:
Authorization: Basic YWRtaW46c2VjcmV0=
-
Сервер декодирует заголовок и проверяет логин/пароль вручную или через middleware
Middleware — это промежуточный слой, который обрабатывает запрос до (или после) основного обработчика запроса.
✅ Плюсы:
Настраивается за 1 минуту (например, в
Nginx
илиFastAPI
)Не требует куки, базы сессий, генерации токенов
Стандарт HTTP с 1990-х, реализован во многих библиотеках
❌ Минусы:
Даже после логина — логин и пароль уходят в каждом запросе
“Выйти” невозможно — только очистив кэш браузера или токен
Перехват трафика = кража пароля
Нет ролей, scopes, сессий
Нет revoke, логов, контроля, аналитики
Где применимо:
✅ Подходит:
Админ-панели “для своих”
Внутренние инструменты (за VPN)
Быстрые MVP, тестовые API
❌ Не подходит:
Публичные веб-приложения
SPA и мобильные клиенты
Продакшн без HTTPS
Пример в FastAPI:
from fastapi import FastAPI, Depends
from fastapi.security import HTTPBasic, HTTPBasicCredentials
app = FastAPI()
security = HTTPBasic()
@app.get("/secret-resource")
def read_secret(credentials: HTTPBasicCredentials = Depends(security)):
if credentials.username == "admin" and credentials.password == "secret":
return {"message": "Welcome!"}
return {"error": "Unauthorized"}

Применение в NGINX
⚠️ Это наиболее частый сценарий использования basic auth
Nginx может сам проверять логин и пароль до того, как запрос попадёт в backend (например, FastAPI, Django и т.д.).

Это удобно, когда нужно:
Защитить dev-сервер, staging или внутренний API
Поставить “заглушку” на admin-панель
Не лезть в код приложения вообще
Как работает:
Пользователь заходит на защищённый URL
Браузер показывает окно ввода логина/пароля
Nginx проверяет их с помощью файла
.htpasswd
Только после этого отдаёт доступ

Важно:
Обязательно использовать HTTPS — иначе логин/пароль летят в открытом виде
Один и тот же логин/пароль у всех (без логики на стороне приложения)
Нет сессий, ролей, разграничения доступа — только "допущен или нет"
Session auth
Это старый, проверенный способ аутентификации, который широко используется в классических веб-приложениях (Django, Flask, Rails и др.)
Схема работы session based auth
-
Пользователь логинится — отправляет логин/пароль
→
POST /login
с{"username": "...", "password": "..."}
-
Сервер проверяет их и создаёт сессию
→ Сохраняем в памяти или базе связь сессии с учеткой
-
Сервер присваивает сессии уникальный ID (обычно — случайная строка)
→ Генерируем session_id
-
Этот ID отправляется пользователю в cookie
→ например,
HTTP/1.1 200 OK Set-Cookie: sessionid=abc123; HttpOnly; Path=/; Secure
-
При каждом следующем запросе браузер автоматически отправляет cookie
→ например
GET /profile HTTP/1.1 Host: example.com Cookie: sessionid=abc123
-
Сервер по ID находит сессию и “узнаёт” пользователя
→ сверяем сессию в базе и вытаскиваем учетку к которой привязана сессия
✅ Плюсы
Простота внедрения: легко реализуется на многих фреймворках
Безопасность: сессионный ID хранится только на сервере, токен не содержит чувствительную информацию
Простое управление: можно легко аннулировать сессии, контролировать время жизни
Меньшая нагрузка на клиент: вся логика аутентификации на сервере
Устойчивость к XSS-атакам: при правильной реализации с HttpOnly cookies
❌ Минусы
Хранение состояния: требует хранения сессий на сервере (память/БД/Redis)
Масштабируемость: сложнее распределять нагрузку между серверами
Проблемы с CORS: усложняется работа при кросс-доменных запросах
Уязвимость к CSRF-атакам: требуются дополнительные меры защиты
Производительность: дополнительные запросы к хранилищу сессий
Пример в FastAPI:
Вот короткий пример реализации на FastAPI:
Этот пример показывает базовую реализацию сессионной аутентификации с использованием FastAPI и Redis для хранения сессий.
Ключевые моменты:
Сессионный ID генерируется как
UUID
и хранится вRedis
с ограниченным временем жизниКуки устанавливаются с флагами
HttpOnly
иSameSite
для защитыРеализованы основные эндпоинты: вход, выход и получение информации о текущем пользователе
При выходе сессия удаляется из
Redis
и куки очищаются
from fastapi import FastAPI, Depends, HTTPException, status, Response, Request
from fastapi.security import HTTPBasic, HTTPBasicCredentials
from pydantic import BaseModel
import uuid
import redis
from typing import Optional
app = FastAPI()
security = HTTPBasic()
# Подключение к Redis
r = redis.Redis(host='localhost', port=6379, db=0)
SESSION_EXPIRE = 3600 # 1 час
# Модели данных
class User(BaseModel):
username: str
password: str
class UserInDB(User):
hashed_password: str
# Заглушка для базы данных пользователей
fake_users_db = {
"user1": {
"username": "user1",
"hashed_password": "password1" # В реальном приложении используйте хеширование паролей
}
}
# Проверка учетных данных
def get_current_user(credentials: HTTPBasicCredentials = Depends(security)):
user = fake_users_db.get(credentials.username)
if not user or user["hashed_password"] != credentials.password:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Неверные учетные данные",
headers={"WWW-Authenticate": "Basic"},
)
return user
# Получение пользователя по сессии
def get_user_by_session(request: Request):
session_id = request.cookies.get("session_id")
if not session_id:
return None
username = r.get(f"session:{session_id}")
if not username:
return None
return fake_users_db.get(username.decode())
# Маршруты
@app.post("/login")
def login(response: Response, user: User = Depends(get_current_user)):
# Создание сессии
session_id = str(uuid.uuid4())
r.setex(f"session:{session_id}", SESSION_EXPIRE, user["username"])
# Установка cookie
response.set_cookie(
key="session_id",
value=session_id,
httponly=True,
max_age=SESSION_EXPIRE,
samesite="lax",
secure=True # В production должно быть True
)
return {"message": "Успешный вход"}
@app.get("/me")
def read_user(user: Optional[dict] = Depends(get_user_by_session)):
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Требуется аутентификация"
)
return {"username": user["username"]}
@app.post("/logout")
def logout(response: Response, request: Request):
session_id = request.cookies.get("session_id")
if session_id:
r.delete(f"session:{session_id}")
response.delete_cookie(key="session_id")
return {"message": "Выход выполнен успешно"}

Token based auth
Схема работы token-based авторизации (Без refresh token)
-
Пользователь отправляет логин и пароль
→
POST /login
с{"username": "...", "password": "..."}
-
Сервер проверяет учётные данные
→ Сравнивает логин и пароль с логин-паролем из базы/хранилища.
-
Сервер создаёт JWT access token
→ С помощью функции из пункта 2. По сути генерирует специальную строку и не хранит ее, просто возвращает как результат метода
-
Токен возвращается пользователю
→ В теле ответа или
HttpOnly
cookie -
Клиент сохраняет токен
→ В localStorage (❌ небезопасно) или в cookie (✅ безопасно)
LocalStorage — это хранилище в браузере, где можно сохранять данные в формате
ключ:значение
-
Дальнейшие запросы идут с токеном
→ В заголовке:
Authorization: Bearer <token>
-
Сервер проверяет подпись и
exp
токена→ Если всё ок — пользователь авторизован
-
Если exp прошел, значит токен более невалиден.
→ Возвращаемся на пункт 1.

Токен может быть любой, лишь бы ты реализовал логику процессинга. НО, обычно используется JWT токен, так как это стало “понятным стандартом” для реализации авторизации.
JWT
https://jwt.io/ - оф дока, обязательно к посещению!
Демо пример
JWT (JSON Web Token) — это токен, состоящий из трёх частей, разделённых точками header.payload.signature
Каждая часть:
Header — метаданные: тип токена и алгоритм подписи
Payload — данные (claims): кто вы, срок действия, роли и т.д.
Signature — цифровая подпись (проверка подлинности) </aside>
Передача токена осуществляется в заголовке HTTP
Authentication: Bearer {your_token_here}
Base64 делает их безопасными для передачи по HTTP:
Без спецсимволов (
{}
,"
,:
)Без пробелов, переносов
Только ASCII: удобно в URL, заголовках
Как создаётся JWT
пример токена
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.KMUFsIDTnFmyG3nMiGM6H9FNFUROf3wh7SmqJp-QV30
Давай для начала рассмотрим какую информацию он может хранить и потом поймем как сделать свой токен.
Header:
{
"alg": "HS256",
"typ": "JWT"
}
Payload:
{
"sub": "1234567890", # user uid
"name": "John Doe", # user name (например, для фронта)
"admin": true, # роль пользователя
"iat": 1516239022, # время когда токен был выпущен секунды начиная с 01-01-1970
"exp": 1716666000 # время когда токен "протухнет"
}
Signature:
Подпись. Это опциональная штука, можно и без нее, но тогда не проверить кто выпустил токен (см. ниже)
a-string-secret-at-least-256-bits-long
Пример генерации токена:
можешь запустить локально, установив пакет pyJWT
import jwt
from datetime import datetime, timedelta
SECRET_KEY = "your-secret-key"
ALGORITHM = "HS256"
def generate_token(data: dict):
payload = data.copy()
payload["exp"] = datetime.utcnow() + timedelta(minutes=15)
return jwt.encode(payload, SECRET_KEY, algorithm=ALGORITHM)
# Пример использования
token = generate_token({"sub": "user_id_123"})
print(token)
? Что кладут в payload? (Так называемые claims)
Claim |
Назначение |
Обязательный? |
---|---|---|
sub |
ID пользователя |
✅ Да |
exp |
Время истечения токена (timestamp) |
✅ Да |
iat |
Когда был выпущен |
⚠️ Желательно |
nbf |
“Не раньше чем” (not before) |
❌ Опционально |
iss |
Кто выдал токен (issuer) |
❌ Опционально |
aud |
Для кого токен предназначен (audience) |
❌ Опционально |
Пользовательские |
Например, role, email, scopes |
❌ Опционально |
Алгоритмы подписи: HS256 vs RS256
HS256 — симметричная подпись. Один секретный ключ. Просто, но не масштабируется.
RS256 — асимметричная. Частный ключ для подписи, публичный — для проверки. Лучше для микросервисов и OAuth2.
Ты можешь начать с HS256, а в полном проекте — перейти на RS256 или интеграцию с Keycloak, где это будет работать автоматически.
⚠️ Ошибки, которые часто совершают:
❌ Не ставят exp (и получают “вечные” токены)
❌ Добавляют в токен пароль или чувствительные данные
❌ Используют слишком короткий или слабый
SECRET_KEY
❌ Отправляют токен без Bearer схемы в заголовке
❌ Не верифицируют подпись на сервере
Refresh token flow
Refresh token используется для получения нового access token
без повторной аутентификации пользователя. Его основная схема работы:
-
При авторизации клиент получает два токена:
Access token (короткоживущий, например 15-30 минут)
Refresh token (долгоживущий, например 7-30 дней)
-
Когда access token истекает:
Клиент отправляет
refresh
token на специальный endpoint (например,POST /refresh
)Сервер проверяет валидность
refresh
токенаПри успешной проверке сервер генерирует новый
access
token и возвращает его клиентуОпционально: генерирует новый
refresh
token (rotation strategy)
-
Схема запроса обновления:
POST /refresh Content-Type: application/json { "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ..." }
-
Ответ:
{ "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVC...", "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVC...", // Опционально "token_type": "Bearer", "expires_in": 900 }
Особенности refresh токенов:
Структура: Могут быть JWT или просто случайные строки (
UUID
)Хранение: Обязательно в базе данных или
Redis
на стороне сервераБезопасность: Хранятся на стороне клиента в
HttpOnly
,Secure
,SameSite=strict
cookie или в защищенном хранилищеСрок жизни: Значительно дольше
access
токена, но имеет конечный срок
Рекомендации по реализации:
Создавайте уникальный
refresh
token для каждого устройства пользователяДобавьте fingerprint устройства для дополнительной защиты
Используйте одноразовые
refresh
токены (rotation strategy)
Отзыв JWT (revoke)
Одним из недостатков JWT является сложность отзыва токенов, так как они по умолчанию валидны до истечения срока.
Существует несколько подходов к решению этой проблемы:
-
Blacklist отозванных токенов
# Пример хранения отозванных токенов в Redis def revoke_token(token): token_data = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) jti = token_data.get("jti", token_data.get("sub")) # уникальный ID токена expiration = token_data["exp"] - int(datetime.utcnow().timestamp()) redis_client.setex(f"revoked:{jti}", expiration, "1") def is_token_revoked(token): token_data = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) jti = token_data.get("jti", token_data.get("sub")) return redis_client.exists(f"revoked:{jti}")
-
Контроль через Refresh токены
Храните только refresh токены в базе данных
При логауте - удаляйте refresh токен из базы
Держите короткий срок жизни access токенов
Выпуск с версией/идентификатором пользовательской сессии
def generate_token(user_id, session_id):
return jwt.encode({
"sub": user_id,
"sid": session_id, # Идентификатор сессии
"exp": datetime.utcnow() + timedelta(minutes=15)
}, SECRET_KEY, algorithm=ALGORITHM)
def verify_token(token, active_sessions):
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
# Проверяем не только подпись, но и актуальность сессии
if payload["sid"] not in active_sessions.get(payload["sub"], []):
raise Exception("Token revoked")
return payload

✅ Плюсы:
Stateless - сервер не хранит сессии, что упрощает масштабирование
Самодостаточность - вся необходимая информация содержится в токене
Кросс-доменная работа - легко использовать в микросервисной архитектуре
Производительность - не требуется обращение к БД для проверки авторизации
Гибкость - можно включать различные claims (роли, права и т.д.)
Стандартизация - широко используемый формат с большим количеством библиотек
Разделение ответственности - возможность выделить авторизацию в отдельный сервис
❌ Минусы:
Размер - JWT обычно больше, чем session ID (особенно с большим количеством claims)
Сложность отзыва - нет встроенного механизма отзыва (нужна доп. реализация)
Безопасность хранения - сложно безопасно хранить на клиенте
Утечка информации - payload не зашифрован, только закодирован в Base64
Усложнение при использовании refresh токенов - требует хранения состояния
Риск XSS-атак - при неправильном хранении в localStorage
Неизменяемость - нельзя изменить данные в токене без перевыпуска
Истекшие токены - до истечения срока клиент продолжает использовать неактуальный токен
Где хранить токен на клиенте
Хранилище |
Access Token |
Refresh Token |
Комментарий |
---|---|---|---|
HttpOnly Cookie |
✅ Хорошо |
✅ Лучший вариант |
Защита от XSS, уязвим к CSRF (нужна доп. защита) |
localStorage |
❌ Небезопасно |
❌ Очень небезопасно |
Уязвим к XSS-атакам |
sessionStorage |
⚠️ Относительно безопасно |
❌ Небезопасно |
Живет только во время сессии браузера |
Memory (JS переменная) |
✅ Хорошо |
⚠️ Не рекомендуется |
Исчезает при обновлении страницы |
IndexedDB |
⚠️ Зависит от реализации |
❌ Небезопасно |
Требует шифрования |
Рекомендуемый подход:
Access token:
HttpOnly
cookie или in-memory storageRefresh token: Только
HttpOnly
,Secure
,SameSite=strict cookie
Дополнительные меры безопасности:
-
Anti-CSRF токены
Генерируйте уникальный токен для каждой сессии
Проверяйте его при запросах, влияющих на состояние
-
Fingerprinting устройства
Привязывайте refresh токен к характеристикам устройства
Проверяйте соответствие при обновлении токена
SSO (Single-sign on)
SSO (Single Sign-On) — это механизм, при котором пользователь логинится один раз, чтобы получить доступ ко всем связанным системам, без повторной аутентификации.
? Сам SSO — это поведение, а не технология.
А реализуется он через Keycloak, Okta, Azure AD и т.п.
User (пользователь) — хочет войти, знает свой логин и пароль
Service Provider (SP) — сайт, в который он заходит (например, GitLab)
Identity Provider (IdP) — сервис, который подтверждает личность (например, Keycloak, Google, Okta)
Как это выглядит для пользователя:
Заходишь на
app1.company.com
→ логинишьсяПереходишь на
app2.company.com
→ уже залогинен-
Переходишь в
dashboard.partner.com
→ тоже залогинен→ без ввода пароля
Как это работает под капотом
Пользователь заходит на
Service A
Его редиректят на IdP
Он логинится один раз
IdP возвращает токен или сессию
Пользователь получает доступ к
Service A
При переходе на
Service B
, тот снова обращается к IdP → уже есть сессия → вход без пароля
SSO работает через протоколы:
SAML — старый, XML, часто в enterprise
OpenID Connect (OIDC) — надстройка над OAuth2, современный стандарт
Иногда — Kerberos (в Windows-доменных сетях)
Пример:
Допустим, у тебя есть три приложения:
С SSO:
Все они доверяют одному IdP (например, Keycloak)
Пользователь логинится один раз — и получает доступ ко всем трём
✅ Плюсы SSO
Удобно для пользователей (1 логин → много доступов)
Централизованный контроль безопасности
Идеально для корпоративной среды, микросервисов
Поддерживает MFA, LDAP, OAuth2, Google Login и др.
❌ Минусы
Одна точка отказа (если IdP не работает — никто не войдёт)
Нужно уметь правильно конфигурировать
Требует понимания протоколов (OIDC/SAML)
OAuth 2.0
OAuth 2.0 — это протокол авторизации, который позволяет приложению получить доступ к данным пользователя на другом сервисе, без передачи логина и пароля.
? OAuth 2.0 ≠ не аутентификация, а доступ к ресурсам по доверенности.
«OAuth2 — это протокол, который позволяет приложениям действовать от имени пользователя, не зная его пароля. Всё через токены, всё под контролем.»
Пример:
Ты логинишься на сайте через Google:
Этот сайт не получает твой пароль
Google показывает: “Это приложение хочет доступ к вашей почте”
Ты нажимаешь “Разрешить”
Приложение получает токен — и может читать твои письма (или что там запросили)
Кто участвует в процессе
Resource Owner — пользователь (ты)
Client — приложение, которое хочет доступ (например, Zoom, Notion)
Authorization Server — кто выдает токен (например, Google)
Resource Server — API, к которому клиент хочет доступ (например, Gmail API)
Основной поток: Authorization Code Flow
Этапы:
-
Клиент (frontend) редиректит пользователя на Google:
<https://accounts.google.com/o/oauth2/auth?client\_id=...&redirect\_uri=>...
Пользователь логинится и разрешает доступ
-
Google редиректит обратно с
code
:<https://your-app.com/callback?code=abc123>
Сервер (backend) обменивает
code
на access token (и опционально refresh token)-
С этого момента клиент может использовать JWT-токен:
Authorization: Bearer <token>

Access и Refresh Token
По аналогии с JWT-токеном refresh-токен обновляет access-токен по истечению его срока жизни
Access Token — временный (часто 1 час), используется для запросов к API
Refresh Token — долговечный, используется для получения нового access token без логина
OAuth ≠ Login
OAuth не занимается “кто ты”, он занимается “можно ли тебе это”.
Но если добавить OIDC (OpenID Connect) — появляется ID Token, и можно использовать OAuth2 как логин (SSO).
✅ Плюсы OAuth2
Не требует передавать логины/пароли другим сервисам
Можно ограничить доступ по scope (
read
,write
,profile
)Работает с большинством крупных API
Основной протокол для “Войти через Google/Facebook/GitHub”
❌ Минусы
Сложная конфигурация (client_id, redirect_uri, state)
Есть много вариантов (flows): authorization code, client credentials, etc.
Если сделать неправильно — легко открыть уязвимость
Keycloak (SSO / OpenID Connect)
Keycloak — это готовая система управления пользователями и авторизацией, которая реализует протоколы OIDC (OpenID Connect) и OAuth2.0. Она предоставляет SSO, MFA, вход через сторонние сервисы и централизованный контроль доступа.
Keycloak — это всё в одном: логин, токены, роли, пользователи, OAuth2, OIDC, MFA и SSO.
Как работает:
Пользователь кликает “Войти” в одном из приложений
Браузер редиректится на Keycloak
Keycloak логинит пользователя (через форму, LDAP, Google и т.д.)
Возвращает пользователя с
code
обратно в приложениеПриложение обменивает
code
наaccess token
,ID token
,refresh token
Пользователь получает доступ к сервису
В другом приложении пользователь уже авторизован (SSO)
Механика (технически):
Keycloak — это Identity Provider (IdP)
Работает через OIDC (добавляет аутентификацию к OAuth2)
-
Выдаёт токены:
Access token (доступ к API)
ID token (информация о пользователе)
Refresh token (обновление токенов)
-
Поддерживает:
JWT (RS256 по умолчанию)
Google/GitHub/LDAP как сторонние IdP
MFA (2FA), политики доступа, роли, группы

✅ Плюсы:
Централизованная аутентификация
Полноценный SSO. Входит в одно — авторизован во всём
Поддержка стандартов: OIDC, OAuth2, SAML
Гибкость: UI логин, MFA, роли, политики, custom flows
Масштабируемо и стабильно: Отлично подходит для production / enterprise
❌ Минусы:
Нужно разобраться с realмами, клиентами, маппингами
Требует сервера с Keycloak, либо Docker
Единая точка отказа (IdP): Если Keycloak упал — никто не войдёт
Сложно для маленьких проектов
Где применимо:
✅ Подходит:
Микросервисная архитектура
Корпоративные платформы
Приложения с множеством клиентов и ролей
SSO через Google, GitHub, LDAP
❌ Не подходит:
Простейшие проекты без сложных пользователей
MVP с одним входом
Там, где нет инфраструктуры
Пример на FastAPI:
Приложение регистрируется как "Client" в Keycloak
Пользователь логинится → возвращается
code
FastAPI обменивает
code
на токены через HTTP-запрос:
import requests
def exchange_code_for_token(code: str):
res = requests.post("<https://keycloak/auth/realms/myrealm/protocol/openid-connect/token>", data={
"client_id": "myapp",
"client_secret": "secret",
"grant_type": "authorization_code",
"code": code,
"redirect_uri": "<https://myapp/callback>"
})
return res.json()
Сравнение видов авторизации
Ну и на последок шпаргалка по сравнениям видов авторизации

Надеюсь тебе было интересно, не забудь сделать домашние задания в каждой ветке! А так до встречи в следующей статье, пока!