lintry

定义生成totp和验证

1 +/**
2 + * Created by lintry on 2017/5/19.
3 + */
4 +
5 +module.exports = require('./lib/totp');
1 +/**
2 + * Created by lintry on 2017/5/19.
3 + */
4 +
5 +const Authenticator = function (secret, options) {
6 + if (!(this instanceof Authenticator)) {
7 + return new Authenticator(secret, options)
8 + }
9 +
10 + const _ = require('lodash'),
11 + path = require('path'),
12 + crypto_utils = require('kml-crypto-utils'),
13 + speakeasy = require('speakeasy'),
14 + qr = require('qr-image');
15 +
16 + options = options || {};
17 +
18 + this.secret = crypto_utils.AESDecode(secret);
19 +
20 + const TOTP_OPTIONS = this.totp_options = {
21 + secret: this.secret,
22 + encoding: options.encoding || 'base32',
23 + step: options.step || 30
24 + };
25 +
26 +
27 + /**
28 + * 验证token有效性
29 + * @type {Authenticator.verify}
30 + */
31 + const verify = this.verify = function(token) {
32 + return speakeasy.totp.verify(_.merge({token: token}, TOTP_OPTIONS));
33 + };
34 +
35 + /**
36 + * 在options.window的范围内验证token的有效性
37 + * @type {Authenticator.verifyDelta}
38 + */
39 + const verifyDelta = this.verifyDelta = function(token) {
40 + return speakeasy.totp.verifyDelta(_.merge({token: token}, TOTP_OPTIONS));
41 + };
42 +
43 + /**
44 + * 获取QR显示值
45 + * @type {Authenticator.getOtpAuth}
46 + */
47 + const getOtpAuth = this.getOtpAuth = function (title, issuer) {
48 + let _title = title || 'HowAreU';
49 + let _issuer = encodeURIComponent(issuer || 'TOTP-KEY');
50 + return `otpauth://totp/${_title}?secret=${this.secret}&issuer=${_issuer}`;
51 + };
52 +
53 + /**
54 + * 生成svg的QR图片内容
55 + * @type {Authenticator.getQR}
56 + */
57 + const getQR = this.getQR = function(title, issuer) {
58 + return qr.imageSync(getOtpAuth(title, issuer), { type: 'svg' });
59 + };
60 +
61 + /**
62 + * 生成新的token
63 + * @type {Authenticator.totp}
64 + */
65 + const totp = this.totp = function () {
66 + return speakeasy.totp(_.merge({}, TOTP_OPTIONS));
67 + };
68 +
69 + return this;
70 +};
71 +
72 +module.exports = Authenticator;
...\ No newline at end of file ...\ No newline at end of file
1 +/**
2 + * Created by lintry on 2017/5/19.
3 + */
4 +
5 +const TOTP = function (options) {
6 + if (!(this instanceof TOTP)) {
7 + return new TOTP(options)
8 + }
9 +
10 + const _ = require('lodash'),
11 + crypto_utils = require('kml-crypto-utils'),
12 + speakeasy = require('speakeasy'),
13 + Authenticator = require('./authenticator');
14 +
15 + options = options || {};
16 +
17 + const TOTP_OPTIONS = {
18 + encoding: options.encoding || 'base32',
19 + step: options.step || 30
20 + };
21 +
22 + /**
23 + * 生成密钥并加密
24 + * @return {*}
25 + */
26 + this.createSecret = function() {
27 + return crypto_utils.AESEncode(speakeasy.generateSecret({length: 20}).base32);
28 + };
29 +
30 + /**
31 + * 解析密钥
32 + * @param secret
33 + * @return {Authenticator}
34 + */
35 + this.parse = function (secret) {
36 + return new Authenticator(secret, TOTP_OPTIONS)
37 + };
38 +
39 + return this;
40 +};
41 +
42 +module.exports = TOTP;
...\ No newline at end of file ...\ No newline at end of file
...@@ -13,7 +13,15 @@ ...@@ -13,7 +13,15 @@
13 "keywords": [], 13 "keywords": [],
14 "author": "lintry <shenlin00@gmail.com>", 14 "author": "lintry <shenlin00@gmail.com>",
15 "license": "MIT", 15 "license": "MIT",
16 + "files": [
17 + "/*",
18 + "!/.*",
19 + "!/test"
20 + ],
16 "dependencies": { 21 "dependencies": {
22 + "kml-crypto-utils": "git+ssh://git@gitlab.kmlab.com:comm/crypto-utils.git#1.1.7",
23 + "lodash": "^4.17.4",
24 + "qr-image": "^3.2.0",
17 "speakeasy": "^2.0.0" 25 "speakeasy": "^2.0.0"
18 } 26 }
19 } 27 }
......
1 +/**
2 + * Created by lintry on 2017/5/19.
3 + */
4 +
5 +const Authenticator = require('../lib/authenticator');
6 +const chalk = require('chalk');
7 +const fs = require('fs-extra');
8 +const path = require('path');
9 +
10 +let secret = 'vH6OdbUEjSukTqlDvW3TYdusjiOIkxRnAHNTjJewfZa5yNueG9wx1N9pJMFOmPAV';
11 +let authenticator = new Authenticator(secret);
12 +
13 +console.log(chalk.cyan('totp的secret'));
14 +console.log(secret);
15 +
16 +let token = process.argv[2];
17 +if (!token) {
18 + token = authenticator.totp();
19 + console.error(chalk.red('none token found, we generate a new token instead'), token);
20 + return;
21 +}
22 +console.log(chalk.green('token is'), token);
23 +
24 +let verify = authenticator.verify(token);
25 +console.log(chalk.magenta('verify is '), (verify ? chalk.green : chalk.red)(verify));
26 +console.log(chalk.blue('verifyDelta is '), authenticator.verifyDelta(token));
27 +
28 +
29 +let img_path = path.resolve(process.cwd(), 'img');
30 +let qr = path.resolve(img_path, 'qr.svg');
31 +
32 +fs.ensureDir(img_path, function (err, added_root) {
33 + if (err) {
34 + return console.error(chalk.red('create folder error'), chalk.red(JSON.stringify(err, null, 4)));
35 + }
36 + added_root && console.log(chalk.green(img_path + ' is created'));
37 +
38 + let fd = fs.openSync(qr, 'w');
39 + fs.writeSync(fd, authenticator.getQR('totp@gitlab.kmlab.com', '通行密钥'));
40 + fs.closeSync(fd);
41 +
42 + console.log(chalk.yellow(authenticator.getOtpAuth('totp@gitlab.kmlab.com', '通行密钥')))
43 +});
44 +
45 +console.log('QR SVG output is', img_path, qr);
...\ No newline at end of file ...\ No newline at end of file
1 +/**
2 + * Created by lintry on 2017/5/19.
3 + */
4 +
5 +const TOTP = require('../lib/totp');
6 +const chalk = require('chalk');
7 +const fs = require('fs-extra');
8 +
9 +let totp = new TOTP();
10 +
11 +let secret = totp.createSecret();
12 +console.log(chalk.cyan('创建totp的secret'));
13 +console.log(secret);
14 +
15 +console.log(chalk.magenta('secret'), secret);
16 +
17 +let authenticator = totp.parse(secret);
18 +
19 +console.log(authenticator.totp_options);
20 +
21 +let token = authenticator.totp();
22 +console.log(chalk.green('token is'), token);
23 +
24 +console.log(chalk.magenta('verify is '), authenticator.verify(token));
...\ No newline at end of file ...\ No newline at end of file