Updated to use HS256 signatures for tokens

no-issue

This makes the tokens a little more acceptable in plaintext emails
This commit is contained in:
Fabien O'Carroll 2019-10-10 16:21:03 +07:00
parent 5d2e20fbb7
commit 7a512f992b
2 changed files with 12 additions and 27 deletions

View File

@ -2,8 +2,7 @@ const jwt = require('jsonwebtoken');
module.exports = MagicLink;
/**
* @typedef { Buffer | string } RsaPublicKey
* @typedef { Buffer | string } RsaPrivateKey
* @typedef { import('jsonwebtoken').Secret } Secret
* @typedef { import('nodemailer').Transporter } MailTransporter
* @typedef { import('nodemailer').SentMessageInfo } SentMessageInfo
* @typedef { string } JSONWebToken
@ -61,20 +60,18 @@ function defaultGetSubject(type) {
*
* @param {object} options
* @param {MailTransporter} options.transporter
* @param {RsaPublicKey} options.publicKey
* @param {RsaPrivateKey} options.privateKey
* @param {Secret} options.secret
* @param {(token: JSONWebToken, type: string) => URL} options.getSigninURL
* @param {typeof defaultGetText} [options.getText]
* @param {typeof defaultGetHTML} [options.getHTML]
* @param {typeof defaultGetSubject} [options.getSubject]
*/
function MagicLink(options) {
if (!options || !options.transporter || !options.publicKey || !options.privateKey || !options.getSigninURL) {
throw new Error('Missing options. Expects {transporter, publicKey, privateKey, getSigninURL}');
if (!options || !options.transporter || !options.secret || !options.getSigninURL) {
throw new Error('Missing options. Expects {transporter, secret, getSigninURL}');
}
this.transporter = options.transporter;
this.publicKey = options.publicKey;
this.privateKey = options.privateKey;
this.secret = options.secret;
this.getSigninURL = options.getSigninURL;
this.getText = options.getText || defaultGetText;
this.getHTML = options.getHTML || defaultGetHTML;
@ -93,10 +90,10 @@ function MagicLink(options) {
MagicLink.prototype.sendMagicLink = async function sendMagicLink(options) {
const token = jwt.sign({
user: options.user
}, this.privateKey, {
}, this.secret, {
audience: '@tryghost/magic-link',
issuer: '@tryghost/magic-link',
algorithm: 'RS512',
algorithm: 'HS256',
subject: options.email,
expiresIn: '10m'
});
@ -123,10 +120,10 @@ MagicLink.prototype.sendMagicLink = async function sendMagicLink(options) {
*/
MagicLink.prototype.getUserFromToken = function getUserFromToken(token) {
/** @type {object} */
const claims = jwt.verify(token, this.publicKey, {
const claims = jwt.verify(token, this.secret, {
audience: '@tryghost/magic-link',
issuer: '@tryghost/magic-link',
algorithms: ['RS512'],
algorithms: ['HS256'],
maxAge: '10m'
});
return claims.user;

View File

@ -4,17 +4,7 @@ const MagicLink = require('../');
const crypto = require('crypto');
const sandbox = sinon.createSandbox();
const {publicKey, privateKey} = crypto.generateKeyPairSync('rsa', {
modulusLength: 4096,
publicKeyEncoding: {
type: 'pkcs1',
format: 'pem'
},
privateKeyEncoding: {
type: 'pkcs1',
format: 'pem'
}
});
const secret = crypto.randomBytes(64);
describe('MagicLink', function () {
it('Exports a function', function () {
@ -24,8 +14,7 @@ describe('MagicLink', function () {
describe('#sendMagicLink', function () {
it('Sends an email to the user with a link generated from getSigninURL(token, type)', async function () {
const options = {
publicKey,
privateKey,
secret,
getSigninURL: sandbox.stub().returns('FAKEURL'),
getText: sandbox.stub().returns('SOMETEXT'),
getHTML: sandbox.stub().returns('SOMEHTML'),
@ -59,8 +48,7 @@ describe('MagicLink', function () {
describe('#getUserFromToken', function () {
it('Returns the user data which from the token that was encoded by #sendMagicLink', async function () {
const options = {
publicKey,
privateKey,
secret,
getSigninURL: sandbox.stub().returns('FAKEURL'),
getText: sandbox.stub().returns('SOMETEXT'),
getHTML: sandbox.stub().returns('SOMEHTML'),