otpauth
Version:
One Time Password (HOTP/TOTP) library for Node.js, Deno and browsers
199 lines (190 loc) • 4.74 kB
JavaScript
import { HOTP } from './hotp';
import { Secret } from './secret';
/**
* TOTP: Time-Based One-Time Password Algorithm.
* {@link https://tools.ietf.org/html/rfc6238|RFC 6238}
*/
class TOTP {
/**
* Default configuration.
* @type {{
* issuer: string,
* label: string,
* algorithm: string,
* digits: number,
* period: number
* window: number
* }}
*/
static get defaults() {
return {
issuer: '',
label: 'OTPAuth',
algorithm: 'SHA1',
digits: 6,
period: 30,
window: 1,
};
}
/**
* Creates a TOTP object.
* @param {Object} [config] Configuration options.
* @param {string} [config.issuer=''] Account provider.
* @param {string} [config.label='OTPAuth'] Account label.
* @param {Secret|string} [config.secret=Secret] Secret key.
* @param {string} [config.algorithm='SHA1'] HMAC hashing algorithm.
* @param {number} [config.digits=6] Token length.
* @param {number} [config.period=30] Token time-step duration.
*/
constructor({
issuer = TOTP.defaults.issuer,
label = TOTP.defaults.label,
secret = new Secret(),
algorithm = TOTP.defaults.algorithm,
digits = TOTP.defaults.digits,
period = TOTP.defaults.period,
} = {}) {
/**
* Account provider.
* @type {string}
*/
this.issuer = issuer;
/**
* Account label.
* @type {string}
*/
this.label = label;
/**
* Secret key.
* @type {Secret}
*/
this.secret = typeof secret === 'string'
? Secret.fromBase32(secret)
: secret;
/**
* HMAC hashing algorithm.
* @type {string}
*/
this.algorithm = algorithm.toUpperCase();
/**
* Token length.
* @type {number}
*/
this.digits = digits;
/**
* Token time-step duration.
* @type {number}
*/
this.period = period;
}
/**
* Generates a TOTP token.
* @param {Object} config Configuration options.
* @param {Secret} config.secret Secret key.
* @param {string} [config.algorithm='SHA1'] HMAC hashing algorithm.
* @param {number} [config.digits=6] Token length.
* @param {number} [config.period=30] Token time-step duration.
* @param {number} [config.timestamp=Date.now] Timestamp value in milliseconds.
* @returns {string} Token.
*/
static generate({
secret,
algorithm,
digits,
period = TOTP.defaults.period,
timestamp = Date.now(),
}) {
return HOTP.generate({
secret,
algorithm,
digits,
counter: Math.floor(timestamp / 1000 / period),
});
}
/**
* Generates a TOTP token.
* @param {Object} [config] Configuration options.
* @param {number} [config.timestamp=Date.now] Timestamp value in milliseconds.
* @returns {string} Token.
*/
generate({
timestamp = Date.now(),
} = {}) {
return TOTP.generate({
secret: this.secret,
algorithm: this.algorithm,
digits: this.digits,
period: this.period,
timestamp,
});
}
/**
* Validates a TOTP token.
* @param {Object} config Configuration options.
* @param {string} config.token Token value.
* @param {Secret} config.secret Secret key.
* @param {string} [config.algorithm='SHA1'] HMAC hashing algorithm.
* @param {number} config.digits Token length.
* @param {number} [config.period=30] Token time-step duration.
* @param {number} [config.timestamp=Date.now] Timestamp value in milliseconds.
* @param {number} [config.window=1] Window of counter values to test.
* @returns {number|null} Token delta, or null if the token is not found.
*/
static validate({
token,
secret,
algorithm,
digits,
period = TOTP.defaults.period,
timestamp = Date.now(),
window,
}) {
return HOTP.validate({
token,
secret,
algorithm,
digits,
counter: Math.floor(timestamp / 1000 / period),
window,
});
}
/**
* Validates a TOTP token.
* @param {Object} config Configuration options.
* @param {string} config.token Token value.
* @param {number} [config.timestamp=Date.now] Timestamp value in milliseconds.
* @param {number} [config.window=1] Window of counter values to test.
* @returns {number|null} Token delta, or null if the token is not found.
*/
validate({
token,
timestamp,
window,
}) {
return TOTP.validate({
token,
secret: this.secret,
algorithm: this.algorithm,
digits: this.digits,
period: this.period,
timestamp,
window,
});
}
/**
* Returns a Google Authenticator key URI.
* @returns {string} URI.
*/
toString() {
const e = encodeURIComponent;
return 'otpauth://totp/'
+ `${this.issuer.length > 0
? `${e(this.issuer)}:${e(this.label)}?issuer=${e(this.issuer)}&`
: `${e(this.label)}?`}`
+ `secret=${e(this.secret.base32)}&`
+ `algorithm=${e(this.algorithm)}&`
+ `digits=${e(this.digits)}&`
+ `period=${e(this.period)}`;
}
}
export { TOTP };