@react-foundry/fastify-consent-cookies 0.1.9 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/common.js +1 -2
- package/dist/index.js +11 -17
- package/package.json +7 -9
- package/dist/common.mjs +0 -1
- package/dist/index.mjs +0 -122
package/dist/common.js
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1
|
+
export {};
|
package/dist/index.js
CHANGED
|
@@ -1,12 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.fastifyConsentCookies = exports.defaultSecret = void 0;
|
|
7
|
-
const cookie_1 = __importDefault(require("cookie"));
|
|
8
|
-
const cryptr_1 = __importDefault(require("cryptr"));
|
|
9
|
-
const fastify_plugin_1 = __importDefault(require("fastify-plugin"));
|
|
1
|
+
import cookie from 'cookie';
|
|
2
|
+
import Cryptr from 'cryptr';
|
|
3
|
+
import fp from 'fastify-plugin';
|
|
10
4
|
const consentCookie = {
|
|
11
5
|
name: 'consent',
|
|
12
6
|
description: 'A store of the cookies that you have consented to.',
|
|
@@ -26,12 +20,12 @@ const decodeClear = (v, req) => {
|
|
|
26
20
|
}
|
|
27
21
|
};
|
|
28
22
|
const joinSet = (v, s) => Array.from(v).join(s);
|
|
29
|
-
|
|
30
|
-
const fastifyConsentCookiesPlugin = async (fastify, { cookies: _cookies, secret =
|
|
31
|
-
if (secret ===
|
|
23
|
+
export const defaultSecret = 'changeme';
|
|
24
|
+
const fastifyConsentCookiesPlugin = async (fastify, { cookies: _cookies, secret = defaultSecret, ...defaults }) => {
|
|
25
|
+
if (secret === defaultSecret) {
|
|
32
26
|
fastify.log.warn('Cookie secret has not been set; cookies could be decrypted!');
|
|
33
27
|
}
|
|
34
|
-
const cryptr = new
|
|
28
|
+
const cryptr = new Cryptr(secret, { encoding: 'base64' });
|
|
35
29
|
const encodeSecure = (v) => cryptr.encrypt(encodeClear(v));
|
|
36
30
|
const decodeSecure = (v, req) => {
|
|
37
31
|
try {
|
|
@@ -52,7 +46,7 @@ const fastifyConsentCookiesPlugin = async (fastify, { cookies: _cookies, secret
|
|
|
52
46
|
fastify.decorateReply('setCookie');
|
|
53
47
|
fastify.decorateReply('setCookieConsent');
|
|
54
48
|
fastify.addHook('preHandler', async (req, reply) => {
|
|
55
|
-
const cookieData =
|
|
49
|
+
const cookieData = cookie.parse(req.headers.cookie || '');
|
|
56
50
|
const _consent = cookieData[consentCookie.name];
|
|
57
51
|
const consent = _consent || '';
|
|
58
52
|
const active = (cookies
|
|
@@ -99,7 +93,7 @@ const fastifyConsentCookiesPlugin = async (fastify, { cookies: _cookies, secret
|
|
|
99
93
|
const overrun = size - maxSize;
|
|
100
94
|
req.log.warn(`Attempting to set cookie, '${name}', which is ${overrun} bytes larger than allowed (4kiB) and likely to be rejected`);
|
|
101
95
|
}
|
|
102
|
-
this.header('Set-Cookie',
|
|
96
|
+
this.header('Set-Cookie', cookie.serialize(name, content, {
|
|
103
97
|
path: '/',
|
|
104
98
|
domain: undefined,
|
|
105
99
|
sameSite: 'strict',
|
|
@@ -121,8 +115,8 @@ const fastifyConsentCookiesPlugin = async (fastify, { cookies: _cookies, secret
|
|
|
121
115
|
reply.setCookieConsent = setCookieConsent;
|
|
122
116
|
});
|
|
123
117
|
};
|
|
124
|
-
|
|
118
|
+
export const fastifyConsentCookies = fp(fastifyConsentCookiesPlugin, {
|
|
125
119
|
fastify: '5.x',
|
|
126
120
|
name: 'consent-cookies',
|
|
127
121
|
});
|
|
128
|
-
|
|
122
|
+
export default fastifyConsentCookies;
|
package/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@react-foundry/fastify-consent-cookies",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Fastify plugin to parse and set cookies only with user consent.",
|
|
5
|
+
"type": "module",
|
|
5
6
|
"main": "dist/index.js",
|
|
6
7
|
"exports": {
|
|
7
8
|
".": {
|
|
8
9
|
"types": "./dist/index.d.ts",
|
|
9
|
-
"import": "./dist/index.
|
|
10
|
-
"
|
|
11
|
-
"default": "./dist/index.mjs"
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"default": "./dist/index.js"
|
|
12
12
|
}
|
|
13
13
|
},
|
|
14
14
|
"files": [
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"jest-environment-jsdom": "30.3.0",
|
|
26
26
|
"ts-jest": "29.4.9",
|
|
27
27
|
"typescript": "5.9.3",
|
|
28
|
-
"@react-foundry/types-helpers": "0.1
|
|
28
|
+
"@react-foundry/types-helpers": "0.2.1"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"cookie": "^1.1.1",
|
|
@@ -34,11 +34,9 @@
|
|
|
34
34
|
},
|
|
35
35
|
"scripts": {
|
|
36
36
|
"test": "NODE_OPTIONS=--experimental-vm-modules jest",
|
|
37
|
-
"build": "
|
|
38
|
-
"build:esm": "tsc -m es2022 && find dist -name '*.js' -exec sh -c 'mv \"$0\" \"${0%.js}.mjs\"' {} \\;",
|
|
39
|
-
"build:cjs": "tsc",
|
|
37
|
+
"build": "tsc",
|
|
40
38
|
"clean": "rm -rf dist tsconfig.tsbuildinfo"
|
|
41
39
|
},
|
|
42
|
-
"module": "dist/index.
|
|
40
|
+
"module": "dist/index.js",
|
|
43
41
|
"typings": "dist/index.d.ts"
|
|
44
42
|
}
|
package/dist/common.mjs
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/index.mjs
DELETED
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
import cookie from 'cookie';
|
|
2
|
-
import Cryptr from 'cryptr';
|
|
3
|
-
import fp from 'fastify-plugin';
|
|
4
|
-
const consentCookie = {
|
|
5
|
-
name: 'consent',
|
|
6
|
-
description: 'A store of the cookies that you have consented to.',
|
|
7
|
-
httpOnly: false,
|
|
8
|
-
sameSite: 'lax'
|
|
9
|
-
};
|
|
10
|
-
const id = (v) => v;
|
|
11
|
-
const encodeClear = (v) => JSON.stringify(v);
|
|
12
|
-
const decodeClear = (v, req) => {
|
|
13
|
-
try {
|
|
14
|
-
return JSON.parse(v);
|
|
15
|
-
}
|
|
16
|
-
catch (e) {
|
|
17
|
-
const log = (req?.log || console);
|
|
18
|
-
log.warn('Unable to parse cookie data as JSON');
|
|
19
|
-
return undefined;
|
|
20
|
-
}
|
|
21
|
-
};
|
|
22
|
-
const joinSet = (v, s) => Array.from(v).join(s);
|
|
23
|
-
export const defaultSecret = 'changeme';
|
|
24
|
-
const fastifyConsentCookiesPlugin = async (fastify, { cookies: _cookies, secret = defaultSecret, ...defaults }) => {
|
|
25
|
-
if (secret === defaultSecret) {
|
|
26
|
-
fastify.log.warn('Cookie secret has not been set; cookies could be decrypted!');
|
|
27
|
-
}
|
|
28
|
-
const cryptr = new Cryptr(secret, { encoding: 'base64' });
|
|
29
|
-
const encodeSecure = (v) => cryptr.encrypt(encodeClear(v));
|
|
30
|
-
const decodeSecure = (v, req) => {
|
|
31
|
-
try {
|
|
32
|
-
return decodeClear(cryptr.decrypt(v), req);
|
|
33
|
-
}
|
|
34
|
-
catch (e) {
|
|
35
|
-
const log = (req || fastify).log;
|
|
36
|
-
log.warn('Unable to decrypt cookie data');
|
|
37
|
-
return undefined;
|
|
38
|
-
}
|
|
39
|
-
};
|
|
40
|
-
const cookies = ([
|
|
41
|
-
consentCookie,
|
|
42
|
-
..._cookies
|
|
43
|
-
]).filter(id);
|
|
44
|
-
fastify.decorateRequest('cookiesMeta');
|
|
45
|
-
fastify.decorateRequest('cookies');
|
|
46
|
-
fastify.decorateReply('setCookie');
|
|
47
|
-
fastify.decorateReply('setCookieConsent');
|
|
48
|
-
fastify.addHook('preHandler', async (req, reply) => {
|
|
49
|
-
const cookieData = cookie.parse(req.headers.cookie || '');
|
|
50
|
-
const _consent = cookieData[consentCookie.name];
|
|
51
|
-
const consent = _consent || '';
|
|
52
|
-
const active = (cookies
|
|
53
|
-
.filter(e => e.group === undefined || consent.includes(e.name))
|
|
54
|
-
.reduce((acc, { name, ...cur }) => ({
|
|
55
|
-
...acc,
|
|
56
|
-
[name]: cur
|
|
57
|
-
}), {}));
|
|
58
|
-
const decryptedCookies = (Object.keys(cookieData)
|
|
59
|
-
.filter(e => active[e])
|
|
60
|
-
.reduce((acc, cur) => ({
|
|
61
|
-
...acc,
|
|
62
|
-
[cur]: (active[cur].httpOnly === false
|
|
63
|
-
? decodeClear(cookieData[cur])
|
|
64
|
-
: decodeSecure(cookieData[cur]))
|
|
65
|
-
}), {}));
|
|
66
|
-
const foundCookieNames = new Set(Object.keys(cookieData));
|
|
67
|
-
const activeNames = new Set(Object.keys(active));
|
|
68
|
-
const cookieNames = new Set(Object.keys(decryptedCookies));
|
|
69
|
-
const rejected = foundCookieNames.difference(cookieNames);
|
|
70
|
-
req.log.debug(`${foundCookieNames.size} cookies found on request; ${joinSet(foundCookieNames, ', ')}`);
|
|
71
|
-
req.log.debug(`${activeNames.size} cookies are active; ${joinSet(activeNames, ', ')}`);
|
|
72
|
-
req.log.debug(`${cookieNames.size} cookies are available; ${joinSet(cookieNames, ', ')}`);
|
|
73
|
-
if (rejected.size) {
|
|
74
|
-
req.log.warn(`${rejected.size} cookies were rejected; ${joinSet(rejected, ', ')}`);
|
|
75
|
-
}
|
|
76
|
-
req.cookiesMeta = cookies.map(({ name, description, group }) => ({
|
|
77
|
-
name,
|
|
78
|
-
description,
|
|
79
|
-
group,
|
|
80
|
-
consent: consent.includes(name)
|
|
81
|
-
}));
|
|
82
|
-
req.cookies = decryptedCookies;
|
|
83
|
-
const setCookie = function (name, value, options) {
|
|
84
|
-
if (active[name]) {
|
|
85
|
-
const { description, group, httpOnly: _httpOnly, ...declaration } = active[name];
|
|
86
|
-
const httpOnly = _httpOnly !== false;
|
|
87
|
-
const content = (!httpOnly
|
|
88
|
-
? encodeClear(value)
|
|
89
|
-
: encodeSecure(value));
|
|
90
|
-
const size = content.length;
|
|
91
|
-
const maxSize = 4096;
|
|
92
|
-
if (size > maxSize) {
|
|
93
|
-
const overrun = size - maxSize;
|
|
94
|
-
req.log.warn(`Attempting to set cookie, '${name}', which is ${overrun} bytes larger than allowed (4kiB) and likely to be rejected`);
|
|
95
|
-
}
|
|
96
|
-
this.header('Set-Cookie', cookie.serialize(name, content, {
|
|
97
|
-
path: '/',
|
|
98
|
-
domain: undefined,
|
|
99
|
-
sameSite: 'strict',
|
|
100
|
-
secure: true,
|
|
101
|
-
...defaults,
|
|
102
|
-
...declaration,
|
|
103
|
-
...options,
|
|
104
|
-
httpOnly
|
|
105
|
-
}));
|
|
106
|
-
}
|
|
107
|
-
else {
|
|
108
|
-
throw new Error(`No consent for cookie, "${name}".`);
|
|
109
|
-
}
|
|
110
|
-
};
|
|
111
|
-
const setCookieConsent = function (value) {
|
|
112
|
-
this.setCookie(consentCookie.name, value);
|
|
113
|
-
};
|
|
114
|
-
reply.setCookie = setCookie;
|
|
115
|
-
reply.setCookieConsent = setCookieConsent;
|
|
116
|
-
});
|
|
117
|
-
};
|
|
118
|
-
export const fastifyConsentCookies = fp(fastifyConsentCookiesPlugin, {
|
|
119
|
-
fastify: '5.x',
|
|
120
|
-
name: 'consent-cookies',
|
|
121
|
-
});
|
|
122
|
-
export default fastifyConsentCookies;
|