Skip to content

Commit

Permalink
feat: add autoChips to adaptation CHIPS mode (#47)
Browse files Browse the repository at this point in the history
  • Loading branch information
fengmk2 committed Jan 3, 2024
1 parent 8889d2c commit 38d6408
Show file tree
Hide file tree
Showing 4 changed files with 353 additions and 2 deletions.
8 changes: 8 additions & 0 deletions README.zh-CN.md
Expand Up @@ -28,6 +28,14 @@ ctx.cookies.set('key', 'value', options);

每次设置或读取 signed cookie 或者 encrypt cookie 的时候,会用 keys 进行加密。每次加密都通过 keys 数组的第一个 key 进行加密,解密会从先到后逐个 key 尝试解密。读取 signed cookie 时,如果发现不是用第一个 key 进行加密时,会更新签名为第一个 key 加密的值。读取 encrypt cookie 时不会进行更新操作。

### `defaultCookieOptions`

全局默认配置:

- autoChips - `Boolean` 是否开启 [CHIPS](https://developers.google.com/privacy-sandbox/3pcd/chips#security_design) 的自动适配方案,
会自动给 Cookie 新增一个 `__Host` 为前缀的分区 Cookie,优先读取非分区 Cookie,读取失败则尝试读取 `__Host` 前缀的同名 Cookie 适配三方 Cookie 禁止逻辑。
一旦配置 `autoChips=true`,那么会强制忽略 `partitioned``removeUnpartitioned` 参数。

## 设置 cookie

通过 `cookies.set(key, value, options)` 的方式来设置一个 cookie。其中 options 支持的参数有:
Expand Down
9 changes: 8 additions & 1 deletion index.d.ts
Expand Up @@ -5,6 +5,13 @@
* `import {CookieGetOptions, CookieSetOptions} from 'egg-cookies'`.
*/
declare namespace EggCookies {
interface DefaultCookieOptions {
/**
* Auto get and set `__Host` prefix cookie to adaptation CHIPS mode (The default value is false).
*/
autoChips?: boolean;
}

interface CookieGetOptions {
/**
* Whether to sign or not (The default value is true).
Expand Down Expand Up @@ -72,7 +79,7 @@ declare namespace EggCookies {

declare class EggCookies {

constructor(ctx?: any, keys?: any);
constructor(ctx?: any, keys?: any, opts?: EggCookies.DefaultCookieOptions);

/**
* Get the Egg's cookies by name with optional options.
Expand Down
40 changes: 39 additions & 1 deletion lib/cookies.js
Expand Up @@ -23,6 +23,7 @@ class Cookies {
this._keys = keys;
// default cookie options
this._defaultCookieOptions = defaultCookieOptions;
this._autoChips = defaultCookieOptions && defaultCookieOptions.autoChips;
this.ctx = ctx;
this.secure = this.ctx.secure;
this.app = ctx.app;
Expand Down Expand Up @@ -54,6 +55,15 @@ class Cookies {
*/
get(name, opts) {
opts = opts || {};
let value = this._get(name, opts);
if (value === undefined && this._autoChips) {
// try to read __Host-${name} prefix cookie
value = this._get(this._formatChipsCookieName(name), opts);
}
return value;
}

_get(name, opts) {
const signed = computeSigned(opts);

const header = this.ctx.get('cookie');
Expand Down Expand Up @@ -93,6 +103,10 @@ class Cookies {

set(name, value, opts) {
opts = Object.assign({}, this._defaultCookieOptions, opts);
if (this._autoChips) {
opts.partitioned = false;
opts.removeUnpartitioned = false;
}
const signed = computeSigned(opts);
value = value || '';
if (!this.secure && opts.secure) {
Expand All @@ -116,6 +130,7 @@ class Cookies {
// fixed SameSite=None: Known Incompatible Clients
const userAgent = this.ctx.get('user-agent');
let isSameSiteNone = false;
let autoChips = this._autoChips;
if (opts.sameSite && typeof opts.sameSite === 'string' && opts.sameSite.toLowerCase() === 'none') {
isSameSiteNone = true;
if (opts.secure === false || !this.secure || (userAgent && !this.isSameSiteNoneCompatible(userAgent))) {
Expand All @@ -124,10 +139,11 @@ class Cookies {
isSameSiteNone = false;
}
}
if (opts.partitioned) {
if (autoChips || opts.partitioned) {
// allow to set partitioned: secure=true and sameSite=none and chrome >= 118
if (!isSameSiteNone || opts.secure === false || !this.secure || (userAgent && !this.isPartitionedCompatible(userAgent))) {
// Non-secure context or Incompatible clients, don't send partitioned property
autoChips = false;
opts.partitioned = false;
}
}
Expand All @@ -153,6 +169,24 @@ class Cookies {
headers = ignoreCookiesByName(headers, removeUnpartitionedCookie.name);
headers = pushCookie(headers, removeUnpartitionedCookie);
}
} else if (autoChips) {
// add __Host-${name} prefix cookie
const newCookieName = this._formatChipsCookieName(name);
const newCookieOpts = Object.assign({}, opts, {
partitioned: true,
});
const newPartitionedCookie = new Cookie(newCookieName, value, newCookieOpts);
// if user not set secure, reset secure to ctx.secure
if (opts.secure === undefined) newPartitionedCookie.attrs.secure = this.secure;

headers = pushCookie(headers, newPartitionedCookie);
// signed
if (signed) {
newPartitionedCookie.value = value && this.keys.sign(newPartitionedCookie.toString());
newPartitionedCookie.name += '.sig';
headers = ignoreCookiesByName(headers, newPartitionedCookie.name);
headers = pushCookie(headers, newPartitionedCookie);
}
}

const cookie = new Cookie(name, value, opts);
Expand All @@ -171,6 +205,10 @@ class Cookies {
return this;
}

_formatChipsCookieName(name) {
return `__Host-${name}`;
}

_parseChromiumAndMajorVersion(userAgent) {
if (!this[PARSED_UA]) {
this[PARSED_UA] = parseChromiumAndMajorVersion(userAgent);
Expand Down

0 comments on commit 38d6408

Please sign in to comment.