Skip to content

Commit

Permalink
fix: support remove unpartitioned same name cookie first (#44)
Browse files Browse the repository at this point in the history
  • Loading branch information
fengmk2 authored Dec 28, 2023
1 parent efa8a63 commit b81f041
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 3 deletions.
1 change: 1 addition & 0 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ ctx.cookies.set('key', 'value', options);
- maxAge - `Number` cookie 的最大有效时间,如果设置了 maxAge,将会覆盖 expires 的值。
- secure - `Boolean` 是否只在加密信道中传输,注意,如果请求为 http 时,不允许设置为 true https 时自动设置为 true。
- partitioned - `Boolean` 是否设置独立分区状态([CHIPS](https://developers.google.com/privacy-sandbox/3pcd/chips))的 Cookie。注意,只有 `secure` 为 true 的时候此配置才会生效。
- removeUnpartitioned - `Boolean` 是否删除非独立分区状态的同名 cookie。注意,只有 `partitioned` 为 true 的时候此配置才会生效。
- httpOnly - `Boolean` 如果设置为 ture,则浏览器中不允许读取这个 cookie 的值。
- overwrite - `Boolean` 如果设置为 true,在一个请求上重复写入同一个 key 将覆盖前一次写入的值,默认为 false。
- signed - `Boolean` 是否需要对 cookie 进行签名,需要配合 get 时传递 signed 参数,此时前端无法篡改这个 cookie,默认为 true。
Expand Down
4 changes: 4 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ declare namespace EggCookies {
* Is it partitioned or not.
*/
partitioned?: boolean;
/**
* Remove unpartitioned same name cookie or not.
*/
removeUnpartitioned?: boolean;
}

class CookieError extends Error { }
Expand Down
32 changes: 29 additions & 3 deletions lib/cookies.js
Original file line number Diff line number Diff line change
Expand Up @@ -128,11 +128,32 @@ class Cookies {
}
}

const cookie = new Cookie(name, value, opts);
// remove unpartitioned same name cookie first
if (opts.partitioned && opts.removeUnpartitioned) {
const overwrite = opts.overwrite;
if (overwrite) {
opts.overwrite = false;
headers = ignoreCookiesByName(headers, name);
}
const removeCookieOpts = Object.assign({}, opts, {
partitioned: false,
});
const removeUnpartitionedCookie = new Cookie(name, '', removeCookieOpts);
// if user not set secure, reset secure to ctx.secure
if (opts.secure === undefined) removeUnpartitionedCookie.attrs.secure = this.secure;

headers = pushCookie(headers, removeUnpartitionedCookie);
// signed
if (signed) {
removeUnpartitionedCookie.name += '.sig';
headers = ignoreCookiesByName(headers, removeUnpartitionedCookie.name);
headers = pushCookie(headers, removeUnpartitionedCookie);
}
}

const cookie = new Cookie(name, value, opts);
// if user not set secure, reset secure to ctx.secure
if (opts.secure === undefined) cookie.attrs.secure = this.secure;

headers = pushCookie(headers, cookie);

// signed
Expand Down Expand Up @@ -199,11 +220,16 @@ function computeSigned(opts) {

function pushCookie(cookies, cookie) {
if (cookie.attrs.overwrite) {
cookies = cookies.filter(c => !c.startsWith(cookie.name + '='));
cookies = ignoreCookiesByName(cookies, cookie.name);
}
cookies.push(cookie.toHeader());
return cookies;
}

function ignoreCookiesByName(cookies, name) {
const prefix = `${name}=`;
return cookies.filter(c => !c.startsWith(prefix));
}

Cookies.CookieError = CookieError;
module.exports = Cookies;
46 changes: 46 additions & 0 deletions test/lib/cookies.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -513,5 +513,51 @@ describe('test/lib/cookies.test.js', () => {
assert(str.includes('; path=/; httponly'));
}
});

it('should remove unpartitioned property first', () => {
const cookies = Cookies({
secure: true,
headers: {
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.3945.29 Safari/537.36',
},
}, { secure: true }, { partitioned: true, removeUnpartitioned: true });
const opts = {
signed: 1,
};
cookies.set('foo', 'hello', opts);

assert(opts.signed === 1);
assert(opts.secure === undefined);
const headers = cookies.ctx.response.headers['set-cookie'];
// console.log(headers);
assert.equal(headers.length, 4);
assert.equal(headers[0], 'foo=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT; secure; httponly');
assert.equal(headers[1], 'foo.sig=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT; secure; httponly');
assert.equal(headers[2], 'foo=hello; path=/; secure; httponly; partitioned');
assert.equal(headers[3], 'foo.sig=ZWbaA4bWk8ByBuYVgfmJ2DMvhhS3sOctMbfXAQ2vnwI; path=/; secure; httponly; partitioned');
});

it('should remove unpartitioned property first with overwrite = true', () => {
const cookies = Cookies({
secure: true,
headers: {
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.3945.29 Safari/537.36',
},
}, { secure: true }, { partitioned: true, removeUnpartitioned: true, overwrite: true });
const opts = {
signed: 1,
};
cookies.set('foo', 'hello2222', opts);
cookies.set('foo', 'hello', opts);

assert(opts.signed === 1);
assert(opts.secure === undefined);
const headers = cookies.ctx.response.headers['set-cookie'];
assert.equal(headers.length, 4);
assert.equal(headers[0], 'foo=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT; secure; httponly');
assert.equal(headers[1], 'foo.sig=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT; secure; httponly');
assert.equal(headers[2], 'foo=hello; path=/; secure; httponly; partitioned');
assert.equal(headers[3], 'foo.sig=ZWbaA4bWk8ByBuYVgfmJ2DMvhhS3sOctMbfXAQ2vnwI; path=/; secure; httponly; partitioned');
});
});
});

0 comments on commit b81f041

Please sign in to comment.