Иногда * при доступе к document.cookie
на странице входа я получаю пустую строку, хотя:
- файлы cookie перечислены в инструментах разработчика Chrome и Firefox,
- Флаг httpOnly интересующего меня файла cookie установлен на false,
- путь к файлу cookie, который меня интересует, установлен в '/'.
Желаемое поведение
Одностраничное приложение My React (SPA) имеет страницу входа, которая содержит элемент <form />
для отправки учетных данных для входа в бэкэнд. Когда получен ответ от серверной части и аутентификация прошла успешно, я проверяю, правильно ли установлен файл cookie аутентификации. В этом случае будет запущено перенаправление, показывающее контент для вошедших в систему пользователей.
Фактическое поведение
К сожалению, как и 15% попыток входа в систему, document.cookie
возвращает пустую строку, которая предотвращает перенаправление и удерживает пользователя на странице входа. Нажатие F5
не помогает, но при замене пути URL-адреса вручную после успешного запроса на вход (например, обновление «www.website.tld / login» на «www.website.tld / < strong> start ') пользователь перенаправляется на желаемую страницу, предназначенную только для авторизованных пользователей.
Я не могу воспроизвести ошибку вручную. Просто кажется, что это происходит случайно. Но когда это происходит, и я смотрю в консоль разработчика, я вижу все файлы cookie с бэкэнда (настроены правильно).
Дополнительная информация
- сервер django работает в бэкэнде
- желаемый файл cookie устанавливается с помощью
response.set_cookie('key', 'value', secure=False httponly=False, samesite='strict')
- Библиотеки JS (axios, response-router)
Связанный:
- Не удается получить доступ к файлам cookie из document.cookie в JS, но браузер показывает, что файлы cookie существуют (httpOnly)
- Невозможно получить доступ к cookie с помощью document.cookie в JS а> (httpOnly)
Страница входа (JSX)
import React, { useState } from "react";
import { Redirect } from "react-router-dom";
import axios from "axios";
/**
* We're using cookies.js to read cookies.
* Source: https://github.com/madmurphy/cookies.js
*/
function hasItem(sKey) {
return new RegExp(
"(?:^|;\\s*)" +
encodeURIComponent(sKey).replace(/[\-\.\+\*]/g, "\\$&") +
"\\s*\\="
).test(document.cookie);
}
export const LoginPage = () => {
const [isAuthenticated, setIsAuthenticated] = useState(false);
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
function handleSubmit(e) {
e.preventDefault();
function onSuccess(response) {
// handle response
// [...]
// sometimes console.log(document.cookie) returns empty string
if (hasItem("auth_cookie")) {
setIsAuthenticated(true);
} else {
console.warn("Cookie not found!");
}
}
function onFailure(error) {
// handle error
}
const conf = {
headers: new Headers({
"Content-Type": "application/json; charset=UTF-8",
Origin: window.location.origin
})
};
axios
.post("/api/login/", { username, password }, conf)
.then(response => {
onSuccess(response);
})
.catch(error => {
onFailure(error);
});
}
if (isAuthenticated) {
return <Redirect to="/start" />;
}
return (
<div className="login-page">
<form
name="login-form"
method="post"
onSubmit={e => handleSubmit(e)}
action="api/login"
target="hiddenFrame"
>
<iframe className="invisible-frame" src="" name="hiddenFrame" />
<div>
<label htmlFor="username">Email</label>
<input
name="username"
type="text"
onChange={e => setUsername(e.target.value)}
/>
</div>
<div>
<label htmlFor="password">Password</label>
<input
name="password"
type="password"
onChange={e => setPassword(e.target.value)}
/>
</div>
<button type="submit">Submit</button>
</form>
</div>
);
};
Маршрутизация (JSX)
import React from "react";
import { Route, Redirect } from "react-router-dom";
const RootLayout = () => {
return (
<div className="root-layout">
<Switch>
<PublicRoute path="/login" component={LoginPage} />
<PrivateRoute path="/" component={App} />
</Switch>
</div>
);
};
/**
* Component that handles redirection when user is logged in already
*/
const PublicRoute = ({ component: ChildComponent, ...remainingProps }) => {
let isAuthenticated = hasItem("auth_cookie");
return (
<Route
render={props =>
isAuthenticated ? <Redirect to="/" /> : <ChildComponent {...props} />
}
{...remainingProps}
/>
);
};
/**
* Component that handles redirection when user has been logged out.
* E.g. when authentication cookie expires.
*/
const PrivateRoute = ({ component: ChildComponent, ...remainingProps }) => {
let isAuthenticated = hasItem("auth_cookie");
return (
<Route
render={props =>
!isAuthenticated ? (
<Redirect to="/login" />
) : (
<ChildComponent {...props} />
)
}
{...remainingProps}
/>
);
};
const App = () => (
<Switch>
<Route exact path="/" render={() => <Redirect to="/start" />} />
<Route exact path="/start" component={StartPage} />
<Route exact path="/blog" component={BlogPage} />
{/*...*/}
</Switch>
);
* I know, that's probably not how a post should start...
withCredentials: true
в вашу axios conf? - person tudor.gergely   schedule 31.03.2020httponly=True
. Внешнему интерфейсу вообще не нужен доступ к файлам cookie аутентификации, и вы должны использовать бэкэнд для аутентификации. - person str   schedule 31.03.2020samesite="lax"
(или нет) помогает! Я еще не тестировал все функции, но первое впечатление хорошее. Теперь я смог воспроизвести ошибку. Доступ к файлам cookie работает только тогда, когда я ввожу URL-адрес в браузере вручную или использую закладку. При входе в SPA по ссылке на внешнем сайте файлы cookie недоступны. Является ли страница, содержащая ссылку на SPA, «первой стороной», которая не позволяет мне читать файлы cookie, когда для их флагаsamesite
установлено строгое значение? - person davsto   schedule 01.04.2020httponly=True
файлов cookie будет работать даже сsamesite='strict'
в SPA (и это также будет немного более безопасно). - person str   schedule 01.04.2020