@fastify/cookie 11.0.1 → 11.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/dependabot.yml +41 -1
- package/.github/workflows/ci.yml +12 -2
- package/.github/workflows/lock-threads.yml +19 -0
- package/LICENSE +1 -1
- package/README.md +36 -33
- package/benchmark/cookie-multi.js +1 -1
- package/benchmark/cookie.js +1 -1
- package/eslint.config.js +6 -0
- package/{plugin.js → index.js} +2 -2
- package/package.json +38 -24
- package/signer.js +1 -1
- package/test/cookie.test.js +504 -536
- package/test/signer.test.js +51 -64
- package/types/{plugin.d.ts → index.d.ts} +21 -26
- package/types/index.tst.ts +290 -0
- package/.github/stale.yml +0 -21
- package/.taprc +0 -2
- package/types/plugin.test-d.ts +0 -249
package/test/signer.test.js
CHANGED
|
@@ -1,138 +1,125 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const { test } = require('
|
|
3
|
+
const { beforeEach, describe, test } = require('node:test')
|
|
4
4
|
const sinon = require('sinon')
|
|
5
5
|
const crypto = require('node:crypto')
|
|
6
|
-
const { Signer, sign, unsign } = require('../signer')
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
t.plan(5)
|
|
7
|
+
const { Signer, sign, unsign } = require('../signer')
|
|
10
8
|
|
|
9
|
+
describe('default', () => {
|
|
11
10
|
const secret = 'my-secret'
|
|
12
11
|
const signer = Signer(secret)
|
|
13
12
|
|
|
14
|
-
|
|
13
|
+
test('signer.sign should throw if there is no value provided', (t) => {
|
|
15
14
|
t.plan(1)
|
|
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
|
-
|
|
23
|
-
const input = 'some-value'
|
|
24
|
-
const result = signer.sign(input)
|
|
25
|
-
|
|
26
|
-
t.equal(result, sign(input, secret))
|
|
27
|
-
t.throws(() => sign(undefined), 'Cookie value must be provided as a string.')
|
|
15
|
+
t.assert.throws(() => signer.sign(undefined), err => err.message === 'Cookie value must be provided as a string.')
|
|
28
16
|
})
|
|
29
17
|
|
|
30
|
-
|
|
18
|
+
test('sign', (t) => {
|
|
31
19
|
t.plan(5)
|
|
32
20
|
|
|
33
21
|
const input = 'some-value'
|
|
34
22
|
const result = signer.sign(input)
|
|
35
23
|
|
|
36
|
-
t.
|
|
37
|
-
t.
|
|
38
|
-
t.
|
|
39
|
-
t.
|
|
24
|
+
t.assert.strictEqual(result, sign(input, secret))
|
|
25
|
+
t.assert.strictEqual(result, sign(input, [secret]))
|
|
26
|
+
t.assert.strictEqual(result, sign(input, Buffer.from(secret)))
|
|
27
|
+
t.assert.strictEqual(result, sign(input, [Buffer.from(secret)]))
|
|
40
28
|
|
|
41
|
-
t.throws(() => sign(undefined), '
|
|
29
|
+
t.assert.throws(() => sign(undefined), err => err.message === 'Secret key must be a string or Buffer.')
|
|
42
30
|
})
|
|
43
31
|
|
|
44
|
-
|
|
32
|
+
test('signer.unsign', (t) => {
|
|
45
33
|
t.plan(4)
|
|
46
34
|
|
|
47
35
|
const input = signer.sign('some-value', secret)
|
|
48
36
|
const result = signer.unsign(input)
|
|
49
37
|
|
|
50
|
-
t.
|
|
51
|
-
t.
|
|
52
|
-
t.
|
|
53
|
-
t.throws(() => signer.unsign(undefined), 'Signed cookie string must be provided.')
|
|
38
|
+
t.assert.strictEqual(result.valid, true)
|
|
39
|
+
t.assert.strictEqual(result.renew, false)
|
|
40
|
+
t.assert.strictEqual(result.value, 'some-value')
|
|
41
|
+
t.assert.throws(() => signer.unsign(undefined), err => err.message === 'Signed cookie string must be provided.')
|
|
54
42
|
})
|
|
55
43
|
|
|
56
|
-
|
|
44
|
+
test('unsign', (t) => {
|
|
57
45
|
t.plan(6)
|
|
58
46
|
|
|
59
47
|
const input = sign('some-value', secret)
|
|
60
48
|
const result = unsign(input, secret)
|
|
61
49
|
|
|
62
|
-
t.
|
|
63
|
-
t.
|
|
64
|
-
t.
|
|
65
|
-
t.
|
|
66
|
-
t.throws(() => unsign(undefined), 'Secret key must be a string or Buffer.')
|
|
67
|
-
t.throws(() => unsign(undefined, secret), 'Signed cookie string must be provided.')
|
|
50
|
+
t.assert.strictEqual(result.valid, true)
|
|
51
|
+
t.assert.strictEqual(result.renew, false)
|
|
52
|
+
t.assert.strictEqual(result.value, 'some-value')
|
|
53
|
+
t.assert.deepStrictEqual(result, unsign(input, [secret]))
|
|
54
|
+
t.assert.throws(() => unsign(undefined), err => err.message === 'Secret key must be a string or Buffer.')
|
|
55
|
+
t.assert.throws(() => unsign(undefined, secret), err => err.message === 'Signed cookie string must be provided.')
|
|
68
56
|
})
|
|
69
57
|
})
|
|
70
58
|
|
|
71
|
-
|
|
72
|
-
t.plan(3)
|
|
59
|
+
describe('key rotation', () => {
|
|
73
60
|
const secret1 = 'my-secret-1'
|
|
74
61
|
const secret2 = 'my-secret-2'
|
|
75
62
|
const secret3 = 'my-secret-3'
|
|
76
63
|
const signer = Signer([secret1, secret2, secret3])
|
|
77
64
|
const signSpy = sinon.spy(crypto, 'createHmac')
|
|
78
65
|
|
|
79
|
-
|
|
66
|
+
beforeEach(() => {
|
|
80
67
|
signSpy.resetHistory()
|
|
81
68
|
})
|
|
82
69
|
|
|
83
|
-
|
|
70
|
+
test('signer.sign always signs using first key', (t) => {
|
|
84
71
|
t.plan(1)
|
|
85
72
|
|
|
86
73
|
const input = 'some-value'
|
|
87
74
|
const result = signer.sign(input)
|
|
88
75
|
|
|
89
|
-
t.
|
|
76
|
+
t.assert.strictEqual(result, sign(input, secret1))
|
|
90
77
|
})
|
|
91
78
|
|
|
92
|
-
|
|
79
|
+
test('signer.unsign tries to decode using all keys till it finds', (t) => {
|
|
93
80
|
t.plan(4)
|
|
94
81
|
|
|
95
82
|
const input = sign('some-value', secret2)
|
|
96
83
|
signSpy.resetHistory()
|
|
97
84
|
const result = signer.unsign(input)
|
|
98
85
|
|
|
99
|
-
t.
|
|
100
|
-
t.
|
|
101
|
-
t.
|
|
102
|
-
t.
|
|
86
|
+
t.assert.strictEqual(result.valid, true)
|
|
87
|
+
t.assert.strictEqual(result.renew, true)
|
|
88
|
+
t.assert.strictEqual(result.value, 'some-value')
|
|
89
|
+
t.assert.strictEqual(signSpy.callCount, 2) // should have returned early when the right key was found
|
|
103
90
|
})
|
|
104
91
|
|
|
105
|
-
|
|
92
|
+
test('signer.unsign failure response', (t) => {
|
|
106
93
|
t.plan(4)
|
|
107
94
|
|
|
108
95
|
const input = sign('some-value', 'invalid-secret')
|
|
109
96
|
signSpy.resetHistory()
|
|
110
97
|
const result = signer.unsign(input)
|
|
111
98
|
|
|
112
|
-
t.
|
|
113
|
-
t.
|
|
114
|
-
t.
|
|
115
|
-
t.
|
|
99
|
+
t.assert.strictEqual(result.valid, false)
|
|
100
|
+
t.assert.strictEqual(result.renew, false)
|
|
101
|
+
t.assert.strictEqual(result.value, null)
|
|
102
|
+
t.assert.strictEqual(signSpy.callCount, 3) // should have tried all 3
|
|
116
103
|
})
|
|
117
104
|
})
|
|
118
105
|
|
|
119
|
-
|
|
120
|
-
t
|
|
121
|
-
|
|
122
|
-
t.test('Signer needs a string or Buffer as secret', (t) => {
|
|
106
|
+
describe('Signer', () => {
|
|
107
|
+
test('Signer needs a string or Buffer as secret', (t) => {
|
|
123
108
|
t.plan(6)
|
|
124
|
-
|
|
125
|
-
t.throws(() => Signer(
|
|
126
|
-
t.
|
|
127
|
-
t.doesNotThrow(() => Signer(
|
|
128
|
-
t.doesNotThrow(() => Signer(
|
|
129
|
-
t.doesNotThrow(() => Signer(
|
|
109
|
+
|
|
110
|
+
t.assert.throws(() => Signer(1), err => err.message === 'Secret key must be a string or Buffer.')
|
|
111
|
+
t.assert.throws(() => Signer(undefined), err => err.message === 'Secret key must be a string or Buffer.')
|
|
112
|
+
t.assert.doesNotThrow(() => Signer('secret'))
|
|
113
|
+
t.assert.doesNotThrow(() => Signer(['secret']))
|
|
114
|
+
t.assert.doesNotThrow(() => Signer(Buffer.from('deadbeef76543210', 'hex')))
|
|
115
|
+
t.assert.doesNotThrow(() => Signer([Buffer.from('deadbeef76543210', 'hex')]))
|
|
130
116
|
})
|
|
131
117
|
|
|
132
|
-
|
|
118
|
+
test('Signer handles algorithm properly', (t) => {
|
|
133
119
|
t.plan(3)
|
|
134
|
-
|
|
135
|
-
t.
|
|
136
|
-
t.doesNotThrow(() => Signer('secret', '
|
|
120
|
+
|
|
121
|
+
t.assert.throws(() => Signer('secret', 'invalid'), err => err.message === 'Algorithm invalid not supported.')
|
|
122
|
+
t.assert.doesNotThrow(() => Signer('secret', 'sha512'))
|
|
123
|
+
t.assert.doesNotThrow(() => Signer('secret', 'sha256'))
|
|
137
124
|
})
|
|
138
125
|
})
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/// <reference types='node' />
|
|
2
2
|
|
|
3
|
-
import { FastifyPluginCallback } from
|
|
3
|
+
import { FastifyPluginCallback } from 'fastify'
|
|
4
4
|
|
|
5
|
-
declare module
|
|
5
|
+
declare module 'fastify' {
|
|
6
6
|
interface FastifyInstance extends SignerMethods {
|
|
7
7
|
/**
|
|
8
8
|
* Serialize a cookie name-value pair into a Set-Cookie header string
|
|
@@ -42,7 +42,7 @@ declare module "fastify" {
|
|
|
42
42
|
* Signs the specified cookie using the secret/signer provided.
|
|
43
43
|
* @param value cookie value
|
|
44
44
|
*/
|
|
45
|
-
|
|
45
|
+
signCookie(value: string): string;
|
|
46
46
|
|
|
47
47
|
/**
|
|
48
48
|
* Unsigns the specified cookie using the secret/signer provided.
|
|
@@ -55,7 +55,7 @@ declare module "fastify" {
|
|
|
55
55
|
name: string,
|
|
56
56
|
value: string,
|
|
57
57
|
options?: fastifyCookie.CookieSerializeOptions
|
|
58
|
-
) => FastifyReply
|
|
58
|
+
) => FastifyReply
|
|
59
59
|
|
|
60
60
|
interface FastifyReply {
|
|
61
61
|
/**
|
|
@@ -96,7 +96,7 @@ declare module "fastify" {
|
|
|
96
96
|
|
|
97
97
|
type FastifyCookiePlugin = FastifyPluginCallback<
|
|
98
98
|
NonNullable<fastifyCookie.FastifyCookieOptions>
|
|
99
|
-
|
|
99
|
+
>
|
|
100
100
|
|
|
101
101
|
declare namespace fastifyCookie {
|
|
102
102
|
interface SignerBase {
|
|
@@ -106,8 +106,8 @@ declare namespace fastifyCookie {
|
|
|
106
106
|
|
|
107
107
|
export class Signer implements SignerBase {
|
|
108
108
|
constructor (secrets: string | Array<string> | Buffer | Array<Buffer>, algorithm?: string)
|
|
109
|
-
sign: (value: string) => string
|
|
110
|
-
unsign: (input: string) => UnsignResult
|
|
109
|
+
sign: (value: string) => string
|
|
110
|
+
unsign: (input: string) => UnsignResult
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
export interface SerializeOptions {
|
|
@@ -143,17 +143,11 @@ declare namespace fastifyCookie {
|
|
|
143
143
|
decode?: (encodedURIComponent: string) => string;
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
-
type HookType = 'onRequest' | 'preParsing' | 'preValidation' | 'preHandler'
|
|
146
|
+
type HookType = 'onRequest' | 'preParsing' | 'preValidation' | 'preHandler' | 'preSerialization'
|
|
147
147
|
|
|
148
|
-
export
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
parseOptions?: fastifyCookie.CookieSerializeOptions;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
export type Sign = (value: string, secret: string | Buffer, algorithm?: string) => string;
|
|
155
|
-
export type Unsign = (input: string, secret: string | Buffer, algorithm?: string) => UnsignResult;
|
|
156
|
-
export type SignerFactory = (secrets: string | string[] | Buffer | Buffer[], algorithm?: string) => SignerBase;
|
|
148
|
+
export type Sign = (value: string, secret: string | Buffer, algorithm?: string) => string
|
|
149
|
+
export type Unsign = (input: string, secret: string | Buffer, algorithm?: string) => UnsignResult
|
|
150
|
+
export type SignerFactory = (secrets: string | string[] | Buffer | Buffer[], algorithm?: string) => SignerBase
|
|
157
151
|
|
|
158
152
|
export type UnsignResult = {
|
|
159
153
|
valid: true;
|
|
@@ -165,9 +159,9 @@ declare namespace fastifyCookie {
|
|
|
165
159
|
value: null;
|
|
166
160
|
}
|
|
167
161
|
|
|
168
|
-
export const signerFactory: SignerFactory
|
|
169
|
-
export const sign: Sign
|
|
170
|
-
export const unsign: Unsign
|
|
162
|
+
export const signerFactory: SignerFactory
|
|
163
|
+
export const sign: Sign
|
|
164
|
+
export const unsign: Unsign
|
|
171
165
|
|
|
172
166
|
export interface FastifyCookie extends FastifyCookiePlugin {
|
|
173
167
|
parse: (cookieHeader: string, opts?: ParseOptions) => { [key: string]: string };
|
|
@@ -178,19 +172,20 @@ declare namespace fastifyCookie {
|
|
|
178
172
|
unsign: Unsign;
|
|
179
173
|
}
|
|
180
174
|
|
|
181
|
-
export const fastifyCookie: FastifyCookie
|
|
175
|
+
export const fastifyCookie: FastifyCookie
|
|
182
176
|
|
|
183
177
|
export interface FastifyCookieOptions {
|
|
184
|
-
secret?: string | string[] | Buffer | Buffer[] | SignerBase;
|
|
178
|
+
secret?: string | string[] | Buffer | Buffer[] | Signer | SignerBase;
|
|
185
179
|
algorithm?: string;
|
|
180
|
+
hook?: HookType | false;
|
|
186
181
|
parseOptions?: CookieSerializeOptions;
|
|
187
182
|
}
|
|
188
183
|
|
|
189
|
-
export { fastifyCookie as default }
|
|
184
|
+
export { fastifyCookie as default }
|
|
190
185
|
}
|
|
191
186
|
|
|
192
|
-
declare function fastifyCookie(
|
|
187
|
+
declare function fastifyCookie (
|
|
193
188
|
...params: Parameters<FastifyCookiePlugin>
|
|
194
|
-
): ReturnType<FastifyCookiePlugin
|
|
189
|
+
): ReturnType<FastifyCookiePlugin>
|
|
195
190
|
|
|
196
|
-
export = fastifyCookie
|
|
191
|
+
export = fastifyCookie
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
import { expect } from 'tstyche'
|
|
2
|
+
import * as fastifyCookieStar from '..'
|
|
3
|
+
import fastifyCookieCjsImport = require('..')
|
|
4
|
+
import cookie, { fastifyCookie as fastifyCookieNamed, Signer } from '..'
|
|
5
|
+
import fastify, {
|
|
6
|
+
type FastifyInstance,
|
|
7
|
+
type FastifyReply,
|
|
8
|
+
type setCookieWrapper
|
|
9
|
+
} from 'fastify'
|
|
10
|
+
|
|
11
|
+
const fastifyCookieCjs = require('..')
|
|
12
|
+
|
|
13
|
+
const app: FastifyInstance = fastify()
|
|
14
|
+
app.register(fastifyCookieNamed)
|
|
15
|
+
app.register(cookie)
|
|
16
|
+
app.register(fastifyCookieCjs)
|
|
17
|
+
app.register(fastifyCookieCjsImport.default)
|
|
18
|
+
app.register(fastifyCookieCjsImport.fastifyCookie)
|
|
19
|
+
app.register(fastifyCookieStar.default)
|
|
20
|
+
app.register(fastifyCookieStar.fastifyCookie)
|
|
21
|
+
|
|
22
|
+
expect(fastifyCookieNamed).type.toBe<fastifyCookieStar.FastifyCookie>()
|
|
23
|
+
expect(cookie).type.toBe<fastifyCookieStar.FastifyCookie>()
|
|
24
|
+
expect(
|
|
25
|
+
fastifyCookieCjsImport.default
|
|
26
|
+
).type.toBe<fastifyCookieStar.FastifyCookie>()
|
|
27
|
+
expect(
|
|
28
|
+
fastifyCookieCjsImport.fastifyCookie
|
|
29
|
+
).type.toBe<fastifyCookieStar.FastifyCookie>()
|
|
30
|
+
expect(fastifyCookieStar.default).type.toBe<fastifyCookieStar.FastifyCookie>()
|
|
31
|
+
expect(
|
|
32
|
+
fastifyCookieStar.fastifyCookie
|
|
33
|
+
).type.toBe<fastifyCookieStar.FastifyCookie>()
|
|
34
|
+
expect(fastifyCookieCjs).type.toBe<any>()
|
|
35
|
+
|
|
36
|
+
expect(cookie.sign).type.toBe<fastifyCookieStar.Sign>()
|
|
37
|
+
expect(cookie.unsign).type.toBe<fastifyCookieStar.Unsign>()
|
|
38
|
+
expect(cookie.signerFactory).type.toBe<fastifyCookieStar.SignerFactory>()
|
|
39
|
+
|
|
40
|
+
expect(fastifyCookieNamed.sign).type.toBe<fastifyCookieStar.Sign>()
|
|
41
|
+
expect(fastifyCookieNamed.unsign).type.toBe<fastifyCookieStar.Unsign>()
|
|
42
|
+
expect(
|
|
43
|
+
fastifyCookieNamed.signerFactory
|
|
44
|
+
).type.toBe<fastifyCookieStar.SignerFactory>()
|
|
45
|
+
|
|
46
|
+
const server = fastify()
|
|
47
|
+
|
|
48
|
+
server.register(cookie)
|
|
49
|
+
|
|
50
|
+
server.after((_err) => {
|
|
51
|
+
expect(
|
|
52
|
+
server.serializeCookie('sessionId', 'aYb4uTIhdBXC')
|
|
53
|
+
).type.toBe<string>()
|
|
54
|
+
|
|
55
|
+
expect(server.parseCookie('sessionId=aYb4uTIhdBXC')).type.toBe<{
|
|
56
|
+
[key: string]: string;
|
|
57
|
+
}>()
|
|
58
|
+
|
|
59
|
+
server.get('/', (request, reply) => {
|
|
60
|
+
const test = request.cookies.test
|
|
61
|
+
expect(test).type.toBe<string | undefined>()
|
|
62
|
+
|
|
63
|
+
expect(reply.cookie).type.toBe<setCookieWrapper>()
|
|
64
|
+
expect(reply.setCookie).type.toBe<setCookieWrapper>()
|
|
65
|
+
|
|
66
|
+
expect(
|
|
67
|
+
reply
|
|
68
|
+
.setCookie('test', test!, { domain: 'example.com', path: '/' })
|
|
69
|
+
.clearCookie('foo')
|
|
70
|
+
.send({ hello: 'world' })
|
|
71
|
+
).type.toBe<FastifyReply>()
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
expect(server.signCookie).type.toBe<(value: string) => string>()
|
|
75
|
+
expect(server.unsignCookie).type.toBe<
|
|
76
|
+
(value: string) => fastifyCookieStar.UnsignResult
|
|
77
|
+
>()
|
|
78
|
+
|
|
79
|
+
server.get('/', (request, reply) => {
|
|
80
|
+
expect(request.signCookie).type.toBe<(value: string) => string>()
|
|
81
|
+
expect(reply.signCookie).type.toBe<(value: string) => string>()
|
|
82
|
+
expect(request.unsignCookie).type.toBe<
|
|
83
|
+
(value: string) => fastifyCookieStar.UnsignResult
|
|
84
|
+
>()
|
|
85
|
+
expect(reply.unsignCookie).type.toBe<
|
|
86
|
+
(value: string) => fastifyCookieStar.UnsignResult
|
|
87
|
+
>()
|
|
88
|
+
})
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
const serverWithHttp2 = fastify({ http2: true })
|
|
92
|
+
|
|
93
|
+
serverWithHttp2.register(cookie)
|
|
94
|
+
|
|
95
|
+
serverWithHttp2.after(() => {
|
|
96
|
+
serverWithHttp2.get('/', (request, reply) => {
|
|
97
|
+
const test = request.cookies.test
|
|
98
|
+
reply
|
|
99
|
+
.setCookie('test', test!, { domain: 'example.com', path: '/' })
|
|
100
|
+
.clearCookie('foo')
|
|
101
|
+
.send({ hello: 'world' })
|
|
102
|
+
})
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
const testSamesiteOptionsApp = fastify()
|
|
106
|
+
|
|
107
|
+
testSamesiteOptionsApp.register(cookie)
|
|
108
|
+
testSamesiteOptionsApp.after(() => {
|
|
109
|
+
server.get('/test-samesite-option-true', (request, reply) => {
|
|
110
|
+
const test = request.cookies.test
|
|
111
|
+
reply.setCookie('test', test!, { sameSite: true }).send({ hello: 'world' })
|
|
112
|
+
})
|
|
113
|
+
server.get('/test-samesite-option-false', (request, reply) => {
|
|
114
|
+
const test = request.cookies.test
|
|
115
|
+
reply
|
|
116
|
+
.setCookie('test', test!, { sameSite: false })
|
|
117
|
+
.send({ hello: 'world' })
|
|
118
|
+
})
|
|
119
|
+
server.get('/test-samesite-option-lax', (request, reply) => {
|
|
120
|
+
const test = request.cookies.test
|
|
121
|
+
reply
|
|
122
|
+
.setCookie('test', test!, { sameSite: 'lax' })
|
|
123
|
+
.send({ hello: 'world' })
|
|
124
|
+
})
|
|
125
|
+
server.get('/test-samesite-option-strict', (request, reply) => {
|
|
126
|
+
const test = request.cookies.test
|
|
127
|
+
reply
|
|
128
|
+
.setCookie('test', test!, { sameSite: 'strict' })
|
|
129
|
+
.send({ hello: 'world' })
|
|
130
|
+
})
|
|
131
|
+
server.get('/test-samesite-option-none', (request, reply) => {
|
|
132
|
+
const test = request.cookies.test
|
|
133
|
+
reply
|
|
134
|
+
.setCookie('test', test!, { sameSite: 'none' })
|
|
135
|
+
.send({ hello: 'world' })
|
|
136
|
+
})
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
const appWithImplicitHttpSigned = fastify()
|
|
140
|
+
|
|
141
|
+
appWithImplicitHttpSigned.register(cookie, {
|
|
142
|
+
secret: 'testsecret'
|
|
143
|
+
})
|
|
144
|
+
appWithImplicitHttpSigned.register(cookie, {
|
|
145
|
+
secret: 'testsecret',
|
|
146
|
+
algorithm: 'sha512'
|
|
147
|
+
})
|
|
148
|
+
appWithImplicitHttpSigned.after(() => {
|
|
149
|
+
server.get('/', (request, reply) => {
|
|
150
|
+
appWithImplicitHttpSigned.unsignCookie(request.cookies.test!)
|
|
151
|
+
appWithImplicitHttpSigned.unsignCookie('test')
|
|
152
|
+
|
|
153
|
+
reply.unsignCookie(request.cookies.test!)
|
|
154
|
+
reply.unsignCookie('test')
|
|
155
|
+
|
|
156
|
+
request.unsignCookie(request.cookies.anotherTest!)
|
|
157
|
+
request.unsignCookie('anotherTest')
|
|
158
|
+
|
|
159
|
+
reply.send({ hello: 'world' })
|
|
160
|
+
})
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
const appWithRotationSecret = fastify()
|
|
164
|
+
|
|
165
|
+
appWithRotationSecret.register(cookie, {
|
|
166
|
+
secret: ['testsecret']
|
|
167
|
+
})
|
|
168
|
+
appWithRotationSecret.after(() => {
|
|
169
|
+
server.get('/', (request, reply) => {
|
|
170
|
+
reply.unsignCookie(request.cookies.test!)
|
|
171
|
+
const unsigned = reply.unsignCookie('test')
|
|
172
|
+
|
|
173
|
+
expect(unsigned.valid).type.toBe<boolean>()
|
|
174
|
+
if (unsigned.valid) {
|
|
175
|
+
expect(unsigned.value).type.toBe<string>()
|
|
176
|
+
} else {
|
|
177
|
+
expect(unsigned.value).type.toBe<null>()
|
|
178
|
+
}
|
|
179
|
+
expect(unsigned.renew).type.toBe<boolean>()
|
|
180
|
+
|
|
181
|
+
reply.send({ hello: 'world' })
|
|
182
|
+
})
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
const appWithParseOptions = fastify()
|
|
186
|
+
|
|
187
|
+
const parseOptions: fastifyCookieStar.CookieSerializeOptions = {
|
|
188
|
+
domain: 'example.com',
|
|
189
|
+
encode: (value: string) => value,
|
|
190
|
+
expires: new Date(),
|
|
191
|
+
httpOnly: true,
|
|
192
|
+
maxAge: 3600,
|
|
193
|
+
path: '/',
|
|
194
|
+
sameSite: 'lax',
|
|
195
|
+
secure: true,
|
|
196
|
+
signed: true,
|
|
197
|
+
partitioned: false
|
|
198
|
+
}
|
|
199
|
+
expect(parseOptions).type.toBe<fastifyCookieStar.CookieSerializeOptions>()
|
|
200
|
+
|
|
201
|
+
appWithParseOptions.register(cookie, {
|
|
202
|
+
secret: 'testsecret',
|
|
203
|
+
parseOptions
|
|
204
|
+
})
|
|
205
|
+
appWithParseOptions.after(() => {
|
|
206
|
+
server.get('/', (request, reply) => {
|
|
207
|
+
const unsigned = reply.unsignCookie(request.cookies.test!)
|
|
208
|
+
|
|
209
|
+
expect(unsigned.valid).type.toBe<boolean>()
|
|
210
|
+
if (unsigned.valid) {
|
|
211
|
+
expect(unsigned.value).type.toBe<string>()
|
|
212
|
+
} else {
|
|
213
|
+
expect(unsigned.value).type.toBe<null>()
|
|
214
|
+
}
|
|
215
|
+
expect(unsigned.renew).type.toBe<boolean>()
|
|
216
|
+
})
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
const appWithCustomSigner = fastify()
|
|
220
|
+
|
|
221
|
+
appWithCustomSigner.register(cookie, {
|
|
222
|
+
secret: {
|
|
223
|
+
sign: (x) => x + '.signed',
|
|
224
|
+
unsign: (x) => {
|
|
225
|
+
if (x.endsWith('.signed')) {
|
|
226
|
+
return { renew: false, valid: true, value: x.slice(0, -7) }
|
|
227
|
+
}
|
|
228
|
+
return { renew: false, valid: false, value: null }
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
})
|
|
232
|
+
appWithCustomSigner.after(() => {
|
|
233
|
+
server.get('/', (request, reply) => {
|
|
234
|
+
reply.unsignCookie(request.cookies.test!)
|
|
235
|
+
const unsigned = reply.unsignCookie('test')
|
|
236
|
+
|
|
237
|
+
expect(unsigned.valid).type.toBe<boolean>()
|
|
238
|
+
if (unsigned.valid) {
|
|
239
|
+
expect(unsigned.value).type.toBe<string>()
|
|
240
|
+
} else {
|
|
241
|
+
expect(unsigned.value).type.toBe<null>()
|
|
242
|
+
}
|
|
243
|
+
expect(unsigned.renew).type.toBe<boolean>()
|
|
244
|
+
|
|
245
|
+
reply.send({ hello: 'world' })
|
|
246
|
+
})
|
|
247
|
+
})
|
|
248
|
+
|
|
249
|
+
expect(new fastifyCookieStar.Signer('secretString')).type.toBe<Signer>()
|
|
250
|
+
expect(
|
|
251
|
+
new fastifyCookieStar.Signer(['secretStringInArray'])
|
|
252
|
+
).type.toBe<Signer>()
|
|
253
|
+
expect(
|
|
254
|
+
new fastifyCookieStar.Signer(Buffer.from('secretString'))
|
|
255
|
+
).type.toBe<Signer>()
|
|
256
|
+
expect(
|
|
257
|
+
new fastifyCookieStar.Signer([Buffer.from('secretStringInArray')])
|
|
258
|
+
).type.toBe<Signer>()
|
|
259
|
+
|
|
260
|
+
const signer = new fastifyCookieStar.Signer(['secretStringInArray'], 'sha256')
|
|
261
|
+
signer.sign('Lorem Ipsum')
|
|
262
|
+
signer.unsign('Lorem Ipsum')
|
|
263
|
+
|
|
264
|
+
const appWithHook: FastifyInstance = fastify()
|
|
265
|
+
|
|
266
|
+
appWithHook.register(cookie, { hook: false })
|
|
267
|
+
appWithHook.register(cookie, { hook: 'onRequest' })
|
|
268
|
+
appWithHook.register(cookie, { hook: 'preHandler' })
|
|
269
|
+
appWithHook.register(cookie, { hook: 'preParsing' })
|
|
270
|
+
appWithHook.register(cookie, { hook: 'preSerialization' })
|
|
271
|
+
appWithHook.register(cookie, { hook: 'preValidation' })
|
|
272
|
+
|
|
273
|
+
expect(appWithHook.register)
|
|
274
|
+
.type.not.toBeCallableWith(cookie, { hook: true })
|
|
275
|
+
expect(appWithHook.register)
|
|
276
|
+
.type.not.toBeCallableWith(cookie, { hook: 'false' })
|
|
277
|
+
|
|
278
|
+
expect(cookie.parse).type.toBe<
|
|
279
|
+
(
|
|
280
|
+
cookieHeader: string,
|
|
281
|
+
opts?: fastifyCookieStar.ParseOptions
|
|
282
|
+
) => { [key: string]: string }
|
|
283
|
+
>()
|
|
284
|
+
expect(cookie.serialize).type.toBe<
|
|
285
|
+
(
|
|
286
|
+
name: string,
|
|
287
|
+
value: string,
|
|
288
|
+
opts?: fastifyCookieStar.SerializeOptions
|
|
289
|
+
) => string
|
|
290
|
+
>()
|
package/.github/stale.yml
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
# Number of days of inactivity before an issue becomes stale
|
|
2
|
-
daysUntilStale: 15
|
|
3
|
-
# Number of days of inactivity before a stale issue is closed
|
|
4
|
-
daysUntilClose: 7
|
|
5
|
-
# Issues with these labels will never be considered stale
|
|
6
|
-
exemptLabels:
|
|
7
|
-
- "discussion"
|
|
8
|
-
- "feature request"
|
|
9
|
-
- "bug"
|
|
10
|
-
- "help wanted"
|
|
11
|
-
- "plugin suggestion"
|
|
12
|
-
- "good first issue"
|
|
13
|
-
# Label to use when marking an issue as stale
|
|
14
|
-
staleLabel: stale
|
|
15
|
-
# Comment to post when marking an issue as stale. Set to `false` to disable
|
|
16
|
-
markComment: >
|
|
17
|
-
This issue has been automatically marked as stale because it has not had
|
|
18
|
-
recent activity. It will be closed if no further activity occurs. Thank you
|
|
19
|
-
for your contributions.
|
|
20
|
-
# Comment to post when closing a stale issue. Set to `false` to disable
|
|
21
|
-
closeComment: false
|
package/.taprc
DELETED