Danger Zone
Caution!
This guide covers advanced configurations that have implications on security or reliability.
For standard use cases, the defaults should be sufficient.
Guardrails
WARNING
Only modify guardrails if you have specific requirements and understand the security implications.
Guardrails are validation limits that protect against common security vulnerabilities and implementation errors:
- Prevent weak secrets that can be brute-forced
- Limit verification windows to reduce replay attack surface
- Reject extreme values that indicate bugs or attacks
By default, otplib enforces sensible limits based on RFC recommendations and security best practices. However, some production systems may have legitimate needs to override these limits.
Why Override Guardrails?
Valid reasons to customize guardrails:
- Legacy System Integration: Existing systems using non-standard parameters
- Regulatory Requirements: Industry-specific compliance needs
- Specialized Hardware: Devices with unique constraints
- Testing: Controlled test environments requiring extreme values
How to Override Guardrails
The createGuardrails() factory is provided to allow you to override the default guardrails. It returns a frozen (immutable) guardrails object.
import { createGuardrails } from "@otplib/core";
// Returns a frozen (immutable) guardrails object
const guardrails = createGuardrails({
MIN_SECRET_BYTES: 10,
MAX_WINDOW: 20,
});
// Attempting to modify throws an error
guardrails.MAX_WINDOW = 30; // TypeError: Cannot assign to read only propertyYou only need to specify the guardrails you want to override. Unspecified limits use their defaults.
WARNING
There is NO validation performed on the guardrails that are set this way. It is up to the developer to ensure that the guardrails are valid.
For example, setting MIN_SECRET_BYTES to a value higher than MAX_SECRET_BYTES might result in unexpected behavior.
Overridable Guardrails
| Setting | Default | Risk | When to modify |
|---|---|---|---|
| MIN_SECRET_BYTES | 16 bytes (128 bits) | Secrets become vulnerable to brute-force attacks. A 10-byte secret has only 2^80 possible values. | Only when integrating with legacy systems that cannot be upgraded. |
| MAX_SECRET_BYTES | 1024 bytes | Potential DoS attacks through excessive memory consumption. | Rarely needed. Standard secrets are 20-32 bytes. |
| MIN_PERIOD | 1 second | Below 1 second, TOTP, behaviour will become unpredicatable. | Use HOTP instead if you need event-based OTPs. |
| MAX_PERIOD | 3600 seconds (1 hour) | Tokens remain valid longer, increasing replay attack window. | Specialized systems with coarse time granularity (e.g., daily batch processes). |
| MAX_WINDOW | 10 positions | Larger verification windows increase replay attack surface exponentially. | Systems with extreme clock drift or poor network conditions. Consider fixing the underlying issue instead. |
Usage Examples
Functional API
import { generate, verify, createGuardrails } from "@otplib/hotp";
import { NodeCryptoPlugin } from "@otplib/plugin-crypto-node";
// Create custom guardrails
const customGuardrails = createGuardrails({
MIN_SECRET_BYTES: 10, // Lower limit for legacy system
MAX_WINDOW: 20, // Larger window for poor network
});
// Pass to generate/verify functions
const token = await generate({
secret: mySecret,
counter: 0,
crypto: new NodeCryptoPlugin(),
guardrails: customGuardrails,
});
const result = await verify({
secret: mySecret,
token,
counter: 0,
counterTolerance: 15,
crypto: new NodeCryptoPlugin(),
guardrails: customGuardrails,
});Class-Based API
Instance-Level Guardrails
import { HOTP, createGuardrails } from "@otplib/hotp";
import { NodeCryptoPlugin } from "@otplib/plugin-crypto-node";
import { ScureBase32Plugin } from "@otplib/plugin-base32-scure";
// Create custom guardrails using the factory function
const customGuardrails = createGuardrails({
MAX_WINDOW: 20,
});
// Configure guardrails for all operations on this instance
const hotp = new HOTP({
secret: "JBSWY3DPEHPK3PXP",
crypto: new NodeCryptoPlugin(),
base32: new ScureBase32Plugin(),
guardrails: customGuardrails,
});
// All generate/verify calls use the instance guardrails
const token = await hotp.generate(0);
const result = await hotp.verify({ token, counter: 0 });Method-Level Overrides
import { HOTP, createGuardrails } from "@otplib/hotp";
import { NodeCryptoPlugin } from "@otplib/plugin-crypto-node";
import { ScureBase32Plugin } from "@otplib/plugin-base32-scure";
// Instance with standard guardrails
const hotp = new HOTP({
secret: "JBSWY3DPEHPK3PXP",
crypto: new NodeCryptoPlugin(),
base32: new ScureBase32Plugin(),
});
// Override guardrails for a specific operation
const token = await hotp.generate(0, {
guardrails: createGuardrails({ MIN_SECRET_BYTES: 10 }),
});
// Verification with different override
const result = await hotp.verify(
{ token, counter: 0 },
{
counterTolerance: 15,
guardrails: createGuardrails({ MAX_WINDOW: 20 }),
},
);TOTP Example
import { TOTP, createGuardrails } from "@otplib/totp";
import { NodeCryptoPlugin } from "@otplib/plugin-crypto-node";
import { ScureBase32Plugin } from "@otplib/plugin-base32-scure";
const totp = new TOTP({
secret: "JBSWY3DPEHPK3PXP",
period: 60, // 60-second periods
crypto: new NodeCryptoPlugin(),
base32: new ScureBase32Plugin(),
guardrails: createGuardrails({
MAX_PERIOD: 120, // Allow up to 2-minute periods
MAX_WINDOW: 5, // Tighter window than default
}),
});
const token = await totp.generate();
const result = await totp.verify(token, { epochTolerance: 30 });Related Documentation
- Security Best Practices - General security guidelines
- Advanced Usage - Advanced configuration options
- RFC Implementations - Standards compliance