totpify
Version:
Advanced TOTP Library with enhanced security features and algorithm support
140 lines • 5.33 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.generateTOTP = generateTOTP;
exports.verifyTOTP = verifyTOTP;
exports.generateQRCode = generateQRCode;
exports.generateRandomSecret = generateRandomSecret;
const utils_1 = require("./utils");
const QRCode = __importStar(require("qrcode"));
/**
* Generates a Time-Based One-Time Password (TOTP) according to RFC 623
* @param secret The secret key as a string (Base32 encoded) or a Uint8Array
* @param options Options for TOTP generation
* @returns The generated TOTP code
*/
function generateTOTP(secret, options = {}) {
const { algorithm = 'SHA-1', digits = 6, period = 30, timestamp = Date.now() } = options;
if (!secret) {
throw new Error('Secret must be provided');
}
let secretBytes;
if (typeof secret === 'string') {
try {
secretBytes = (0, utils_1.decodeBase32)(secret);
}
catch (error) {
throw new Error(`Invalid secret: ${error.message}`);
}
}
else {
secretBytes = secret;
}
const counter = Math.floor(timestamp / 1000 / period);
return (0, utils_1.generateHOTP)(secretBytes, counter, algorithm, digits);
}
/**
* Verifies a Time-Based One-Time Password (TOTP) allowing for time skew
* @param token The TOTP code to verify
* @param secret The secret key as a string (Base32 encoded) or a Uint8Array
* @param options Options for TOTP verification
* @returns Result object with valid flag and time drift information
*/
function verifyTOTP(token, secret, options = {}) {
const { window = 1, algorithm = 'SHA-1', digits = 6, period = 30, timestamp = Date.now() } = options;
if (!token || token.length !== digits || !/^\d+$/.test(token)) {
return { valid: false };
}
for (let i = -window; i <= window; i++) {
const checkTime = timestamp + i * period * 1000;
const generatedToken = generateTOTP(secret, {
algorithm,
digits,
period,
timestamp: checkTime,
});
if (generatedToken === token) {
return { valid: true, delta: i };
}
}
return { valid: false };
}
/**
* Generates a QR code for easy TOTP setup with authenticator apps.
* The QR code follows the 'otpauth://' URI format.
* @param secret The secret key (Base32 encoded)
* @param options Options for QR code generation
* @returns Promise resolving to a data URL containing the QR code
*/
async function generateQRCode(secret, options = {}) {
const { issuer = 'Totpify', account = 'user', width = 256, height = 256, } = options;
const normalizedSecret = (0, utils_1.normalizeSecret)(secret);
const encodedIssuer = encodeURIComponent(issuer);
const encodedAccount = encodeURIComponent(account);
const uri = `otpauth://totp/${encodedIssuer}:${encodedAccount}?secret=${normalizedSecret}&issuer=${encodedIssuer}`;
try {
return await QRCode.toDataURL(uri, {
errorCorrectionLevel: 'H',
type: 'image/png',
margin: 1,
width
});
}
catch (error) {
throw new Error(`QR code generation failed: ${error.message}`);
}
}
/**
* Generates a random secret key in Base32 format for use with TOTP
* @param length The length of the secret key in bytes (default: 20)
* @returns Base32 encoded random secret
*/
function generateRandomSecret(length = 20) {
const randomBytes = new Uint8Array(length);
if (typeof window !== 'undefined' && window.crypto) {
window.crypto.getRandomValues(randomBytes);
}
else {
const crypto = require('crypto');
randomBytes.set(crypto.randomBytes(length));
}
const base32Chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
let result = '';
for (let i = 0; i < length; i++) {
result += base32Chars[randomBytes[i] % base32Chars.length];
}
return result;
}
//# sourceMappingURL=totp.js.map