Вопрос-Ответ

FastAPI is not returning cookies to React frontend

FastAPI не возвращает файлы cookie во внешний интерфейс React

Почему FastAPI не возвращает файлы cookie в мой внешний интерфейс, который является приложением React?

Вот мой код:

@router.post("/login")
def user_login(response: Response,username :str = Form(),password :str = Form(),db: Session = Depends(get_db)):
user = db.query(models.User).filter(models.User.mobile_number==username).first()
if not user:
raise HTTPException(400, detail='wrong phone number or password')
if not verify_password(password, user.password):
raise HTTPException(400, detail='wrong phone number or password')


access_token = create_access_token(data={"sub": user.mobile_number})
response.set_cookie(key="fakesession", value="fake-cookie-session-value") #here I am set cookie
return {"status":"success"}

Когда я захожу в систему из автодокументов Swagger UI, я вижу файл cookie в заголовках ответов с помощью DevTools в браузере Chrome. Однако, когда я захожу в систему из своего приложения React, файл cookie не возвращается. Я использую axios для отправки запроса следующим образом:

await axios.post(login_url, formdata)

Переведено автоматически
Ответ 1

Сначала создайте файл cookie, как показано в примере ниже, и убедитесь, что при выполнении запроса Axios POST не возвращается ошибка, и что вы получаете 'status': 'success' ответ с 200 кодом состояния. Возможно, вы также захотите ознакомиться с этим ответом, в котором также объясняется, как использовать флаги max_age и expires.

from fastapi import FastAPI, Response

app = FastAPI()

@app.get('/')
def main(response: Response):
response.set_cookie(key='token', value='some-token-value', httponly=True)
return {'status': 'success'}

Во—вторых, как вы упомянули, вы используете React во внешнем интерфейсе — который должен прослушивать порт, отличный от того, который используется для серверной части FastAPI, что означает, что вы выполняете запросы CORS - вам нужно установить для withCredentials свойства значение true (по умолчанию оно установлено в false), чтобы разрешить получение / отправку учетных данных, таких как файлы cookie и заголовки HTTP-аутентификации, от / к другим источникам. Два сервера с одинаковым доменом и протоколом, но с разными портами, например, http://localhost:8000 и http://localhost:3000 считаются разными источниками (см. Документацию FastAPI по CORS и этот ответ, в котором подробно описаны файлы cookie в целом, а также решения для настройки междоменных файлов cookie, которые в вашем случае на самом деле вам не нужны, поскольку домен одинаков как для серверной части, так и для внешнего интерфейса, и, следовательно, установка файлов cookie в обычном режиме будет работать просто отлично).

Обратите внимание, что если вы обращаетесь к интерфейсу React, набирая http://localhost:3000 в адресной строке вашего браузера, то ваши запросы Axios к серверной части FastAPI должны использовать localhost домен в URL, например, axios.post('http://localhost:8000',..., а не axios.post('http://127.0.0.1:8000',..., поскольку localhost и 127.0.0.1 - это два разных домена, и, следовательно, файл cookie в противном случае не был бы создан для localhost домена, как это было бы сделано для 127.0.0.1, т. Е. домена, используемого в axios запросе (и тогда это было бы случаем перекрестного доступа). -файлы cookie домена, как описано в связанном ответе выше, который, опять же, в вашем случае не понадобился бы).

Таким образом, чтобы принять файлы cookie, отправленные сервером, вам необходимо использовать withCredentials: true в вашем запросе Axios; в противном случае файлы cookie будут проигнорированы в ответе (что является поведением по умолчанию, когда withCredentials установлено значение false; следовательно, различные домены не могут устанавливать файлы cookie для своего собственного домена). То же самое withCredentials: true свойство должно включаться в каждый последующий запрос к вашему API, если вы хотите, чтобы файл cookie отправлялся на сервер, чтобы пользователь мог пройти аутентификацию и получить доступ к защищенным маршрутам.

Следовательно, запрос Axios, включающий учетные данные, должен выглядеть следующим образом:

await axios.post(url, data, {withCredentials: true}))

Эквивалент в fetch() запросе (т. Е. С использованием Fetch API) равен credentials: 'include'. Значение по умолчанию для credentials равно same-origin. Использование credentials: 'include' приведет к тому, что браузер будет включать учетные данные как в запросы одного источника, так и в запросы разных источников, а также установит любые файлы cookie, отправленные обратно в ответах разных источников. Например:

fetch('https://example.com', {
credentials: 'include'
});

Важное примечание

Поскольку вы выполняете запрос из разных источников, для того чтобы любой из вышеперечисленных способов сработал, вам нужно будет явно указать разрешенные источники, как описано в этом ответе (за кулисами, то есть установить Access-Control-Allow-Origin заголовок ответа). Например:

origins = ['http://localhost:3000', 'http://127.0.0.1:3000',
'https://localhost:3000', 'https://127.0.0.1:3000']

Using the * wildcard instead would mean that all origins are allowed; however, that would also only allow certain types of communication, excluding everything that involves credentials, such as cookies, authorization headers, etc—hence, you should not use the * wildcard.

Also, make sure to set allow_credentials=True when using the CORSMiddleware (which sets the Access-Control-Allow-Credentials response header to true).

Example (see here):

app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
python