OTP Utility for Google Authenticator
This package provides comprehensive utilities for generating and verifying One-Time Passwords (OTP), adhering to both HOTP (HMAC-Based One-Time Password Algorithm) as defined in RFC 4226 and TOTP (Time-Based One-Time Password Algorithm) as defined in RFC 6238. It is designed to be compatible with popular OTP mechanisms like Google Authenticator. The current stable version is 2.0.1, indicating active development and maintenance. The library differentiates itself by offering direct support for generating Google Authenticator-compatible URLs, parsing existing OTP URLs or base32-encoded secrets, and including a JSON reviver for seamless serialization and deserialization of OTP objects. While a specific release cadence is not formally stated, significant refactors, such as the TypeScript conversion in v1.0.0, highlight ongoing efforts to modernize and improve the codebase.
Common errors
-
TypeError: OTP is not a constructor
cause Attempting to import the OTP class using CommonJS `require()` syntax or an incorrect named import with ESM.fixUse the correct ESM default import: `import OTP from 'otp';` Ensure your environment supports ESM modules (e.g., Node.js with `"type": "module"` in `package.json` or transpilation). -
UnhandledPromiseRejectionWarning: Promise { <pending> }cause Calling `otpInstance.totp()` or `otpInstance.hotp()` without awaiting the returned Promise.fixAlways `await` the asynchronous OTP generation methods: `const code = await otpInstance.totp();` -
Error: Invalid secret length
cause The provided secret, after base32 decoding, does not result in a key length of 64 or 128 bits, which are the supported `keySize` values.fixEnsure your secret (either a Buffer or Base32-encoded string) corresponds to a decoded length of 8 bytes (64 bits) or 16 bytes (128 bits). Adjust `keySize` option if using a non-default length. -
Error: Failed to parse OTP URL
cause The string passed to `OTP.parse()` is not a valid `otpauth://` URL format or is malformed.fixVerify that the URL string conforms to the `otpauth://` scheme, including type, label, and parameters like `secret` and `issuer`.
Warnings
- breaking Version 1.0.0 introduced a significant TypeScript refactor, Buffer cleanup, and dependency removal. Users upgrading from pre-1.0.0 versions may encounter breaking changes related to import paths, module exports (ESM vs CJS), and how secrets (especially Buffer types) are handled.
- gotcha The `hotp()` and `totp()` methods return Promises. Forgetting to `await` their results will lead to unhandled promise rejections and incorrect OTP retrieval.
- gotcha OTP secrets (keys) are highly sensitive. Improper storage (e.g., plaintext in code or insecure databases) or transmission (e.g., over unencrypted channels) can compromise the security of your two-factor authentication.
- gotcha For HOTP, the counter (`hotp(counter)`) must be managed externally and reliably incremented for each use. If the counter is not correctly synchronized between the client and server, HOTP codes will consistently fail validation.
- gotcha The `keySize` option for OTP generation (default 64) refers to the *bits* of the key after decoding, which means a 64-bit key or 128-bit key. Ensure your provided `secret` (when base32 decoded) matches the expected key length for robust security.
Install
-
npm install otp -
yarn add otp -
pnpm add otp
Imports
- OTP
const OTP = require('otp');import OTP from 'otp';
- OTP.parse
import { parse } from 'otp'; // parse is a static method of the OTP classimport OTP from 'otp'; const options = OTP.parse('otpauth://...'); - OTP.reviveJSON
import { reviveJSON } from 'otp'; // reviveJSON is a static method of the OTP classimport OTP from 'otp'; const obj = JSON.parse(jsonString, OTP.reviveJSON);
Quickstart
import OTP from 'otp';
async function demonstrateOtp() {
// Use a securely generated, base32-encoded secret.
// For demonstration, a common test secret "JBSWY3DPEHPK3PXP" (Hello!) is used.
const secret = 'JBSWY3DPEHPK3PXP'; // Replace with a strong, random secret in production
console.log('Using Secret (Base32):', secret);
// Initialize OTP with a secret and desired name
const otpInstance = new OTP({
name: 'MyOTPApp',
secret: secret,
codeLength: 6,
timeSlice: 30, // Default is 30 seconds
});
// --- TOTP Demonstration ---
console.log('\n--- TOTP ---');
const currentTotpCode = await otpInstance.totp();
console.log(`Current TOTP code (valid for ~30s): ${currentTotpCode}`);
console.log('Google Authenticator URL (TOTP):', otpInstance.totpURL);
// --- HOTP Demonstration ---
console.log('\n--- HOTP ---');
// For HOTP, the counter must be managed and incremented server-side
const counter = 123;
const hotpCode = await otpInstance.hotp(counter);
console.log(`HOTP code for counter ${counter}: ${hotpCode}`);
console.log('Google Authenticator URL (HOTP):', otpInstance.hotpURL);
// --- Parsing an OTP URL ---
console.log('\n--- URL Parsing ---');
const exampleTotpUrl = otpInstance.totpURL; // Or any `otpauth://` URL
const parsedOptions = OTP.parse(exampleTotpUrl);
console.log('Parsed OTP options from URL:', parsedOptions);
// Example: parsedOptions might contain { type: 'totp', label: 'MyOTPApp', secret: 'JBSWY3DPEHPK3PXP', ... }
// --- Reviving an OTP object from JSON ---
console.log('\n--- JSON Reviver ---');
const stringifiedOtp = JSON.stringify(otpInstance);
console.log('Stringified OTP object (truncated):', stringifiedOtp.substring(0, 100) + '...');
const revivedOtp = JSON.parse(stringifiedOtp, OTP.reviveJSON);
console.log('Revived OTP object secret matches original:', revivedOtp.secret === otpInstance.secret);
}
demonstrateOtp().catch(console.error);