mirror of
https://github.com/0rangebananaspy/authelia.git
synced 2024-09-14 22:47:21 +07:00
Move TOTP Validator and Generator to typescript
This commit is contained in:
parent
40e02d23bf
commit
bf74667726
|
@ -8,6 +8,7 @@
|
|||
},
|
||||
"scripts": {
|
||||
"test": "./node_modules/.bin/mocha --compilers ts:ts-node/register --recursive test/unitary",
|
||||
"test-dbg": "./node_modules/.bin/mocha --debug-brk --compilers ts:ts-node/register --recursive test/unitary",
|
||||
"int-test": "./node_modules/.bin/mocha --recursive test/integration",
|
||||
"coverage": "./node_modules/.bin/istanbul cover _mocha -- -R spec --recursive test",
|
||||
"build-ts": "tsc",
|
||||
|
|
|
@ -10,7 +10,7 @@ interface DatedDocument {
|
|||
date: Date;
|
||||
}
|
||||
|
||||
export class AuthenticationRegulator {
|
||||
export default class AuthenticationRegulator {
|
||||
private _user_data_store: any;
|
||||
private _lock_time_in_seconds: number;
|
||||
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
import * as winston from "winston";
|
||||
import nodemailer = require("nodemailer");
|
||||
|
||||
export interface Nodemailer {
|
||||
createTransport: (options?: any, defaults?: Object) => nodemailer.Transporter;
|
||||
}
|
||||
|
||||
export interface GlobalDependencies {
|
||||
u2f: object;
|
||||
nodemailer: Nodemailer;
|
||||
ldapjs: object;
|
||||
session: any;
|
||||
winston: winston.Winston;
|
||||
speakeasy: object;
|
||||
nedb: any;
|
||||
}
|
||||
|
||||
export type NodemailerDependencies = Nodemailer;
|
||||
|
||||
export interface NotifierDependencies {
|
||||
nodemailer: Nodemailer;
|
||||
}
|
|
@ -1,10 +1,12 @@
|
|||
|
||||
import { UserConfiguration } from "./Configuration";
|
||||
import { GlobalDependencies } from "./Dependencies";
|
||||
import { AuthenticationRegulator } from "./AuthenticationRegulator";
|
||||
import { GlobalDependencies } from "../types/Dependencies";
|
||||
import AuthenticationRegulator from "./AuthenticationRegulator";
|
||||
import UserDataStore from "./UserDataStore";
|
||||
import ConfigurationAdapter from "./ConfigurationAdapter";
|
||||
import { NotifierFactory } from "./notifiers/NotifierFactory";
|
||||
import TOTPValidator from "./TOTPValidator";
|
||||
import TOTPGenerator from "./TOTPGenerator";
|
||||
|
||||
import * as Express from "express";
|
||||
import * as BodyParser from "body-parser";
|
||||
|
@ -58,10 +60,13 @@ export default class Server {
|
|||
const notifier = NotifierFactory.build(config.notifier, deps);
|
||||
const ldap = new Ldap(deps, config.ldap);
|
||||
const accessController = new AccessController(config.access_control, deps.winston);
|
||||
const totpValidator = new TOTPValidator(deps.speakeasy);
|
||||
const totpGenerator = new TOTPGenerator(deps.speakeasy);
|
||||
|
||||
app.set("logger", deps.winston);
|
||||
app.set("ldap", ldap);
|
||||
app.set("totp engine", deps.speakeasy);
|
||||
app.set("totp validator", totpValidator);
|
||||
app.set("totp generator", totpGenerator);
|
||||
app.set("u2f", deps.u2f);
|
||||
app.set("user data store", data_store);
|
||||
app.set("notifier", notifier);
|
||||
|
|
16
src/lib/TOTPGenerator.ts
Normal file
16
src/lib/TOTPGenerator.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
|
||||
import * as speakeasy from "speakeasy";
|
||||
import { Speakeasy } from "../types/Dependencies";
|
||||
import BluebirdPromise = require("bluebird");
|
||||
|
||||
export default class TOTPGenerator {
|
||||
private speakeasy: Speakeasy;
|
||||
|
||||
constructor(speakeasy: Speakeasy) {
|
||||
this.speakeasy = speakeasy;
|
||||
}
|
||||
|
||||
generate(options: speakeasy.GenerateOptions): speakeasy.Key {
|
||||
return this.speakeasy.generateSecret(options);
|
||||
}
|
||||
}
|
23
src/lib/TOTPValidator.ts
Normal file
23
src/lib/TOTPValidator.ts
Normal file
|
@ -0,0 +1,23 @@
|
|||
|
||||
import { Speakeasy } from "../types/Dependencies";
|
||||
import BluebirdPromise = require("bluebird");
|
||||
|
||||
const TOTP_ENCODING = "base32";
|
||||
|
||||
export default class TOTPValidator {
|
||||
private speakeasy: Speakeasy;
|
||||
|
||||
constructor(speakeasy: Speakeasy) {
|
||||
this.speakeasy = speakeasy;
|
||||
}
|
||||
|
||||
validate(token: string, secret: string): BluebirdPromise<void> {
|
||||
const real_token = this.speakeasy.totp({
|
||||
secret: secret,
|
||||
encoding: TOTP_ENCODING
|
||||
});
|
||||
|
||||
if (token == real_token) return BluebirdPromise.resolve();
|
||||
return BluebirdPromise.reject("Wrong challenge");
|
||||
}
|
||||
}
|
|
@ -2,7 +2,7 @@ import * as Promise from "bluebird";
|
|||
import * as path from "path";
|
||||
import Nedb = require("nedb");
|
||||
import { NedbAsync } from "nedb";
|
||||
import { TOTPSecret } from "./TOTPSecret";
|
||||
import { TOTPSecret } from "../types/TOTPSecret";
|
||||
|
||||
// Constants
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ import * as fs from "fs";
|
|||
import * as ejs from "ejs";
|
||||
import nodemailer = require("nodemailer");
|
||||
|
||||
import { NodemailerDependencies } from "../Dependencies";
|
||||
import { NodemailerDependencies } from "../../types/Dependencies";
|
||||
import { Identity } from "../Identity";
|
||||
import { INotifier } from "../notifiers/INotifier";
|
||||
import { GmailNotifierConfiguration } from "../Configuration";
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
|
||||
import { NotifierConfiguration } from "..//Configuration";
|
||||
import { NotifierDependencies } from "../Dependencies";
|
||||
import { NotifierDependencies } from "../../types/Dependencies";
|
||||
import { INotifier } from "./INotifier";
|
||||
|
||||
import { GMailNotifier } from "./GMailNotifier";
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
|
||||
module.exports = totp_fn;
|
||||
|
||||
var totp = require('../totp');
|
||||
var objectPath = require('object-path');
|
||||
var exceptions = require('../../../src/lib/exceptions');
|
||||
|
||||
|
@ -20,14 +19,14 @@ function totp_fn(req, res) {
|
|||
}
|
||||
|
||||
var token = req.body.token;
|
||||
var totp_engine = req.app.get('totp engine');
|
||||
var totpValidator = req.app.get('totp validator');
|
||||
var data_store = req.app.get('user data store');
|
||||
|
||||
logger.debug('POST 2ndfactor totp: Fetching secret for user %s', userid);
|
||||
data_store.get_totp_secret(userid)
|
||||
.then(function(doc) {
|
||||
logger.debug('POST 2ndfactor totp: TOTP secret is %s', JSON.stringify(doc));
|
||||
return totp.validate(totp_engine, token, doc.secret.base32)
|
||||
return totpValidator.validate(token, doc.secret.base32);
|
||||
})
|
||||
.then(function() {
|
||||
logger.debug('POST 2ndfactor totp: TOTP validation succeeded');
|
||||
|
|
|
@ -47,8 +47,8 @@ function post(req, res) {
|
|||
}
|
||||
|
||||
var user_data_store = req.app.get('user data store');
|
||||
var totp = req.app.get('totp engine');
|
||||
var secret = totp.generateSecret();
|
||||
var totpGenerator = req.app.get('totp generator');
|
||||
var secret = totpGenerator.generate();
|
||||
|
||||
logger.debug('POST new-totp-secret: save the TOTP secret in DB');
|
||||
user_data_store.set_totp_secret(userid, secret)
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
|
||||
module.exports = {
|
||||
'validate': validate
|
||||
}
|
||||
|
||||
var Promise = require('bluebird');
|
||||
|
||||
function validate(totp_engine, token, totp_secret) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
var real_token = totp_engine.totp({
|
||||
secret: totp_secret,
|
||||
encoding: 'base32'
|
||||
});
|
||||
|
||||
if(token == real_token) {
|
||||
resolve();
|
||||
}
|
||||
else {
|
||||
reject('Wrong challenge');
|
||||
}
|
||||
});
|
||||
}
|
27
src/types/Dependencies.ts
Normal file
27
src/types/Dependencies.ts
Normal file
|
@ -0,0 +1,27 @@
|
|||
import * as winston from "winston";
|
||||
import * as speakeasy from "speakeasy";
|
||||
import nodemailer = require("nodemailer");
|
||||
import session = require("express-session");
|
||||
import nedb = require("nedb");
|
||||
|
||||
export type Nodemailer = typeof nodemailer;
|
||||
export type Speakeasy = typeof speakeasy;
|
||||
export type Winston = typeof winston;
|
||||
export type Session = typeof session;
|
||||
export type Nedb = typeof nedb;
|
||||
|
||||
export interface GlobalDependencies {
|
||||
u2f: object;
|
||||
nodemailer: Nodemailer;
|
||||
ldapjs: object;
|
||||
session: Session;
|
||||
winston: Winston;
|
||||
speakeasy: Speakeasy;
|
||||
nedb: Nedb;
|
||||
}
|
||||
|
||||
export type NodemailerDependencies = Nodemailer;
|
||||
|
||||
export interface NotifierDependencies {
|
||||
nodemailer: Nodemailer;
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
import { AuthenticationRegulator } from "../../src/lib/AuthenticationRegulator";
|
||||
import AuthenticationRegulator from "../../src/lib/AuthenticationRegulator";
|
||||
import UserDataStore from "../../src/lib/UserDataStore";
|
||||
import * as MockDate from "mockdate";
|
||||
|
||||
|
|
|
@ -2,11 +2,11 @@
|
|||
import Server from "../../src/lib/Server";
|
||||
import Ldap = require("../../src/lib/ldap");
|
||||
|
||||
import * as Promise from "bluebird";
|
||||
import * as speakeasy from "speakeasy";
|
||||
import * as request from "request";
|
||||
import * as nedb from "nedb";
|
||||
import { TOTPSecret } from "../../src/lib/TOTPSecret";
|
||||
import Promise = require("bluebird");
|
||||
import speakeasy = require("speakeasy");
|
||||
import request = require("request");
|
||||
import nedb = require("nedb");
|
||||
import { TOTPSecret } from "../../src/types/TOTPSecret";
|
||||
|
||||
|
||||
const requestp = Promise.promisifyAll(request) as request.RequestAsync;
|
||||
|
@ -29,7 +29,6 @@ describe("test the server", function () {
|
|||
beforeEach(function () {
|
||||
const config = {
|
||||
port: PORT,
|
||||
totp_secret: "totp_secret",
|
||||
ldap: {
|
||||
url: "ldap://127.0.0.1:389",
|
||||
base_dn: "ou=users,dc=example,dc=com",
|
||||
|
|
30
test/unitary/TOTPValidator.ts
Normal file
30
test/unitary/TOTPValidator.ts
Normal file
|
@ -0,0 +1,30 @@
|
|||
|
||||
import TOTPValidator from "../../src/lib/TOTPValidator";
|
||||
import sinon = require("sinon");
|
||||
import Promise = require("bluebird");
|
||||
import SpeakeasyMock = require("./mocks/speakeasy");
|
||||
|
||||
describe("test TOTP validation", function() {
|
||||
let totpValidator: TOTPValidator;
|
||||
|
||||
beforeEach(() => {
|
||||
SpeakeasyMock.totp.returns("token");
|
||||
totpValidator = new TOTPValidator(SpeakeasyMock as any);
|
||||
});
|
||||
|
||||
it("should validate the TOTP token", function() {
|
||||
const totp_secret = "NBD2ZV64R9UV1O7K";
|
||||
const token = "token";
|
||||
return totpValidator.validate(token, totp_secret);
|
||||
});
|
||||
|
||||
it("should not validate a wrong TOTP token", function() {
|
||||
const totp_secret = "NBD2ZV64R9UV1O7K";
|
||||
const token = "wrong token";
|
||||
return totpValidator.validate(token, totp_secret)
|
||||
.catch(function() {
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -4,7 +4,7 @@ import * as request from "request";
|
|||
|
||||
import Server from "../../src/lib/Server";
|
||||
import { UserConfiguration } from "../../src/lib/Configuration";
|
||||
import { GlobalDependencies } from "../../src/lib/Dependencies";
|
||||
import { GlobalDependencies } from "../../src/types/Dependencies";
|
||||
import * as tmp from "tmp";
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
|
||||
import sinon = require("sinon");
|
||||
import { Nodemailer } from "../../../src/lib/Dependencies";
|
||||
|
||||
export = {
|
||||
createTransport: sinon.stub()
|
||||
|
|
7
test/unitary/mocks/speakeasy.ts
Normal file
7
test/unitary/mocks/speakeasy.ts
Normal file
|
@ -0,0 +1,7 @@
|
|||
|
||||
import sinon = require("sinon");
|
||||
|
||||
export = {
|
||||
totp: sinon.stub(),
|
||||
generateSecret: sinon.stub()
|
||||
};
|
|
@ -9,7 +9,7 @@ import { NotifierFactory } from "../../../src/lib/notifiers/NotifierFactory";
|
|||
import { GMailNotifier } from "../../../src/lib/notifiers/GMailNotifier";
|
||||
import { FileSystemNotifier } from "../../../src/lib/notifiers/FileSystemNotifier";
|
||||
|
||||
import { NotifierDependencies } from "../../../src/lib/Dependencies";
|
||||
import { NotifierDependencies } from "../../../src/types/Dependencies";
|
||||
|
||||
|
||||
describe("test notifier", function() {
|
||||
|
|
|
@ -7,7 +7,7 @@ var winston = require('winston');
|
|||
|
||||
describe('test totp route', function() {
|
||||
var req, res;
|
||||
var totp_engine;
|
||||
var totpValidator;
|
||||
var user_data_store;
|
||||
|
||||
beforeEach(function() {
|
||||
|
@ -33,8 +33,8 @@ describe('test totp route', function() {
|
|||
};
|
||||
|
||||
var config = { totp_secret: 'secret' };
|
||||
totp_engine = {
|
||||
totp: sinon.stub()
|
||||
totpValidator = {
|
||||
validate: sinon.stub()
|
||||
}
|
||||
|
||||
user_data_store = {};
|
||||
|
@ -47,14 +47,14 @@ describe('test totp route', function() {
|
|||
user_data_store.get_totp_secret.returns(Promise.resolve(doc));
|
||||
|
||||
app_get.withArgs('logger').returns(winston);
|
||||
app_get.withArgs('totp engine').returns(totp_engine);
|
||||
app_get.withArgs('totp validator').returns(totpValidator);
|
||||
app_get.withArgs('config').returns(config);
|
||||
app_get.withArgs('user data store').returns(user_data_store);
|
||||
});
|
||||
|
||||
|
||||
it('should send status code 204 when totp is valid', function(done) {
|
||||
totp_engine.totp.returns('abc');
|
||||
totpValidator.validate.returns(Promise.resolve("ok"));
|
||||
res.send = sinon.spy(function() {
|
||||
// Second factor passed
|
||||
assert.equal(true, req.session.auth_session.second_factor)
|
||||
|
@ -65,7 +65,7 @@ describe('test totp route', function() {
|
|||
});
|
||||
|
||||
it('should send status code 401 when totp is not valid', function(done) {
|
||||
totp_engine.totp.returns('bad_token');
|
||||
totpValidator.validate.returns(Promise.reject('bad_token'));
|
||||
res.send = sinon.spy(function() {
|
||||
assert.equal(false, req.session.auth_session.second_factor)
|
||||
assert.equal(401, res.status.getCall(0).args[0]);
|
||||
|
@ -75,7 +75,7 @@ describe('test totp route', function() {
|
|||
});
|
||||
|
||||
it('should send status code 401 when session has not been initiated', function(done) {
|
||||
totp_engine.totp.returns('abc');
|
||||
totpValidator.validate.returns(Promise.resolve('abc'));
|
||||
res.send = sinon.spy(function() {
|
||||
assert.equal(403, res.status.getCall(0).args[0]);
|
||||
done();
|
||||
|
|
|
@ -81,7 +81,9 @@ describe('test totp register', function() {
|
|||
|
||||
function test_post_secret() {
|
||||
it('should send the secret in json format', function(done) {
|
||||
req.app.get.withArgs('totp engine').returns(require('speakeasy'));
|
||||
req.app.get.withArgs('totp generator').returns({
|
||||
generate: sinon.stub().returns({ otpauth_url: "abc"})
|
||||
});
|
||||
req.session.auth_session.identity_check = {};
|
||||
req.session.auth_session.identity_check.userid = 'user';
|
||||
req.session.auth_session.identity_check.challenge = 'totp-register';
|
||||
|
@ -92,7 +94,9 @@ describe('test totp register', function() {
|
|||
});
|
||||
|
||||
it('should clear the session for reauthentication', function(done) {
|
||||
req.app.get.withArgs('totp engine').returns(require('speakeasy'));
|
||||
req.app.get.withArgs('totp generator').returns({
|
||||
generate: sinon.stub().returns({ otpauth_url: "abc"})
|
||||
});
|
||||
req.session.auth_session.identity_check = {};
|
||||
req.session.auth_session.identity_check.userid = 'user';
|
||||
req.session.auth_session.identity_check.challenge = 'totp-register';
|
||||
|
@ -114,7 +118,9 @@ describe('test totp register', function() {
|
|||
});
|
||||
|
||||
it('should return 500 if db throws', function(done) {
|
||||
req.app.get.withArgs('totp engine').returns(require('speakeasy'));
|
||||
req.app.get.withArgs('totp generator').returns({
|
||||
generate: sinon.stub().returns({ otpauth_url: "abc" })
|
||||
});
|
||||
req.session.auth_session.identity_check = {};
|
||||
req.session.auth_session.identity_check.userid = 'user';
|
||||
req.session.auth_session.identity_check.challenge = 'totp-register';
|
||||
|
|
|
@ -1,30 +1,32 @@
|
|||
|
||||
import * as assert from "assert";
|
||||
import * as sinon from "sinon";
|
||||
import assert = require("assert");
|
||||
import sinon = require ("sinon");
|
||||
import nedb = require("nedb");
|
||||
import * as express from "express";
|
||||
import * as winston from "winston";
|
||||
import * as speakeasy from "speakeasy";
|
||||
import * as u2f from "authdog";
|
||||
import express = require("express");
|
||||
import winston = require("winston");
|
||||
import speakeasy = require("speakeasy");
|
||||
import u2f = require("authdog");
|
||||
import nodemailer = require("nodemailer");
|
||||
import session = require("express-session");
|
||||
|
||||
import { AppConfiguration, UserConfiguration } from "../../src/lib/Configuration";
|
||||
import { GlobalDependencies, Nodemailer } from "../../src/lib/Dependencies";
|
||||
import { GlobalDependencies, Nodemailer } from "../../src/types/Dependencies";
|
||||
import Server from "../../src/lib/Server";
|
||||
|
||||
|
||||
describe("test server configuration", function () {
|
||||
let deps: GlobalDependencies;
|
||||
let sessionMock: sinon.SinonSpy;
|
||||
|
||||
before(function () {
|
||||
const transporter = {
|
||||
sendMail: sinon.stub().yields()
|
||||
};
|
||||
|
||||
const nodemailer: Nodemailer = {
|
||||
createTransport: sinon.spy(function () {
|
||||
return transporter;
|
||||
})
|
||||
};
|
||||
const createTransport = sinon.stub(nodemailer, "createTransport");
|
||||
createTransport.returns(transporter);
|
||||
|
||||
sessionMock = sinon.spy(session);
|
||||
|
||||
deps = {
|
||||
nodemailer: nodemailer,
|
||||
|
@ -37,9 +39,7 @@ describe("test server configuration", function () {
|
|||
return { on: sinon.spy() };
|
||||
})
|
||||
},
|
||||
session: sinon.spy(function () {
|
||||
return function (req: express.Request, res: express.Response, next: express.NextFunction) { next(); };
|
||||
})
|
||||
session: sessionMock as any
|
||||
};
|
||||
});
|
||||
|
||||
|
@ -66,7 +66,7 @@ describe("test server configuration", function () {
|
|||
const server = new Server();
|
||||
server.start(config, deps);
|
||||
|
||||
assert(deps.session.calledOnce);
|
||||
assert.equal(deps.session.getCall(0).args[0].cookie.domain, "example.com");
|
||||
assert(sessionMock.calledOnce);
|
||||
assert.equal(sessionMock.getCall(0).args[0].cookie.domain, "example.com");
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,69 +0,0 @@
|
|||
|
||||
import Server from "../../src/lib/Server";
|
||||
|
||||
import { UserConfiguration } from "../../src/lib/Configuration";
|
||||
import { GlobalDependencies } from "../../src/lib/Dependencies";
|
||||
import * as express from "express";
|
||||
|
||||
const sinon = require("sinon");
|
||||
const assert = require("assert");
|
||||
|
||||
describe("test server configuration", function () {
|
||||
let deps: GlobalDependencies;
|
||||
|
||||
before(function () {
|
||||
const transporter = {
|
||||
sendMail: sinon.stub().yields()
|
||||
};
|
||||
|
||||
const nodemailer = {
|
||||
createTransport: sinon.spy(function () {
|
||||
return transporter;
|
||||
})
|
||||
};
|
||||
|
||||
deps = {
|
||||
nodemailer: nodemailer,
|
||||
speakeasy: sinon.spy(),
|
||||
u2f: sinon.spy(),
|
||||
nedb: require("nedb"),
|
||||
winston: sinon.spy(),
|
||||
ldapjs: {
|
||||
createClient: sinon.spy(function () {
|
||||
return { on: sinon.spy() };
|
||||
})
|
||||
},
|
||||
session: sinon.spy(function () {
|
||||
return function (req: express.Request, res: express.Response, next: express.NextFunction) { next(); };
|
||||
})
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
it("should set cookie scope to domain set in the config", function () {
|
||||
const config = {
|
||||
notifier: {
|
||||
gmail: {
|
||||
username: "user@example.com",
|
||||
password: "password"
|
||||
}
|
||||
},
|
||||
session: {
|
||||
domain: "example.com",
|
||||
secret: "secret"
|
||||
},
|
||||
ldap: {
|
||||
url: "http://ldap",
|
||||
base_dn: "cn=test,dc=example,dc=com",
|
||||
user: "user",
|
||||
password: "password"
|
||||
}
|
||||
};
|
||||
|
||||
const server = new Server();
|
||||
server.start(config, deps);
|
||||
|
||||
assert(deps.session.calledOnce);
|
||||
assert.equal(deps.session.getCall(0).args[0].cookie.domain, "example.com");
|
||||
});
|
||||
});
|
|
@ -1,32 +0,0 @@
|
|||
|
||||
const totp = require("../../src/lib/totp");
|
||||
const sinon = require("sinon");
|
||||
import Promise = require("bluebird");
|
||||
|
||||
describe("test TOTP validation", function() {
|
||||
it("should validate the TOTP token", function() {
|
||||
const totp_secret = "NBD2ZV64R9UV1O7K";
|
||||
const token = "token";
|
||||
const totp_mock = sinon.mock();
|
||||
totp_mock.returns("token");
|
||||
const speakeasy_mock = {
|
||||
totp: totp_mock
|
||||
};
|
||||
return totp.validate(speakeasy_mock, token, totp_secret);
|
||||
});
|
||||
|
||||
it("should not validate a wrong TOTP token", function() {
|
||||
const totp_secret = "NBD2ZV64R9UV1O7K";
|
||||
const token = "wrong token";
|
||||
const totp_mock = sinon.mock();
|
||||
totp_mock.returns("token");
|
||||
const speakeasy_mock = {
|
||||
totp: totp_mock
|
||||
};
|
||||
return totp.validate(speakeasy_mock, token, totp_secret)
|
||||
.catch(function() {
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user