@fastify/cookie 9.3.0 → 9.4.0
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/README.md +22 -0
- package/cookie.js +20 -0
- package/package.json +3 -3
- package/plugin.js +1 -8
- package/test/cookie-module.test.js +18 -0
- package/test/cookie.test.js +1 -1
- package/types/plugin.d.ts +16 -8
- package/types/plugin.test-d.ts +24 -12
package/README.md
CHANGED
|
@@ -96,8 +96,16 @@ fastify.get('/', (req, reply) => {
|
|
|
96
96
|
- An `Array` can be passed if key rotation is desired. Read more about it in [Rotating signing secret](#rotating-secret).
|
|
97
97
|
- More sophisticated cookie signing mechanisms can be implemented by supplying an `Object`. Read more about it in [Custom cookie signer](#custom-cookie-signer).
|
|
98
98
|
|
|
99
|
+
- `hook`: the [Fastify Hook](https://fastify.dev/docs/latest/Reference/Lifecycle/#lifecycle) to register the parsing of cookie into. Default: `onRequest`.
|
|
100
|
+
|
|
101
|
+
- `algorithm`: the [algorithm](https://nodejs.org/api/crypto.html#cryptogethashes) to use to sign the cookies. Default: `sha256`.
|
|
102
|
+
|
|
99
103
|
- `parseOptions`: An `Object` to modify the serialization of set cookies.
|
|
100
104
|
|
|
105
|
+
### :warning: Security Considerations :warning:
|
|
106
|
+
|
|
107
|
+
It is recommended to use `sha256` or stronger hashing algorithm as well as a `secret` that is at least 20 bytes long.
|
|
108
|
+
|
|
101
109
|
#### parseOptions
|
|
102
110
|
|
|
103
111
|
##### domain
|
|
@@ -151,6 +159,20 @@ attribute. When truthy, the `Partitioned` attribute is set, otherwise it is not.
|
|
|
151
159
|
|
|
152
160
|
More information about can be found in [the proposal](https://github.com/privacycg/CHIPS).
|
|
153
161
|
|
|
162
|
+
|
|
163
|
+
##### priority
|
|
164
|
+
|
|
165
|
+
Specifies the `string` to be the value for the [`Priority` `Set-Cookie` attribute](https://tools.ietf.org/html/draft-west-cookie-priority-00#section-4.1).
|
|
166
|
+
|
|
167
|
+
- `'low'` will set the `Priority` attribute to `Low`.
|
|
168
|
+
- `'medium'` will set the `Priority` attribute to `Medium`, the default priority when not set.
|
|
169
|
+
- `'high'` will set the `Priority` attribute to `High`.
|
|
170
|
+
|
|
171
|
+
More information about the different priority levels can be found in
|
|
172
|
+
[the specification](https://tools.ietf.org/html/draft-west-cookie-priority-00#section-4.1).
|
|
173
|
+
|
|
174
|
+
⚠️ **Warning:** This is an attribute that has not yet been fully standardized, and may change in the future without reflecting the semver versioning. This also means many clients may ignore the attribute until they understand it.
|
|
175
|
+
|
|
154
176
|
##### path
|
|
155
177
|
|
|
156
178
|
Specifies the value for the [`Path` `Set-Cookie` attribute](https://datatracker.ietf.org/doc/html/rfc6265#section-5.2.4). By default, the path
|
package/cookie.js
CHANGED
|
@@ -152,6 +152,26 @@ function serialize (name, val, opt) {
|
|
|
152
152
|
str += '; Path=' + opt.path
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
+
if (opt.priority) {
|
|
156
|
+
const priority = typeof opt.priority === 'string'
|
|
157
|
+
? opt.priority.toLowerCase()
|
|
158
|
+
: opt.priority
|
|
159
|
+
|
|
160
|
+
switch (priority) {
|
|
161
|
+
case 'low':
|
|
162
|
+
str += '; Priority=Low'
|
|
163
|
+
break
|
|
164
|
+
case 'medium':
|
|
165
|
+
str += '; Priority=Medium'
|
|
166
|
+
break
|
|
167
|
+
case 'high':
|
|
168
|
+
str += '; Priority=High'
|
|
169
|
+
break
|
|
170
|
+
default:
|
|
171
|
+
throw new TypeError('option priority is invalid')
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
155
175
|
if (opt.expires) {
|
|
156
176
|
if (typeof opt.expires.toUTCString !== 'function') {
|
|
157
177
|
throw new TypeError('option expires is invalid')
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fastify/cookie",
|
|
3
|
-
"version": "9.
|
|
3
|
+
"version": "9.4.0",
|
|
4
4
|
"description": "Plugin for fastify to add support for cookies",
|
|
5
5
|
"main": "plugin.js",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -44,11 +44,11 @@
|
|
|
44
44
|
"@types/node": "^20.1.0",
|
|
45
45
|
"benchmark": "^2.1.4",
|
|
46
46
|
"fastify": "^4.0.0",
|
|
47
|
-
"sinon": "^
|
|
47
|
+
"sinon": "^18.0.0",
|
|
48
48
|
"snazzy": "^9.0.0",
|
|
49
49
|
"standard": "^17.0.0",
|
|
50
50
|
"tap": "^16.0.0",
|
|
51
|
-
"tsd": "^0.
|
|
51
|
+
"tsd": "^0.31.0"
|
|
52
52
|
},
|
|
53
53
|
"dependencies": {
|
|
54
54
|
"fastify-plugin": "^4.0.0",
|
package/plugin.js
CHANGED
|
@@ -22,7 +22,7 @@ function fastifyCookieSetCookie (reply, name, value, options) {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
if (opts.secure === 'auto') {
|
|
25
|
-
if (
|
|
25
|
+
if (reply.request.protocol === 'https') {
|
|
26
26
|
opts.secure = true
|
|
27
27
|
} else {
|
|
28
28
|
opts.sameSite = 'lax'
|
|
@@ -187,13 +187,6 @@ function getHook (hook = 'onRequest') {
|
|
|
187
187
|
return hooks[hook]
|
|
188
188
|
}
|
|
189
189
|
|
|
190
|
-
function isConnectionSecure (request) {
|
|
191
|
-
return (
|
|
192
|
-
request.raw.socket?.encrypted === true ||
|
|
193
|
-
request.headers['x-forwarded-proto'] === 'https'
|
|
194
|
-
)
|
|
195
|
-
}
|
|
196
|
-
|
|
197
190
|
const fastifyCookie = fp(plugin, {
|
|
198
191
|
fastify: '4.x',
|
|
199
192
|
name: '@fastify/cookie'
|
|
@@ -203,3 +203,21 @@ test('unencoded', (t) => {
|
|
|
203
203
|
}), /argument val is invalid/)
|
|
204
204
|
t.end()
|
|
205
205
|
})
|
|
206
|
+
|
|
207
|
+
test('serializer: priority', (t) => {
|
|
208
|
+
t.plan(8)
|
|
209
|
+
t.same(cookie.serialize('foo', 'bar', { priority: 'Low' }), 'foo=bar; Priority=Low')
|
|
210
|
+
t.same(cookie.serialize('foo', 'bar', { priority: 'low' }), 'foo=bar; Priority=Low')
|
|
211
|
+
t.same(cookie.serialize('foo', 'bar', { priority: 'Medium' }), 'foo=bar; Priority=Medium')
|
|
212
|
+
t.same(cookie.serialize('foo', 'bar', { priority: 'medium' }), 'foo=bar; Priority=Medium')
|
|
213
|
+
t.same(cookie.serialize('foo', 'bar', { priority: 'High' }), 'foo=bar; Priority=High')
|
|
214
|
+
t.same(cookie.serialize('foo', 'bar', { priority: 'high' }), 'foo=bar; Priority=High')
|
|
215
|
+
|
|
216
|
+
t.throws(cookie.serialize.bind(cookie, 'foo', 'bar', {
|
|
217
|
+
priority: 'foo'
|
|
218
|
+
}), /option priority is invalid/)
|
|
219
|
+
t.throws(cookie.serialize.bind(cookie, 'foo', 'bar', {
|
|
220
|
+
priority: true
|
|
221
|
+
}), /option priority is invalid/)
|
|
222
|
+
t.end()
|
|
223
|
+
})
|
package/test/cookie.test.js
CHANGED
|
@@ -854,7 +854,7 @@ test('create signed cookie manually using signCookie decorator', async (t) => {
|
|
|
854
854
|
})
|
|
855
855
|
|
|
856
856
|
test('handle secure:auto of cookieOptions', async (t) => {
|
|
857
|
-
const fastify = Fastify()
|
|
857
|
+
const fastify = Fastify({ trustProxy: true })
|
|
858
858
|
|
|
859
859
|
await fastify.register(plugin)
|
|
860
860
|
|
package/types/plugin.d.ts
CHANGED
|
@@ -115,22 +115,26 @@ declare namespace fastifyCookie {
|
|
|
115
115
|
domain?: string;
|
|
116
116
|
/** Specifies a function that will be used to encode a cookie's value. Since value of a cookie has a limited character set (and must be a simple string), this function can be used to encode a value into a string suited for a cookie's value. */
|
|
117
117
|
encode?(val: string): string;
|
|
118
|
-
/**
|
|
118
|
+
/** The expiration `date` used for the `Expires` attribute. */
|
|
119
119
|
expires?: Date;
|
|
120
|
-
/**
|
|
120
|
+
/** Add the `HttpOnly` attribute. Defaults to `false`. */
|
|
121
121
|
httpOnly?: boolean;
|
|
122
|
-
/**
|
|
122
|
+
/** A `number` in seconds that specifies the `Max-Age` attribute. */
|
|
123
123
|
maxAge?: number;
|
|
124
|
+
/** A `boolean` indicating whether the cookie is tied to the top-level site where it's initially set and cannot be accessed from elsewhere. */
|
|
124
125
|
partitioned?: boolean;
|
|
125
|
-
/**
|
|
126
|
+
/** The `Path` attribute. */
|
|
126
127
|
path?: string;
|
|
127
128
|
/** A `boolean` or one of the `SameSite` string attributes. E.g.: `lax`, `none` or `strict`. */
|
|
128
129
|
sameSite?: 'lax' | 'none' | 'strict' | boolean;
|
|
129
|
-
/**
|
|
130
|
+
/** One of the `Priority` string attributes (`low`, `medium` or `high`) specifying a retention priority for HTTP cookies that will be respected by user agents during cookie eviction. */
|
|
131
|
+
priority?: 'low' | 'medium' | 'high';
|
|
132
|
+
/** Add the `Secure` attribute. Defaults to `false`. */
|
|
130
133
|
secure?: boolean;
|
|
131
134
|
}
|
|
132
135
|
|
|
133
136
|
export interface CookieSerializeOptions extends Omit<SerializeOptions, 'secure'> {
|
|
137
|
+
/** Add the `Secure` attribute. Value can be set to `"auto"`; in this case the `Secure` attribute will only be added for HTTPS requests. Defaults to `false`. */
|
|
134
138
|
secure?: boolean | 'auto';
|
|
135
139
|
signed?: boolean;
|
|
136
140
|
}
|
|
@@ -151,10 +155,14 @@ declare namespace fastifyCookie {
|
|
|
151
155
|
export type Unsign = (input: string, secret: string | Buffer, algorithm?: string) => UnsignResult;
|
|
152
156
|
export type SignerFactory = (secrets: string | string[] | Buffer | Buffer[], algorithm?: string) => SignerBase;
|
|
153
157
|
|
|
154
|
-
export
|
|
155
|
-
valid:
|
|
158
|
+
export type UnsignResult = {
|
|
159
|
+
valid: true;
|
|
156
160
|
renew: boolean;
|
|
157
|
-
value: string
|
|
161
|
+
value: string;
|
|
162
|
+
} | {
|
|
163
|
+
valid: false;
|
|
164
|
+
renew: false;
|
|
165
|
+
value: null;
|
|
158
166
|
}
|
|
159
167
|
|
|
160
168
|
export const signerFactory: SignerFactory;
|
package/types/plugin.test-d.ts
CHANGED
|
@@ -150,11 +150,15 @@ appWithRotationSecret.register(cookie, {
|
|
|
150
150
|
appWithRotationSecret.after(() => {
|
|
151
151
|
server.get('/', (request, reply) => {
|
|
152
152
|
reply.unsignCookie(request.cookies.test!);
|
|
153
|
-
const
|
|
153
|
+
const unsigned = reply.unsignCookie('test');
|
|
154
154
|
|
|
155
|
-
expectType<boolean>(valid);
|
|
156
|
-
|
|
157
|
-
|
|
155
|
+
expectType<boolean>(unsigned.valid);
|
|
156
|
+
if (unsigned.valid) {
|
|
157
|
+
expectType<string>(unsigned.value);
|
|
158
|
+
} else {
|
|
159
|
+
expectType<null>(unsigned.value);
|
|
160
|
+
}
|
|
161
|
+
expectType<boolean>(unsigned.renew);
|
|
158
162
|
|
|
159
163
|
reply.send({ hello: 'world' });
|
|
160
164
|
});
|
|
@@ -182,11 +186,15 @@ appWithParseOptions.register(cookie, {
|
|
|
182
186
|
});
|
|
183
187
|
appWithParseOptions.after(() => {
|
|
184
188
|
server.get('/', (request, reply) => {
|
|
185
|
-
const
|
|
189
|
+
const unsigned = reply.unsignCookie(request.cookies.test!);
|
|
186
190
|
|
|
187
|
-
expectType<boolean>(valid);
|
|
188
|
-
|
|
189
|
-
|
|
191
|
+
expectType<boolean>(unsigned.valid);
|
|
192
|
+
if (unsigned.valid) {
|
|
193
|
+
expectType<string>(unsigned.value);
|
|
194
|
+
} else {
|
|
195
|
+
expectType<null>(unsigned.value);
|
|
196
|
+
}
|
|
197
|
+
expectType<boolean>(unsigned.renew);
|
|
190
198
|
});
|
|
191
199
|
});
|
|
192
200
|
|
|
@@ -204,11 +212,15 @@ appWithCustomSigner.register(cookie, {
|
|
|
204
212
|
appWithCustomSigner.after(() => {
|
|
205
213
|
server.get('/', (request, reply) => {
|
|
206
214
|
reply.unsignCookie(request.cookies.test!)
|
|
207
|
-
const
|
|
215
|
+
const unsigned = reply.unsignCookie('test')
|
|
208
216
|
|
|
209
|
-
expectType<boolean>(valid)
|
|
210
|
-
|
|
211
|
-
|
|
217
|
+
expectType<boolean>(unsigned.valid);
|
|
218
|
+
if (unsigned.valid) {
|
|
219
|
+
expectType<string>(unsigned.value);
|
|
220
|
+
} else {
|
|
221
|
+
expectType<null>(unsigned.value);
|
|
222
|
+
}
|
|
223
|
+
expectType<boolean>(unsigned.renew);
|
|
212
224
|
|
|
213
225
|
reply.send({ hello: 'world' })
|
|
214
226
|
})
|