@fastify/cookie 7.3.1 → 8.1.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/.github/workflows/ci.yml +1 -0
- package/README.md +8 -7
- package/benchmark/signer-multi.js +18 -0
- package/benchmark/signer.js +17 -0
- package/package.json +6 -7
- package/plugin.js +57 -19
- package/signer.js +108 -24
- package/test/cookie.test.js +140 -12
- package/test/signer.test.js +72 -17
- package/{plugin.d.ts → types/plugin.d.ts} +52 -40
- package/{test → types}/plugin.test-d.ts +35 -3
package/.github/workflows/ci.yml
CHANGED
package/README.md
CHANGED
|
@@ -33,7 +33,8 @@ const fastify = require('fastify')()
|
|
|
33
33
|
|
|
34
34
|
fastify.register(require('@fastify/cookie'), {
|
|
35
35
|
secret: "my-secret", // for cookies signature
|
|
36
|
-
|
|
36
|
+
hook: 'onRequest', // set to false to disable cookie autoparsing or set autoparsing on any of the following hooks: 'onRequest', 'preParsing', 'preHandler', 'preValidation'. default: 'onRequest'
|
|
37
|
+
parseOptions: {} // options for parsing cookies
|
|
37
38
|
})
|
|
38
39
|
|
|
39
40
|
fastify.get('/', (req, reply) => {
|
|
@@ -192,9 +193,9 @@ fastify.register(require('@fastify/cookie'), {
|
|
|
192
193
|
|
|
193
194
|
### Manual cookie unsigning
|
|
194
195
|
|
|
195
|
-
The method `unsignCookie(value)` is added to the `fastify` instance and the `reply` object
|
|
196
|
-
via the Fastify `decorate`
|
|
197
|
-
the provided signer's (or the default signer if no custom implementation is provided) `unsign` method on the cookie.
|
|
196
|
+
The method `unsignCookie(value)` is added to the `fastify` instance, to the `request` and the `reply` object
|
|
197
|
+
via the Fastify `decorate`, `decorateRequest` and `decorateReply` APIs, if a secret was provided as option.
|
|
198
|
+
Using it on a signed cookie will call the the provided signer's (or the default signer if no custom implementation is provided) `unsign` method on the cookie.
|
|
198
199
|
|
|
199
200
|
**Example:**
|
|
200
201
|
|
|
@@ -236,12 +237,12 @@ test('Request requires signed cookie', async () => {
|
|
|
236
237
|
|
|
237
238
|
### Manual signing/unsigning with low level utilities
|
|
238
239
|
|
|
239
|
-
with
|
|
240
|
+
with Signer
|
|
240
241
|
|
|
241
242
|
```js
|
|
242
|
-
const {
|
|
243
|
+
const { Signer } = require('@fastify/cookie');
|
|
243
244
|
|
|
244
|
-
const signer =
|
|
245
|
+
const signer = new Signer('secret');
|
|
245
246
|
const signedValue = signer.sign('test');
|
|
246
247
|
const {valid, renew, value } = signer.unsign(signedValue);
|
|
247
248
|
```
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
const Benchmark = require('benchmark')
|
|
2
|
+
const Signer = require('../signer')
|
|
3
|
+
const suite = new Benchmark.Suite()
|
|
4
|
+
|
|
5
|
+
const signer = Signer(['abcd', 'asdf', 'vadfa'])
|
|
6
|
+
const signedValue = signer.sign('test', 'vadfa')
|
|
7
|
+
|
|
8
|
+
suite
|
|
9
|
+
.add('sign', function () {
|
|
10
|
+
signer.sign('test')
|
|
11
|
+
})
|
|
12
|
+
.add('unsign', function () {
|
|
13
|
+
signer.unsign(signedValue)
|
|
14
|
+
})
|
|
15
|
+
.on('cycle', function (event) {
|
|
16
|
+
console.log(String(event.target))
|
|
17
|
+
})
|
|
18
|
+
.run({ delay: 0 })
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
const Benchmark = require('benchmark')
|
|
2
|
+
const Signer = require('../signer')
|
|
3
|
+
const suite = new Benchmark.Suite()
|
|
4
|
+
|
|
5
|
+
const signer = Signer('abcd')
|
|
6
|
+
const signedValue = signer.sign('test')
|
|
7
|
+
suite
|
|
8
|
+
.add('sign', function () {
|
|
9
|
+
signer.sign('test')
|
|
10
|
+
})
|
|
11
|
+
.add('unsign', function () {
|
|
12
|
+
signer.unsign(signedValue)
|
|
13
|
+
})
|
|
14
|
+
.on('cycle', function (event) {
|
|
15
|
+
console.log(String(event.target))
|
|
16
|
+
})
|
|
17
|
+
.run({ delay: 0 })
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fastify/cookie",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "8.1.0",
|
|
4
4
|
"description": "Plugin for fastify to add support for cookies",
|
|
5
5
|
"main": "plugin.js",
|
|
6
|
-
"types": "plugin.d.ts",
|
|
6
|
+
"types": "types/plugin.d.ts",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"lint": "standard | snazzy",
|
|
9
9
|
"lint:ci": "standard",
|
|
@@ -39,19 +39,18 @@
|
|
|
39
39
|
},
|
|
40
40
|
"homepage": "https://github.com/fastify/fastify-cookie#readme",
|
|
41
41
|
"devDependencies": {
|
|
42
|
+
"@fastify/pre-commit": "^2.0.2",
|
|
42
43
|
"@types/node": "^18.0.0",
|
|
43
|
-
"
|
|
44
|
-
"
|
|
44
|
+
"benchmark": "^2.1.4",
|
|
45
|
+
"fastify": "^4.0.0",
|
|
45
46
|
"sinon": "^14.0.0",
|
|
46
47
|
"snazzy": "^9.0.0",
|
|
47
48
|
"standard": "^17.0.0",
|
|
48
49
|
"tap": "^16.0.0",
|
|
49
|
-
"tsd": "^0.22.0"
|
|
50
|
-
"typescript": "^4.5.5"
|
|
50
|
+
"tsd": "^0.22.0"
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
53
|
"cookie": "^0.5.0",
|
|
54
|
-
"cookie-signature": "^1.1.0",
|
|
55
54
|
"fastify-plugin": "^4.0.0"
|
|
56
55
|
},
|
|
57
56
|
"tsd": {
|
package/plugin.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const { sign, unsign } = require('cookie-signature')
|
|
4
3
|
const fp = require('fastify-plugin')
|
|
5
4
|
const cookie = require('cookie')
|
|
6
5
|
|
|
7
|
-
const
|
|
6
|
+
const { Signer, sign, unsign } = require('./signer')
|
|
8
7
|
|
|
9
8
|
function fastifyCookieSetCookie (reply, name, value, options, signer) {
|
|
10
9
|
const opts = Object.assign({}, options)
|
|
@@ -51,34 +50,70 @@ function fastifyCookieClearCookie (reply, name, options) {
|
|
|
51
50
|
return fastifyCookieSetCookie(reply, name, '', opts)
|
|
52
51
|
}
|
|
53
52
|
|
|
54
|
-
function onReqHandlerWrapper (fastify) {
|
|
55
|
-
return
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
53
|
+
function onReqHandlerWrapper (fastify, hook) {
|
|
54
|
+
return hook === 'preParsing'
|
|
55
|
+
? function fastifyCookieHandler (fastifyReq, fastifyRes, payload, done) {
|
|
56
|
+
fastifyReq.cookies = {} // New container per request. Issue #53
|
|
57
|
+
const cookieHeader = fastifyReq.raw.headers.cookie
|
|
58
|
+
if (cookieHeader) {
|
|
59
|
+
fastifyReq.cookies = fastify.parseCookie(cookieHeader)
|
|
60
|
+
}
|
|
61
|
+
done()
|
|
60
62
|
}
|
|
61
|
-
done
|
|
63
|
+
: function fastifyCookieHandler (fastifyReq, fastifyRes, done) {
|
|
64
|
+
fastifyReq.cookies = {} // New container per request. Issue #53
|
|
65
|
+
const cookieHeader = fastifyReq.raw.headers.cookie
|
|
66
|
+
if (cookieHeader) {
|
|
67
|
+
fastifyReq.cookies = fastify.parseCookie(cookieHeader)
|
|
68
|
+
}
|
|
69
|
+
done()
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function getHook (hook = 'onRequest') {
|
|
74
|
+
const hooks = {
|
|
75
|
+
onRequest: 'onRequest',
|
|
76
|
+
preParsing: 'preParsing',
|
|
77
|
+
preValidation: 'preValidation',
|
|
78
|
+
preHandler: 'preHandler',
|
|
79
|
+
[false]: false
|
|
62
80
|
}
|
|
81
|
+
|
|
82
|
+
return hooks[hook]
|
|
63
83
|
}
|
|
64
84
|
|
|
65
85
|
function plugin (fastify, options, next) {
|
|
66
|
-
const secret = options.secret
|
|
86
|
+
const secret = options.secret
|
|
87
|
+
const hook = getHook(options.hook)
|
|
88
|
+
if (hook === undefined) {
|
|
89
|
+
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\''))
|
|
90
|
+
}
|
|
67
91
|
const enableRotation = Array.isArray(secret)
|
|
68
|
-
const
|
|
92
|
+
const algorithm = options.algorithm || 'sha256'
|
|
93
|
+
const signer = typeof secret === 'string' || enableRotation ? new Signer(secret, algorithm) : secret
|
|
69
94
|
|
|
70
95
|
fastify.decorate('parseCookie', parseCookie)
|
|
71
|
-
|
|
72
|
-
|
|
96
|
+
|
|
97
|
+
if (typeof secret !== 'undefined') {
|
|
98
|
+
fastify.decorate('signCookie', signCookie)
|
|
99
|
+
fastify.decorate('unsignCookie', unsignCookie)
|
|
100
|
+
|
|
101
|
+
fastify.decorateRequest('signCookie', signCookie)
|
|
102
|
+
fastify.decorateRequest('unsignCookie', unsignCookie)
|
|
103
|
+
|
|
104
|
+
fastify.decorateReply('signCookie', signCookie)
|
|
105
|
+
fastify.decorateReply('unsignCookie', unsignCookie)
|
|
106
|
+
}
|
|
73
107
|
|
|
74
108
|
fastify.decorateRequest('cookies', null)
|
|
75
|
-
fastify.decorateRequest('unsignCookie', unsignCookie)
|
|
76
|
-
fastify.decorateReply('setCookie', setCookie)
|
|
77
109
|
fastify.decorateReply('cookie', setCookie)
|
|
110
|
+
|
|
111
|
+
fastify.decorateReply('setCookie', setCookie)
|
|
78
112
|
fastify.decorateReply('clearCookie', clearCookie)
|
|
79
|
-
fastify.decorateReply('unsignCookie', unsignCookie)
|
|
80
113
|
|
|
81
|
-
|
|
114
|
+
if (hook) {
|
|
115
|
+
fastify.addHook(hook, onReqHandlerWrapper(fastify, hook))
|
|
116
|
+
}
|
|
82
117
|
|
|
83
118
|
next()
|
|
84
119
|
|
|
@@ -127,14 +162,17 @@ const fastifyCookie = fp(plugin, {
|
|
|
127
162
|
* - `import { fastifyCookie } from 'fastify-cookie'`
|
|
128
163
|
* - `import fastifyCookie from 'fastify-cookie'`
|
|
129
164
|
*/
|
|
165
|
+
fastifyCookie.signerFactory = Signer
|
|
130
166
|
fastifyCookie.fastifyCookie = fastifyCookie
|
|
131
167
|
fastifyCookie.default = fastifyCookie
|
|
132
168
|
module.exports = fastifyCookie
|
|
133
169
|
|
|
134
|
-
fastifyCookie.fastifyCookie.signerFactory =
|
|
170
|
+
fastifyCookie.fastifyCookie.signerFactory = Signer
|
|
171
|
+
fastifyCookie.fastifyCookie.Signer = Signer
|
|
135
172
|
fastifyCookie.fastifyCookie.sign = sign
|
|
136
173
|
fastifyCookie.fastifyCookie.unsign = unsign
|
|
137
174
|
|
|
138
|
-
module.exports.signerFactory =
|
|
175
|
+
module.exports.signerFactory = Signer
|
|
176
|
+
module.exports.Signer = Signer
|
|
139
177
|
module.exports.sign = sign
|
|
140
178
|
module.exports.unsign = unsign
|
package/signer.js
CHANGED
|
@@ -1,32 +1,116 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
// Inspired by node-cookie-signature
|
|
4
|
+
// https://github.com/tj/node-cookie-signature
|
|
5
|
+
//
|
|
6
|
+
// The MIT License
|
|
7
|
+
// Copyright (c) 2012–2022 LearnBoost <tj@learnboost.com> and other contributors;
|
|
4
8
|
|
|
5
|
-
|
|
6
|
-
const secrets = Array.isArray(secret) ? secret : [secret]
|
|
7
|
-
const [signingKey] = secrets
|
|
9
|
+
const crypto = require('crypto')
|
|
8
10
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
let renew = false
|
|
16
|
-
let value = null
|
|
17
|
-
|
|
18
|
-
for (const key of secrets) {
|
|
19
|
-
const result = cookieSignature.unsign(signedValue, key)
|
|
20
|
-
|
|
21
|
-
if (result !== false) {
|
|
22
|
-
valid = true
|
|
23
|
-
renew = key !== signingKey
|
|
24
|
-
value = result
|
|
25
|
-
break
|
|
26
|
-
}
|
|
27
|
-
}
|
|
11
|
+
const base64PaddingRE = /=/g
|
|
12
|
+
|
|
13
|
+
function Signer (secrets, algorithm = 'sha256') {
|
|
14
|
+
if (!(this instanceof Signer)) {
|
|
15
|
+
return new Signer(secrets, algorithm)
|
|
16
|
+
}
|
|
28
17
|
|
|
29
|
-
|
|
18
|
+
this.secrets = Array.isArray(secrets) ? secrets : [secrets]
|
|
19
|
+
this.signingKey = this.secrets[0]
|
|
20
|
+
this.algorithm = algorithm
|
|
21
|
+
|
|
22
|
+
validateSecrets(this.secrets)
|
|
23
|
+
validateAlgorithm(this.algorithm)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function validateSecrets (secrets) {
|
|
27
|
+
for (const secret of secrets) {
|
|
28
|
+
if (typeof secret !== 'string') {
|
|
29
|
+
throw new TypeError('Secret key must be a string.')
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
|
+
|
|
34
|
+
function validateAlgorithm (algorithm) {
|
|
35
|
+
// validate that the algorithm is supported by the node runtime
|
|
36
|
+
try {
|
|
37
|
+
crypto.createHmac(algorithm, crypto.randomBytes(16))
|
|
38
|
+
} catch (e) {
|
|
39
|
+
throw new TypeError(`Algorithm ${algorithm} not supported.`)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function _sign (value, secret, algorithm) {
|
|
44
|
+
if (typeof value !== 'string') {
|
|
45
|
+
throw new TypeError('Cookie value must be provided as a string.')
|
|
46
|
+
}
|
|
47
|
+
return value + '.' + crypto
|
|
48
|
+
.createHmac(algorithm, secret)
|
|
49
|
+
.update(value)
|
|
50
|
+
.digest('base64')
|
|
51
|
+
// remove base64 padding (=) as it has special meaning in cookies
|
|
52
|
+
.replace(base64PaddingRE, '')
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function _unsign (signedValue, secrets, algorithm) {
|
|
56
|
+
if (typeof signedValue !== 'string') {
|
|
57
|
+
throw new TypeError('Signed cookie string must be provided.')
|
|
58
|
+
}
|
|
59
|
+
const value = signedValue.slice(0, signedValue.lastIndexOf('.'))
|
|
60
|
+
const actual = Buffer.from(signedValue.slice(signedValue.lastIndexOf('.') + 1))
|
|
61
|
+
|
|
62
|
+
for (const secret of secrets) {
|
|
63
|
+
const expected = Buffer.from(crypto
|
|
64
|
+
.createHmac(algorithm, secret)
|
|
65
|
+
.update(value)
|
|
66
|
+
.digest('base64')
|
|
67
|
+
// remove base64 padding (=) as it has special meaning in cookies
|
|
68
|
+
.replace(base64PaddingRE, ''))
|
|
69
|
+
if (
|
|
70
|
+
expected.length === actual.length &&
|
|
71
|
+
crypto.timingSafeEqual(expected, actual)
|
|
72
|
+
) {
|
|
73
|
+
return {
|
|
74
|
+
valid: true,
|
|
75
|
+
renew: secret !== secrets[0],
|
|
76
|
+
value
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
valid: false,
|
|
83
|
+
renew: false,
|
|
84
|
+
value: null
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
Signer.prototype.sign = function (value) {
|
|
89
|
+
return _sign(value, this.signingKey, this.algorithm)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
Signer.prototype.unsign = function (signedValue) {
|
|
93
|
+
return _unsign(signedValue, this.secrets, this.algorithm)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function sign (value, secret, algorithm = 'sha256') {
|
|
97
|
+
const secrets = Array.isArray(secret) ? secret : [secret]
|
|
98
|
+
|
|
99
|
+
validateSecrets(secrets)
|
|
100
|
+
|
|
101
|
+
return _sign(value, secrets[0], algorithm)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function unsign (signedValue, secret, algorithm = 'sha256') {
|
|
105
|
+
const secrets = Array.isArray(secret) ? secret : [secret]
|
|
106
|
+
|
|
107
|
+
validateSecrets(secrets)
|
|
108
|
+
|
|
109
|
+
return _unsign(signedValue, secrets, algorithm)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
module.exports = Signer
|
|
113
|
+
module.exports.signerFactory = Signer
|
|
114
|
+
module.exports.Signer = Signer
|
|
115
|
+
module.exports.sign = sign
|
|
116
|
+
module.exports.unsign = unsign
|
package/test/cookie.test.js
CHANGED
|
@@ -4,7 +4,7 @@ const tap = require('tap')
|
|
|
4
4
|
const test = tap.test
|
|
5
5
|
const Fastify = require('fastify')
|
|
6
6
|
const sinon = require('sinon')
|
|
7
|
-
const
|
|
7
|
+
const { sign, unsign } = require('../signer')
|
|
8
8
|
const plugin = require('../')
|
|
9
9
|
|
|
10
10
|
test('cookies get set correctly', (t) => {
|
|
@@ -151,7 +151,7 @@ test('share options for setCookie and clearCookie', (t) => {
|
|
|
151
151
|
const cookies = res.cookies
|
|
152
152
|
t.equal(cookies.length, 2)
|
|
153
153
|
t.equal(cookies[0].name, 'foo')
|
|
154
|
-
t.equal(cookies[0].value,
|
|
154
|
+
t.equal(cookies[0].value, sign('foo', secret))
|
|
155
155
|
t.equal(cookies[0].maxAge, 36000)
|
|
156
156
|
|
|
157
157
|
t.equal(cookies[1].name, 'foo')
|
|
@@ -190,7 +190,7 @@ test('expires should not be overridden in clearCookie', (t) => {
|
|
|
190
190
|
const cookies = res.cookies
|
|
191
191
|
t.equal(cookies.length, 2)
|
|
192
192
|
t.equal(cookies[0].name, 'foo')
|
|
193
|
-
t.equal(cookies[0].value,
|
|
193
|
+
t.equal(cookies[0].value, sign('foo', secret))
|
|
194
194
|
const expires = new Date(cookies[0].expires)
|
|
195
195
|
t.ok(expires < new Date(Date.now() + 5000))
|
|
196
196
|
|
|
@@ -378,7 +378,7 @@ test('cookies signature', (t) => {
|
|
|
378
378
|
const cookies = res.cookies
|
|
379
379
|
t.equal(cookies.length, 1)
|
|
380
380
|
t.equal(cookies[0].name, 'foo')
|
|
381
|
-
t.
|
|
381
|
+
t.same(unsign(cookies[0].value, secret), { valid: true, renew: false, value: 'foo' })
|
|
382
382
|
})
|
|
383
383
|
})
|
|
384
384
|
|
|
@@ -406,7 +406,7 @@ test('cookies signature', (t) => {
|
|
|
406
406
|
const cookies = res.cookies
|
|
407
407
|
t.equal(cookies.length, 1)
|
|
408
408
|
t.equal(cookies[0].name, 'foo')
|
|
409
|
-
t.
|
|
409
|
+
t.same(unsign(cookies[0].value, secret1), { valid: true, renew: false, value: 'cookieVal' }) // decode using first key
|
|
410
410
|
})
|
|
411
411
|
})
|
|
412
412
|
|
|
@@ -427,7 +427,7 @@ test('cookies signature', (t) => {
|
|
|
427
427
|
method: 'GET',
|
|
428
428
|
url: '/test1',
|
|
429
429
|
headers: {
|
|
430
|
-
cookie: `foo=${
|
|
430
|
+
cookie: `foo=${sign('foo', secret)}`
|
|
431
431
|
}
|
|
432
432
|
}, (err, res) => {
|
|
433
433
|
t.error(err)
|
|
@@ -452,7 +452,7 @@ test('cookies signature', (t) => {
|
|
|
452
452
|
method: 'GET',
|
|
453
453
|
url: '/test1',
|
|
454
454
|
headers: {
|
|
455
|
-
cookie: `foo=${
|
|
455
|
+
cookie: `foo=${sign('foo', secret)}`
|
|
456
456
|
}
|
|
457
457
|
}, (err, res) => {
|
|
458
458
|
t.error(err)
|
|
@@ -477,7 +477,7 @@ test('cookies signature', (t) => {
|
|
|
477
477
|
method: 'GET',
|
|
478
478
|
url: '/test1',
|
|
479
479
|
headers: {
|
|
480
|
-
cookie: `foo=${
|
|
480
|
+
cookie: `foo=${sign('foo', secret)}`
|
|
481
481
|
}
|
|
482
482
|
}, (err, res) => {
|
|
483
483
|
t.error(err)
|
|
@@ -503,7 +503,7 @@ test('cookies signature', (t) => {
|
|
|
503
503
|
method: 'GET',
|
|
504
504
|
url: '/test1',
|
|
505
505
|
headers: {
|
|
506
|
-
cookie: `foo=${
|
|
506
|
+
cookie: `foo=${sign('foo', secret2)}`
|
|
507
507
|
}
|
|
508
508
|
}, (err, res) => {
|
|
509
509
|
t.error(err)
|
|
@@ -529,7 +529,7 @@ test('cookies signature', (t) => {
|
|
|
529
529
|
method: 'GET',
|
|
530
530
|
url: '/test1',
|
|
531
531
|
headers: {
|
|
532
|
-
cookie: `foo=${
|
|
532
|
+
cookie: `foo=${sign('foo', secret2)}`
|
|
533
533
|
}
|
|
534
534
|
}, (err, res) => {
|
|
535
535
|
t.error(err)
|
|
@@ -555,7 +555,7 @@ test('cookies signature', (t) => {
|
|
|
555
555
|
method: 'GET',
|
|
556
556
|
url: '/test1',
|
|
557
557
|
headers: {
|
|
558
|
-
cookie: `foo=${
|
|
558
|
+
cookie: `foo=${sign('foo', 'invalid-secret')}`
|
|
559
559
|
}
|
|
560
560
|
}, (err, res) => {
|
|
561
561
|
t.error(err)
|
|
@@ -581,7 +581,7 @@ test('cookies signature', (t) => {
|
|
|
581
581
|
method: 'GET',
|
|
582
582
|
url: '/test1',
|
|
583
583
|
headers: {
|
|
584
|
-
cookie: `foo=${
|
|
584
|
+
cookie: `foo=${sign('foo', 'invalid-secret')}`
|
|
585
585
|
}
|
|
586
586
|
}, (err, res) => {
|
|
587
587
|
t.error(err)
|
|
@@ -814,3 +814,131 @@ test('handle secure:auto of cookieOptions', async (t) => {
|
|
|
814
814
|
t.same(cookies2[0].secure, null)
|
|
815
815
|
t.equal(cookies2[0].path, '/')
|
|
816
816
|
})
|
|
817
|
+
|
|
818
|
+
test('should not decorate fastify, request and reply if no secret was provided', async (t) => {
|
|
819
|
+
t.plan(8)
|
|
820
|
+
const fastify = Fastify()
|
|
821
|
+
|
|
822
|
+
await fastify.register(plugin)
|
|
823
|
+
|
|
824
|
+
t.notOk(fastify.signCookie)
|
|
825
|
+
t.notOk(fastify.unsignCookie)
|
|
826
|
+
|
|
827
|
+
fastify.get('/testDecorators', (req, reply) => {
|
|
828
|
+
t.notOk(req.signCookie)
|
|
829
|
+
t.notOk(reply.signCookie)
|
|
830
|
+
t.notOk(req.unsignCookie)
|
|
831
|
+
t.notOk(reply.unsignCookie)
|
|
832
|
+
|
|
833
|
+
reply.send({
|
|
834
|
+
unsigned: req.unsignCookie(req.cookies.foo)
|
|
835
|
+
})
|
|
836
|
+
})
|
|
837
|
+
|
|
838
|
+
const res = await fastify.inject({
|
|
839
|
+
method: 'GET',
|
|
840
|
+
url: '/testDecorators'
|
|
841
|
+
})
|
|
842
|
+
|
|
843
|
+
t.equal(res.statusCode, 500)
|
|
844
|
+
t.same(JSON.parse(res.body), {
|
|
845
|
+
statusCode: 500,
|
|
846
|
+
error: 'Internal Server Error',
|
|
847
|
+
message: 'req.unsignCookie is not a function'
|
|
848
|
+
})
|
|
849
|
+
})
|
|
850
|
+
|
|
851
|
+
test('dont add auto cookie parsing to onRequest-hook if hook-option is set to false', (t) => {
|
|
852
|
+
t.plan(6)
|
|
853
|
+
const fastify = Fastify()
|
|
854
|
+
fastify.register(plugin, { hook: false })
|
|
855
|
+
|
|
856
|
+
for (const hook of ['preValidation', 'preHandler', 'preParsing']) {
|
|
857
|
+
fastify.addHook(hook, async (req) => {
|
|
858
|
+
t.equal(req.cookies, null)
|
|
859
|
+
})
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
fastify.get('/disable', (req, reply) => {
|
|
863
|
+
t.equal(req.cookies, null)
|
|
864
|
+
reply.send()
|
|
865
|
+
})
|
|
866
|
+
|
|
867
|
+
fastify.inject({
|
|
868
|
+
method: 'GET',
|
|
869
|
+
url: '/disable',
|
|
870
|
+
headers: {
|
|
871
|
+
cookie: 'bar=bar'
|
|
872
|
+
}
|
|
873
|
+
}, (err, res) => {
|
|
874
|
+
t.error(err)
|
|
875
|
+
t.equal(res.statusCode, 200)
|
|
876
|
+
})
|
|
877
|
+
})
|
|
878
|
+
|
|
879
|
+
test('result in an error if hook-option is set to an invalid value', (t) => {
|
|
880
|
+
t.plan(1)
|
|
881
|
+
const fastify = Fastify()
|
|
882
|
+
|
|
883
|
+
t.rejects(
|
|
884
|
+
() => fastify.register(plugin, { hook: true }),
|
|
885
|
+
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'")
|
|
886
|
+
)
|
|
887
|
+
})
|
|
888
|
+
|
|
889
|
+
test('correct working plugin if hook-option to preParsing', (t) => {
|
|
890
|
+
t.plan(5)
|
|
891
|
+
const fastify = Fastify()
|
|
892
|
+
fastify.register(plugin, { hook: 'preParsing' })
|
|
893
|
+
|
|
894
|
+
fastify.addHook('onRequest', async (req) => {
|
|
895
|
+
t.equal(req.cookies, null)
|
|
896
|
+
})
|
|
897
|
+
|
|
898
|
+
fastify.addHook('preValidation', async (req) => {
|
|
899
|
+
t.equal(req.cookies.bar, 'bar')
|
|
900
|
+
})
|
|
901
|
+
|
|
902
|
+
fastify.get('/preparsing', (req, reply) => {
|
|
903
|
+
t.equal(req.cookies.bar, 'bar')
|
|
904
|
+
reply.send()
|
|
905
|
+
})
|
|
906
|
+
|
|
907
|
+
fastify.inject({
|
|
908
|
+
method: 'GET',
|
|
909
|
+
url: '/preparsing',
|
|
910
|
+
headers: {
|
|
911
|
+
cookie: 'bar=bar'
|
|
912
|
+
}
|
|
913
|
+
}, (err, res) => {
|
|
914
|
+
t.error(err)
|
|
915
|
+
t.equal(res.statusCode, 200)
|
|
916
|
+
})
|
|
917
|
+
})
|
|
918
|
+
|
|
919
|
+
test('if cookies are not set, then the handler creates an empty req.cookies object', (t) => {
|
|
920
|
+
t.plan(5)
|
|
921
|
+
const fastify = Fastify()
|
|
922
|
+
fastify.register(plugin, { hook: 'preParsing' })
|
|
923
|
+
|
|
924
|
+
fastify.addHook('onRequest', async (req) => {
|
|
925
|
+
t.equal(req.cookies, null)
|
|
926
|
+
})
|
|
927
|
+
|
|
928
|
+
fastify.addHook('preValidation', async (req) => {
|
|
929
|
+
t.ok(req.cookies)
|
|
930
|
+
})
|
|
931
|
+
|
|
932
|
+
fastify.get('/preparsing', (req, reply) => {
|
|
933
|
+
t.ok(req.cookies)
|
|
934
|
+
reply.send()
|
|
935
|
+
})
|
|
936
|
+
|
|
937
|
+
fastify.inject({
|
|
938
|
+
method: 'GET',
|
|
939
|
+
url: '/preparsing'
|
|
940
|
+
}, (err, res) => {
|
|
941
|
+
t.error(err)
|
|
942
|
+
t.equal(res.statusCode, 200)
|
|
943
|
+
})
|
|
944
|
+
})
|
package/test/signer.test.js
CHANGED
|
@@ -2,33 +2,67 @@
|
|
|
2
2
|
|
|
3
3
|
const { test } = require('tap')
|
|
4
4
|
const sinon = require('sinon')
|
|
5
|
-
const
|
|
6
|
-
const
|
|
5
|
+
const crypto = require('crypto')
|
|
6
|
+
const { Signer, sign, unsign } = require('../signer')
|
|
7
7
|
|
|
8
|
-
test('default',
|
|
9
|
-
t.plan(
|
|
8
|
+
test('default', t => {
|
|
9
|
+
t.plan(5)
|
|
10
10
|
|
|
11
11
|
const secret = 'my-secret'
|
|
12
|
-
const signer =
|
|
12
|
+
const signer = Signer(secret)
|
|
13
13
|
|
|
14
|
-
t.test('signer.sign', (t) => {
|
|
14
|
+
t.test('signer.sign should throw if there is no value provided', (t) => {
|
|
15
15
|
t.plan(1)
|
|
16
16
|
|
|
17
|
+
t.throws(() => signer.sign(undefined), 'Cookie value must be provided as a string.')
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
t.test('signer.sign', (t) => {
|
|
21
|
+
t.plan(2)
|
|
22
|
+
|
|
17
23
|
const input = 'some-value'
|
|
18
24
|
const result = signer.sign(input)
|
|
19
25
|
|
|
20
|
-
t.equal(result,
|
|
26
|
+
t.equal(result, sign(input, secret))
|
|
27
|
+
t.throws(() => sign(undefined), 'Cookie value must be provided as a string.')
|
|
21
28
|
})
|
|
22
29
|
|
|
23
|
-
t.test('
|
|
30
|
+
t.test('sign', (t) => {
|
|
24
31
|
t.plan(3)
|
|
25
32
|
|
|
26
|
-
const input =
|
|
33
|
+
const input = 'some-value'
|
|
34
|
+
const result = signer.sign(input)
|
|
35
|
+
|
|
36
|
+
t.equal(result, sign(input, secret))
|
|
37
|
+
t.equal(result, sign(input, [secret]))
|
|
38
|
+
|
|
39
|
+
t.throws(() => sign(undefined), 'Cookie value must be provided as a string.')
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
t.test('signer.unsign', (t) => {
|
|
43
|
+
t.plan(4)
|
|
44
|
+
|
|
45
|
+
const input = signer.sign('some-value', secret)
|
|
27
46
|
const result = signer.unsign(input)
|
|
28
47
|
|
|
29
48
|
t.equal(result.valid, true)
|
|
30
49
|
t.equal(result.renew, false)
|
|
31
50
|
t.equal(result.value, 'some-value')
|
|
51
|
+
t.throws(() => signer.unsign(undefined), 'Signed cookie string must be provided.')
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
t.test('unsign', (t) => {
|
|
55
|
+
t.plan(6)
|
|
56
|
+
|
|
57
|
+
const input = sign('some-value', secret)
|
|
58
|
+
const result = unsign(input, secret)
|
|
59
|
+
|
|
60
|
+
t.equal(result.valid, true)
|
|
61
|
+
t.equal(result.renew, false)
|
|
62
|
+
t.equal(result.value, 'some-value')
|
|
63
|
+
t.same(result, unsign(input, [secret]))
|
|
64
|
+
t.throws(() => unsign(undefined), 'Secret key must be a string.')
|
|
65
|
+
t.throws(() => unsign(undefined, secret), 'Signed cookie string must be provided.')
|
|
32
66
|
})
|
|
33
67
|
})
|
|
34
68
|
|
|
@@ -37,11 +71,11 @@ test('key rotation', (t) => {
|
|
|
37
71
|
const secret1 = 'my-secret-1'
|
|
38
72
|
const secret2 = 'my-secret-2'
|
|
39
73
|
const secret3 = 'my-secret-3'
|
|
40
|
-
const signer =
|
|
41
|
-
const
|
|
74
|
+
const signer = Signer([secret1, secret2, secret3])
|
|
75
|
+
const signSpy = sinon.spy(crypto, 'createHmac')
|
|
42
76
|
|
|
43
77
|
t.beforeEach(() => {
|
|
44
|
-
|
|
78
|
+
signSpy.resetHistory()
|
|
45
79
|
})
|
|
46
80
|
|
|
47
81
|
t.test('signer.sign always signs using first key', (t) => {
|
|
@@ -50,30 +84,51 @@ test('key rotation', (t) => {
|
|
|
50
84
|
const input = 'some-value'
|
|
51
85
|
const result = signer.sign(input)
|
|
52
86
|
|
|
53
|
-
t.equal(result,
|
|
87
|
+
t.equal(result, sign(input, secret1))
|
|
54
88
|
})
|
|
55
89
|
|
|
56
90
|
t.test('signer.unsign tries to decode using all keys till it finds', (t) => {
|
|
57
91
|
t.plan(4)
|
|
58
92
|
|
|
59
|
-
const input =
|
|
93
|
+
const input = sign('some-value', secret2)
|
|
94
|
+
signSpy.resetHistory()
|
|
60
95
|
const result = signer.unsign(input)
|
|
61
96
|
|
|
62
97
|
t.equal(result.valid, true)
|
|
63
98
|
t.equal(result.renew, true)
|
|
64
99
|
t.equal(result.value, 'some-value')
|
|
65
|
-
t.equal(
|
|
100
|
+
t.equal(signSpy.callCount, 2) // should have returned early when the right key was found
|
|
66
101
|
})
|
|
67
102
|
|
|
68
103
|
t.test('signer.unsign failure response', (t) => {
|
|
69
104
|
t.plan(4)
|
|
70
105
|
|
|
71
|
-
const input =
|
|
106
|
+
const input = sign('some-value', 'invalid-secret')
|
|
107
|
+
signSpy.resetHistory()
|
|
72
108
|
const result = signer.unsign(input)
|
|
73
109
|
|
|
74
110
|
t.equal(result.valid, false)
|
|
75
111
|
t.equal(result.renew, false)
|
|
76
112
|
t.equal(result.value, null)
|
|
77
|
-
t.equal(
|
|
113
|
+
t.equal(signSpy.callCount, 3) // should have tried all 3
|
|
114
|
+
})
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
test('Signer', t => {
|
|
118
|
+
t.plan(2)
|
|
119
|
+
|
|
120
|
+
t.test('Signer needs a string as secret', (t) => {
|
|
121
|
+
t.plan(4)
|
|
122
|
+
t.throws(() => Signer(1), 'Secret key must be a string.')
|
|
123
|
+
t.throws(() => Signer(undefined), 'Secret key must be a string.')
|
|
124
|
+
t.doesNotThrow(() => Signer('secret'))
|
|
125
|
+
t.doesNotThrow(() => Signer(['secret']))
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
t.test('Signer handles algorithm properly', (t) => {
|
|
129
|
+
t.plan(3)
|
|
130
|
+
t.throws(() => Signer('secret', 'invalid'), 'Algorithm invalid not supported.')
|
|
131
|
+
t.doesNotThrow(() => Signer('secret', 'sha512'))
|
|
132
|
+
t.doesNotThrow(() => Signer('secret', 'sha256'))
|
|
78
133
|
})
|
|
79
134
|
})
|
|
@@ -3,16 +3,7 @@
|
|
|
3
3
|
import { FastifyPluginCallback } from "fastify";
|
|
4
4
|
|
|
5
5
|
declare module "fastify" {
|
|
6
|
-
interface FastifyInstance {
|
|
7
|
-
/**
|
|
8
|
-
* Unsigns the specified cookie using the secret provided.
|
|
9
|
-
* @param value Cookie value
|
|
10
|
-
*/
|
|
11
|
-
unsignCookie(value: string): {
|
|
12
|
-
valid: boolean;
|
|
13
|
-
renew: boolean;
|
|
14
|
-
value: string | null;
|
|
15
|
-
};
|
|
6
|
+
interface FastifyInstance extends SignerMethods {
|
|
16
7
|
/**
|
|
17
8
|
* Manual cookie parsing method
|
|
18
9
|
* @docs https://github.com/fastify/fastify-cookie#manual-cookie-parsing
|
|
@@ -21,29 +12,34 @@ declare module "fastify" {
|
|
|
21
12
|
parseCookie(cookieHeader: string): {
|
|
22
13
|
[key: string]: string;
|
|
23
14
|
};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface FastifyRequest extends SignerMethods {
|
|
24
18
|
/**
|
|
25
|
-
*
|
|
26
|
-
* @docs https://github.com/fastify/fastify-cookie#manual-cookie-parsing
|
|
27
|
-
* @param value cookie value
|
|
19
|
+
* Request cookies
|
|
28
20
|
*/
|
|
29
|
-
|
|
21
|
+
cookies: { [cookieName: string]: string | undefined };
|
|
30
22
|
}
|
|
31
23
|
|
|
32
|
-
interface
|
|
24
|
+
interface FastifyReply extends SignerMethods {
|
|
33
25
|
/**
|
|
34
26
|
* Request cookies
|
|
35
27
|
*/
|
|
36
28
|
cookies: { [cookieName: string]: string | undefined };
|
|
29
|
+
}
|
|
37
30
|
|
|
31
|
+
interface SignerMethods {
|
|
38
32
|
/**
|
|
39
|
-
*
|
|
33
|
+
* Signs the specified cookie using the secret/signer provided.
|
|
34
|
+
* @param value cookie value
|
|
35
|
+
*/
|
|
36
|
+
signCookie(value: string): string;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Unsigns the specified cookie using the secret/signer provided.
|
|
40
40
|
* @param value Cookie value
|
|
41
41
|
*/
|
|
42
|
-
unsignCookie(value: string):
|
|
43
|
-
valid: boolean;
|
|
44
|
-
renew: boolean;
|
|
45
|
-
value: string | null;
|
|
46
|
-
};
|
|
42
|
+
unsignCookie(value: string): fastifyCookie.UnsignResult;
|
|
47
43
|
}
|
|
48
44
|
|
|
49
45
|
export type setCookieWrapper = (
|
|
@@ -89,11 +85,7 @@ declare module "fastify" {
|
|
|
89
85
|
* Unsigns the specified cookie using the secret provided.
|
|
90
86
|
* @param value Cookie value
|
|
91
87
|
*/
|
|
92
|
-
unsignCookie(value: string):
|
|
93
|
-
valid: boolean;
|
|
94
|
-
renew: boolean;
|
|
95
|
-
value: string | null;
|
|
96
|
-
};
|
|
88
|
+
unsignCookie(value: string): fastifyCookie.UnsignResult;
|
|
97
89
|
}
|
|
98
90
|
}
|
|
99
91
|
|
|
@@ -102,36 +94,54 @@ type FastifyCookiePlugin = FastifyPluginCallback<
|
|
|
102
94
|
>;
|
|
103
95
|
|
|
104
96
|
declare namespace fastifyCookie {
|
|
105
|
-
|
|
106
|
-
sign: (
|
|
107
|
-
unsign: (input: string) =>
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
97
|
+
interface SignerBase {
|
|
98
|
+
sign: (value: string) => string;
|
|
99
|
+
unsign: (input: string) => UnsignResult;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export class Signer implements SignerBase {
|
|
103
|
+
constructor (secrets: string | Array<string>, algorithm?: string)
|
|
104
|
+
sign: (value: string) => string;
|
|
105
|
+
unsign: (input: string) => UnsignResult;
|
|
112
106
|
}
|
|
113
107
|
|
|
114
108
|
export interface CookieSerializeOptions {
|
|
109
|
+
/** The `Domain` attribute. */
|
|
115
110
|
domain?: string;
|
|
116
111
|
encode?(val: string): string;
|
|
112
|
+
/** The expiration `date` used for the `Expires` attribute. If both `expires` and `maxAge` are set, then `expires` is used. */
|
|
117
113
|
expires?: Date;
|
|
114
|
+
/** The `boolean` value of the `HttpOnly` attribute. Defaults to true. */
|
|
118
115
|
httpOnly?: boolean;
|
|
116
|
+
/** A `number` in milliseconds that specifies the `Expires` attribute by adding the specified milliseconds to the current date. If both `expires` and `maxAge` are set, then `expires` is used. */
|
|
119
117
|
maxAge?: number;
|
|
118
|
+
/** The `Path` attribute. Defaults to `/` (the root path). */
|
|
120
119
|
path?: string;
|
|
121
120
|
priority?: "low" | "medium" | "high";
|
|
122
|
-
|
|
123
|
-
|
|
121
|
+
/** A `boolean` or one of the `SameSite` string attributes. E.g.: `lax`, `node` or `strict`. */
|
|
122
|
+
sameSite?: 'lax' | 'none' | 'strict' | boolean;
|
|
123
|
+
/** The `boolean` value of the `Secure` attribute. Set this option to false when communicating over an unencrypted (HTTP) connection. Value can be set to `auto`; in this case the `Secure` attribute will be set to false for HTTP request, in case of HTTPS it will be set to true. Defaults to true. */
|
|
124
|
+
secure?: boolean | 'auto';
|
|
124
125
|
signed?: boolean;
|
|
125
126
|
}
|
|
126
127
|
|
|
128
|
+
type HookType = 'onRequest' | 'preParsing' | 'preValidation' | 'preHandler' | 'preSerialization';
|
|
129
|
+
|
|
127
130
|
export interface FastifyCookieOptions {
|
|
128
131
|
secret?: string | string[] | Signer;
|
|
132
|
+
hook?: HookType | false;
|
|
129
133
|
parseOptions?: fastifyCookie.CookieSerializeOptions;
|
|
130
134
|
}
|
|
131
135
|
|
|
132
|
-
export type Sign = (value: string, secret: string) => string;
|
|
133
|
-
export type Unsign = (input: string, secret: string
|
|
134
|
-
export type SignerFactory = (
|
|
136
|
+
export type Sign = (value: string, secret: string, algorithm?: string) => string;
|
|
137
|
+
export type Unsign = (input: string, secret: string, algorithm?: string) => UnsignResult;
|
|
138
|
+
export type SignerFactory = (secrets: string | Array<string>, algorithm?: string) => SignerBase;
|
|
139
|
+
|
|
140
|
+
export interface UnsignResult {
|
|
141
|
+
valid: boolean;
|
|
142
|
+
renew: boolean;
|
|
143
|
+
value: string | null;
|
|
144
|
+
}
|
|
135
145
|
|
|
136
146
|
export const signerFactory: SignerFactory;
|
|
137
147
|
export const sign: Sign;
|
|
@@ -139,6 +149,7 @@ declare namespace fastifyCookie {
|
|
|
139
149
|
|
|
140
150
|
export interface FastifyCookie extends FastifyCookiePlugin {
|
|
141
151
|
signerFactory: SignerFactory;
|
|
152
|
+
Signer: Signer;
|
|
142
153
|
sign: Sign;
|
|
143
154
|
unsign: Unsign;
|
|
144
155
|
}
|
|
@@ -146,7 +157,8 @@ declare namespace fastifyCookie {
|
|
|
146
157
|
export const fastifyCookie: FastifyCookie;
|
|
147
158
|
|
|
148
159
|
export interface FastifyCookieOptions {
|
|
149
|
-
secret?: string | string[] |
|
|
160
|
+
secret?: string | string[] | SignerBase;
|
|
161
|
+
algorithm?: string;
|
|
150
162
|
parseOptions?: CookieSerializeOptions;
|
|
151
163
|
}
|
|
152
164
|
|
|
@@ -157,4 +169,4 @@ declare function fastifyCookie(
|
|
|
157
169
|
...params: Parameters<FastifyCookiePlugin>
|
|
158
170
|
): ReturnType<FastifyCookiePlugin>;
|
|
159
171
|
|
|
160
|
-
export = fastifyCookie;
|
|
172
|
+
export = fastifyCookie;
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import cookie from '
|
|
2
|
-
import { expectType } from 'tsd';
|
|
1
|
+
import cookie from '..';
|
|
2
|
+
import { expectError, expectType } from 'tsd';
|
|
3
3
|
import * as fastifyCookieStar from '..';
|
|
4
4
|
import fastifyCookieCjsImport = require('..');
|
|
5
5
|
import fastifyCookieDefault, { fastifyCookie as fastifyCookieNamed } from '..';
|
|
6
|
-
import fastify, { FastifyInstance, FastifyReply, setCookieWrapper } from 'fastify';
|
|
6
|
+
import fastify, { FastifyInstance, FastifyPluginCallback, FastifyReply, setCookieWrapper } from 'fastify';
|
|
7
|
+
import { Server } from 'http';
|
|
7
8
|
|
|
8
9
|
const fastifyCookieCjs = require('..');
|
|
9
10
|
|
|
@@ -58,6 +59,16 @@ server.after((_err) => {
|
|
|
58
59
|
.send({ hello: 'world' })
|
|
59
60
|
);
|
|
60
61
|
});
|
|
62
|
+
|
|
63
|
+
expectType<(value: string) => string>(server.signCookie)
|
|
64
|
+
expectType<(value: string) => fastifyCookieStar.UnsignResult>(server.unsignCookie)
|
|
65
|
+
|
|
66
|
+
server.get('/', (request, reply) => {
|
|
67
|
+
expectType<(value: string) => string>(request.signCookie)
|
|
68
|
+
expectType<(value: string) => string>(reply.signCookie)
|
|
69
|
+
expectType<(value: string) => fastifyCookieStar.UnsignResult>(request.unsignCookie)
|
|
70
|
+
expectType<(value: string) => fastifyCookieStar.UnsignResult>(reply.unsignCookie)
|
|
71
|
+
});
|
|
61
72
|
});
|
|
62
73
|
|
|
63
74
|
const serverWithHttp2 = fastify({ http2: true });
|
|
@@ -109,6 +120,10 @@ const appWithImplicitHttpSigned = fastify();
|
|
|
109
120
|
appWithImplicitHttpSigned.register(cookie, {
|
|
110
121
|
secret: 'testsecret',
|
|
111
122
|
});
|
|
123
|
+
appWithImplicitHttpSigned.register(cookie, {
|
|
124
|
+
secret: 'testsecret',
|
|
125
|
+
algorithm: 'sha512'
|
|
126
|
+
});
|
|
112
127
|
appWithImplicitHttpSigned.after(() => {
|
|
113
128
|
server.get('/', (request, reply) => {
|
|
114
129
|
appWithImplicitHttpSigned.unsignCookie(request.cookies.test!);
|
|
@@ -194,3 +209,20 @@ appWithCustomSigner.after(() => {
|
|
|
194
209
|
reply.send({ hello: 'world' })
|
|
195
210
|
})
|
|
196
211
|
})
|
|
212
|
+
|
|
213
|
+
new fastifyCookieStar.Signer('secretString')
|
|
214
|
+
new fastifyCookieStar.Signer(['secretStringInArray'])
|
|
215
|
+
const signer = new fastifyCookieStar.Signer(['secretStringInArray'], 'sha256')
|
|
216
|
+
signer.sign('Lorem Ipsum')
|
|
217
|
+
signer.unsign('Lorem Ipsum')
|
|
218
|
+
|
|
219
|
+
const appWithHook: FastifyInstance = fastify();
|
|
220
|
+
|
|
221
|
+
appWithHook.register(cookie, { hook: false });
|
|
222
|
+
appWithHook.register(cookie, { hook: 'onRequest' });
|
|
223
|
+
appWithHook.register(cookie, { hook: 'preHandler' });
|
|
224
|
+
appWithHook.register(cookie, { hook: 'preParsing' });
|
|
225
|
+
appWithHook.register(cookie, { hook: 'preSerialization' });
|
|
226
|
+
appWithHook.register(cookie, { hook: 'preValidation' });
|
|
227
|
+
expectError(appWithHook.register(cookie, { hook: true }));
|
|
228
|
+
expectError(appWithHook.register(cookie, { hook: 'false' }));
|