You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
144 lines
4.0 KiB
144 lines
4.0 KiB
3 years ago
|
'use strict'
|
||
|
|
||
|
const fp = require('fastify-plugin')
|
||
|
const cookie = require('cookie')
|
||
|
|
||
|
const { Signer, sign, unsign } = require('./signer')
|
||
|
|
||
|
function fastifyCookieSetCookie (reply, name, value, options, signer) {
|
||
|
const opts = Object.assign({}, options)
|
||
|
if (opts.expires && Number.isInteger(opts.expires)) {
|
||
|
opts.expires = new Date(opts.expires)
|
||
|
}
|
||
|
|
||
|
if (opts.signed) {
|
||
|
value = signer.sign(value)
|
||
|
}
|
||
|
|
||
|
if (opts.secure === 'auto') {
|
||
|
if (isConnectionSecure(reply.request)) {
|
||
|
opts.secure = true
|
||
|
} else {
|
||
|
opts.sameSite = 'lax'
|
||
|
opts.secure = false
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const serialized = cookie.serialize(name, value, opts)
|
||
|
let setCookie = reply.getHeader('Set-Cookie')
|
||
|
if (!setCookie) {
|
||
|
reply.header('Set-Cookie', serialized)
|
||
|
return reply
|
||
|
}
|
||
|
|
||
|
if (typeof setCookie === 'string') {
|
||
|
setCookie = [setCookie]
|
||
|
}
|
||
|
|
||
|
setCookie.push(serialized)
|
||
|
reply.removeHeader('Set-Cookie')
|
||
|
reply.header('Set-Cookie', setCookie)
|
||
|
return reply
|
||
|
}
|
||
|
|
||
|
function fastifyCookieClearCookie (reply, name, options) {
|
||
|
const opts = Object.assign({ path: '/' }, options || { }, {
|
||
|
expires: new Date(1),
|
||
|
signed: undefined,
|
||
|
maxAge: undefined
|
||
|
})
|
||
|
return fastifyCookieSetCookie(reply, name, '', opts)
|
||
|
}
|
||
|
|
||
|
function onReqHandlerWrapper (fastify) {
|
||
|
return function fastifyCookieOnReqHandler (fastifyReq, fastifyRes, done) {
|
||
|
fastifyReq.cookies = {} // New container per request. Issue #53
|
||
|
const cookieHeader = fastifyReq.raw.headers.cookie
|
||
|
if (cookieHeader) {
|
||
|
fastifyReq.cookies = fastify.parseCookie(cookieHeader)
|
||
|
}
|
||
|
done()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function plugin (fastify, options, next) {
|
||
|
const secret = options.secret || ''
|
||
|
const enableRotation = Array.isArray(secret)
|
||
|
const algorithm = options.algorithm || 'sha256'
|
||
|
const signer = typeof secret === 'string' || enableRotation ? new Signer(secret, algorithm) : secret
|
||
|
|
||
|
fastify.decorate('parseCookie', parseCookie)
|
||
|
fastify.decorate('signCookie', signCookie)
|
||
|
fastify.decorate('unsignCookie', unsignCookie)
|
||
|
|
||
|
fastify.decorateRequest('cookies', null)
|
||
|
fastify.decorateRequest('unsignCookie', unsignCookie)
|
||
|
fastify.decorateReply('setCookie', setCookie)
|
||
|
fastify.decorateReply('cookie', setCookie)
|
||
|
fastify.decorateReply('clearCookie', clearCookie)
|
||
|
fastify.decorateReply('unsignCookie', unsignCookie)
|
||
|
|
||
|
fastify.addHook('onRequest', onReqHandlerWrapper(fastify))
|
||
|
|
||
|
next()
|
||
|
|
||
|
// ***************************
|
||
|
function parseCookie (cookieHeader) {
|
||
|
return cookie.parse(cookieHeader, options.parseOptions)
|
||
|
}
|
||
|
|
||
|
function signCookie (value) {
|
||
|
return signer.sign(value)
|
||
|
}
|
||
|
|
||
|
function unsignCookie (value) {
|
||
|
return signer.unsign(value)
|
||
|
}
|
||
|
|
||
|
function setCookie (name, value, cookieOptions) {
|
||
|
const opts = Object.assign({}, options.parseOptions, cookieOptions)
|
||
|
return fastifyCookieSetCookie(this, name, value, opts, signer)
|
||
|
}
|
||
|
|
||
|
function clearCookie (name, options) {
|
||
|
return fastifyCookieClearCookie(this, name, options)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function isConnectionSecure (request) {
|
||
|
return (
|
||
|
request.raw.socket?.encrypted === true ||
|
||
|
request.headers['x-forwarded-proto'] === 'https'
|
||
|
)
|
||
|
}
|
||
|
|
||
|
const fastifyCookie = fp(plugin, {
|
||
|
fastify: '4.x',
|
||
|
name: '@fastify/cookie'
|
||
|
})
|
||
|
|
||
|
/**
|
||
|
* These export configurations enable JS and TS developers
|
||
|
* to consume fastify-cookie in whatever way best suits their needs.
|
||
|
* Some examples of supported import syntax includes:
|
||
|
* - `const fastifyCookie = require('fastify-cookie')`
|
||
|
* - `const { fastifyCookie } = require('fastify-cookie')`
|
||
|
* - `import * as fastifyCookie from 'fastify-cookie'`
|
||
|
* - `import { fastifyCookie } from 'fastify-cookie'`
|
||
|
* - `import fastifyCookie from 'fastify-cookie'`
|
||
|
*/
|
||
|
fastifyCookie.signerFactory = Signer
|
||
|
fastifyCookie.fastifyCookie = fastifyCookie
|
||
|
fastifyCookie.default = fastifyCookie
|
||
|
module.exports = fastifyCookie
|
||
|
|
||
|
fastifyCookie.fastifyCookie.signerFactory = Signer
|
||
|
fastifyCookie.fastifyCookie.Signer = Signer
|
||
|
fastifyCookie.fastifyCookie.sign = sign
|
||
|
fastifyCookie.fastifyCookie.unsign = unsign
|
||
|
|
||
|
module.exports.signerFactory = Signer
|
||
|
module.exports.Signer = Signer
|
||
|
module.exports.sign = sign
|
||
|
module.exports.unsign = unsign
|