Эта статья будет интересна тем, кто особо не заморачивался, как устроена авторизация в бекенде, но хочет очень быстро въехать в тему. Мы не будем погружаться в самые недры реализации, но точно поймем как это работает и как прикрутить это на практике. Я буду использовать python и FastAPI, просто потому что так проще показать, но эти подходы работают и для других языков/фреймворков.

Это топ видов авторизации по API для бекенд сервисов. Конечно есть еще варианты, но эти - основа, которая встречается в 99% случаев.

Что будет в этой статье дальше
  • Basic Auth — быстро, просто, но почти всегда плохая идея

  • Session — подходит для серверных HTML-приложений

  • JWT — лучший выбор для REST API и SPA, если правильно реализовать

  • OAuth2 — must-have для входа через сторонние сервисы, делегирования прав

  • Keycloak — оптимален, если нужен SSO, MFA, LDAP, Google Login и централизованное управление пользователями

Все что будет описано ниже, я разобрал в видео формате для твоего удобства! Выбирай подходящий тебе вид контента и изучай!

Как работать с материалом в этой статье
  1. Обязательно изучи материал по теме

  2. Посмотри демо пример, это репозиторий в котором я реализовал для тебя этот вид авторизации

  3. Сделай домашнее задание из файлика tasks.md в этой ветке, например tasks.md

  4. Разбери возникшие вопросы с гуглом, или пиши мне в телегу

Итак для начала нам следует вспомнить некоторые понятия, которые помогут дальше говорить на одном языке.

image.png
Три компонента для получения доступа к ресурсу

Идентификация (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 секунд”.

image.png
Схематично как это работает

Как работает:

  1. Пользователь вводит логин и пароль

  2. Эти данные кодируются в base64: username:password
    Base64 не шифрует, а просто кодирует → нужен HTTPS

  3. В каждом запросе отправляется заголовок:

    Authorization: Basic YWRtaW46c2VjcmV0=
  4. Сервер декодирует заголовок и проверяет логин/пароль вручную или через 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"}
UML sequence диаграмма последовательности действий
UML sequence диаграмма последовательности действий

Применение в NGINX

⚠️ Это наиболее частый сценарий использования basic auth

Демо пример
Документация

Nginx может сам проверять логин и пароль до того, как запрос попадёт в backend (например, FastAPI, Django и т.д.).

image.png
Схематично как работает basic-auth вместе c nginx

Это удобно, когда нужно:

  • Защитить dev-сервер, staging или внутренний API

  • Поставить “заглушку” на admin-панель

  • Не лезть в код приложения вообще

Как работает:

  1. Пользователь заходит на защищённый URL

  2. Браузер показывает окно ввода логина/пароля

  3. Nginx проверяет их с помощью файла .htpasswd

  4. Только после этого отдаёт доступ

UML sequence диаграмма последовательности действий
UML sequence диаграмма последовательности действий

Важно:

  • Обязательно использовать HTTPS — иначе логин/пароль летят в открытом виде

  • Один и тот же логин/пароль у всех (без логики на стороне приложения)

  • Нет сессий, ролей, разграничения доступа — только "допущен или нет"

Session auth

Демо пример

Это старый, проверенный способ аутентификации, который широко используется в классических веб-приложениях (Django, Flask, Rails и др.)

Схема работы session based auth

  1. Пользователь логинится — отправляет логин/пароль

    POST /login с {"username": "...", "password": "..."}

  2. Сервер проверяет их и создаёт сессию

    → Сохраняем в памяти или базе связь сессии с учеткой

  3. Сервер присваивает сессии уникальный ID (обычно — случайная строка)

    → Генерируем session_id

  4. Этот ID отправляется пользователю в cookie

    → например,

    HTTP/1.1 200 OK
    Set-Cookie: sessionid=abc123; HttpOnly; Path=/; Secure
  5. При каждом следующем запросе браузер автоматически отправляет cookie

    → например

    GET /profile HTTP/1.1
    Host: example.com
    Cookie: sessionid=abc123
  6. Сервер по ID находит сессию и “узнаёт” пользователя

    → сверяем сессию в базе и вытаскиваем учетку к которой привязана сессия

✅ Плюсы

  • Простота внедрения: легко реализуется на многих фреймворках

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

  • Простое управление: можно легко аннулировать сессии, контролировать время жизни

  • Меньшая нагрузка на клиент: вся логика аутентификации на сервере

  • Устойчивость к XSS-атакам: при правильной реализации с HttpOnly cookies

❌ Минусы

  • Хранение состояния: требует хранения сессий на сервере (память/БД/Redis)

  • Масштабируемость: сложнее распределять нагрузку между серверами

  • Проблемы с CORS: усложняется работа при кросс-доменных запросах

  • Уязвимость к CSRF-атакам: требуются дополнительные меры защиты

  • Производительность: дополнительные запросы к хранилищу сессий

Пример в FastAPI:

Вот короткий пример реализации на FastAPI:

Этот пример показывает базовую реализацию сессионной аутентификации с использованием FastAPI и Redis для хранения сессий.

Ключевые моменты:

  1. Сессионный ID генерируется как UUID и хранится в Redis с ограниченным временем жизни

  2. Куки устанавливаются с флагами HttpOnly и SameSite для защиты

  3. Реализованы основные эндпоинты: вход, выход и получение информации о текущем пользователе

  4. При выходе сессия удаляется из 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": "Выход выполнен успешно"}
UML sequence диаграмма последовательности действий
UML sequence диаграмма последовательности действий

Token based auth

Схема работы token-based авторизации (Без refresh token)

  1. Пользователь отправляет логин и пароль

    POST /login с {"username": "...", "password": "..."}

  2. Сервер проверяет учётные данные

    → Сравнивает логин и пароль с логин-паролем из базы/хранилища.

  3. Сервер создаёт JWT access token

    → С помощью функции из пункта 2. По сути генерирует специальную строку и не хранит ее, просто возвращает как результат метода

  4. Токен возвращается пользователю

    → В теле ответа или HttpOnly cookie

  5. Клиент сохраняет токен

    → В localStorage (❌ небезопасно) или в cookie (✅ безопасно)

    LocalStorage — это хранилище в браузере, где можно сохранять данные в формате ключ:значение

  6. Дальнейшие запросы идут с токеном

    → В заголовке: Authorization: Bearer <token>

  7. Сервер проверяет подпись и exp токена

    → Если всё ок — пользователь авторизован

  8. Если exp прошел, значит токен более невалиден.

    → Возвращаемся на пункт 1.

Схематично схема авторизации с токеном
Схематично схема авторизации с токеном

Токен может быть любой, лишь бы ты реализовал логику процессинга. НО, обычно используется JWT токен, так как это стало “понятным стандартом” для реализации авторизации.

JWT

https://jwt.io/ - оф дока, обязательно к посещению!
Демо пример

JWT (JSON Web Token) — это токен, состоящий из трёх частей, разделённых точками header.payload.signature

Каждая часть:

  1. Header — метаданные: тип токена и алгоритм подписи

  2. Payload — данные (claims): кто вы, срок действия, роли и т.д.

  3. 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 без повторной аутентификации пользователя. Его основная схема работы:

  1. При авторизации клиент получает два токена:

    • Access token (короткоживущий, например 15-30 минут)

    • Refresh token (долгоживущий, например 7-30 дней)

  2. Когда access token истекает:

    • Клиент отправляет refresh token на специальный endpoint (например, POST /refresh)

    • Сервер проверяет валидность refresh токена

    • При успешной проверке сервер генерирует новый access token и возвращает его клиенту

    • Опционально: генерирует новый refresh token (rotation strategy)

  3. Схема запроса обновления:

    POST /refresh
    Content-Type: application/json
    
    {
      "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ..."
    }
  4. Ответ:

    {
      "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 является сложность отзыва токенов, так как они по умолчанию валидны до истечения срока.

Существует несколько подходов к решению этой проблемы:

  1. 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}")
  2. Контроль через Refresh токены

    • Храните только refresh токены в базе данных

    • При логауте - удаляйте refresh токен из базы

    • Держите короткий срок жизни access токенов

  3. Выпуск с версией/идентификатором пользовательской сессии

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
UML sequence диаграмма последовательности действий
UML sequence диаграмма последовательности действий

✅ Плюсы:

  • 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 storage

  • Refresh token: Только HttpOnly, Secure, SameSite=strict cookie

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

  1. Anti-CSRF токены

    • Генерируйте уникальный токен для каждой сессии

    • Проверяйте его при запросах, влияющих на состояние

  2. Fingerprinting устройства

    • Привязывайте refresh токен к характеристикам устройства

    • Проверяйте соответствие при обновлении токена

SSO (Single-sign on)

SSO (Single Sign-On) — это механизм, при котором пользователь логинится один раз, чтобы получить доступ ко всем связанным системам, без повторной аутентификации.

? Сам SSO — это поведение, а не технология.
А реализуется он через Keycloak, Okta, Azure AD и т.п.

  1. User (пользователь) — хочет войти, знает свой логин и пароль

  2. Service Provider (SP) — сайт, в который он заходит (например, GitLab)

  3. Identity Provider (IdP) — сервис, который подтверждает личность (например, Keycloak, Google, Okta)

Как это выглядит для пользователя:

  1. Заходишь на app1.company.com → логинишься

  2. Переходишь на app2.company.com → уже залогинен

  3. Переходишь в dashboard.partner.com → тоже залогинен

    → без ввода пароля

Как это работает под капотом

  1. Пользователь заходит на Service A

  2. Его редиректят на IdP

  3. Он логинится один раз

  4. IdP возвращает токен или сессию

  5. Пользователь получает доступ к Service A

  6. При переходе на 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 показывает: “Это приложение хочет доступ к вашей почте”

  • Ты нажимаешь “Разрешить”

  • Приложение получает токен — и может читать твои письма (или что там запросили)

Кто участвует в процессе

  1. Resource Owner — пользователь (ты)

  2. Client — приложение, которое хочет доступ (например, Zoom, Notion)

  3. Authorization Server — кто выдает токен (например, Google)

  4. Resource Server — API, к которому клиент хочет доступ (например, Gmail API)

Основной поток: Authorization Code Flow

Этапы:

  1. Клиент (frontend) редиректит пользователя на Google:

    <https://accounts.google.com/o/oauth2/auth?client\_id=...&redirect\_uri=>...
  2. Пользователь логинится и разрешает доступ

  3. Google редиректит обратно с code:

    <https://your-app.com/callback?code=abc123>
  4. Сервер (backend) обменивает code на access token (и опционально refresh token)

  5. С этого момента клиент может использовать JWT-токен:

    Authorization: Bearer <token>
UML sequence диаграмма последовательности действий
UML sequence диаграмма последовательности действий

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.

Как работает:

  1. Пользователь кликает “Войти” в одном из приложений

  2. Браузер редиректится на Keycloak

  3. Keycloak логинит пользователя (через форму, LDAP, Google и т.д.)

  4. Возвращает пользователя с code обратно в приложение

  5. Приложение обменивает code на access token, ID token, refresh token

  6. Пользователь получает доступ к сервису

  7. В другом приложении пользователь уже авторизован (SSO)

Механика (технически):

  • Keycloak — это Identity Provider (IdP)

  • Работает через OIDC (добавляет аутентификацию к OAuth2)

  • Выдаёт токены:

    • Access token (доступ к API)

    • ID token (информация о пользователе)

    • Refresh token (обновление токенов)

  • Поддерживает:

    • JWT (RS256 по умолчанию)

    • Google/GitHub/LDAP как сторонние IdP

    • MFA (2FA), политики доступа, роли, группы

UML sequence диаграмма последовательности действий
UML sequence диаграмма последовательности действий

✅ Плюсы:

  • Централизованная аутентификация

  • Полноценный 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()

Сравнение видов авторизации

Ну и на последок шпаргалка по сравнениям видов авторизации

Табличка сравнения видов авторизации.
Табличка сравнения видов авторизации.

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

Комментарии (1)