Skip to content

Commit

Permalink
Expose signer (#193)
Browse files Browse the repository at this point in the history
* feat: expose signer

* fix: code

* fix: code

* docs: fix

* Update README.md

Co-authored-by: Matteo Collina <matteo.collina@gmail.com>

* docs: fix

* refactor: add fastify.signCookie decorator

* Update test/cookie.test.js

Co-authored-by: Matteo Collina <matteo.collina@gmail.com>

* Update test/cookie.test.js

Co-authored-by: Matteo Collina <matteo.collina@gmail.com>

* Update test/cookie.test.js

Co-authored-by: Matteo Collina <matteo.collina@gmail.com>

* fix: code

* refactor: move utilities export to plugin.js

* fix: export

Co-authored-by: Matteo Collina <matteo.collina@gmail.com>
  • Loading branch information
budarin and mcollina authored Jul 15, 2022
1 parent 1aa687e commit 8da8fbe
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 2 deletions.
46 changes: 45 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ the provided signer's (or the default signer if no custom implementation is prov
fastify.register(require('@fastify/cookie'), { secret: 'my-secret' })

fastify.get('/', (req, rep) => {
if (fastify.unsign(req.cookie.foo).valid === false) {
if (fastify.unsignCookie(req.cookie.foo).valid === false) {
rep.send('cookie is invalid')
return
}
Expand All @@ -210,6 +210,50 @@ fastify.get('/', (req, rep) => {
})
```

### Other cases of manual signing

Sometimes the service under test should only accept requests with signed cookies, but it does not generate them itself.

**Example:**

```js

test('Request requires signed cookie', async () => {
const response = await app.inject({
method: 'GET',
url: '/',
headers: {
cookies : {
'sid': app.signCookie(sidValue)
}
},
});

expect(response.statusCode).toBe(200);
});
```

### Manual signing/unsigning with low level utilities

with signerFactory

```js
const { signerFactory } = require('@fastify/cookie');

const signer = signerFactory('secret');
const signedValue = signer.sign('test');
const {valid, renew, value } = signer.unsign(signedValue);
```

with sign/unsign utilities

```js
const { sign, unsign } = require('@fastify/cookie');

const signedValue = sign('test', 'secret');
const unsignedvalue = unsign(signedValue, 'secret');
```


## License

Expand Down
12 changes: 11 additions & 1 deletion plugin.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ declare module 'fastify' {
parseCookie(cookieHeader: string): {
[key: string]: string;
};
/**
* Manual cookie signing method
* @docs https://github.com/fastify/fastify-cookie#manual-cookie-parsing
* @param value cookie value
*/
signCookie(value: string): string;
}

interface FastifyRequest {
Expand Down Expand Up @@ -106,6 +112,10 @@ interface Signer {
};
}

declare const signerFactory: Signer;
declare const sign: (value: string, secret: string) => string;
declare const unsign: (input: string, secret: string) => string | false;

export interface FastifyCookieOptions {
secret?: string | string[] | Signer;
parseOptions?: CookieSerializeOptions;
Expand All @@ -114,4 +124,4 @@ export interface FastifyCookieOptions {
declare const fastifyCookie: FastifyPluginCallback<NonNullable<FastifyCookieOptions>>;

export default fastifyCookie;
export { fastifyCookie };
export { fastifyCookie, signerFactory, sign, unsign };
14 changes: 14 additions & 0 deletions plugin.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
'use strict'

const { sign, unsign } = require('cookie-signature')
const fp = require('fastify-plugin')
const cookie = require('cookie')

Expand Down Expand Up @@ -58,6 +59,7 @@ function plugin (fastify, options, next) {
const signer = typeof secret === 'string' || enableRotation ? signerFactory(secret) : secret

fastify.decorate('parseCookie', parseCookie)
fastify.decorate('signCookie', signCookie)
fastify.decorate('unsignCookie', unsignCookie)

fastify.decorateRequest('cookies', null)
Expand All @@ -76,6 +78,10 @@ function plugin (fastify, options, next) {
return cookie.parse(cookieHeader, options.parseOptions)
}

function signCookie (value) {
return signer.sign(value)
}

function unsignCookie (value) {
return signer.unsign(value)
}
Expand Down Expand Up @@ -108,3 +114,11 @@ const fastifyCookie = fp(plugin, {
fastifyCookie.fastifyCookie = fastifyCookie
fastifyCookie.default = fastifyCookie
module.exports = fastifyCookie

fastifyCookie.fastifyCookie.signerFactory = signerFactory
fastifyCookie.fastifyCookie.sign = sign
fastifyCookie.fastifyCookie.unsign = unsign

module.exports.signerFactory = signerFactory
module.exports.sign = sign
module.exports.unsign = unsign
20 changes: 20 additions & 0 deletions test/cookie.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -756,3 +756,23 @@ test('cookies set with plugin options parseOptions field', (t) => {
}
)
})

test('create signed cookie manually using signCookie decorator', async (t) => {
const fastify = Fastify()

await fastify.register(plugin, { secret: 'secret' })

fastify.get('/test1', (req, reply) => {
reply.send({
unsigned: req.unsignCookie(req.cookies.foo)
})
})

const res = await fastify.inject({
method: 'GET',
url: '/test1',
headers: { cookie: `foo=${fastify.signCookie('bar')}` }
})
t.equal(res.statusCode, 200)
t.same(JSON.parse(res.body), { unsigned: { value: 'bar', renew: false, valid: true } })
})

0 comments on commit 8da8fbe

Please sign in to comment.