JavaRush /Курсы /Модуль 4: FastAPI /Настройка рефреш-токенов для обновления доступа

Настройка рефреш-токенов для обновления доступа

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

Для начала — немного теории. JWT-токены имеют ограниченный срок действия. Это сделано для повышения безопасности: если злоумышленник каким-то образом украдёт токен, его "срок жизни" будет ограничен. Но это создаёт неудобства для ваших пользователей: как только срок действия токена истекает, они вынуждены снова вводить свои данные для входа.

Рефреш-токен приходит на помощь. Основные его преимущества:

  • Долговременное хранение: рефреш-токен имеет более длительный срок действия.
  • Обновление доступа: позволяет пользователю получить новый Access-токен без повторного ввода данных.
  • Восстановление сессий: делает пользовательский опыт более плавным.

Рефреш-токены — это как ключ от дома, спрятанный под ковриком. Мы знаем, что он там есть, но пользуемся только в случае необходимости. Главное — чтобы коврик никто не унес.


Реализация рефреш-токенов в FastAPI

Теперь, когда мы разобрались, зачем нужны рефреш-токены, перейдём к практике.

Итак, главная функция рефреш-токена — это обновление Access-токена. Для этого мы создадим новый эндпоинт /token/refresh, который будет принимать рефреш-токен, проверять его валидность и, если всё хорошо, выдавать новый Access-токен.

Начнём с основного кода:


from fastapi import FastAPI, Depends, HTTPException
from fastapi.security import OAuth2PasswordBearer
from jose import JWTError, jwt
from datetime import datetime, timedelta
from pydantic import BaseModel

app = FastAPI()

# Секреты и алгоритмы для токенов
SECRET_KEY = "your_secret_key"
REFRESH_SECRET_KEY = "your_refresh_secret_key"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 15
REFRESH_TOKEN_EXPIRE_DAYS = 7

# OAuth2 схема
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

# Модель для токенов
class Token(BaseModel):
    access_token: str
    refresh_token: str
    token_type: str

# Модель для данных пользователя
class User(BaseModel):
    username: str

# Временное хранилище пользователей и рефреш-токенов
users_db = {
    "johndoe": {"username": "johndoe", "hashed_password": "fakehashedpassword"}
}
refresh_tokens_store = {}

# Генерация Access-токена
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

# Генерация Refresh-токена
def create_refresh_token(data: dict):
    to_encode = data.copy()
    expire = datetime.utcnow() + timedelta(days=REFRESH_TOKEN_EXPIRE_DAYS)
    to_encode.update({"exp": expire})
    encoded_jwt = jwt.encode(to_encode, REFRESH_SECRET_KEY, algorithm=ALGORITHM)
    return encoded_jwt

# Вход пользователя и генерация токенов
@app.post("/token", response_model=Token)
async def login(username: str, password: str):
    user = users_db.get(username)
    if not user or password != "password":  # Пример проверки, лучше использовать хэш
        raise HTTPException(status_code=400, detail="Invalid credentials")

    access_token = create_access_token(
        data={"sub": user["username"]}, 
        expires_delta=timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
    )
    refresh_token = create_refresh_token(data={"sub": user["username"]})
    refresh_tokens_store[user["username"]] = refresh_token

    return {"access_token": access_token, "refresh_token": refresh_token, "token_type": "bearer"}

# Обновление Access-токена
@app.post("/token/refresh", response_model=Token)
async def refresh_token(refresh_token: str):
    try:
        # Проверка рефреш-токена
        payload = jwt.decode(refresh_token, REFRESH_SECRET_KEY, algorithms=[ALGORITHM])
        username: str = payload.get("sub")
        if username is None or refresh_tokens_store.get(username) != refresh_token:
            raise HTTPException(status_code=401, detail="Invalid refresh token")

        # Если всё хорошо, создаём новый Access-токен
        access_token = create_access_token(
            data={"sub": username}, 
            expires_delta=timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
        )
        return {"access_token": access_token, "refresh_token": refresh_token, "token_type": "bearer"}
    except JWTError:
        raise HTTPException(status_code=401, detail="Invalid token")
  1. Хранилище рефреш-токенов:
    В данном примере мы храним рефреш-токены в памяти (refresh_tokens_store). Это не лучший подход, поскольку в реальных приложениях рефреш-токены должны храниться безопасно (например, в базе данных).
  2. Создание токенов:
    Мы разделили генерацию access_token и refresh_token, используя разные секретные ключи (SECRET_KEY и REFRESH_SECRET_KEY). Это повышает безопасность системы.
  3. Эндпоинт /token/refresh:
    Проверяет валидность рефреш-токена.
    Генерирует новый Access-токен, используя ту же информацию о пользователе.
  4. Срок действия токенов:
    Access-токен живёт 15 минут, тогда как рефреш-токен — целых 7 дней.

Практические замечания

  1. Безопасное хранение токенов:
    • Access-токен может быть сохранён в памяти клиента (например, в localStorage или sessionStorage).
    • Рефреш-токен должен храниться только в HttpOnly cookie. Это защитит его от атак XSS.
  2. Истечение срока рефреш-токена:
    При истечении рефреш-токена пользователь будет вынужден войти в систему заново.
  3. Двухфакторная аутентификация:
    Включение рефреш-токенов позволяет легко внедрять двухфакторную аутентификацию.

Типичные ошибки

Одной из распространённых ошибок является использование одного и того же секретного ключа для Access и Refresh-токенов. Это повышает риск компрометации системы, так как злоумышленник, получивший доступ к ключу, сможет создавать оба типа токенов.


Теперь ваша система авторизации в FastAPI стала ещё ближе к боевой реальности: пользователи не будут разлогиниваться каждые 15 минут, что сделает их взаимодействие с приложением более плавным, а безопасность сети останется на высоте!

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