Я работаю над приложением 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);
}
Вот код для проверки моего токена 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.
Любые предложения очень ценятся, и спасибо, что нашли время, чтобы прочитать это!
Чтобы получить токен из файла cookie вместо заголовка Authorization
, который используется по умолчанию для OAuth2PasswordBearer, сообщите FastAPI, что вместо этого вы хотите, чтобы токен исходил из файла cookie.
def get_current_user(access_token: str = Cookie(...), db: Session = Depends(database.get_db)):
Это предполагает, что токен был назван access_token
(и не просто токен). При необходимости измените имя.