JavaRush /Курсы /Модуль 4: FastAPI /Типы токенов в OAuth2: Access и Refresh Tokens

Типы токенов в OAuth2: Access и Refresh Tokens

Модуль 4: FastAPI
4 уровень , 1 лекция
Открыта

Теперь мы углубимся в детали токенов, которые являются важнейшим механизмом в OAuth2.

Сегодня поговорим об Access Tokens и Refresh Tokens — двух ключевых видах токенов, которые обеспечивают доступ и его обновление.

Почему это важно? Без них вам придётся спрашивать пользователя логин и пароль каждые пять минут. А это, согласитесь, не очень весело.

OAuth2 перестал быть чем-то сверхъестественным в веб-разработке.

Практически каждый современный сервис, от API вашего любимого мессенджера до платёжных шлюзов, использует токены для управления доступом. Использование токенов позволяет:

  1. Избежать передачи логина и пароля с каждым запросом (спасибо за это безопасность).
  2. Разграничить доступ, предоставив токены с разными уровнями разрешений.
  3. Управлять временем жизни токенов, чтобы ограничить их "власть" над ресурсами.

Access Tokens: токены для доступа

Access Token — это цифровой ключ, который предоставляет клиенту доступ к защищённым ресурсам через API.

Это как пропуск на корпоративную вечеринку — предъявил его на входе, и ты внутри.

Access Token отправляется клиентом в каждом запросе к API. Обычно он добавляется в заголовок запроса в формате:


Authorization: Bearer <access_token>

Пример использования с библиотекой httpx:


import httpx

access_token = "eyJhbGciOiJIUzI1NiIsInR5cCI6..."
headers = {"Authorization": f"Bearer {access_token}"}

response = httpx.get("https://api.example.com/protected-route", headers=headers)
print(response.json())

Время жизни (TTL, Time To Live)

Access Tokens имеют ограниченное время жизни, обычно от 5 до 30 минут.

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

Важно: после истечения срока действия токена сервер будет отклонять запросы с этим токеном, отправляя ошибку вроде 401 Unauthorized.


Refresh Tokens: обновляем доступ

Если Access Token — это пропуск на вечеринку, то Refresh Token — это ваш "привилегированный статус" для получения нового пропуска, если старый уже не действует. Он используется для выдачи нового Access Token, когда срок действия текущего токена истёк.

Refresh Tokens имеют гораздо большее время жизни (иногда дни или недели), что делает их удобным инструментом для автоматического обновления сессий пользователей.

Когда истекает срок действия Access Token, клиент может отправить Refresh Token на сервер, чтобы получить новый Access Token, не беспокоя пользователя. Типичный запрос выглядит так:


POST /token/refresh HTTP/1.1
Content-Type: application/json

{
    "refresh_token": "e60efbb7-1bda-4e5b-9aca-..."
}

Ответ от сервера:


{
    "access_token": "new-access-token-12345",
    "refresh_token": "new-refresh-token-67890"
}

Пример реализации Refresh Token в FastAPI


from fastapi.security import OAuth2PasswordRequestForm
from fastapi import FastAPI, HTTPException, Depends
from datetime import datetime, timedelta
from jose import JWTError, jwt

# Конфигурации
SECRET_KEY = "your-secret-key"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30
REFRESH_TOKEN_EXPIRE_DAYS = 7

app = FastAPI()

# Модель пользователя (упрощённая)
fake_users_db = {
    "johndoe": {
        "username": "johndoe",
        "hashed_password": "fakehashedpassword",  # Используйте реальное хэширование!
    }
}


def create_access_token(data: dict, expires_delta: timedelta):
    to_encode = data.copy()
    expire = datetime.utcnow() + expires_delta
    to_encode.update({"exp": expire})
    encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
    return encoded_jwt


def create_refresh_token(data: dict):
    expires_delta = timedelta(days=REFRESH_TOKEN_EXPIRE_DAYS)
    return create_access_token(data, expires_delta)


@app.post("/token")
async def login(form_data: OAuth2PasswordRequestForm = Depends()):
    user = fake_users_db.get(form_data.username)
    if not user or user["hashed_password"] != form_data.password:
        raise HTTPException(status_code=400, detail="Incorrect username or password")
    
    # Генерация токенов
    access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
    access_token = create_access_token({"sub": user["username"]}, access_token_expires)
    refresh_token = create_refresh_token({"sub": user["username"]})
    
    return {"access_token": access_token, "refresh_token": refresh_token, "token_type": "bearer"}


@app.post("/token/refresh")
async def refresh_token(refresh_token: str):
    try:
        payload = jwt.decode(refresh_token, SECRET_KEY, algorithms=[ALGORITHM])
        username = payload.get("sub")
        if username is None:
            raise HTTPException(status_code=401, detail="Invalid refresh token")
        # Создаём новый Access Token
        access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
        access_token = create_access_token({"sub": username}, access_token_expires)
        return {"access_token": access_token, "token_type": "bearer"}
    except JWTError:
        raise HTTPException(status_code=401, detail="Invalid refresh token")

Как хранить Refresh Tokens?

Refresh Tokens обычно хранятся более безопасно, чем Access Tokens. Хорошей практикой будет:

  • На клиенте: хранить Refresh Token в HttpOnly cookie (невидимый для JavaScript, чтобы защитить его от XSS-атак).
  • На сервере: можно сохранить Refresh Token в базе данных для проверки валидности (хотя это необязательно).

Риски и защита токенов

Риски
  1. Утечка токена: если токен попадёт в чужие руки, злоумышленник сможет получить доступ к вашим ресурсам.
  2. Повторное использование Refresh Token: если злоумышленник украдёт Refresh Token, он сможет генерировать новые Access Tokens.
Меры защиты
  1. HTTPs: всегда используйте HTTPS для передачи токенов.
  2. HttpOnly Cookies: храните токены в защищённых cookie.
  3. Короткое время жизни Access Token: уменьшайте TTL для Access Tokens, чтобы минимизировать потенциальный ущерб от их утечки.
  4. Ротация Refresh Token: создавайте новый Refresh Token каждый раз, когда создаётся Access Token, чтобы старый Refresh Token становился недействительным.

Пример использования токенов в реальных приложениях

Допустим, вы создаёте приложение для заказов пиццы через FastAPI.

Пользователь заходит в приложение, логинится, и сервер выдаёт Access и Refresh Tokens.

Access Token позволяет ему заказывать пиццу, а Refresh Token обеспечивает автоматическую подзарядку Access Token, пока пользователь наслаждается своим заказом.

Теперь представьте, что токены украли. Если вы настроили механизм ротации и короткого времени жизни, злоумышленники не смогут использовать украденные токены для дальнейших запросов. Бинго, вы спасли чей-то "пицца-день"!


На этом всё на сегодня. В следующей лекции мы углубимся в JWT и увидим, как они используются для генерации и верификации токенов.

Будьте готовы, будет весело и интересно!

1
Задача
Модуль 4: FastAPI, 4 уровень, 1 лекция
Недоступна
Создание эндпоинта для выдачи токенов
Создание эндпоинта для выдачи токенов
1
Задача
Модуль 4: FastAPI, 4 уровень, 1 лекция
Недоступна
Проверка действия Access Token
Проверка действия Access Token
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ