We are working on log in functionality with headless React and Pantheon with REST web services.
When I send login request to Drupal site in localhost, it sets a session cookie in my browser.
However, it doesn't do that for Drupal site deployed to Pantheon.
The cookie:
set-cookie: SSESS4ca8b3ff629fc50c99bed7678686=VYfXzM-S18y0n8PylU9HuHBVrEyZezRiwfirxB%2ClL; expires=Fri, 18-Nov-2022 18:14:03 GMT; Max-Age=2000000; path=/; domain=.oursitename.pantheonsite.io; secure; HttpOnly
Login request:
const body = {
name: name,
pass: pass,
};
const config = {
headers: {
"Content-Type": "application/json",
"X-CSRF-Token": token,
},
withCredentials: true,
};
axios
.post(
`${process.env.REACT_APP_DRUPAL_URL}/user/login?_format=json`,
body,
config
)
Update:
Cookie is getting stored in Mozilla browser, but not in Chrome.
You need to append STYXKEY to the cookie's name. Cookies without that prefix are stripped from the request at the edge in Pantheon
You may find interesting as well this official documentation about pantheon and cookies
https://pantheon.io/docs/cookies
Related
I am trying to access an endpoint on my Express server that has an http only cookie as part of the authentication dance to gain access.
Here we set the http only cookie:
res.cookie('jwt', refreshToken, { httpOnly: true, sameSite: 'none', secure: true, maxAge: 3 * 24 * 60 * 60 * 1000 });
return res.send({ accessToken, role: userModel.role, profileId: userModel.profile });
Here is my endpoint I am trying to integration test:
router.delete('/auth/delete/:id', basicAuth, checkAccountId, mentors.deleteMentorProfile);
Inside basicAuth there is this: export const basicAuth = passport.authenticate('jwt', { session: false, failWithError: true });
What I think is required is that the httponly cookie is sent with the request to this endpoint. The line says passport.authenticate('jwt', ...) hence I must need the 'jwt' value to be there in a cookie.
And here is the code from my integration test:
const logInResponse = await api.post('/api/users/authenticate').send(userAcctCreationDetails);
const jwtForTest = logInResponse.body.accessToken;
const allMentors = await Mentor.find({});
const testAccountMentorId = allMentors.find((mentor) => mentor.email === userToDelete.email)?._id.toString();
// act
const cookies = logInResponse.headers['set-cookie'][0];
// console.log(cookies);
const deletedProfileResponse = await api
.delete('/api/mentor/auth/delete/' + testAccountMentorId)
.set('Authorization', 'Bearer ' + jwtForTest)
.set('jwt', cookies); // this fails!
The line console.log(cookies) says some authentication credentials ending in jwt=eyJhbGciOi...-2n21vxTNPOlS94-YeFhSN7o; Max-Age=259200; Path=/; Expires=Thu, 15 Dec 2022 01:26:24 GMT; HttpOnly; Secure; SameSite=None
My coworker tells me "httponly means the client can't access it" but can I at least send it back? How does a browser send it back if not via javascript?
Note I did google it and i found extreme scarcity of information about supertest and HTTP only cookies.
I am trying to set up Authentication with Deno, Oak and JWT.
Objective:
Send cookie with jwt token in the response.
Set cookie in the browser
Use the cookie for subsequent requests.
Route: Cookie being set
export const getUsers = async ({ response, cookies }) => {
const users = await userCollection.find().toArray();
await cookies.set("token", "1234567890", {
sameSite: "lax",
});
response.body = users;
};
handling the Cors issue
app.use(
oakCors({
credentials: true,
origin: /^.+localhost:(3000|4200|8080)$/,
})
);
Response headers has the cookie but the same is not set in Application --> Cookies.
Please help me understand this issue.
using ctx.cookies.set is how you set a cookie in Oak, have in mind that by default it's httpOnly. Your browser might not show it in that case or you're looking in the wrong place.
From the screenshot we can see that Oak is setting the cookie correctly in the response headers:
token=1234567890; path=/; samesite=lax; httponly
To check that the cookie is set correctly, just add:
console.log(await ctx.cookies.get('token'));
And you'll see that in subsequent requests will log 1234567890 correctly.
Since you're mentioning CORS I suspect that you're looking the cookie to be present in the wrong domain, you should be looking for the cookie in:
localhost:8000 (server setting the cookie)
And not in your front end domain:port
So, if you issue the request from: http://localhost:3000 to http://localhost:8000, when you're in :3000 there will be no cookie present in Application > Cookies > http://localhost:3000, but it'll be in Application > Cookies > http://localhost:8000
I have various country domains which I wish to share cookie data with the primary domain, ie for auth purposes so that users remain logged in or identified when transitioning between these domains.
All is working as expected in Chrome, however Firefox is refusing to send non-session cookies along with cross-site requests. (session cookies are being included successfully). There are no warnings or errors within the firefox console relating to this.
When a cookie is set on the primary domain, I am setting SameSite to none and secure to true:
set-cookie: nonsessioncookie=abc1234; expires=Sun, 03-Sep-2023 13:36:26 GMT; path=/; SameSite=none; secure=true;
I then perform a jQuery request from a secondary domain to the primary domain:
$.ajax({
url: primaryHost + "/Services/AuthRequest.ashx",
data: { 'sdtoken': stoken},
xhrFields: {
withCredentials: true
},
crossDomain: true,
success: function (data) {
// Do Something
}
});
The following Request headers are sent, however notice the only cookie being sent is a session cookie; the 'nonsessioncookie' cookie is missing from the request:
Origin: https://secondary.domain
Connection: keep-alive
Referer: https://secondary.domain/
Cookie: ASP.NET_SessionId=rpchfdrzqzp5zujwi24fjll2
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
And the Response contains the following headers returned from the primary domain
access-control-allow-origin: https://secondary.domain
access-control-allow-methods: POST, GET, OPTIONS
access-control-allow-credentials: true
Chrome successfully passes the 'nonsessioncookie', however firefox doesn't, even though SameSite and secure is set.
Any thoughts what could be preventing non-session cookies from being passed?
Summary
Once set-cookie header is sent in a response it takes another request before the cookie is visible in handle() function in the hooks.ts file.
Example
User POSTs username & password to the login endpoint;
Enpoint responds with set access_token cookie header;
User should be redirected to a protected page. (FAILS)
It fails because auth guard checks if the cookie exists, but it can't be seen from a code side at this point, only in the browser end.
Refresh the page
User is now able to be redirected to a protected page.
Minimal reproduction
It has dummy login/logout functionality & protected user profile. There are also server side console logs which shows that cookie lags to be recognised in a hook.
As mentioned in the comments, your server returns the HTTP status code 200 which does not redirect the Client after the Login. The browser assumes it is already at its final destination due to the status code.
In this case, best practice is to use the status code 302: Wikipedia
Modified /src/routes/auth/login.ts
import * as cookie from 'cookie';
export const post = (request) => {
return {
status: 302,
headers: {
location: '/',
'set-cookie': `${cookie.serialize('token', 'VALUE_OF_THE_COOKIE')}; path=/; HttpOnly`
}
}
};
export const del = (request) => {
return {
status: 302,
headers: {
location: '/',
'set-cookie': `${cookie.serialize('token', '')}; path=/; HttpOnly; maxAge: 0`
}
}
};
I created an API /user/auth where I can send using POST a Json object like:
var user = {"username":"alex", "password":"m"}
$http(
{
method: 'POST',
url: '/api/v1/user/auth',
data: user,
}
).
success(function(data, status, headers, config) {
console.log(data)
}).
error(function(data, status, headers, config) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});
The response from Django is the following:
Access-Control-Allow-Credentials:true
Access-Control-Allow-Headers:Content-Type,*
Access-Control-Allow-Methods:POST,GET,OPTIONS,PUT,DELETE
Access-Control-Allow-Origin:*
Content-Language:fr
Content-Type:application/json
Date:Fri, 30 Aug 2013 15:22:01 GMT
Server:WSGIServer/0.1 Python/2.7.5
Set-Cookie:sessionid=w63m0aoo8m3vfmvv0vk5d6w1708ftsrk; Path=/
Vary:Accept, Accept-Language, Cookie
So Django returns a good cookie but I don't know why, Chrome doesn't set this cookie in Resource.
The request is sent from 127.0.0.1:8000 to 127.0.0.1:8080; I use this middleware to handle CROS requests and I also set:
SESSION_COOKIE_HTTPONLY = False
The problematic line is:
Access-Control-Allow-Origin: *
The credential request doesn't work with a wildcard allow origin. You have to specifically set the name, like :
Access-Control-Allow-Origin: http://127.0.0.1:8080
You can find more information here:
https://developer.mozilla.org/en-US/docs/HTTP/Access_control_CORS?redirectlocale=en-US&redirectslug=HTTP_access_control#Requests_with_credentials
Ok thanks to Bqm link to mozilla I finally found why the cookie was not set.
Indeed you need to set in the header you sent:
Access-Control-Allow-Credentials: true
In Angular this is done with this method:
$http(
{
method: 'POST',
url: '/api/v1/user/auth',
data: user,
withCredentials: true
}
)
Once your backend will answer with a setCookie, the browser will then be able to set the cookie in your browser.