the title speaks for itself: From one moment to another, I am unable to set cookies from the server page of my Sveltekit project.
In the +page.server.js of my page I have an action in which I am setting a cookie with the set function. After that, when reloading the page, I am trying to retrieve the cookie with the get function inside the load function, but the cookie is undefined. I am quite convinced that it is not set at all, but I don't know why.
export const load = async ({ cookies }) => {
const myCookie = cookies.get('myCookie');
}
export const actions = {
default: async ({ cookies }) => {
//stuff
cookies.set('myCookie', true);
throw redirect(303, '/');
}
}
I specify that this is a procedure I have already done in other projects and it has always worked. What could be the problem? Thank you for your help.
UPDATE:
I don't know if it helps, but I want to specify that I am using Pocketbase and that a cookie is set in the hook.server.js of the project, which I can see in the browser and works correctly:
export async function handle({ event, resolve }) {
event.locals.pb = new PocketBase('http://127.0.0.1:8090');
event.locals.pb.authStore.loadFromCookie(event.request.headers.get('cookie') || '');
if (event.locals.pb.authStore.isValid) {
event.locals.user = serializeNonPOJOs(event.locals.pb.authStore.model);
} else {
event.locals.user = undefined;
}
const response = await resolve(event, {
transformPageChunk: ({ html }) => minify(html, minification_options)
})
response.headers.set('set-cookie', event.locals.pb.authStore.exportToCookie({ secure: false }));
return response
}
I want to save/persist/preserve a cookie or localStorage token that is set by a cy.request(), so that I don't have to use a custom command to login on every test. This should work for tokens like jwt (json web tokens) that are stored in the client's localStorage.
To update this thread, there is already a better solution available for preserving cookies (by #bkucera); but now there is a workaround available now to save and restore local storage between the tests (in case needed). I recently faced this issue; and found this solution working.
This solution is by using helper commands and consuming them inside the tests,
Inside - cypress/support/<some_command>.js
let LOCAL_STORAGE_MEMORY = {};
Cypress.Commands.add("saveLocalStorage", () => {
Object.keys(localStorage).forEach(key => {
LOCAL_STORAGE_MEMORY[key] = localStorage[key];
});
});
Cypress.Commands.add("restoreLocalStorage", () => {
Object.keys(LOCAL_STORAGE_MEMORY).forEach(key => {
localStorage.setItem(key, LOCAL_STORAGE_MEMORY[key]);
});
});
Then in test,
beforeEach(() => {
cy.restoreLocalStorage();
});
afterEach(() => {
cy.saveLocalStorage();
});
Reference: https://github.com/cypress-io/cypress/issues/461#issuecomment-392070888
From the Cypress docs
For persisting cookies: By default, Cypress automatically clears all cookies before each test to prevent state from building up.
You can configure specific cookies to be preserved across tests using the Cypress.Cookies api:
// now any cookie with the name 'session_id' will
// not be cleared before each test runs
Cypress.Cookies.defaults({
preserve: "session_id"
})
NOTE: Before Cypress v5.0 the configuration key is "whitelist", not "preserve".
For persisting localStorage: It's not built in ATM, but you can achieve it manually right now because the method thats clear local storage is publicly exposed as Cypress.LocalStorage.clear.
You can backup this method and override it based on the keys sent in.
const clear = Cypress.LocalStorage.clear
Cypress.LocalStorage.clear = function (keys, ls, rs) {
// do something with the keys here
if (keys) {
return clear.apply(this, arguments)
}
}
You can add your own login command to Cypress, and use the cypress-localstorage-commands package to persist localStorage between tests.
In support/commands:
import "cypress-localstorage-commands";
Cypress.Commands.add('loginAs', (UserEmail, UserPwd) => {
cy.request({
method: 'POST',
url: "/loginWithToken",
body: {
user: {
email: UserEmail,
password: UserPwd,
}
}
})
.its('body')
.then((body) => {
cy.setLocalStorage("accessToken", body.accessToken);
cy.setLocalStorage("refreshToken", body.refreshToken);
});
});
Inside your tests:
describe("when user FOO is logged in", ()=> {
before(() => {
cy.loginAs("foo#foo.com", "fooPassword");
cy.saveLocalStorage();
});
beforeEach(() => {
cy.visit("/your-private-page");
cy.restoreLocalStorage();
});
it('should exist accessToken in localStorage', () => {
cy.getLocalStorage("accessToken").should("exist");
});
it('should exist refreshToken in localStorage', () => {
cy.getLocalStorage("refreshToken").should("exist");
});
});
Here is the solution that worked for me:
Cypress.LocalStorage.clear = function (keys, ls, rs) {
return;
before(() => {
LocalStorage.clear();
Login();
})
Control of cookie clearing is supported by Cypress: https://docs.cypress.io/api/cypress-api/cookies.html
I'm not sure about local storage, but for cookies, I ended up doing the following to store all cookies between tests once.
beforeEach(function () {
cy.getCookies().then(cookies => {
const namesOfCookies = cookies.map(c => c.name)
Cypress.Cookies.preserveOnce(...namesOfCookies)
})
})
According to the documentation, Cypress.Cookies.defaults will maintain the changes for every test run after that. In my opinion, this is not ideal as this increases test suite coupling.
I added a more robust response in this Cypress issue: https://github.com/cypress-io/cypress/issues/959#issuecomment-828077512
I know this is an old question but wanted to share my solution either way in case someone needs it.
For keeping a google token cookie, there is a library called
cypress-social-login. It seems to have other OAuth providers as a milestone.
It's recommended by the cypress team and can be found on the cypress plugin page.
https://github.com/lirantal/cypress-social-logins
This Cypress library makes it possible to perform third-party logins
(think oauth) for services such as GitHub, Google or Facebook.
It does so by delegating the login process to a puppeteer flow that
performs the login and returns the cookies for the application under
test so they can be set by the calling Cypress flow for the duration
of the test.
I can see suggestions to use whitelist. But it does not seem to work during cypress run.
Tried below methods in before() and beforeEach() respectively:
Cypress.Cookies.defaults({
whitelist: "token"
})
and
Cypress.Cookies.preserveOnce('token');
But none seemed to work. But either method working fine while cypress open i.e. GUI mode. Any ideas where I am coming short?
2023 Updated on Cypress v12 or more:
Since Cypress Version 12 you can use the new cy.session()
it cache and restore cookies, localStorage, and sessionStorage (i.e. session data) in order to recreate a consistent browser context between tests.
Here's how to use it
// Caching session when logging in via page visit
cy.session(name, () => {
cy.visit('/login')
cy.get('[data-test=name]').type(name)
cy.get('[data-test=password]').type('s3cr3t')
cy.get('form').contains('Log In').click()
cy.url().should('contain', '/login-successful')
})
I have a working Django REST API which returns this:
{
"id": 1,
"brand": "peugeot",
"model": "3008",
"variant": "allure"
}
I am using the following code to fetch the above data:
render() {
const { brand, model, variant } = this.props;
let url = `http://127.0.0.1:8000/api/car/${brand}/${model}/${variant}/`;
console.log(url) <== url is correct when checked in console
fetch(url)
.then(response => response.json())
.then(data => data.length === 0 ? this.setState({
data : data
}) : null ) <== I have used a condition for setState to stop fetching infintely
const { data } = this.state;
console.log(data) <== This is a blank object with no data in console
console.log(data.id) <== This is undefined in console
return (
<div>
{data.id} <== No data is shown on webpage
Car Details
</div>
);
}
No error is shown when I try to fetch the data on my webpage. What am I doing wrong?
P.S. Data can be fetched from the same API server when I have an array of objects, and I use map to loop over the data. Over here I am trying to fetch a single item so there is no array, just an object. Am I doing something wrong with the syntax?
You should never fetch or setState inside the render function.
render is called many times due to all kinds of side effects, i.e scrolling, clicking, props changing etc. This kind of code could cause all kinds of trouble.
If you need to perform the request once, call the fetch function inside componentDidMount. Also, I believe your callbacks should look something like this:
fetch(url)
.then(response => response.json())
.then(data => this.setState({ data : data }))
Taken from the docs:
componentDidMount() is invoked immediately after a component is mounted. Initialization that requires DOM nodes should go here. If you need to load data from a remote endpoint, this is a good place to instantiate the network request.
I changed the condition before 'setState' to JSON.stringify(data) !== JSON.stringify(this.state.data) and now it works.
should it be:
.then(data => data.length > 0 ? this.setState({ data }) : null )
Using ASPNETCORE OpenId authentication middleware and Cookie middleware. I always see that cookies from OpenId authentication are set to expire at 1969-12-31 (in Chrome debugger). I assume this means the cookies are SESSION cookies; I want to make them persistent cookies so the user will be prompted to login less frequently. So I added the ExpireTimeSpan and IsPersistent=true as suggested in other posts, but I still see that my cookie Expires is 1969-12-31.
What am I doing wrong?
services.AddAuthentication(options =>
{
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddAzureAd(options =>
{
Configuration.Bind("AzureAd", options);
})
.AddCookie(p =>
{
p.ExpireTimeSpan = TimeSpan.FromDays(30);
p.SlidingExpiration = true;
});
services.Configure<AuthenticationProperties>(props =>
{
props.IsPersistent = true;
props.ExpiresUtc = new DateTimeOffset(DateTime.Now, TimeSpan.FromDays(30));
});
Got helped on the aspnetcore security forum, and arrived at the following solution:
.AddCookie(p =>
{
p.SlidingExpiration = true;
p.Events.OnSigningIn = (context) =>
{
context.CookieOptions.Expires = DateTimeOffset.UtcNow.AddDays(30);
return Task.CompletedTask;
};
});
I also implemented a Logout page (call to AuthenticationHttpContextExtensions.SignOutAsync(HttpContext)) to give the user more control over cookie lifetimes.
Trying to get all cookies from the page, using chrome extension.
For example the page os https://ya.ru.
Here is my code:
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
var domain = getDomain(tabs[0].url);
chrome.cookies.getAll({
domain: domain
}, function(cookies) {
console.log(cookies);
});
});
This code will return all cookies (8) for domain ya.ru.
But the problem is when I'm opening the browser console I see cookies not only for ya.ru domain:
Same situation is on google and other sites. So I'm having multiple domain cookies on one page. How to get ALL cookies on the page?
Thank for your time.
Devtools shows cookies for all resource URLs requested by the page (source code) so we can do the same by accessing Performance API in the content script code that we'll execute in the page:
chrome.tabs.executeScript({
code: 'performance.getEntriesByType("resource").map(e => e.name)',
}, data => {
if (chrome.runtime.lastError || !data || !data[0]) return;
const urls = data[0].map(url => url.split(/[#?]/)[0]);
const uniqueUrls = [...new Set(urls).values()].filter(Boolean);
Promise.all(
uniqueUrls.map(url =>
new Promise(resolve => {
chrome.cookies.getAll({url}, resolve);
})
)
).then(results => {
// convert the array of arrays into a deduplicated flat array of cookies
const cookies = [
...new Map(
[].concat(...results)
.map(c => [JSON.stringify(c), c])
).values()
];
// do something with the cookies here
console.log(uniqueUrls, cookies);
});
});
Important notes:
Your manifest.json should have "<all_urls>" in "permissions" or an explicit list of URL match patterns that will allow the corresponding results in chrome.cookies.getAll.
Cookie deduplication code above may be slow when there are thousands of cookies.
We're reading cookies for an URL, not domain, because it's the only reliable way to get an encompassing cookie (imgur.com) for something like i.stack.imgur.com