@fastify/cookie 7.1.0 → 7.3.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
@@ -99,7 +99,8 @@ with a value of `'foo'` on the cookie path `/`.
99
99
  + `name`: a string name for the cookie to be set
100
100
  + `value`: a string value for the cookie
101
101
  + `options`: an options object as described in the [cookie serialize][cs] documentation
102
- with a extra param "signed" for signed cookie
102
+ + `options.signed`: the cookie should be signed
103
+ + `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.
103
104
 
104
105
  #### Securing the cookie
105
106
 
@@ -201,7 +202,7 @@ the provided signer's (or the default signer if no custom implementation is prov
201
202
  fastify.register(require('@fastify/cookie'), { secret: 'my-secret' })
202
203
 
203
204
  fastify.get('/', (req, rep) => {
204
- if (fastify.unsign(req.cookie.foo).valid === false) {
205
+ if (fastify.unsignCookie(req.cookie.foo).valid === false) {
205
206
  rep.send('cookie is invalid')
206
207
  return
207
208
  }
@@ -210,6 +211,50 @@ fastify.get('/', (req, rep) => {
210
211
  })
211
212
  ```
212
213
 
214
+ ### Other cases of manual signing
215
+
216
+ Sometimes the service under test should only accept requests with signed cookies, but it does not generate them itself.
217
+
218
+ **Example:**
219
+
220
+ ```js
221
+
222
+ test('Request requires signed cookie', async () => {
223
+ const response = await app.inject({
224
+ method: 'GET',
225
+ url: '/',
226
+ headers: {
227
+ cookies : {
228
+ 'sid': app.signCookie(sidValue)
229
+ }
230
+ },
231
+ });
232
+
233
+ expect(response.statusCode).toBe(200);
234
+ });
235
+ ```
236
+
237
+ ### Manual signing/unsigning with low level utilities
238
+
239
+ with signerFactory
240
+
241
+ ```js
242
+ const { fastifyCookie } = require('@fastify/cookie');
243
+
244
+ const signer = fastifyCookie.signerFactory('secret');
245
+ const signedValue = signer.sign('test');
246
+ const {valid, renew, value } = signer.unsign(signedValue);
247
+ ```
248
+
249
+ with sign/unsign utilities
250
+
251
+ ```js
252
+ const { fastifyCookie } = require('@fastify/cookie');
253
+
254
+ const signedValue = fastifyCookie.sign('test', 'secret');
255
+ const unsignedvalue = fastifyCookie.unsign(signedValue, 'secret');
256
+ ```
257
+
213
258
 
214
259
  ## License
215
260
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fastify/cookie",
3
- "version": "7.1.0",
3
+ "version": "7.3.1",
4
4
  "description": "Plugin for fastify to add support for cookies",
5
5
  "main": "plugin.js",
6
6
  "types": "plugin.d.ts",
@@ -52,7 +52,7 @@
52
52
  "dependencies": {
53
53
  "cookie": "^0.5.0",
54
54
  "cookie-signature": "^1.1.0",
55
- "fastify-plugin": "^3.0.1"
55
+ "fastify-plugin": "^4.0.0"
56
56
  },
57
57
  "tsd": {
58
58
  "directory": "test"
package/plugin.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  /// <reference types='node' />
2
2
 
3
- import { FastifyPluginCallback } from 'fastify';
3
+ import { FastifyPluginCallback } from "fastify";
4
4
 
5
- declare module 'fastify' {
5
+ declare module "fastify" {
6
6
  interface FastifyInstance {
7
7
  /**
8
8
  * Unsigns the specified cookie using the secret provided.
@@ -21,6 +21,12 @@ declare module 'fastify' {
21
21
  parseCookie(cookieHeader: string): {
22
22
  [key: string]: string;
23
23
  };
24
+ /**
25
+ * Manual cookie signing method
26
+ * @docs https://github.com/fastify/fastify-cookie#manual-cookie-parsing
27
+ * @param value cookie value
28
+ */
29
+ signCookie(value: string): string;
24
30
  }
25
31
 
26
32
  interface FastifyRequest {
@@ -43,7 +49,7 @@ declare module 'fastify' {
43
49
  export type setCookieWrapper = (
44
50
  name: string,
45
51
  value: string,
46
- options?: CookieSerializeOptions
52
+ options?: fastifyCookie.CookieSerializeOptions
47
53
  ) => FastifyReply;
48
54
 
49
55
  interface FastifyReply {
@@ -57,20 +63,27 @@ declare module 'fastify' {
57
63
  setCookie(
58
64
  name: string,
59
65
  value: string,
60
- options?: CookieSerializeOptions
66
+ options?: fastifyCookie.CookieSerializeOptions
61
67
  ): this;
62
68
 
63
69
  /**
64
70
  * @alias setCookie
65
71
  */
66
- cookie(name: string, value: string, options?: CookieSerializeOptions): this;
72
+ cookie(
73
+ name: string,
74
+ value: string,
75
+ options?: fastifyCookie.CookieSerializeOptions
76
+ ): this;
67
77
 
68
78
  /**
69
79
  * clear response cookie
70
80
  * @param name Cookie name
71
81
  * @param options Serialize options
72
82
  */
73
- clearCookie(name: string, options?: CookieSerializeOptions): this;
83
+ clearCookie(
84
+ name: string,
85
+ options?: fastifyCookie.CookieSerializeOptions
86
+ ): this;
74
87
 
75
88
  /**
76
89
  * Unsigns the specified cookie using the secret provided.
@@ -84,34 +97,64 @@ declare module 'fastify' {
84
97
  }
85
98
  }
86
99
 
87
- export interface CookieSerializeOptions {
88
- domain?: string;
89
- encode?(val: string): string;
90
- expires?: Date;
91
- httpOnly?: boolean;
92
- maxAge?: number;
93
- path?: string;
94
- priority?: 'low' | 'medium' | 'high';
95
- sameSite?: boolean | 'lax' | 'strict' | 'none';
96
- secure?: boolean;
97
- signed?: boolean;
98
- }
100
+ type FastifyCookiePlugin = FastifyPluginCallback<
101
+ NonNullable<fastifyCookie.FastifyCookieOptions>
102
+ >;
99
103
 
100
- interface Signer {
101
- sign: (input: string) => string;
102
- unsign: (input: string) => {
103
- valid: boolean;
104
- renew: boolean;
105
- value: string | null;
106
- };
107
- }
104
+ declare namespace fastifyCookie {
105
+ export interface Signer {
106
+ sign: (input: string) => string;
107
+ unsign: (input: string) => {
108
+ valid: boolean;
109
+ renew: boolean;
110
+ value: string | null;
111
+ };
112
+ }
113
+
114
+ export interface CookieSerializeOptions {
115
+ domain?: string;
116
+ encode?(val: string): string;
117
+ expires?: Date;
118
+ httpOnly?: boolean;
119
+ maxAge?: number;
120
+ path?: string;
121
+ priority?: "low" | "medium" | "high";
122
+ sameSite?: boolean | "lax" | "strict" | "none";
123
+ secure?: boolean;
124
+ signed?: boolean;
125
+ }
126
+
127
+ export interface FastifyCookieOptions {
128
+ secret?: string | string[] | Signer;
129
+ parseOptions?: fastifyCookie.CookieSerializeOptions;
130
+ }
131
+
132
+ export type Sign = (value: string, secret: string) => string;
133
+ export type Unsign = (input: string, secret: string) => string | false;
134
+ export type SignerFactory = (secret: string) => Signer;
135
+
136
+ export const signerFactory: SignerFactory;
137
+ export const sign: Sign;
138
+ export const unsign: Unsign;
139
+
140
+ export interface FastifyCookie extends FastifyCookiePlugin {
141
+ signerFactory: SignerFactory;
142
+ sign: Sign;
143
+ unsign: Unsign;
144
+ }
145
+
146
+ export const fastifyCookie: FastifyCookie;
147
+
148
+ export interface FastifyCookieOptions {
149
+ secret?: string | string[] | Signer;
150
+ parseOptions?: CookieSerializeOptions;
151
+ }
108
152
 
109
- export interface FastifyCookieOptions {
110
- secret?: string | string[] | Signer;
111
- parseOptions?: CookieSerializeOptions;
153
+ export { fastifyCookie as default };
112
154
  }
113
155
 
114
- declare const fastifyCookie: FastifyPluginCallback<NonNullable<FastifyCookieOptions>>;
156
+ declare function fastifyCookie(
157
+ ...params: Parameters<FastifyCookiePlugin>
158
+ ): ReturnType<FastifyCookiePlugin>;
115
159
 
116
- export default fastifyCookie;
117
- export { fastifyCookie };
160
+ export = fastifyCookie;
package/plugin.js CHANGED
@@ -1,5 +1,6 @@
1
1
  'use strict'
2
2
 
3
+ const { sign, unsign } = require('cookie-signature')
3
4
  const fp = require('fastify-plugin')
4
5
  const cookie = require('cookie')
5
6
 
@@ -15,6 +16,15 @@ function fastifyCookieSetCookie (reply, name, value, options, signer) {
15
16
  value = signer.sign(value)
16
17
  }
17
18
 
19
+ if (opts.secure === 'auto') {
20
+ if (isConnectionSecure(reply.request)) {
21
+ opts.secure = true
22
+ } else {
23
+ opts.sameSite = 'lax'
24
+ opts.secure = false
25
+ }
26
+ }
27
+
18
28
  const serialized = cookie.serialize(name, value, opts)
19
29
  let setCookie = reply.getHeader('Set-Cookie')
20
30
  if (!setCookie) {
@@ -58,6 +68,7 @@ function plugin (fastify, options, next) {
58
68
  const signer = typeof secret === 'string' || enableRotation ? signerFactory(secret) : secret
59
69
 
60
70
  fastify.decorate('parseCookie', parseCookie)
71
+ fastify.decorate('signCookie', signCookie)
61
72
  fastify.decorate('unsignCookie', unsignCookie)
62
73
 
63
74
  fastify.decorateRequest('cookies', null)
@@ -76,6 +87,10 @@ function plugin (fastify, options, next) {
76
87
  return cookie.parse(cookieHeader, options.parseOptions)
77
88
  }
78
89
 
90
+ function signCookie (value) {
91
+ return signer.sign(value)
92
+ }
93
+
79
94
  function unsignCookie (value) {
80
95
  return signer.unsign(value)
81
96
  }
@@ -90,6 +105,13 @@ function plugin (fastify, options, next) {
90
105
  }
91
106
  }
92
107
 
108
+ function isConnectionSecure (request) {
109
+ return (
110
+ request.raw.socket?.encrypted === true ||
111
+ request.headers['x-forwarded-proto'] === 'https'
112
+ )
113
+ }
114
+
93
115
  const fastifyCookie = fp(plugin, {
94
116
  fastify: '4.x',
95
117
  name: '@fastify/cookie'
@@ -108,3 +130,11 @@ const fastifyCookie = fp(plugin, {
108
130
  fastifyCookie.fastifyCookie = fastifyCookie
109
131
  fastifyCookie.default = fastifyCookie
110
132
  module.exports = fastifyCookie
133
+
134
+ fastifyCookie.fastifyCookie.signerFactory = signerFactory
135
+ fastifyCookie.fastifyCookie.sign = sign
136
+ fastifyCookie.fastifyCookie.unsign = unsign
137
+
138
+ module.exports.signerFactory = signerFactory
139
+ module.exports.sign = sign
140
+ module.exports.unsign = unsign
@@ -756,3 +756,61 @@ test('cookies set with plugin options parseOptions field', (t) => {
756
756
  }
757
757
  )
758
758
  })
759
+
760
+ test('create signed cookie manually using signCookie decorator', async (t) => {
761
+ const fastify = Fastify()
762
+
763
+ await fastify.register(plugin, { secret: 'secret' })
764
+
765
+ fastify.get('/test1', (req, reply) => {
766
+ reply.send({
767
+ unsigned: req.unsignCookie(req.cookies.foo)
768
+ })
769
+ })
770
+
771
+ const res = await fastify.inject({
772
+ method: 'GET',
773
+ url: '/test1',
774
+ headers: { cookie: `foo=${fastify.signCookie('bar')}` }
775
+ })
776
+ t.equal(res.statusCode, 200)
777
+ t.same(JSON.parse(res.body), { unsigned: { value: 'bar', renew: false, valid: true } })
778
+ })
779
+
780
+ test('handle secure:auto of cookieOptions', async (t) => {
781
+ const fastify = Fastify()
782
+
783
+ await fastify.register(plugin)
784
+
785
+ fastify.get('/test1', (req, reply) => {
786
+ reply
787
+ .setCookie('foo', 'foo', { path: '/', secure: 'auto' })
788
+ .send()
789
+ })
790
+
791
+ const res = await fastify.inject({
792
+ method: 'GET',
793
+ url: '/test1',
794
+ headers: { 'x-forwarded-proto': 'https' }
795
+ })
796
+
797
+ const cookies = res.cookies
798
+ t.equal(cookies.length, 1)
799
+ t.equal(cookies[0].name, 'foo')
800
+ t.equal(cookies[0].value, 'foo')
801
+ t.equal(cookies[0].secure, true)
802
+ t.equal(cookies[0].path, '/')
803
+
804
+ const res2 = await fastify.inject({
805
+ method: 'GET',
806
+ url: '/test1'
807
+ })
808
+
809
+ const cookies2 = res2.cookies
810
+ t.equal(cookies2.length, 1)
811
+ t.equal(cookies2[0].name, 'foo')
812
+ t.equal(cookies2[0].value, 'foo')
813
+ t.equal(cookies2[0].sameSite, 'Lax')
814
+ t.same(cookies2[0].secure, null)
815
+ t.equal(cookies2[0].path, '/')
816
+ })
@@ -1,13 +1,10 @@
1
- import fastify, { FastifyInstance, FastifyPluginCallback, FastifyReply, setCookieWrapper } from 'fastify';
2
- import { Server } from 'http';
1
+ import cookie from '../plugin';
3
2
  import { expectType } from 'tsd';
4
3
  import * as fastifyCookieStar from '..';
5
- import fastifyCookieDefault, {
6
- fastifyCookie as fastifyCookieNamed
7
- } from '..';
8
- import cookie, { FastifyCookieOptions } from '../plugin';
9
-
10
4
  import fastifyCookieCjsImport = require('..');
5
+ import fastifyCookieDefault, { fastifyCookie as fastifyCookieNamed } from '..';
6
+ import fastify, { FastifyInstance, FastifyReply, setCookieWrapper } from 'fastify';
7
+
11
8
  const fastifyCookieCjs = require('..');
12
9
 
13
10
  const app: FastifyInstance = fastify();
@@ -19,16 +16,24 @@ app.register(fastifyCookieCjsImport.fastifyCookie);
19
16
  app.register(fastifyCookieStar.default);
20
17
  app.register(fastifyCookieStar.fastifyCookie);
21
18
 
22
- expectType<FastifyPluginCallback<FastifyCookieOptions, Server>>(fastifyCookieNamed);
23
- expectType<FastifyPluginCallback<FastifyCookieOptions, Server>>(fastifyCookieDefault);
24
- expectType<FastifyPluginCallback<FastifyCookieOptions, Server>>(fastifyCookieCjsImport.default);
25
- expectType<FastifyPluginCallback<FastifyCookieOptions, Server>>(fastifyCookieCjsImport.fastifyCookie);
26
- expectType<FastifyPluginCallback<FastifyCookieOptions, Server>>(fastifyCookieStar.default);
27
- expectType<FastifyPluginCallback<FastifyCookieOptions, Server>>(
19
+ expectType<fastifyCookieStar.FastifyCookie>(fastifyCookieNamed);
20
+ expectType<fastifyCookieStar.FastifyCookie>(fastifyCookieDefault);
21
+ expectType<fastifyCookieStar.FastifyCookie>(fastifyCookieCjsImport.default);
22
+ expectType<fastifyCookieStar.FastifyCookie>(fastifyCookieCjsImport.fastifyCookie);
23
+ expectType<fastifyCookieStar.FastifyCookie>(fastifyCookieStar.default);
24
+ expectType<fastifyCookieStar.FastifyCookie>(
28
25
  fastifyCookieStar.fastifyCookie
29
26
  );
30
27
  expectType<any>(fastifyCookieCjs);
31
28
 
29
+ expectType<fastifyCookieStar.Sign>(fastifyCookieDefault.sign);
30
+ expectType<fastifyCookieStar.Unsign>(fastifyCookieDefault.unsign);
31
+ expectType<fastifyCookieStar.SignerFactory >(fastifyCookieDefault.signerFactory );
32
+
33
+ expectType<fastifyCookieStar.Sign>(fastifyCookieNamed.sign);
34
+ expectType<fastifyCookieStar.Unsign>(fastifyCookieNamed.unsign);
35
+ expectType<fastifyCookieStar.SignerFactory >(fastifyCookieNamed.signerFactory );
36
+
32
37
  const server = fastify();
33
38
 
34
39
  server.register(cookie);