FastAPI для начинающих: API с базой данных и тестамиПользователи и доступВход и JWT access token

Вход и JWT access token

Уроки курсаВход и JWT access token

В прошлом уроке POST /api/users сохранил хеш Argon2 и вернул только id и email. Пароль нигде не попал в ответ. Теперь нужен следующий шаг: пользователь присылает email и пароль повторно, а сервер должен убедиться, что пароль верный, и выдать токен, с которым можно обращаться к защищённым маршрутам. Этот токен — JWT access token.

create_access_token: sub, срок exp и подпись HS256

Продолжайте тот же проект auth_demo. Добавьте строку в requirements.txt:

PyJWT==2.10.1

Переустановите зависимости:

pip install -r requirements.txt

Прежде чем запускать приложение, задайте SECRET_KEY в переменных окружения. Это не пароль, который придумывает человек, — это 32 случайных байта, закодированных в urlsafe-Base64. В bash:

export SECRET_KEY="$(python -c 'import secrets; print(secrets.token_urlsafe(32))')"

В PowerShell:

$env:SECRET_KEY = python -c "import secrets; print(secrets.token_urlsafe(32))"

Значение генерируется один раз и остаётся постоянным на всё время жизни сервиса. Ключ не коммитится в репозиторий, не возвращается через /info и не меняется при каждом перезапуске. Если ключ сменился — все ранее выданные токены, подписанные старым ключом, станут непроверяемыми: новый ключ просто даст другую подпись, которая не совпадёт.

Новый файл app/tokens.py содержит единственную функцию:

from datetime import datetime, timedelta, timezone
import jwt

def create_access_token(token_subject: str, secret_key: str, expires_minutes: int = 30) -> str:
    payload = {
        "sub": token_subject,
        "exp": datetime.now(timezone.utc) + timedelta(minutes=expires_minutes),
    }
    return jwt.encode(payload, secret_key, algorithm="HS256")

Функция принимает token_subject — это User.token_subject, постоянная случайная строка учётной записи, которую не нужно путать с email или числовым id. В sub кладут стабильный идентификатор, по которому сервер потом найдёт пользователя; email туда не помещают, потому что он может измениться.

exp — момент в UTC через 30 минут от текущего времени. PyJWT принимает объект datetime и сам преобразует его в Unix timestamp. Обратите внимание: exp фиксируется в момент выпуска токена. Это не означает, что токен автоматически отклоняется по истечении срока — принимающая сторона обязана явно проверить срок при каждом запросе.

Функция получает secret_key аргументом, а не обращается к get_settings напрямую. Это упрощает тестирование: в тестах можно передать любой ключ, не трогая окружение. В продакшн-коде ключ передаётся из get_settings — та же зависимость, что уже есть в проекте.