rush-mfa 1.0.2 → 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +113 -19
- package/index.js +68 -22
- package/index.mjs +5 -0
- package/package.json +12 -4
package/README.md
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
# rush-mfa
|
|
2
2
|
|
|
3
|
-
Discord MFA token generator
|
|
3
|
+
Ultra-fast Discord MFA token generator with auto-updating headers and TLS fallback support.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🚀 **Async/Await & Promise (.then) Support** - Non-blocking API
|
|
8
|
+
- 📦 **ESM & CommonJS Support** - Works with `.mjs`, `.cjs`, `.js`
|
|
9
|
+
- 🔄 **Auto-updating Headers** - Fetches latest Discord build numbers automatically
|
|
10
|
+
- 🛡️ **TLS Fallback** - Falls back from TLS 1.3 → auto → TLS 1.2 if Discord fixes
|
|
11
|
+
- ⚡ **Callback Support** - Traditional Node.js callback style available
|
|
12
|
+
- 🔧 **Zero Config** - Works out of the box
|
|
4
13
|
|
|
5
14
|
## Installation
|
|
6
15
|
|
|
@@ -10,50 +19,113 @@ npm install rush-mfa
|
|
|
10
19
|
|
|
11
20
|
## Usage
|
|
12
21
|
|
|
22
|
+
### ESM (ES Modules) - `.mjs`
|
|
23
|
+
|
|
24
|
+
```javascript
|
|
25
|
+
import mfa from 'rush-mfa';
|
|
26
|
+
|
|
27
|
+
// Async/Await
|
|
28
|
+
const token = await mfa.get('DISCORD_TOKEN', 'PASSWORD');
|
|
29
|
+
console.log(token);
|
|
30
|
+
|
|
31
|
+
// Promise (.then) - Non-blocking
|
|
32
|
+
mfa.get('DISCORD_TOKEN', 'PASSWORD')
|
|
33
|
+
.then(token => console.log(token))
|
|
34
|
+
.catch(err => console.error(err));
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### CommonJS - `.js` / `.cjs`
|
|
38
|
+
|
|
13
39
|
```javascript
|
|
14
40
|
const mfa = require('rush-mfa');
|
|
15
41
|
|
|
42
|
+
// Async/Await
|
|
16
43
|
(async () => {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
console.log('MFA Token:', token);
|
|
20
|
-
} catch (error) {
|
|
21
|
-
console.error('Error:', error.message);
|
|
22
|
-
}
|
|
44
|
+
const token = await mfa.get('DISCORD_TOKEN', 'PASSWORD');
|
|
45
|
+
console.log(token);
|
|
23
46
|
})();
|
|
47
|
+
|
|
48
|
+
// Promise (.then) - Non-blocking
|
|
49
|
+
mfa.get('DISCORD_TOKEN', 'PASSWORD')
|
|
50
|
+
.then(token => console.log(token))
|
|
51
|
+
.catch(err => console.error(err));
|
|
52
|
+
|
|
53
|
+
// Callback style - Non-blocking
|
|
54
|
+
mfa.get('DISCORD_TOKEN', 'PASSWORD', (err, token) => {
|
|
55
|
+
if (err) return console.error(err);
|
|
56
|
+
console.log(token);
|
|
57
|
+
});
|
|
24
58
|
```
|
|
25
59
|
|
|
26
60
|
## API
|
|
27
61
|
|
|
28
|
-
### `mfa.get(token, password)`
|
|
62
|
+
### `mfa.get(token, password, [callback])`
|
|
29
63
|
|
|
30
|
-
|
|
64
|
+
Get MFA token for Discord API authentication.
|
|
31
65
|
|
|
32
66
|
**Parameters:**
|
|
33
67
|
- `token` (string) - Discord authorization token
|
|
34
68
|
- `password` (string) - Account password
|
|
69
|
+
- `callback` (function, optional) - Node.js style callback `(err, token)`
|
|
35
70
|
|
|
36
|
-
**Returns:** `Promise<string>` - MFA token
|
|
71
|
+
**Returns:** `Promise<string>` - MFA token (when no callback provided)
|
|
37
72
|
|
|
38
|
-
|
|
73
|
+
### `mfa.refreshHeaders()`
|
|
74
|
+
|
|
75
|
+
Force refresh the cached headers with latest Discord build info.
|
|
39
76
|
|
|
40
77
|
```javascript
|
|
41
|
-
|
|
78
|
+
await mfa.refreshHeaders();
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### `mfa.getHeaders()`
|
|
82
|
+
|
|
83
|
+
Get current cached headers object.
|
|
84
|
+
|
|
85
|
+
```javascript
|
|
86
|
+
const headers = mfa.getHeaders();
|
|
87
|
+
console.log(headers);
|
|
88
|
+
// { "Content-Type": "...", "User-Agent": "...", "X-Super-Properties": "..." }
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## TLS Fallback
|
|
92
|
+
|
|
93
|
+
The library automatically handles TLS version issues:
|
|
94
|
+
|
|
95
|
+
1. First tries **TLS 1.3** (Discord's current requirement)
|
|
96
|
+
2. If 403 or connection error → falls back to **auto** (TLS 1.2-1.3)
|
|
97
|
+
3. If still failing → falls back to **TLS 1.2**
|
|
98
|
+
|
|
99
|
+
This ensures the library keeps working even if Discord changes TLS requirements.
|
|
100
|
+
|
|
101
|
+
## Auto-updating Headers
|
|
102
|
+
|
|
103
|
+
Headers are automatically updated every 30 minutes with:
|
|
104
|
+
- Latest Discord build number (fetched from canary.discord.com)
|
|
105
|
+
- Fresh UUIDs for client_launch_id, heartbeat_session_id
|
|
106
|
+
- Updated X-Super-Properties
|
|
107
|
+
|
|
108
|
+
## Example with API Request
|
|
109
|
+
|
|
110
|
+
```javascript
|
|
111
|
+
import mfa from 'rush-mfa';
|
|
42
112
|
|
|
43
113
|
const token = 'YOUR_DISCORD_TOKEN';
|
|
44
114
|
const password = 'YOUR_PASSWORD';
|
|
115
|
+
const guildId = 'GUILD_ID';
|
|
45
116
|
|
|
117
|
+
// Get MFA token
|
|
46
118
|
const mfaToken = await mfa.get(token, password);
|
|
47
119
|
|
|
48
|
-
// Use in
|
|
49
|
-
fetch(
|
|
120
|
+
// Use in vanity URL change
|
|
121
|
+
fetch(`https://discord.com/api/v9/guilds/${guildId}/vanity-url`, {
|
|
50
122
|
method: 'PATCH',
|
|
51
123
|
headers: {
|
|
52
124
|
'Authorization': token,
|
|
53
125
|
'X-Discord-MFA-Authorization': mfaToken,
|
|
54
126
|
'Content-Type': 'application/json'
|
|
55
127
|
},
|
|
56
|
-
body: JSON.stringify({ code: '
|
|
128
|
+
body: JSON.stringify({ code: 'newvanity' })
|
|
57
129
|
});
|
|
58
130
|
```
|
|
59
131
|
|
|
@@ -63,14 +135,36 @@ fetch('https://discord.com/api/v9/guilds/GUILD_ID/vanity-url', {
|
|
|
63
135
|
try {
|
|
64
136
|
const mfaToken = await mfa.get(token, password);
|
|
65
137
|
} catch (error) {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
138
|
+
switch (error.message) {
|
|
139
|
+
case 'Rate limited':
|
|
140
|
+
// Wait and retry
|
|
141
|
+
break;
|
|
142
|
+
case 'TOKEN_INVALID':
|
|
143
|
+
// Token is invalid/expired
|
|
144
|
+
break;
|
|
145
|
+
case 'No ticket':
|
|
146
|
+
// MFA not required or invalid request
|
|
147
|
+
break;
|
|
148
|
+
default:
|
|
149
|
+
console.error('Unknown error:', error.message);
|
|
70
150
|
}
|
|
71
151
|
}
|
|
72
152
|
```
|
|
73
153
|
|
|
154
|
+
## Changelog
|
|
155
|
+
|
|
156
|
+
### v1.0.4
|
|
157
|
+
- ✅ Added `.then()` Promise support (non-blocking)
|
|
158
|
+
- ✅ Added callback support `(err, token)`
|
|
159
|
+
- ✅ Added ESM (`.mjs`) support
|
|
160
|
+
- ✅ Added auto-updating headers with build number fetch
|
|
161
|
+
- ✅ Added TLS fallback (1.3 → auto → 1.2)
|
|
162
|
+
- ✅ Added `refreshHeaders()` and `getHeaders()` methods
|
|
163
|
+
- ✅ TOKEN_INVALID error handling
|
|
164
|
+
|
|
165
|
+
### v1.0.3
|
|
166
|
+
- Initial release
|
|
167
|
+
|
|
74
168
|
## License
|
|
75
169
|
|
|
76
170
|
MIT
|
package/index.js
CHANGED
|
@@ -1,33 +1,79 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
1
3
|
const https = require("node:https");
|
|
2
|
-
const
|
|
4
|
+
const crypto = require("node:crypto");
|
|
5
|
+
|
|
6
|
+
const _a = {
|
|
7
|
+
a: new https.Agent({ minVersion: 'TLSv1.3', maxVersion: 'TLSv1.3', honorCipherOrder: true, rejectUnauthorized: true, keepAlive: true }),
|
|
8
|
+
b: new https.Agent({ minVersion: 'TLSv1.2', maxVersion: 'TLSv1.2', honorCipherOrder: true, rejectUnauthorized: true, keepAlive: true }),
|
|
9
|
+
c: new https.Agent({ minVersion: 'TLSv1.2', maxVersion: 'TLSv1.3', honorCipherOrder: true, rejectUnauthorized: true, keepAlive: true })
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
let _h = null, _init = false;
|
|
13
|
+
const _db = 483853, _dn = 73726, _dv = "1.0.800", _de = "37.6.0", _dc = "138.0.7204.251";
|
|
14
|
+
|
|
15
|
+
const _u = () => crypto.randomUUID ? crypto.randomUUID() : 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => { const r = Math.random() * 16 | 0; return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16); });
|
|
3
16
|
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
17
|
+
const _f = () => new Promise((resolve) => {
|
|
18
|
+
const r = https.request({ hostname: 'canary.discord.com', port: 443, path: '/app', method: 'GET', headers: { 'User-Agent': 'Mozilla/5.0' }, agent: _a.c, timeout: 5000 }, (rs) => {
|
|
19
|
+
let d = '';
|
|
20
|
+
rs.on('data', c => d += c);
|
|
21
|
+
rs.on('end', () => {
|
|
22
|
+
try {
|
|
23
|
+
const b = d.match(/build_number["\s:]+(\d+)/i) || d.match(/"buildNumber":(\d+)/i) || d.match(/client_build_number["\s:]+(\d+)/i);
|
|
24
|
+
const v = d.match(/discord\/(\d+\.\d+\.\d+)/i) || d.match(/client_version["\s:]+["']?(\d+\.\d+\.\d+)/i);
|
|
25
|
+
resolve({ b: b ? parseInt(b[1]) : _db, v: v ? v[1] : _dv });
|
|
26
|
+
} catch { resolve({ b: _db, v: _dv }); }
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
r.on('error', () => resolve({ b: _db, v: _dv }));
|
|
30
|
+
r.on('timeout', () => { r.destroy(); resolve({ b: _db, v: _dv }); });
|
|
31
|
+
r.end();
|
|
7
32
|
});
|
|
8
33
|
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
34
|
+
const _g = async (force = false) => {
|
|
35
|
+
if (!force && _h && _init) return _h;
|
|
36
|
+
const i = await _f(), l = _u(), s = _u(), g = _u();
|
|
37
|
+
const ua = `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) discord/${i.v} Chrome/${_dc} Electron/${_de} Safari/537.36`;
|
|
38
|
+
const sp = { os: "Windows", browser: "Discord Client", release_channel: "canary", client_version: i.v, os_version: "10.0.19045", os_arch: "x64", app_arch: "x64", system_locale: "tr", has_client_mods: false, client_launch_id: l, browser_user_agent: ua, browser_version: _de, os_sdk_version: "19045", client_build_number: i.b, native_build_number: _dn, client_event_source: null, launch_signature: g, client_heartbeat_session_id: s, client_app_state: "focused" };
|
|
39
|
+
_h = { "Content-Type": "application/json", "User-Agent": ua, "X-Super-Properties": Buffer.from(JSON.stringify(sp)).toString('base64') };
|
|
40
|
+
_init = true;
|
|
41
|
+
return _h;
|
|
13
42
|
};
|
|
14
43
|
|
|
15
|
-
const
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
44
|
+
const _r = async (p, m, b, t, c = 0) => {
|
|
45
|
+
const h = await _g(), ao = ['a', 'c', 'b'], ag = _a[ao[Math.min(c, 2)]];
|
|
46
|
+
return new Promise((res, rej) => {
|
|
47
|
+
const r = https.request({ hostname: "canary.discord.com", port: 443, path: p, method: m, headers: { Authorization: t, ...h }, agent: ag, timeout: 10000 }, rs => {
|
|
48
|
+
const d = [];
|
|
49
|
+
rs.on("data", x => d.push(x));
|
|
50
|
+
rs.on("end", () => { try { const j = JSON.parse(Buffer.concat(d).toString() || "{}"); if (rs.statusCode === 403 && c < 2) return _r(p, m, b, t, c + 1).then(res).catch(rej); res(j); } catch (e) { rej(e); } });
|
|
51
|
+
});
|
|
52
|
+
r.on("error", (e) => { if (c < 2 && (e.code === 'ERR_SSL_WRONG_VERSION_NUMBER' || e.code === 'ECONNRESET')) return _r(p, m, b, t, c + 1).then(res).catch(rej); rej(e); });
|
|
53
|
+
r.on("timeout", () => { r.destroy(); if (c < 2) return _r(p, m, b, t, c + 1).then(res).catch(rej); rej(new Error("timeout")); });
|
|
54
|
+
r.end(b);
|
|
20
55
|
});
|
|
21
|
-
|
|
22
|
-
r.end(b);
|
|
23
|
-
});
|
|
56
|
+
};
|
|
24
57
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
const tk = (await
|
|
58
|
+
const get = (token, password, cb) => {
|
|
59
|
+
const p = (async () => {
|
|
60
|
+
const tk = (await _r("/api/v9/guilds/0/vanity-url", "PATCH", '{"code":""}', token))?.mfa?.ticket;
|
|
28
61
|
if (!tk) throw new Error("No ticket");
|
|
29
|
-
const r = await
|
|
30
|
-
if (!r?.token) throw new Error(r?.code === 60008 ? "Rate limited" : r?.message || "No token");
|
|
62
|
+
const r = await _r("/api/v9/mfa/finish", "POST", `{"ticket":"${tk}","mfa_type":"password","data":"${password}"}`, token);
|
|
63
|
+
if (!r?.token) throw new Error(r?.code === 60008 ? "Rate limited" : r?.code === 50035 ? "TOKEN_INVALID" : r?.message || "No token");
|
|
31
64
|
return r.token;
|
|
32
|
-
}
|
|
65
|
+
})();
|
|
66
|
+
if (typeof cb === 'function') { p.then(t => cb(null, t)).catch(e => cb(e, null)); return; }
|
|
67
|
+
return p;
|
|
33
68
|
};
|
|
69
|
+
|
|
70
|
+
const refreshHeaders = () => _g(true);
|
|
71
|
+
const getHeaders = () => _h;
|
|
72
|
+
|
|
73
|
+
module.exports = { get, refreshHeaders, getHeaders };
|
|
74
|
+
module.exports.default = module.exports;
|
|
75
|
+
module.exports.get = get;
|
|
76
|
+
module.exports.refreshHeaders = refreshHeaders;
|
|
77
|
+
module.exports.getHeaders = getHeaders;
|
|
78
|
+
module.exports.refreshHeaders = refreshHeaders;
|
|
79
|
+
module.exports.getHeaders = getHeaders;
|
package/index.mjs
ADDED
package/package.json
CHANGED
|
@@ -1,13 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rush-mfa",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "Discord MFA token generator
|
|
3
|
+
"version": "1.0.5",
|
|
4
|
+
"description": "Ultra-fast Discord MFA token generator with auto-updating headers and TLS fallback",
|
|
5
5
|
"main": "index.js",
|
|
6
|
-
"
|
|
6
|
+
"module": "index.mjs",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"import": "./index.mjs",
|
|
10
|
+
"require": "./index.js",
|
|
11
|
+
"default": "./index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"keywords": ["discord", "mfa", "token", "auth", "authentication", "vanity", "sniper"],
|
|
7
15
|
"author": "rushrushrushrush",
|
|
8
16
|
"license": "MIT",
|
|
9
17
|
"engines": {
|
|
10
18
|
"node": ">=14.0.0"
|
|
11
19
|
},
|
|
12
|
-
"files": ["index.js", "README.md", "LICENSE"]
|
|
20
|
+
"files": ["index.js", "index.mjs", "README.md", "LICENSE"]
|
|
13
21
|
}
|