Skip to content

Commit df7fe0d

Browse files
authored
fix: support cookie v2.x (#361)
* fix: support cookie v2.x v2 removed the / exports in favour of /, which broke every route that sets or reads a cookie (all requests returned 500). - adapt to the new cookie@2 API while keeping the historic / signatures for @fastify/cookie's own public API (fastify.parseCookie, fastify.serializeCookie, module.exports.parse/serialize) - bump the dependency to ^2.0.0 - fix README links that pointed to now-outdated v1 anchors * refactor: update index.js
1 parent 172ffc6 commit df7fe0d

3 files changed

Lines changed: 55 additions & 33 deletions

File tree

README.md

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,13 @@ upon this plugin's actions.
1414
It is also possible to [import the low-level cookie parsing and serialization functions](#importing-serialize-and-parse).
1515

1616
## Install
17+
1718
```sh
1819
npm i @fastify/cookie
1920
```
2021

2122
### Compatibility
23+
2224
| Plugin version | Fastify version |
2325
| ---------------|-----------------|
2426
| `>=10.x` | `^5.x` |
@@ -162,14 +164,13 @@ attribute. When truthy, the `Partitioned` attribute is set, otherwise it is not.
162164

163165
More information about this can be found in [the proposal](https://github.com/privacycg/CHIPS).
164166

165-
166167
##### priority
167168

168169
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).
169170

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`.
173174

174175
More information about the different priority levels can be found in
175176
[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
185186

186187
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).
187188

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.
193194

194195
More information about the different enforcement levels can be found in
195196
[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
214215
`Cookie: foo=foo` then, within your handler, `req.cookies.foo` would equal
215216
`'foo'`.
216217

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.
218219

219220
### Sending
220221

@@ -223,11 +224,11 @@ via the Fastify `decorateReply` API. Thus, in a request handler,
223224
`reply.setCookie('foo', 'foo', {path: '/'})` will set a cookie named `foo`
224225
with a value of `'foo'` on the cookie path `/`.
225226

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.
231232

232233
#### Securing the cookie
233234

@@ -245,8 +246,8 @@ via the Fastify `decorateReply` API. Thus, in a request handler,
245246
`reply.clearCookie('foo', {path: '/'})` will clear a cookie named `foo`
246247
on the cookie path `/`.
247248

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]
250251
documentation. It is optional to pass an `options` object
251252

252253
### Manual cookie parsing
@@ -255,14 +256,16 @@ The method `parseCookie(cookieHeader)` is added to the `fastify` instance
255256
via the Fastify `decorate` API. Thus, `fastify.parseCookie('sessionId=aYb4uTIhdBXC')`
256257
will parse the raw cookie header and return an object `{ "sessionId": "aYb4uTIhdBXC" }`.
257258

258-
[cs]: https://www.npmjs.com/package/cookie#options-1
259+
[cs]: https://github.com/jshttp/cookie#cookiestringifysetcookiesetcookieobj-options
259260

260261
<a id="rotating-secret"></a>
262+
261263
### Rotating signing secret
262264

263265
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.
264266

265267
**Example:**
268+
266269
```js
267270
fastify.register(require('@fastify/cookie'), {
268271
secret: [key1, key2]
@@ -272,11 +275,13 @@ fastify.register(require('@fastify/cookie'), {
272275
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.
273276

274277
Note:
278+
275279
- Key rotation is **only** achieved by redeploying the server again with the new `secret` array.
276280
- 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.
277281
- 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.
278282

279283
**Example:**
284+
280285
```js
281286
fastify.get('/', (req, reply) => {
282287
const result = reply.unsignCookie(req.cookies.myCookie)
@@ -293,11 +298,13 @@ fastify.get('/', (req, reply) => {
293298
```
294299

295300
<a id="custom-cookie-signer"></a>
301+
296302
### Custom cookie signer
297303

298304
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:
299305

300306
**Example:**
307+
301308
```js
302309
fastify.register(require('@fastify/cookie'), {
303310
secret: {
@@ -382,7 +389,6 @@ const signedValue = fastifyCookie.sign('test', 'secret');
382389
const unsignedvalue = fastifyCookie.unsign(signedValue, 'secret');
383390
```
384391

385-
386392
## License
387393

388394
Licensed under [MIT](./LICENSE).

index.js

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
'use strict'
22

33
const fp = require('fastify-plugin')
4-
const cookie = require('cookie')
4+
const { parseCookie: parse, stringifySetCookie } = require('cookie')
55

66
const { Signer, sign, unsign } = require('./signer')
77

8+
function serialize (name, value, options) {
9+
return stringifySetCookie(Object.assign({ name, value }, options))
10+
}
11+
812
const kReplySetCookies = Symbol('fastify.reply.setCookies')
913
const kReplySetCookiesHookRan = Symbol('fastify.reply.setCookiesHookRan')
1014

@@ -29,7 +33,11 @@ function fastifyCookieSetCookie (reply, name, value, options) {
2933
}
3034
}
3135

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+
})
3341

3442
if (reply[kReplySetCookiesHookRan]) {
3543
setCookies(reply)
@@ -77,7 +85,7 @@ function setCookies (reply) {
7785
if (reply[kReplySetCookies].size === 1) {
7886
// Fast path for single cookie
7987
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))
8189
reply[kReplySetCookies].clear()
8290
return
8391
}
@@ -90,7 +98,7 @@ function setCookies (reply) {
9098
}
9199

92100
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))
94102
}
95103

96104
reply.removeHeader('Set-Cookie')
@@ -117,12 +125,20 @@ function plugin (fastify, options, next) {
117125
const secret = options.secret
118126
const hook = getHook(options.hook)
119127
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)
126142
fastify.decorate('parseCookie', parseCookie)
127143

128144
if (secret !== undefined) {
@@ -153,7 +169,7 @@ function plugin (fastify, options, next) {
153169

154170
// ***************************
155171
function parseCookie (cookieHeader) {
156-
return cookie.parse(cookieHeader, options.parseOptions)
172+
return parse(cookieHeader, options.parseOptions)
157173
}
158174

159175
function signCookie (value) {
@@ -206,8 +222,8 @@ module.exports = fastifyCookie
206222
module.exports.default = fastifyCookie // supersedes fastifyCookie.default = fastifyCookie
207223
module.exports.fastifyCookie = fastifyCookie // supersedes fastifyCookie.fastifyCookie = fastifyCookie
208224

209-
module.exports.serialize = cookie.serialize
210-
module.exports.parse = cookie.parse
225+
module.exports.serialize = serialize
226+
module.exports.parse = parse
211227

212228
module.exports.signerFactory = Signer
213229
module.exports.Signer = Signer

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@
6868
"tstyche": "^7.0.0"
6969
},
7070
"dependencies": {
71-
"cookie": "^1.0.0",
71+
"cookie": "^2.0.0",
7272
"fastify-plugin": "^6.0.0"
7373
},
7474
"publishConfig": {

0 commit comments

Comments
 (0)