authenticator.js 1.83 KB
/**
 * Created by lintry on 2017/5/19.
 */

const Authenticator = function (secret, options) {
    if (!(this instanceof Authenticator)) {
        return new Authenticator(secret, options)
    }

    const _ = require('lodash'),
        speakeasy = require('speakeasy'),
        qr = require('qr-image');

    options = options || {};

    this.secret = secret;

    const TOTP_OPTIONS = this.totp_options = {
        secret: this.secret,
        encoding: options.encoding || 'base32',
        step: options.step || 30,
        algorithm: options.algorithm || 'sha512'
    };


    /**
     * 验证token有效性
     * @param token
     */
    this.verify = function (token) {
        return speakeasy.totp.verify(_.merge({}, TOTP_OPTIONS, {token: token}));
    };

    /**
     * 在options.window的范围内验证token的有效性
     * @param token
     * @param window
     */
    this.verifyDelta = function (token, window) {
        return speakeasy.totp.verifyDelta(_.merge({}, TOTP_OPTIONS, {token: token, window: window}));
    };

    /**
     * 获取授权定义地址
     * @param label
     * @param issuer
     * @return {string}
     */
    this.getOtpAuthURL = function (label, issuer) {
        return speakeasy.otpauthURL(_.merge({}, TOTP_OPTIONS, {label: label, issuer: issuer}));
    };

    /**
     * 生成svg的QR图片内容
     * @param label
     * @param issuer
     * @return {{content: *, url: string}}
     */
    this.getQR = function (label, issuer) {
        let url = this.getOtpAuthURL(label, issuer);
        return {
            content: qr.imageSync(url, { type: 'svg' }),
            url: url
        };
    };

    /**
     * 生成新的token
     * @return {String}
     */
    this.totp = function () {
        return speakeasy.totp(_.merge({}, TOTP_OPTIONS));
    };

    return this;
};

module.exports = Authenticator;