Ghost/ghost/members-ssr/index.js
Fabien O'Carroll 02468bfe0c Fixed cookie verification handling
no-issue

turns out the get method fails if the cookie is missing, rather than returning null
2019-04-16 12:22:55 +02:00

108 lines
2.9 KiB
JavaScript

const concat = require('concat-stream');
const Cookies = require('cookies');
const jwt = require('jsonwebtoken');
const ignition = require('ghost-ignition');
const {
UnauthorizedError,
BadRequestError
} = ignition.errors;
const EMPTY = {};
const SIX_MONTHS_MS = 1000 * 60 * 60 * 24 * 184;
const wrapFn = (fn, cookieConfig) => (req, res) => {
return new Promise((resolve, reject) => {
const cookies = new Cookies(req, res, cookieConfig);
req.on('error', reject);
req.pipe(concat(function (buff) {
const body = buff.toString();
resolve(fn(req, res, {body, cookies}));
}));
});
};
module.exports = function create(options = EMPTY) {
if (options === EMPTY) {
throw new Error('Must pass options');
}
const {
cookieMaxAge = SIX_MONTHS_MS,
cookieSecure = true,
cookieName = 'members-ssr',
cookiePath = '/',
cookieKeys,
membersApi
} = options;
if (!membersApi) {
throw new Error('Missing option membersApi');
}
if (!cookieKeys) {
throw new Error('Missing option cookieKeys');
}
const audience = ['members-ssr'];
const cookieConfig = {
keys: [].concat(cookieKeys),
secure: cookieSecure
};
const verifyJwt = token => membersApi.getPublicConfig().then(({publicKey, issuer}) => {
return new Promise((resolve, reject) => {
jwt.verify(token, publicKey, {
algorithms: ['RS512'],
issuer,
audience
}, (err, claims) => {
if (err) {
reject(new UnauthorizedError({err}));
}
resolve(claims);
});
});
});
const exchangeTokenForSession = wrapFn((req, res, {body, cookies}) => {
const token = body;
if (!body || typeof body !== 'string') {
throw new BadRequestError({
message: 'Expected body containing JWT'
});
}
return verifyJwt(token).then(() => {
cookies.set(cookieName, token, {
signed: true,
httpOnly: true,
sameSite: 'lax',
maxAge: cookieMaxAge,
path: cookiePath
});
});
}, cookieConfig);
const getMemberDataFromSession = wrapFn((req, res, {cookies}) => {
try {
const token = cookies.get(cookieName, {
signed: true
});
return verifyJwt(token).then((claims) => {
return membersApi.getMember(claims.sub, token);
});
} catch (e) {
throw new BadRequestError({
message: `Cookie ${cookieName} not found`
});
}
}, cookieConfig);
return {
exchangeTokenForSession,
getMemberDataFromSession
};
};