3 const HOTP
= require('./hotp');
5 class TimeBasedOneTimePassword
extends HOTP
{
8 * @param {object} options options
9 * @param {number} options.codeLength digits in code
10 * @param {Buffer|string} options.key secret key
11 * @param {string} options.keyEncoding secret key encoding
12 * @param {string} options.algorithm algorithm
13 * @param {number} options.timeStepSeconds seconds per increment
14 * @param {number} options.timeStepStartSeconds seconds offset
15 * @param {number} options.driftForward allowed future steps to check
16 * @param {number} options.driftBackward allowed past steps to check
18 constructor(options
) {
19 const _options
= { ...options
};
22 0n
, // Check now first
23 ...Array
.from({ length: this.driftBackward
}, (v
, k
) => BigInt(-(k
+ 1))),
24 ...Array
.from({ length: this.driftForward
}, (v
, k
) => BigInt(k
+ 1)),
28 static get _algorithmKeyLengths() {
30 ...super._algorithmKeyLengths
,
37 * The type used when constructing the otpauth URI.
38 * @returns {string} otp auth type
45 * Derive counter from epoch.
46 * @returns {bigint} time based counter
49 const epoch
= Math
.floor(Date
.now() / 1000);
50 return BigInt(Math
.floor((epoch
- this.timeStepStartSeconds
) / this.timeStepSeconds
));
53 set counter(_
) { /* Ignore assignment */ } // eslint-disable-line class-methods-use-this
55 static get _defaultOptions() {
56 const options
= Object
.assign(super._defaultOptions
, {
58 timeStepStartSeconds: 0,
62 delete options
.counter
;
68 * @param {bigint=} count counter value
69 * @returns {string} code
71 generate(count
= this.counter
) {
72 return super.generate(count
);
77 * @param {string} hotp code
78 * @param {bigint=} count counter value
79 * @returns {boolean} is valid
81 validate(hotp
, count
) {
82 const counter
= count
?? this.counter
;
83 for (const offset
of this.driftOffsets
) {
84 const codeString
= this.generate(counter
+ offset
);
85 if (codeString
=== hotp
.trim()) {
94 module
.exports
= TimeBasedOneTimePassword
;