@fastify/cookie 11.1.0 → 11.1.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/README.md CHANGED
@@ -14,11 +14,13 @@ upon this plugin's actions.
14
14
  It is also possible to [import the low-level cookie parsing and serialization functions](#importing-serialize-and-parse).
15
15
 
16
16
  ## Install
17
+
17
18
  ```sh
18
19
  npm i @fastify/cookie
19
20
  ```
20
21
 
21
22
  ### Compatibility
23
+
22
24
  | Plugin version | Fastify version |
23
25
  | ---------------|-----------------|
24
26
  | `>=10.x` | `^5.x` |
@@ -162,14 +164,13 @@ attribute. When truthy, the `Partitioned` attribute is set, otherwise it is not.
162
164
 
163
165
  More information about this can be found in [the proposal](https://github.com/privacycg/CHIPS).
164
166
 
165
-
166
167
  ##### priority
167
168
 
168
169
  Specifies the `string` to be the value for the [`Priority` `Set-Cookie` attribute](https://datatracker.ietf.org/doc/html/draft-west-cookie-priority-00#section-4.1).
169
170
 
170
- - `'low'` will set the `Priority` attribute to `Low`.
171
- - `'medium'` will set the `Priority` attribute to `Medium`, the default priority when not set.
172
- - `'high'` will set the `Priority` attribute to `High`.
171
+ - `'low'` will set the `Priority` attribute to `Low`.
172
+ - `'medium'` will set the `Priority` attribute to `Medium`, the default priority when not set.
173
+ - `'high'` will set the `Priority` attribute to `High`.
173
174
 
174
175
  More information about the different priority levels can be found in
175
176
  [the specification](https://datatracker.ietf.org/doc/html/draft-west-cookie-priority-00#section-4.1).
@@ -185,11 +186,11 @@ is considered the ["default path"](https://datatracker.ietf.org/doc/html/rfc6265
185
186
 
186
187
  Specifies the `boolean` or `string` to be the value for the [`SameSite` `Set-Cookie` attribute](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis-09#section-5.4.7).
187
188
 
188
- - `true` will set the `SameSite` attribute to `Strict` for strict same site enforcement.
189
- - `false` will not set the `SameSite` attribute.
190
- - `'lax'` will set the `SameSite` attribute to `Lax` for lax same site enforcement.
191
- - `'none'` will set the `SameSite` attribute to `None` for an explicit cross-site cookie.
192
- - `'strict'` will set the `SameSite` attribute to `Strict` for strict same site enforcement.
189
+ - `true` will set the `SameSite` attribute to `Strict` for strict same site enforcement.
190
+ - `false` will not set the `SameSite` attribute.
191
+ - `'lax'` will set the `SameSite` attribute to `Lax` for lax same site enforcement.
192
+ - `'none'` will set the `SameSite` attribute to `None` for an explicit cross-site cookie.
193
+ - `'strict'` will set the `SameSite` attribute to `Strict` for strict same site enforcement.
193
194
 
194
195
  More information about the different enforcement levels can be found in
195
196
  [the specification](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis-09#section-5.4.7).
@@ -214,7 +215,7 @@ as an object named `cookies`. Thus, if a request contains the header
214
215
  `Cookie: foo=foo` then, within your handler, `req.cookies.foo` would equal
215
216
  `'foo'`.
216
217
 
217
- You can pass options to the [cookie parse](https://github.com/jshttp/cookie#cookieparsestr-options) by setting an object named `parseOptions` in the plugin config object.
218
+ You can pass options to the [cookie parse](https://github.com/jshttp/cookie#cookieparsecookiestr-options) by setting an object named `parseOptions` in the plugin config object.
218
219
 
219
220
  ### Sending
220
221
 
@@ -223,11 +224,11 @@ via the Fastify `decorateReply` API. Thus, in a request handler,
223
224
  `reply.setCookie('foo', 'foo', {path: '/'})` will set a cookie named `foo`
224
225
  with a value of `'foo'` on the cookie path `/`.
225
226
 
226
- + `name`: a string name for the cookie to be set
227
- + `value`: a string value for the cookie
228
- + `options`: an options object as described in the [cookie serialize][cs] documentation
229
- + `options.signed`: the cookie should be signed
230
- + `options.secure`: if set to `true` it will set the Secure-flag. If it is set to `"auto"` Secure-flag is set when the connection is using tls.
227
+ - `name`: a string name for the cookie to be set
228
+ - `value`: a string value for the cookie
229
+ - `options`: an options object as described in the [cookie serialize][cs] documentation
230
+ - `options.signed`: the cookie should be signed
231
+ - `options.secure`: if set to `true` it will set the Secure-flag. If it is set to `"auto"` Secure-flag is set when the connection is using tls.
231
232
 
232
233
  #### Securing the cookie
233
234
 
@@ -245,8 +246,8 @@ via the Fastify `decorateReply` API. Thus, in a request handler,
245
246
  `reply.clearCookie('foo', {path: '/'})` will clear a cookie named `foo`
246
247
  on the cookie path `/`.
247
248
 
248
- + `name`: a string name for the cookie to be cleared
249
- + `options`: an options object as described in the [cookie serialize][cs]
249
+ - `name`: a string name for the cookie to be cleared
250
+ - `options`: an options object as described in the [cookie serialize][cs]
250
251
  documentation. It is optional to pass an `options` object
251
252
 
252
253
  ### Manual cookie parsing
@@ -255,14 +256,16 @@ The method `parseCookie(cookieHeader)` is added to the `fastify` instance
255
256
  via the Fastify `decorate` API. Thus, `fastify.parseCookie('sessionId=aYb4uTIhdBXC')`
256
257
  will parse the raw cookie header and return an object `{ "sessionId": "aYb4uTIhdBXC" }`.
257
258
 
258
- [cs]: https://www.npmjs.com/package/cookie#options-1
259
+ [cs]: https://github.com/jshttp/cookie#cookiestringifysetcookiesetcookieobj-options
259
260
 
260
261
  <a id="rotating-secret"></a>
262
+
261
263
  ### Rotating signing secret
262
264
 
263
265
  Key rotation is when an encryption key is retired and replaced by generating a new cryptographic key. To implement rotation, supply an `Array` of keys to `secret` option.
264
266
 
265
267
  **Example:**
268
+
266
269
  ```js
267
270
  fastify.register(require('@fastify/cookie'), {
268
271
  secret: [key1, key2]
@@ -272,11 +275,13 @@ fastify.register(require('@fastify/cookie'), {
272
275
  The plugin will **always** use the first key (`key1`) to sign cookies. When parsing incoming cookies, it will iterate over the supplied array to see if any of the available keys are able to decode the given signed cookie. This ensures that any old signed cookies are still valid.
273
276
 
274
277
  Note:
278
+
275
279
  - Key rotation is **only** achieved by redeploying the server again with the new `secret` array.
276
280
  - Iterating through all secrets is an expensive process, so the rotation list should contain as few keys as possible. Ideally, only the current key and the most recently retired key.
277
281
  - Although previously signed cookies are valid even after rotation, cookies should be updated with the new key as soon as possible. See the following example for how to accomplish this.
278
282
 
279
283
  **Example:**
284
+
280
285
  ```js
281
286
  fastify.get('/', (req, reply) => {
282
287
  const result = reply.unsignCookie(req.cookies.myCookie)
@@ -293,11 +298,13 @@ fastify.get('/', (req, reply) => {
293
298
  ```
294
299
 
295
300
  <a id="custom-cookie-signer"></a>
301
+
296
302
  ### Custom cookie signer
297
303
 
298
304
  The `secret` option optionally accepts an object with `sign` and `unsign` functions. This allows for implementing a custom cookie signing mechanism. See the following example:
299
305
 
300
306
  **Example:**
307
+
301
308
  ```js
302
309
  fastify.register(require('@fastify/cookie'), {
303
310
  secret: {
@@ -382,7 +389,6 @@ const signedValue = fastifyCookie.sign('test', 'secret');
382
389
  const unsignedvalue = fastifyCookie.unsign(signedValue, 'secret');
383
390
  ```
384
391
 
385
-
386
392
  ## License
387
393
 
388
394
  Licensed under [MIT](./LICENSE).
package/index.js CHANGED
@@ -1,10 +1,14 @@
1
1
  'use strict'
2
2
 
3
3
  const fp = require('fastify-plugin')
4
- const cookie = require('cookie')
4
+ const { parseCookie: parse, stringifySetCookie } = require('cookie')
5
5
 
6
6
  const { Signer, sign, unsign } = require('./signer')
7
7
 
8
+ function serialize (name, value, options) {
9
+ return stringifySetCookie(Object.assign({ name, value }, options))
10
+ }
11
+
8
12
  const kReplySetCookies = Symbol('fastify.reply.setCookies')
9
13
  const kReplySetCookiesHookRan = Symbol('fastify.reply.setCookiesHookRan')
10
14
 
@@ -29,7 +33,11 @@ function fastifyCookieSetCookie (reply, name, value, options) {
29
33
  }
30
34
  }
31
35
 
32
- reply[kReplySetCookies].set(`${name};${opts.domain};${opts.path || '/'}`, { name, value, opts })
36
+ reply[kReplySetCookies].set(`${name};${opts.domain};${opts.path || '/'}`, {
37
+ name,
38
+ value,
39
+ opts
40
+ })
33
41
 
34
42
  if (reply[kReplySetCookiesHookRan]) {
35
43
  setCookies(reply)
@@ -77,7 +85,7 @@ function setCookies (reply) {
77
85
  if (reply[kReplySetCookies].size === 1) {
78
86
  // Fast path for single cookie
79
87
  const c = reply[kReplySetCookies].values().next().value
80
- reply.header('Set-Cookie', cookie.serialize(c.name, c.value, c.opts))
88
+ reply.header('Set-Cookie', serialize(c.name, c.value, c.opts))
81
89
  reply[kReplySetCookies].clear()
82
90
  return
83
91
  }
@@ -90,7 +98,7 @@ function setCookies (reply) {
90
98
  }
91
99
 
92
100
  for (const c of reply[kReplySetCookies].values()) {
93
- cookieValue.push(cookie.serialize(c.name, c.value, c.opts))
101
+ cookieValue.push(serialize(c.name, c.value, c.opts))
94
102
  }
95
103
 
96
104
  reply.removeHeader('Set-Cookie')
@@ -117,12 +125,20 @@ function plugin (fastify, options, next) {
117
125
  const secret = options.secret
118
126
  const hook = getHook(options.hook)
119
127
  if (hook === undefined) {
120
- return next(new Error('@fastify/cookie: Invalid value provided for the hook-option. You can set the hook-option only to false, \'onRequest\' , \'preParsing\' , \'preValidation\' or \'preHandler\''))
121
- }
122
- const isSigner = !secret || (typeof secret.sign === 'function' && typeof secret.unsign === 'function')
123
- const signer = isSigner ? secret : new Signer(secret, options.algorithm || 'sha256')
124
-
125
- fastify.decorate('serializeCookie', cookie.serialize)
128
+ return next(
129
+ new Error(
130
+ "@fastify/cookie: Invalid value provided for the hook-option. You can set the hook-option only to false, 'onRequest' , 'preParsing' , 'preValidation' or 'preHandler'"
131
+ )
132
+ )
133
+ }
134
+ const isSigner =
135
+ !secret ||
136
+ (typeof secret.sign === 'function' && typeof secret.unsign === 'function')
137
+ const signer = isSigner
138
+ ? secret
139
+ : new Signer(secret, options.algorithm || 'sha256')
140
+
141
+ fastify.decorate('serializeCookie', serialize)
126
142
  fastify.decorate('parseCookie', parseCookie)
127
143
 
128
144
  if (secret !== undefined) {
@@ -146,14 +162,16 @@ function plugin (fastify, options, next) {
146
162
 
147
163
  if (hook) {
148
164
  fastify.addHook(hook, onReqHandlerWrapper(fastify, hook))
149
- fastify.addHook('onSend', fastifyCookieOnSendHandler)
150
165
  }
166
+ // The onSend writer is independent of the parsing hook: `hook: false` only
167
+ // disables cookie autoparsing, not the ability to set cookies on the reply.
168
+ fastify.addHook('onSend', fastifyCookieOnSendHandler)
151
169
 
152
170
  next()
153
171
 
154
172
  // ***************************
155
173
  function parseCookie (cookieHeader) {
156
- return cookie.parse(cookieHeader, options.parseOptions)
174
+ return parse(cookieHeader, options.parseOptions)
157
175
  }
158
176
 
159
177
  function signCookie (value) {
@@ -206,8 +224,8 @@ module.exports = fastifyCookie
206
224
  module.exports.default = fastifyCookie // supersedes fastifyCookie.default = fastifyCookie
207
225
  module.exports.fastifyCookie = fastifyCookie // supersedes fastifyCookie.fastifyCookie = fastifyCookie
208
226
 
209
- module.exports.serialize = cookie.serialize
210
- module.exports.parse = cookie.parse
227
+ module.exports.serialize = serialize
228
+ module.exports.parse = parse
211
229
 
212
230
  module.exports.signerFactory = Signer
213
231
  module.exports.Signer = Signer
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fastify/cookie",
3
- "version": "11.1.0",
3
+ "version": "11.1.1",
4
4
  "description": "Plugin for fastify to add support for cookies",
5
5
  "main": "index.js",
6
6
  "type": "commonjs",
@@ -68,7 +68,7 @@
68
68
  "tstyche": "^7.0.0"
69
69
  },
70
70
  "dependencies": {
71
- "cookie": "^1.0.0",
71
+ "cookie": "^2.0.0",
72
72
  "fastify-plugin": "^6.0.0"
73
73
  },
74
74
  "publishConfig": {
@@ -929,6 +929,25 @@ test('dont add auto cookie parsing to onRequest-hook if hook-option is set to fa
929
929
  t.assert.strictEqual(res.statusCode, 200)
930
930
  })
931
931
 
932
+ test('setCookie writes Set-Cookie even when hook-option is set to false', async (t) => {
933
+ t.plan(2)
934
+
935
+ const fastify = Fastify()
936
+ fastify.register(plugin, { hook: false })
937
+
938
+ fastify.get('/', (req, reply) => {
939
+ reply.setCookie('foo', 'bar', { path: '/' }).send({ hello: 'world' })
940
+ })
941
+
942
+ const res = await fastify.inject({
943
+ method: 'GET',
944
+ url: '/'
945
+ })
946
+
947
+ t.assert.strictEqual(res.statusCode, 200)
948
+ t.assert.ok(res.headers['set-cookie'], 'Set-Cookie should be written even with hook:false')
949
+ })
950
+
932
951
  test('result in an error if hook-option is set to an invalid value', async (t) => {
933
952
  t.plan(1)
934
953