UNPKG

otplib

Version:

TypeScript-first library for TOTP and HOTP with multi-runtime and plugin support

1 lines 15.2 kB
{"version":3,"sources":["../src/functional.ts","../src/defaults.ts"],"sourcesContent":["import { generateSecret as generateSecretCore, ConfigurationError } from \"@otplib/core\";\nimport {\n generate as generateHOTP,\n generateSync as generateHOTPSync,\n verify as verifyHOTP,\n verifySync as verifyHOTPSync,\n} from \"@otplib/hotp\";\nimport {\n generate as generateTOTP,\n generateSync as generateTOTPSync,\n verify as verifyTOTP,\n verifySync as verifyTOTPSync,\n} from \"@otplib/totp\";\nimport { generateTOTP as generateTOTPURI } from \"@otplib/uri\";\n\nimport {\n defaultCrypto,\n defaultBase32,\n normalizeGenerateOptions,\n normalizeVerifyOptions,\n} from \"./defaults\";\n\nimport type { OTPGenerateOptions, OTPVerifyOptions, OTPStrategy, StrategyHandlers } from \"./types\";\nimport type { CryptoPlugin, Base32Plugin, Digits, HashAlgorithm } from \"@otplib/core\";\nimport type { VerifyResult as HOTPVerifyResult } from \"@otplib/hotp\";\nimport type { VerifyResult as TOTPVerifyResult } from \"@otplib/totp\";\n\nexport type { OTPStrategy };\n\nexport type VerifyResult = TOTPVerifyResult | HOTPVerifyResult;\n\nfunction executeByStrategy<T>(\n strategy: OTPStrategy,\n counter: number | undefined,\n handlers: StrategyHandlers<T>,\n): T {\n if (strategy === \"totp\") {\n return handlers.totp();\n }\n if (strategy === \"hotp\") {\n if (counter === undefined) {\n throw new ConfigurationError(\n \"Counter is required for HOTP strategy. Example: { strategy: 'hotp', counter: 0 }\",\n );\n }\n return handlers.hotp(counter);\n }\n throw new ConfigurationError(\n `Unknown OTP strategy: ${strategy}. Valid strategies are 'totp' or 'hotp'.`,\n );\n}\n\n/**\n * Generate a random secret key for use with OTP\n *\n * The secret is encoded in Base32 format for compatibility with\n * Google Authenticator and other authenticator apps.\n *\n * @param options - Secret generation options\n * @returns Base32-encoded secret key\n *\n * @example\n * ```ts\n * import { generateSecret } from 'otplib';\n *\n * const secret = generateSecret();\n * // Returns: 'JBSWY3DPEHPK3PXP'\n * ```\n *\n * @example With custom plugins\n * ```ts\n * import { generateSecret, NodeCryptoPlugin } from 'otplib';\n *\n * const secret = generateSecret({\n * crypto: new NodeCryptoPlugin(),\n * });\n * ```\n */\nexport function generateSecret(options?: {\n /**\n * Number of random bytes to generate (default: 20)\n * 20 bytes = 160 bits, which provides a good security margin\n */\n length?: number;\n\n /**\n * Crypto plugin to use (default: NobleCryptoPlugin)\n */\n crypto?: CryptoPlugin;\n\n /**\n * Base32 plugin to use (default: ScureBase32Plugin)\n */\n base32?: Base32Plugin;\n}): string {\n const { crypto = defaultCrypto, base32 = defaultBase32, length = 20 } = options || {};\n\n return generateSecretCore({ crypto, base32, length });\n}\n\n/**\n * Generate an otpauth:// URI for QR code generation\n *\n * This URI can be used to generate a QR code that can be scanned\n * by Google Authenticator and other authenticator apps.\n *\n * @param options - URI generation options\n * @returns otpauth:// URI string\n *\n * @example\n * ```ts\n * import { generateURI } from 'otplib';\n *\n * const uri = generateURI({\n * issuer: 'ACME Co',\n * label: 'john@example.com',\n * secret: 'JBSWY3DPEHPK3PXP',\n * });\n * // Returns: 'otpauth://totp/ACME%20Co:john%40example.com?secret=...'\n * ```\n */\nexport function generateURI(options: {\n issuer: string;\n label: string;\n secret: string;\n algorithm?: HashAlgorithm;\n digits?: Digits;\n period?: number;\n}): string {\n const { issuer, label, secret, algorithm = \"sha1\", digits = 6, period = 30 } = options;\n return generateTOTPURI({ issuer, label, secret, algorithm, digits, period });\n}\n\n/**\n * Generate an OTP code\n *\n * Generates a one-time password based on the specified strategy.\n * - 'totp': Time-based OTP (default)\n * - 'hotp': HMAC-based OTP\n *\n * @param options - OTP generation options\n * @returns OTP code\n *\n * @example TOTP\n * ```ts\n * import { generate } from 'otplib';\n *\n * const token = await generate({\n * secret: 'JBSWY3DPEHPK3PXP',\n * });\n * // Returns: '123456'\n * ```\n *\n * @example HOTP\n * ```ts\n * import { generate } from 'otplib';\n *\n * const token = await generate({\n * secret: 'JBSWY3DPEHPK3PXP',\n * strategy: 'hotp',\n * counter: 0,\n * });\n * ```\n *\n * @example With custom plugins\n * ```ts\n * import { generate, NodeCryptoPlugin } from 'otplib';\n *\n * const token = await generate({\n * secret: 'JBSWY3DPEHPK3PXP',\n * crypto: new NodeCryptoPlugin(),\n * });\n * ```\n */\nexport async function generate(options: OTPGenerateOptions): Promise<string> {\n const opts = normalizeGenerateOptions(options);\n const { secret, crypto, base32, algorithm, digits } = opts;\n const commonOptions = { secret, crypto, base32, algorithm, digits };\n\n return executeByStrategy(opts.strategy, opts.counter, {\n totp: () =>\n generateTOTP({\n ...commonOptions,\n period: opts.period,\n epoch: opts.epoch,\n t0: opts.t0,\n }),\n hotp: (counter) =>\n generateHOTP({\n ...commonOptions,\n counter,\n }),\n });\n}\n\n/**\n * Generate an OTP code synchronously\n *\n * This is the synchronous version of {@link generate}. It requires a crypto\n * plugin that supports synchronous HMAC operations.\n *\n * @param options - OTP generation options\n * @returns OTP code\n * @throws {HMACError} If the crypto plugin doesn't support sync operations\n *\n * @example\n * ```ts\n * import { generateSync } from 'otplib';\n *\n * const token = generateSync({\n * secret: 'JBSWY3DPEHPK3PXP',\n * });\n * ```\n */\nexport function generateSync(options: OTPGenerateOptions): string {\n const opts = normalizeGenerateOptions(options);\n const { secret, crypto, base32, algorithm, digits } = opts;\n const commonOptions = { secret, crypto, base32, algorithm, digits };\n\n return executeByStrategy(opts.strategy, opts.counter, {\n totp: () =>\n generateTOTPSync({\n ...commonOptions,\n period: opts.period,\n epoch: opts.epoch,\n t0: opts.t0,\n }),\n hotp: (counter) =>\n generateHOTPSync({\n ...commonOptions,\n counter,\n }),\n });\n}\n\n/**\n * Verify an OTP code\n *\n * Verifies a provided OTP code against the expected value based on the strategy.\n * - 'totp': Time-based OTP (default, Google Authenticator compatible)\n * - 'hotp': HMAC-based OTP\n *\n * Uses constant-time comparison to prevent timing attacks.\n *\n * @param options - OTP verification options\n * @returns Verification result with validity and optional delta\n *\n * @example TOTP\n * ```ts\n * import { verify } from 'otplib';\n *\n * const result = await verify({\n * secret: 'JBSWY3DPEHPK3PXP',\n * token: '123456',\n * });\n * // Returns: { valid: true, delta: 0 }\n * ```\n *\n * @example HOTP\n * ```ts\n * import { verify } from 'otplib';\n *\n * const result = await verify({\n * secret: 'JBSWY3DPEHPK3PXP',\n * token: '123456',\n * strategy: 'hotp',\n * counter: 0,\n * });\n * ```\n *\n * @example With epochTolerance for TOTP\n * ```ts\n * import { verify, NodeCryptoPlugin } from 'otplib';\n *\n * const result = await verify({\n * secret: 'JBSWY3DPEHPK3PXP',\n * token: '123456',\n * epochTolerance: 30,\n * crypto: new NodeCryptoPlugin(),\n * });\n * ```\n */\nexport async function verify(options: OTPVerifyOptions): Promise<VerifyResult> {\n const opts = normalizeVerifyOptions(options);\n const { secret, token, crypto, base32, algorithm, digits } = opts;\n const commonOptions = { secret, token, crypto, base32, algorithm, digits };\n\n return executeByStrategy(opts.strategy, opts.counter, {\n totp: () =>\n verifyTOTP({\n ...commonOptions,\n period: opts.period,\n epoch: opts.epoch,\n t0: opts.t0,\n epochTolerance: opts.epochTolerance,\n }),\n hotp: (counter) =>\n verifyHOTP({\n ...commonOptions,\n counter,\n counterTolerance: opts.counterTolerance,\n }),\n });\n}\n\n/**\n * Verify an OTP code synchronously\n *\n * This is the synchronous version of {@link verify}. It requires a crypto\n * plugin that supports synchronous HMAC operations.\n *\n * @param options - OTP verification options\n * @returns Verification result with validity and optional delta\n * @throws {HMACError} If the crypto plugin doesn't support sync operations\n *\n * @example\n * ```ts\n * import { verifySync } from 'otplib';\n *\n * const result = verifySync({\n * secret: 'JBSWY3DPEHPK3PXP',\n * token: '123456',\n * });\n * ```\n */\nexport function verifySync(options: OTPVerifyOptions): VerifyResult {\n const opts = normalizeVerifyOptions(options);\n const { secret, token, crypto, base32, algorithm, digits } = opts;\n const commonOptions = { secret, token, crypto, base32, algorithm, digits };\n\n return executeByStrategy(opts.strategy, opts.counter, {\n totp: () =>\n verifyTOTPSync({\n ...commonOptions,\n period: opts.period,\n epoch: opts.epoch,\n t0: opts.t0,\n epochTolerance: opts.epochTolerance,\n }),\n hotp: (counter) =>\n verifyHOTPSync({\n ...commonOptions,\n counter,\n counterTolerance: opts.counterTolerance,\n }),\n });\n}\n","/**\n * Default plugin instances\n *\n * Shared across functional and class APIs to ensure singleton behavior\n * and reduce memory overhead.\n */\nimport { ScureBase32Plugin } from \"@otplib/plugin-base32-scure\";\nimport { NobleCryptoPlugin } from \"@otplib/plugin-crypto-noble\";\n\nimport type {\n OTPGenerateOptions,\n OTPVerifyOptions,\n OTPGenerateOptionsWithDefaults,\n OTPVerifyOptionsWithDefaults,\n} from \"./types\";\n\n/**\n * Default crypto plugin instance (Noble Hashes)\n *\n * This plugin provides cross-platform cryptographic operations\n * using the @noble/hashes library.\n */\nexport const defaultCrypto = Object.freeze(new NobleCryptoPlugin());\n\n/**\n * Default Base32 plugin instance (@scure/base)\n *\n * This plugin provides Base32 encoding/decoding operations\n * using the @scure/base library.\n */\nexport const defaultBase32 = Object.freeze(new ScureBase32Plugin());\n\nexport function normalizeGenerateOptions(\n options: OTPGenerateOptions,\n): OTPGenerateOptionsWithDefaults {\n return {\n secret: options.secret,\n strategy: options.strategy ?? \"totp\",\n crypto: options.crypto ?? defaultCrypto,\n base32: options.base32 ?? defaultBase32,\n algorithm: options.algorithm ?? \"sha1\",\n digits: options.digits ?? 6,\n period: options.period ?? 30,\n epoch: options.epoch ?? Math.floor(Date.now() / 1000),\n t0: options.t0 ?? 0,\n counter: options.counter,\n };\n}\n\nexport function normalizeVerifyOptions(options: OTPVerifyOptions): OTPVerifyOptionsWithDefaults {\n return {\n ...normalizeGenerateOptions(options),\n token: options.token,\n epochTolerance: options.epochTolerance ?? 0,\n counterTolerance: options.counterTolerance ?? 0,\n };\n}\n"],"mappings":"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","names":["functional_exports","__export","generate","generateSecret","generateSync","generateURI","verify","verifySync","__toCommonJS","import_core","import_hotp","import_totp","import_uri","import_plugin_base32_scure","import_plugin_crypto_noble","defaultCrypto","defaultBase32","normalizeGenerateOptions","options","normalizeVerifyOptions","executeByStrategy","strategy","counter","handlers","generateSecret","options","crypto","defaultCrypto","base32","defaultBase32","length","generateSecretCore","generateURI","issuer","label","secret","algorithm","digits","period","generateTOTPURI","generate","opts","normalizeGenerateOptions","commonOptions","generateTOTP","generateHOTP","generateSync","generateTOTPSync","generateHOTPSync","verify","normalizeVerifyOptions","token","verifyTOTP","verifyHOTP","verifySync","verifyTOTPSync","verifyHOTPSync"]}