Как проверить JWT, отправленный как файл cookie HttpOnly в FastAPI?

Проблема

Я работаю над приложением FastAPI, которое требует аутентификации для доступа пользователей к определенным конечным точкам. Я использую Oauth2 и Jose из FastAPI для создания JWT для моего процесса аутентификации. Проведя некоторые исследования, кажется, что лучший способ обеспечить защиту токенов на внешнем интерфейсе — это хранить их в HttpOnly Cookies. Я изо всех сил пытаюсь понять, как ПРАВИЛЬНО передать JWT через файлы cookie HttpOnly, чтобы мой сервер FastAPI мог проверять JWT в моих заголовках. В настоящее время, когда я пытаюсь передать токен JWT как HttpOnly Cookie, я получаю 401 Unauthorized Error.

Что я пробовал

Мне удалось успешно аутентифицировать пользователя с помощью токена JWT, когда я кодирую токен в заголовки в виде строки шаблона. Однако, когда я передаю JWT на сервер FastAPI через заголовки в виде файла cookie, мой сервер FastAPI не может аутентифицировать пользователя и возвращает 401 unauthorized error. Я попытался заглянуть на вкладку сети, чтобы увидеть, какие заголовки отправляются в моих запросах на сервер FastApi, чтобы лучше понять, чем отличаются эти два сценария.

Успешный пример с кодом

Это в заголовке, когда я передаю JWT в качестве строки шаблона и получаю ответ 200:

Аутентификация: токен на предъявителя

  async function getPosts() {
    const url = "http://localhost:8000/posts";
    const fetchConfig = {
      headers: {
        Authorization: `Bearer ${tokenValue}`,
      },
    };
    const response = await fetch(url, fetchConfig);
    const posts = await response.json();
  }

Неудачный пример с кодом

Это в заголовке, когда я передаю JWT как файл cookie HttpOnly и получаю ответ 401:

Cookie: access_token="Токен-носитель"

Я также попытался изменить способ установки файла cookie на сервере, чтобы заголовок выглядел так:

Cookie: Authentication="токен носителя"

  async function getPosts() {
    const url = "http://localhost:8000/posts";
    const fetchConfig = {
      credentials: "include",
    };
    const response = await fetch(url, fetchConfig);
    const posts = await response.json();
    console.info(posts);
  }

Код FastAPI

Вот код для проверки моего токена Oauth2, который защищает мои конечные точки API. Это основано на примере в документации FastAPI: FastApi Oauth2

oauth2_scheme = OAuth2PasswordBearer(tokenUrl='login')

SECRET_KEY = settings.SECRET_KEY
ALGORITHM = settings.ALGORITHM
ACCESS_TOKEN_EXPIRE_MINUTES = settings.ACCESS_TOKEN_EXPIRE_MINUTES


def verify_access_token(token: str, credentials_exception):
  try:
    payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
    id: str = payload.get("user_id")
    if id is None:
      raise credentials_exception
    token_data = schemas.TokenData(id=id)
  
  except JWTError:
    raise credentials_exception

  return token_data

def get_current_user(token: str = Depends(oauth2_scheme), db: Session = Depends(database.get_db)):
  credentials_exception = HTTPException(
    status_code=status.HTTP_401_UNAUTHORIZED,
    detail=f"Could not validate credentials",
    headers = {"WWW-Authenticate": "Bearer"}
  )

  token = verify_access_token(token, credentials_exception)
  user = db.query(models.User).filter(models.User.id == token.id).first()

  return user

Вот пример защищенной конечной точки, которая зависит от функции get_current_user из файла oauth2, указанного выше.

@router.get("/", response_model=List[schemas.PostOut])
def get_posts(db: Session = Depends(get_db), current_user: int = Depends(oauth2.get_current_user):
  return {"Message": "Protected Endpoint Reached"}

Похоже, я столкнулся с проблемой, потому что моя функция get_current_user в Oauth2 может получить JWT из заголовка только в следующем формате:

Аутентификация: токен на предъявителя

Кажется, что он не может аутентифицировать токен из заголовка, если он находится в одном из следующих форматов:

Cookie: access_token="Токен-носитель"

Cookie: Authentication="токен носителя"

Нужно ли мне как-то изменить способ отправки заголовков, когда я отправляю их через файлы cookie HttpOnly, или мне, возможно, нужно что-то изменить в моей функции get_current_user, которая позволит ей правильно читать заголовки файлов cookie.

Любые предложения очень ценятся, и спасибо, что нашли время, чтобы прочитать это!


1
201
1

Ответ:

Решено

Чтобы получить токен из файла cookie вместо заголовка Authorization, который используется по умолчанию для OAuth2PasswordBearer, сообщите FastAPI, что вместо этого вы хотите, чтобы токен исходил из файла cookie.

def get_current_user(access_token: str = Cookie(...), db: Session = Depends(database.get_db)):

Это предполагает, что токен был назван access_token (и не просто токен). При необходимости измените имя.