Как стать автором
Поиск
Написать публикацию
Обновить

Коротко про виды авторизации

Уровень сложностиПростой
Время на прочтение16 мин
Количество просмотров5.9K

Эта статья будет интересна тем, кто особо не заморачивался, как устроена авторизация в бекенде, но хочет очень быстро въехать в тему. Мы не будем погружаться в самые недры реализации, но точно поймем как это работает и как прикрутить это на практике. Я буду использовать 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()

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

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

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

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

Теги:
Хабы:
+8
Комментарии8

Публикации

Ближайшие события