Каково назначение Refresh Token в системах аутентификации

Ответ

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

Зачем это нужно?

Основная цель — повысить безопасность. Access Token, который предоставляет доступ к ресурсам, имеет очень короткий срок жизни (например, 15 минут). Если его перехватят, злоумышленник сможет использовать его лишь в течение этого короткого времени. Refresh Token живёт дольше (дни или недели), но может быть использован только для одной цели — запроса нового Access Token.

Типичный сценарий работы:

  1. Пользователь входит в систему, сервер выдает пару токенов: access_token и refresh_token.
  2. Клиент использует access_token для всех запросов к защищенным ресурсам.
  3. Когда access_token истекает, клиент получает ошибку 401 Unauthorized.
  4. Клиент отправляет refresh_token на специальный эндпоинт (например, /api/token/refresh).
  5. Сервер проверяет refresh_token и, если он валиден, выпускает новую пару токенов.

Пример реализации на Python с PyJWT:

import jwt
import datetime
from flask import Flask, jsonify, request

app = Flask(__name__)
SECRET_KEY = "your-super-secret-key"

# Примерная база данных валидных refresh токенов
VALID_REFRESH_TOKENS = set()

@app.route('/login', methods=['POST'])
def login():
    # ... (проверка логина и пароля) ...
    user_id = 123

    # Создаем Access Token на 15 минут
    access_token = jwt.encode(
        {"user_id": user_id, "exp": datetime.datetime.utcnow() + datetime.timedelta(minutes=15)},
        SECRET_KEY, algorithm="HS256"
    )
    # Создаем Refresh Token на 7 дней
    refresh_token = jwt.encode(
        {"user_id": user_id, "exp": datetime.datetime.utcnow() + datetime.timedelta(days=7)},
        SECRET_KEY, algorithm="HS256"
    )
    VALID_REFRESH_TOKENS.add(refresh_token) # Сохраняем токен как валидный
    return jsonify({"access_token": access_token, "refresh_token": refresh_token})

@app.route('/refresh', methods=['POST'])
def refresh():
    token = request.json.get('refresh_token')
    if not token or token not in VALID_REFRESH_TOKENS:
        return jsonify({"error": "Invalid or expired refresh token"}), 401

    try:
        # Декодируем, чтобы получить user_id для нового токена
        data = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
        user_id = data['user_id']

        # Выдаем новый Access Token
        new_access_token = jwt.encode(
            {"user_id": user_id, "exp": datetime.datetime.utcnow() + datetime.timedelta(minutes=15)},
            SECRET_KEY, algorithm="HS256"
        )
        return jsonify({"access_token": new_access_token})
    except jwt.ExpiredSignatureError:
        return jsonify({"error": "Refresh token expired"}), 401

Ключевые аспекты безопасности:

  • Хранение: Refresh Token должен храниться в безопасном месте на клиенте (например, в HttpOnly cookie).
  • Отзыв: При выходе пользователя из системы (logout) или при подозрении на компрометацию Refresh Token должен быть немедленно отозван на сервере.