mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-23 19:02:29 +03:00
Updated magic-link to accept a TokenProvider
no-issue This adds a layer of abstraction between the magic-link module and the token generation, allowing us to switch out the token generation in the future, when implementing single use tokens stored in a database
This commit is contained in:
parent
6957c2725b
commit
37c8c15dd6
@ -1,29 +1,38 @@
|
|||||||
const jwt = require('jsonwebtoken');
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef { import('jsonwebtoken').Secret } Secret
|
|
||||||
* @typedef { import('nodemailer').Transporter } MailTransporter
|
* @typedef { import('nodemailer').Transporter } MailTransporter
|
||||||
* @typedef { import('nodemailer').SentMessageInfo } SentMessageInfo
|
* @typedef { import('nodemailer').SentMessageInfo } SentMessageInfo
|
||||||
* @typedef { string } JSONWebToken
|
|
||||||
* @typedef { string } URL
|
* @typedef { string } URL
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template T
|
||||||
|
* @template D
|
||||||
|
* @typedef {Object} TokenProvider<T, D>
|
||||||
|
* @prop {(data: D) => Promise<T>} create
|
||||||
|
* @prop {(token: T) => Promise<D>} validate
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MagicLink
|
||||||
|
* @template Token
|
||||||
|
* @template TokenData
|
||||||
|
*/
|
||||||
class MagicLink {
|
class MagicLink {
|
||||||
/**
|
/**
|
||||||
* @param {object} options
|
* @param {object} options
|
||||||
* @param {MailTransporter} options.transporter
|
* @param {MailTransporter} options.transporter
|
||||||
* @param {Secret} options.secret
|
* @param {TokenProvider<Token, TokenData>} options.tokenProvider
|
||||||
* @param {(token: JSONWebToken, type: string) => URL} options.getSigninURL
|
* @param {(token: Token, type: string) => URL} options.getSigninURL
|
||||||
* @param {typeof defaultGetText} [options.getText]
|
* @param {typeof defaultGetText} [options.getText]
|
||||||
* @param {typeof defaultGetHTML} [options.getHTML]
|
* @param {typeof defaultGetHTML} [options.getHTML]
|
||||||
* @param {typeof defaultGetSubject} [options.getSubject]
|
* @param {typeof defaultGetSubject} [options.getSubject]
|
||||||
*/
|
*/
|
||||||
constructor(options) {
|
constructor(options) {
|
||||||
if (!options || !options.transporter || !options.secret || !options.getSigninURL) {
|
if (!options || !options.transporter || !options.tokenProvider || !options.getSigninURL) {
|
||||||
throw new Error('Missing options. Expects {transporter, secret, getSigninURL}');
|
throw new Error('Missing options. Expects {transporter, tokenProvider, getSigninURL}');
|
||||||
}
|
}
|
||||||
this.transporter = options.transporter;
|
this.transporter = options.transporter;
|
||||||
this.secret = options.secret;
|
this.tokenProvider = options.tokenProvider;
|
||||||
this.getSigninURL = options.getSigninURL;
|
this.getSigninURL = options.getSigninURL;
|
||||||
this.getText = options.getText || defaultGetText;
|
this.getText = options.getText || defaultGetText;
|
||||||
this.getHTML = options.getHTML || defaultGetHTML;
|
this.getHTML = options.getHTML || defaultGetHTML;
|
||||||
@ -35,15 +44,12 @@ class MagicLink {
|
|||||||
*
|
*
|
||||||
* @param {object} options
|
* @param {object} options
|
||||||
* @param {string} options.email - The email to send magic link to
|
* @param {string} options.email - The email to send magic link to
|
||||||
* @param {object} options.tokenData - The data for token
|
* @param {TokenData} options.tokenData - The data for token
|
||||||
* @param {string=} [options.type='signin'] - The type to be passed to the url and content generator functions
|
* @param {string=} [options.type='signin'] - The type to be passed to the url and content generator functions
|
||||||
* @returns {Promise<{token: JSONWebToken, info: SentMessageInfo}>}
|
* @returns {Promise<{token: Token, info: SentMessageInfo}>}
|
||||||
*/
|
*/
|
||||||
async sendMagicLink(options) {
|
async sendMagicLink(options) {
|
||||||
const token = jwt.sign(options.tokenData, this.secret, {
|
const token = await this.tokenProvider.create(options.tokenData);
|
||||||
algorithm: 'HS256',
|
|
||||||
expiresIn: '10m'
|
|
||||||
});
|
|
||||||
|
|
||||||
const type = options.type || 'signin';
|
const type = options.type || 'signin';
|
||||||
|
|
||||||
@ -63,15 +69,12 @@ class MagicLink {
|
|||||||
* getMagicLink
|
* getMagicLink
|
||||||
*
|
*
|
||||||
* @param {object} options
|
* @param {object} options
|
||||||
* @param {object} options.tokenData - The data for token
|
* @param {TokenData} options.tokenData - The data for token
|
||||||
* @param {string=} [options.type='signin'] - The type to be passed to the url and content generator functions
|
* @param {string=} [options.type='signin'] - The type to be passed to the url and content generator functions
|
||||||
* @returns {Promise<URL>} - signin URL
|
* @returns {Promise<URL>} - signin URL
|
||||||
*/
|
*/
|
||||||
async getMagicLink(options) {
|
async getMagicLink(options) {
|
||||||
const token = jwt.sign(options.tokenData, this.secret, {
|
const token = await this.tokenProvider.create(options.tokenData);
|
||||||
algorithm: 'HS256',
|
|
||||||
expiresIn: '10m'
|
|
||||||
});
|
|
||||||
|
|
||||||
const type = options.type || 'signin';
|
const type = options.type || 'signin';
|
||||||
|
|
||||||
@ -81,15 +84,11 @@ class MagicLink {
|
|||||||
/**
|
/**
|
||||||
* getDataFromToken
|
* getDataFromToken
|
||||||
*
|
*
|
||||||
* @param {JSONWebToken} token - The token to decode
|
* @param {Token} token - The token to decode
|
||||||
* @returns {Promise<object>} data - The data object associated with the magic link
|
* @returns {Promise<TokenData>} data - The data object associated with the magic link
|
||||||
*/
|
*/
|
||||||
async getDataFromToken(token) {
|
async getDataFromToken(token) {
|
||||||
/** @type {object} */
|
const tokenData = await this.tokenProvider.validate(token);
|
||||||
const tokenData = (jwt.verify(token, this.secret, {
|
|
||||||
algorithms: ['HS256'],
|
|
||||||
maxAge: '10m'
|
|
||||||
}));
|
|
||||||
return tokenData;
|
return tokenData;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user