Sometimes*, when accessing document.cookie
in the login page I get an empty string even though:
- cookies are listed in the Chrome and Firefox developer tools,
- httpOnly flag of cookie I'm interested in is set to false,
- path of cookie I'm interested in is set to '/'.
Desired behaviour
My React single page application (SPA) has a login page which contains a <form />
element for sending login credentials to the backend. When the response from the backend is received and the authentication was successful, I check whether the authentication cookie has been set, properly. If that's the case a redirect will be triggered that shows the content for logged in users.
Actual behaviour
Unfortunately, in like 15% of the login attempts, document.cookie
returns an empty string which prevents the redirection and keeps the user on the login page. Pressing F5
doesn't do the trick but when manually replacing a path of the url after a successful login request (e.g. updating 'www.website.tld/login' to 'www.website.tld/start') the user gets forwarded to the desired page which is for logged in users, only.
I'm not able to reproduce the error manually. It just seems to happen randomly. But when it occurs and I have a look into the developer console I can see all the cookies from the backend (set correctly).
Additional information
- a django server is running in the backend
- the desired cookie is set with
response.set_cookie('key', 'value', secure=False httponly=False, samesite='strict')
- JS libs (axios, react-router)
Related:
- Can't access cookies from document.cookie in JS, but browser shows cookies exist (httpOnly)
- Can't access a cookie using document.cookie in JS (httpOnly)
Login page (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: .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>
);
};
Routing (JSX)
import React from "react";
import { Route, Redirect } from "react-router-dom";
const RootLayout = () => {
return (
<div className="root-layout">
<Switch>
<PublicRoute path="/login" ponent={LoginPage} />
<PrivateRoute path="/" ponent={App} />
</Switch>
</div>
);
};
/**
* Component that handles redirection when user is logged in already
*/
const PublicRoute = ({ ponent: 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 = ({ ponent: 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" ponent={StartPage} />
<Route exact path="/blog" ponent={BlogPage} />
{/*...*/}
</Switch>
);
* I know, that's probably not how a post should start...
Sometimes*, when accessing document.cookie
in the login page I get an empty string even though:
- cookies are listed in the Chrome and Firefox developer tools,
- httpOnly flag of cookie I'm interested in is set to false,
- path of cookie I'm interested in is set to '/'.
Desired behaviour
My React single page application (SPA) has a login page which contains a <form />
element for sending login credentials to the backend. When the response from the backend is received and the authentication was successful, I check whether the authentication cookie has been set, properly. If that's the case a redirect will be triggered that shows the content for logged in users.
Actual behaviour
Unfortunately, in like 15% of the login attempts, document.cookie
returns an empty string which prevents the redirection and keeps the user on the login page. Pressing F5
doesn't do the trick but when manually replacing a path of the url after a successful login request (e.g. updating 'www.website.tld/login' to 'www.website.tld/start') the user gets forwarded to the desired page which is for logged in users, only.
I'm not able to reproduce the error manually. It just seems to happen randomly. But when it occurs and I have a look into the developer console I can see all the cookies from the backend (set correctly).
Additional information
- a django server is running in the backend
- the desired cookie is set with
response.set_cookie('key', 'value', secure=False httponly=False, samesite='strict')
- JS libs (axios, react-router)
Related:
- Can't access cookies from document.cookie in JS, but browser shows cookies exist (httpOnly)
- Can't access a cookie using document.cookie in JS (httpOnly)
Login page (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./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>
);
};
Routing (JSX)
import React from "react";
import { Route, Redirect } from "react-router-dom";
const RootLayout = () => {
return (
<div className="root-layout">
<Switch>
<PublicRoute path="/login" ponent={LoginPage} />
<PrivateRoute path="/" ponent={App} />
</Switch>
</div>
);
};
/**
* Component that handles redirection when user is logged in already
*/
const PublicRoute = ({ ponent: 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 = ({ ponent: 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" ponent={StartPage} />
<Route exact path="/blog" ponent={BlogPage} />
{/*...*/}
</Switch>
);
* I know, that's probably not how a post should start...
- 1 Can you add the code where you are checking this? – hiddenuser.2524 Commented Mar 30, 2020 at 17:22
- Thanks for your ment, @tudor.gergely. I added some code, now. – davsto Commented Mar 31, 2020 at 8:53
-
can you try adding
withCredentials: true
to your axios conf? – hiddenuser.2524 Commented Mar 31, 2020 at 9:20 -
It sounds like it could be related to "samesite". Did you try setting it to "lax"? And generally, I would advise to use
httponly=True
. The frontend has no need to access the auth cookies at all and you should use the backend for authentication. – str Commented Mar 31, 2020 at 10:17 -
Setting
samesite="lax"
(or "none") helps! I haven't tested all features yet but the first impression is good. I was able to reproduce the error, now. Accessing the cookies only works, when I enter the URL in the browser manually or use a bookmark. When entering the SPA through a link on an external site the cookies can't be accessed. Is the page that includes the link to the SPA the 'first party' which prevents me from reading the cookies when theirsamesite
flag is set to "strict"? – davsto Commented Apr 1, 2020 at 18:31
1 Answer
Reset to default 3You are running into an issue with sameSite cookies. See SameSite cookies explained for an explanation:
If you set SameSite to Strict, your cookie will only be sent in a first-party context. [...] When the user is on your site, then the cookie will be sent with the request as expected. However when following a link into your site, say from another site or via an email from a friend, on that initial request the cookie will not be sent. This is good when you have cookies relating to functionality that will always be behind an initial navigation, such as changing a password or making a purchase, but is too restrictive for promo_shown. If your reader follows the link into the site, they want the cookie sent so their preference can be applied.
Now you have at least two options:
- Remended: Keep
samesite=strict
and refactor your client code. The frontend does not need access to the auth cookies at all and thus you could sethttponly=True
. Then introduce a backend API that validates a cookie based on requests from the client code. That gives you the added advantage to be less vulnerable to XSS attacks as the frontend code has no access to the auth cookie. - Not remended: Set
samesite
tonone
orlax
.
发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745384792a4625390.html
评论列表(0条)