baltica 0.1.20 → 0.1.22
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
CHANGED
|
@@ -51,29 +51,56 @@ const client = new Client({
|
|
|
51
51
|
port: 19132,
|
|
52
52
|
});
|
|
53
53
|
|
|
54
|
-
// Connect and get server info
|
|
55
54
|
await client.connect();
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Email/Password Authentication
|
|
58
|
+
|
|
59
|
+
You can authenticate directly with your Microsoft account using email and password:
|
|
56
60
|
|
|
57
|
-
|
|
58
|
-
client
|
|
59
|
-
|
|
61
|
+
```typescript
|
|
62
|
+
const client = new Client({
|
|
63
|
+
address: "play.server.com",
|
|
64
|
+
port: 19132,
|
|
65
|
+
email: "your-email@outlook.com",
|
|
66
|
+
password: "your-password",
|
|
60
67
|
});
|
|
61
68
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
69
|
+
await client.connect();
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
**Important Notes:**
|
|
73
|
+
- This method only works with accounts that have 2FA (Two-Factor Authentication) **disabled**
|
|
74
|
+
- The account must have an Xbox profile and own Minecraft Bedrock Edition or launched minecraft at least once.
|
|
75
|
+
- User tokens are cached for ~14 days to minimize login requests (BETA)
|
|
76
|
+
- Tokens are stored in the `tokens` folder by default
|
|
77
|
+
|
|
78
|
+
### Using a Proxy
|
|
79
|
+
|
|
80
|
+
Baltica supports SOCKS5 proxies for client connections:
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
const client = new Client({
|
|
84
|
+
address: "play.server.com",
|
|
85
|
+
port: 19132,
|
|
86
|
+
email: "your-email@outlook.com",
|
|
87
|
+
password: "your-password",
|
|
88
|
+
proxy: {
|
|
89
|
+
host: "proxy.example.com",
|
|
90
|
+
port: 1080,
|
|
91
|
+
userId: "proxy-username", // Optional
|
|
92
|
+
password: "proxy-password", // Optional
|
|
93
|
+
},
|
|
74
94
|
});
|
|
95
|
+
|
|
96
|
+
await client.connect();
|
|
75
97
|
```
|
|
76
98
|
|
|
99
|
+
This is useful for:
|
|
100
|
+
- Bypassing IP restrictions
|
|
101
|
+
- Testing from different geographic locations
|
|
102
|
+
- Managing multiple bot connections
|
|
103
|
+
|
|
77
104
|
### Server Usage
|
|
78
105
|
|
|
79
106
|
Want to create your own Minecraft server? We got you:
|
|
@@ -6,7 +6,7 @@ const worker_1 = require("./worker");
|
|
|
6
6
|
class WorkerClient extends raknet_1.EventEmitter {
|
|
7
7
|
constructor(options) {
|
|
8
8
|
super();
|
|
9
|
-
this._options = { ...raknet_1.
|
|
9
|
+
this._options = { ...(0, raknet_1.createDefaultClientOptions)(), ...options };
|
|
10
10
|
this._worker = (0, worker_1.connect)(this._options);
|
|
11
11
|
this.handleEvents();
|
|
12
12
|
}
|
|
@@ -1,21 +1,40 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Xbox Live Authentication Module
|
|
3
|
+
*
|
|
4
|
+
* Based on the authentication flow from @xboxreplay/xboxlive-auth
|
|
5
|
+
* https://github.com/XboxReplay/xboxlive-auth
|
|
6
|
+
*
|
|
7
|
+
* Modified to support SOCKS5 proxies for all HTTP requests and Minecraft Authentication
|
|
8
|
+
*/
|
|
3
9
|
export interface BedrockTokens {
|
|
4
10
|
chains: string[];
|
|
5
11
|
xuid: string;
|
|
6
12
|
gamertag: string;
|
|
7
13
|
userHash: string;
|
|
8
14
|
}
|
|
15
|
+
export interface ProxyOptions {
|
|
16
|
+
host: string;
|
|
17
|
+
port: number;
|
|
18
|
+
userId?: string;
|
|
19
|
+
password?: string;
|
|
20
|
+
}
|
|
9
21
|
export interface AuthOptions {
|
|
10
22
|
email: string;
|
|
11
23
|
password: string;
|
|
12
24
|
clientPublicKey: string;
|
|
13
25
|
cacheDir?: string;
|
|
26
|
+
proxy?: ProxyOptions;
|
|
14
27
|
}
|
|
15
28
|
/**
|
|
16
29
|
* Authenticates with Xbox Live using email/password and obtains Minecraft Bedrock tokens
|
|
17
|
-
*
|
|
30
|
+
* Supports SOCKS5 proxy for all authentication requests
|
|
18
31
|
*/
|
|
19
32
|
export declare function authenticateWithCredentials(options: AuthOptions): Promise<BedrockTokens>;
|
|
20
|
-
export
|
|
21
|
-
export
|
|
33
|
+
export type Email = string;
|
|
34
|
+
export interface AuthenticateResponse {
|
|
35
|
+
access_token: string;
|
|
36
|
+
token_type: string;
|
|
37
|
+
expires_in: number;
|
|
38
|
+
scope: string;
|
|
39
|
+
user_id: string;
|
|
40
|
+
}
|
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Xbox Live Authentication Module
|
|
4
|
+
*
|
|
5
|
+
* Based on the authentication flow from @xboxreplay/xboxlive-auth
|
|
6
|
+
* https://github.com/XboxReplay/xboxlive-auth
|
|
7
|
+
*
|
|
8
|
+
* Modified to support SOCKS5 proxies for all HTTP requests and Minecraft Authentication
|
|
9
|
+
*/
|
|
2
10
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
11
|
if (k2 === undefined) k2 = k;
|
|
4
12
|
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
@@ -33,15 +41,14 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
41
|
};
|
|
34
42
|
})();
|
|
35
43
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.xnet = exports.live = void 0;
|
|
37
44
|
exports.authenticateWithCredentials = authenticateWithCredentials;
|
|
38
|
-
const xboxlive_auth_1 = require("@xboxreplay/xboxlive-auth");
|
|
39
|
-
Object.defineProperty(exports, "live", { enumerable: true, get: function () { return xboxlive_auth_1.live; } });
|
|
40
|
-
Object.defineProperty(exports, "xnet", { enumerable: true, get: function () { return xboxlive_auth_1.xnet; } });
|
|
41
45
|
const fs = __importStar(require("node:fs"));
|
|
42
46
|
const path = __importStar(require("node:path"));
|
|
43
47
|
const raknet_1 = require("@sanctumterra/raknet");
|
|
48
|
+
const fetch_socks_1 = require("fetch-socks");
|
|
49
|
+
const undici_1 = require("undici");
|
|
44
50
|
const MINECRAFT_BEDROCK_RELYING_PARTY = "https://multiplayer.minecraft.net/";
|
|
51
|
+
const XBOX_AUTH_CLIENT_ID = "00000000441cc96b";
|
|
45
52
|
function hashString(str) {
|
|
46
53
|
let hash = 0;
|
|
47
54
|
for (let i = 0; i < str.length; i++) {
|
|
@@ -63,7 +70,6 @@ function loadCache(cacheFile) {
|
|
|
63
70
|
try {
|
|
64
71
|
if (fs.existsSync(cacheFile)) {
|
|
65
72
|
const cached = JSON.parse(fs.readFileSync(cacheFile, "utf-8"));
|
|
66
|
-
// Check if token is still valid (with 1 hour buffer)
|
|
67
73
|
const expiresAt = new Date(cached.notAfter).getTime();
|
|
68
74
|
if (Date.now() < expiresAt - 3600000) {
|
|
69
75
|
return cached;
|
|
@@ -91,16 +97,47 @@ function saveCache(cacheFile, userToken, userHash, notAfter) {
|
|
|
91
97
|
/* ignore */
|
|
92
98
|
}
|
|
93
99
|
}
|
|
100
|
+
function createProxiedFetch(proxy) {
|
|
101
|
+
if (!proxy) {
|
|
102
|
+
return fetch;
|
|
103
|
+
}
|
|
104
|
+
const dispatcher = (0, fetch_socks_1.socksDispatcher)({
|
|
105
|
+
type: 5,
|
|
106
|
+
host: proxy.host,
|
|
107
|
+
port: proxy.port,
|
|
108
|
+
userId: proxy.userId,
|
|
109
|
+
password: proxy.password,
|
|
110
|
+
});
|
|
111
|
+
return async (input, init) => {
|
|
112
|
+
const url = typeof input === "string" ? input : input.toString();
|
|
113
|
+
const response = await (0, undici_1.fetch)(url, {
|
|
114
|
+
...init,
|
|
115
|
+
dispatcher,
|
|
116
|
+
});
|
|
117
|
+
return response;
|
|
118
|
+
};
|
|
119
|
+
}
|
|
94
120
|
/**
|
|
95
121
|
* Authenticates with Xbox Live using email/password and obtains Minecraft Bedrock tokens
|
|
96
|
-
*
|
|
122
|
+
* Supports SOCKS5 proxy for all authentication requests
|
|
97
123
|
*/
|
|
98
124
|
async function authenticateWithCredentials(options) {
|
|
99
|
-
const { email, password, clientPublicKey, cacheDir } = options;
|
|
125
|
+
const { email, password, clientPublicKey, cacheDir, proxy } = options;
|
|
100
126
|
const cacheFile = cacheDir ? getCacheFile(cacheDir, email) : null;
|
|
127
|
+
const proxiedFetch = createProxiedFetch(proxy);
|
|
128
|
+
// Verify proxy is working by checking our IP
|
|
129
|
+
if (proxy) {
|
|
130
|
+
try {
|
|
131
|
+
const ipResp = await proxiedFetch("https://api.ipify.org?format=json");
|
|
132
|
+
const ipData = (await ipResp.json());
|
|
133
|
+
raknet_1.Logger.info(`Proxy IP verified: ${ipData.ip}`);
|
|
134
|
+
}
|
|
135
|
+
catch (e) {
|
|
136
|
+
raknet_1.Logger.warn(`Could not verify proxy IP: ${e instanceof Error ? e.message : String(e)}`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
101
139
|
let userToken;
|
|
102
140
|
let userHash;
|
|
103
|
-
// Try to use cached user token first
|
|
104
141
|
const cached = cacheFile ? loadCache(cacheFile) : null;
|
|
105
142
|
if (cached) {
|
|
106
143
|
raknet_1.Logger.info("Using cached Xbox user token...");
|
|
@@ -108,49 +145,239 @@ async function authenticateWithCredentials(options) {
|
|
|
108
145
|
userHash = cached.userHash;
|
|
109
146
|
}
|
|
110
147
|
else {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
const
|
|
114
|
-
// Exchange for Xbox user token (valid ~14 days)
|
|
115
|
-
const userTokenResp = await xboxlive_auth_1.xnet.exchangeRpsTicketForUserToken(accessToken, "t");
|
|
148
|
+
raknet_1.Logger.info(`Authenticating with Xbox Live...${proxy ? ` (via proxy ${proxy.host}:${proxy.port})` : ""}`);
|
|
149
|
+
const accessToken = await getMicrosoftAccessToken(email, password, proxiedFetch);
|
|
150
|
+
const userTokenResp = await exchangeRpsTicketForUserToken(accessToken, proxiedFetch);
|
|
116
151
|
userToken = userTokenResp.Token;
|
|
117
152
|
userHash = userTokenResp.DisplayClaims.xui[0].uhs;
|
|
118
|
-
// Cache the user token
|
|
119
153
|
if (cacheFile) {
|
|
120
154
|
saveCache(cacheFile, userToken, userHash, userTokenResp.NotAfter);
|
|
121
155
|
}
|
|
122
156
|
}
|
|
123
|
-
|
|
124
|
-
const xstsResp = await xboxlive_auth_1.xnet.exchangeTokenForXSTSToken(userToken, {
|
|
125
|
-
XSTSRelyingParty: MINECRAFT_BEDROCK_RELYING_PARTY,
|
|
126
|
-
sandboxId: "RETAIL",
|
|
127
|
-
});
|
|
157
|
+
const xstsResp = await exchangeTokenForXSTSToken(userToken, MINECRAFT_BEDROCK_RELYING_PARTY, proxiedFetch);
|
|
128
158
|
const xuid = xstsResp.DisplayClaims.xui[0].xid || "";
|
|
129
|
-
|
|
130
|
-
const chains = await getMinecraftBedrockChains(xstsResp.Token, userHash, clientPublicKey);
|
|
159
|
+
const chains = await getMinecraftBedrockChains(xstsResp.Token, userHash, clientPublicKey, proxiedFetch);
|
|
131
160
|
const gamertag = extractGamertagFromChains(chains);
|
|
132
161
|
raknet_1.Logger.info(`Authenticated as: ${gamertag} (${xuid})`);
|
|
133
162
|
return { chains, xuid, gamertag, userHash };
|
|
134
163
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
164
|
+
/**
|
|
165
|
+
* Get Microsoft access token using email/password via OAuth flow
|
|
166
|
+
* This implements the full browser-like login flow
|
|
167
|
+
*/
|
|
168
|
+
async function getMicrosoftAccessToken(email, password, proxiedFetch) {
|
|
169
|
+
const authUrl = "https://login.live.com/oauth20_authorize.srf";
|
|
170
|
+
const params = new URLSearchParams({
|
|
171
|
+
client_id: XBOX_AUTH_CLIENT_ID,
|
|
172
|
+
redirect_uri: "https://login.live.com/oauth20_desktop.srf",
|
|
173
|
+
response_type: "token",
|
|
174
|
+
scope: "service::user.auth.xboxlive.com::MBI_SSL",
|
|
175
|
+
display: "touch",
|
|
176
|
+
locale: "en",
|
|
177
|
+
});
|
|
178
|
+
const preAuthResp = await proxiedFetch(`${authUrl}?${params}`, {
|
|
179
|
+
headers: {
|
|
180
|
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
|
|
181
|
+
Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
|
|
182
|
+
"Accept-Language": "en-US,en;q=0.5",
|
|
183
|
+
},
|
|
184
|
+
});
|
|
185
|
+
if (!preAuthResp.ok) {
|
|
186
|
+
throw new Error(`Pre-auth request failed: ${preAuthResp.status}`);
|
|
187
|
+
}
|
|
188
|
+
const preAuthHtml = await preAuthResp.text();
|
|
189
|
+
const cookies = extractCookies(preAuthResp.headers);
|
|
190
|
+
const { ppft, urlPost } = extractLoginParams(preAuthHtml);
|
|
191
|
+
const loginBody = new URLSearchParams({
|
|
192
|
+
login: email,
|
|
193
|
+
loginfmt: email,
|
|
194
|
+
passwd: password,
|
|
195
|
+
PPFT: ppft,
|
|
196
|
+
PPSX: "Passpor",
|
|
197
|
+
NewUser: "1",
|
|
198
|
+
FoundMSAs: "",
|
|
199
|
+
fspost: "0",
|
|
200
|
+
i21: "0",
|
|
201
|
+
CookieDisclosure: "0",
|
|
202
|
+
IsFidoSupported: "1",
|
|
203
|
+
isSignupPost: "0",
|
|
204
|
+
isRecoveryAttemptPost: "0",
|
|
205
|
+
i13: "0",
|
|
206
|
+
i19: Math.floor(Math.random() * 100000).toString(),
|
|
207
|
+
});
|
|
208
|
+
const loginResp = await proxiedFetch(urlPost, {
|
|
209
|
+
method: "POST",
|
|
210
|
+
headers: {
|
|
211
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
212
|
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
|
|
213
|
+
Cookie: cookies,
|
|
214
|
+
Referer: `${authUrl}?${params}`,
|
|
215
|
+
Origin: "https://login.live.com",
|
|
216
|
+
},
|
|
217
|
+
body: loginBody.toString(),
|
|
218
|
+
redirect: "manual",
|
|
219
|
+
});
|
|
220
|
+
// Check for access token in redirect
|
|
221
|
+
let location = loginResp.headers.get("location") || "";
|
|
222
|
+
// Follow redirects manually to find the access token
|
|
223
|
+
let attempts = 0;
|
|
224
|
+
while (attempts < 5 && !location.includes("access_token=")) {
|
|
225
|
+
if (!location) {
|
|
226
|
+
// Check if we got an error page
|
|
227
|
+
const responseText = await loginResp.text();
|
|
228
|
+
if (responseText.includes("sErrTxt") ||
|
|
229
|
+
responseText.includes("Your account or password is incorrect")) {
|
|
230
|
+
throw new Error("Invalid credentials");
|
|
231
|
+
}
|
|
232
|
+
if (responseText.includes("Sign in a different way") ||
|
|
233
|
+
responseText.includes("idA_PWD_SwitchToCredPicker")) {
|
|
234
|
+
throw new Error("2FA is enabled on this account. Direct login requires 2FA to be disabled.");
|
|
235
|
+
}
|
|
236
|
+
// Try to extract access token from response body (some flows embed it)
|
|
237
|
+
const tokenMatch = responseText.match(/access_token=([^&"']+)/);
|
|
238
|
+
if (tokenMatch) {
|
|
239
|
+
return decodeURIComponent(tokenMatch[1]);
|
|
240
|
+
}
|
|
241
|
+
throw new Error("Failed to get redirect URL from login response");
|
|
242
|
+
}
|
|
243
|
+
if (location.includes("access_token=")) {
|
|
244
|
+
break;
|
|
245
|
+
}
|
|
246
|
+
const redirectResp = await proxiedFetch(location, {
|
|
247
|
+
headers: {
|
|
248
|
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
|
|
249
|
+
Cookie: cookies,
|
|
250
|
+
},
|
|
251
|
+
redirect: "manual",
|
|
140
252
|
});
|
|
141
|
-
|
|
253
|
+
location = redirectResp.headers.get("location") || "";
|
|
254
|
+
attempts++;
|
|
255
|
+
}
|
|
256
|
+
// Extract access token from URL fragment
|
|
257
|
+
const tokenMatch = location.match(/access_token=([^&]+)/);
|
|
258
|
+
if (tokenMatch) {
|
|
259
|
+
return decodeURIComponent(tokenMatch[1]);
|
|
260
|
+
}
|
|
261
|
+
throw new Error("Failed to obtain access token from Microsoft");
|
|
262
|
+
}
|
|
263
|
+
function extractCookies(headers) {
|
|
264
|
+
const setCookies = headers.get("set-cookie");
|
|
265
|
+
if (!setCookies)
|
|
266
|
+
return "";
|
|
267
|
+
// Parse and combine cookies
|
|
268
|
+
const cookies = [];
|
|
269
|
+
const cookieStrings = setCookies.split(/,(?=[^;]*=)/);
|
|
270
|
+
for (const cookieStr of cookieStrings) {
|
|
271
|
+
const match = cookieStr.match(/^([^=]+)=([^;]*)/);
|
|
272
|
+
if (match) {
|
|
273
|
+
cookies.push(`${match[1].trim()}=${match[2]}`);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
return cookies.join("; ");
|
|
277
|
+
}
|
|
278
|
+
function extractLoginParams(html) {
|
|
279
|
+
let ppft = null;
|
|
280
|
+
// Pattern for sFTTag with escaped quotes (JSON format)
|
|
281
|
+
const sFTTagMatch = html.match(/sFTTag":"<input[^>]*value=\\"([^"\\]+)\\"/);
|
|
282
|
+
if (sFTTagMatch) {
|
|
283
|
+
ppft = sFTTagMatch[1];
|
|
284
|
+
}
|
|
285
|
+
// Fallback patterns
|
|
286
|
+
if (!ppft) {
|
|
287
|
+
const ppftPatterns = [
|
|
288
|
+
/sFTTag:'[^']*value="([^"]+)"/,
|
|
289
|
+
/name="PPFT"[^>]*value="([^"]+)"/,
|
|
290
|
+
/value="([^"]+)"[^>]*name="PPFT"/,
|
|
291
|
+
/<input[^>]*name="PPFT"[^>]*value="([^"]+)"/,
|
|
292
|
+
/"sFT"\s*:\s*"([^"]+)"/,
|
|
293
|
+
/sFT:'([^']+)'/,
|
|
294
|
+
/"sFT":"([^"]+)"/,
|
|
295
|
+
];
|
|
296
|
+
for (const pattern of ppftPatterns) {
|
|
297
|
+
const match = html.match(pattern);
|
|
298
|
+
if (match) {
|
|
299
|
+
ppft = match[1];
|
|
300
|
+
break;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
if (!ppft) {
|
|
305
|
+
throw new Error("Failed to extract PPFT token from login page");
|
|
142
306
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
307
|
+
// Extract urlPost
|
|
308
|
+
const urlPostPatterns = [
|
|
309
|
+
/urlPost:\s*'([^']+)'/,
|
|
310
|
+
/urlPost:\s*"([^"]+)"/,
|
|
311
|
+
/"urlPost"\s*:\s*"([^"]+)"/,
|
|
312
|
+
];
|
|
313
|
+
let urlPost = "https://login.live.com/ppsecure/post.srf";
|
|
314
|
+
for (const pattern of urlPostPatterns) {
|
|
315
|
+
const match = html.match(pattern);
|
|
316
|
+
if (match) {
|
|
317
|
+
urlPost = match[1];
|
|
318
|
+
break;
|
|
148
319
|
}
|
|
149
|
-
throw error;
|
|
150
320
|
}
|
|
321
|
+
return { ppft, urlPost };
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Exchange Microsoft access token for Xbox User Token
|
|
325
|
+
*/
|
|
326
|
+
async function exchangeRpsTicketForUserToken(accessToken, proxiedFetch) {
|
|
327
|
+
const response = await proxiedFetch("https://user.auth.xboxlive.com/user/authenticate", {
|
|
328
|
+
method: "POST",
|
|
329
|
+
headers: {
|
|
330
|
+
"Content-Type": "application/json",
|
|
331
|
+
Accept: "application/json",
|
|
332
|
+
"x-xbl-contract-version": "1",
|
|
333
|
+
},
|
|
334
|
+
body: JSON.stringify({
|
|
335
|
+
RelyingParty: "http://auth.xboxlive.com",
|
|
336
|
+
TokenType: "JWT",
|
|
337
|
+
Properties: {
|
|
338
|
+
AuthMethod: "RPS",
|
|
339
|
+
SiteName: "user.auth.xboxlive.com",
|
|
340
|
+
RpsTicket: `t=${accessToken}`,
|
|
341
|
+
},
|
|
342
|
+
}),
|
|
343
|
+
});
|
|
344
|
+
if (!response.ok) {
|
|
345
|
+
const text = await response.text();
|
|
346
|
+
throw new Error(`Xbox user token exchange failed: ${response.status} - ${text}`);
|
|
347
|
+
}
|
|
348
|
+
return response.json();
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Exchange Xbox User Token for XSTS Token
|
|
352
|
+
*/
|
|
353
|
+
async function exchangeTokenForXSTSToken(userToken, relyingParty, proxiedFetch) {
|
|
354
|
+
const response = await proxiedFetch("https://xsts.auth.xboxlive.com/xsts/authorize", {
|
|
355
|
+
method: "POST",
|
|
356
|
+
headers: {
|
|
357
|
+
"Content-Type": "application/json",
|
|
358
|
+
Accept: "application/json",
|
|
359
|
+
"x-xbl-contract-version": "1",
|
|
360
|
+
},
|
|
361
|
+
body: JSON.stringify({
|
|
362
|
+
RelyingParty: relyingParty,
|
|
363
|
+
TokenType: "JWT",
|
|
364
|
+
Properties: {
|
|
365
|
+
SandboxId: "RETAIL",
|
|
366
|
+
UserTokens: [userToken],
|
|
367
|
+
},
|
|
368
|
+
}),
|
|
369
|
+
});
|
|
370
|
+
if (!response.ok) {
|
|
371
|
+
const text = await response.text();
|
|
372
|
+
throw new Error(`Xbox XSTS token exchange failed: ${response.status} - ${text}`);
|
|
373
|
+
}
|
|
374
|
+
return response.json();
|
|
151
375
|
}
|
|
152
|
-
|
|
153
|
-
|
|
376
|
+
/**
|
|
377
|
+
* Get Minecraft Bedrock authentication chains
|
|
378
|
+
*/
|
|
379
|
+
async function getMinecraftBedrockChains(xstsToken, userHash, clientPublicKey, proxiedFetch) {
|
|
380
|
+
const response = await proxiedFetch("https://multiplayer.minecraft.net/authentication", {
|
|
154
381
|
method: "POST",
|
|
155
382
|
headers: {
|
|
156
383
|
"Content-Type": "application/json",
|
|
@@ -162,11 +389,8 @@ async function getMinecraftBedrockChains(xstsToken, userHash, clientPublicKey) {
|
|
|
162
389
|
if (!response.ok) {
|
|
163
390
|
const text = await response.text();
|
|
164
391
|
if (response.status === 401) {
|
|
165
|
-
throw new Error("Minecraft Bedrock authentication failed (401
|
|
166
|
-
"
|
|
167
|
-
" 1. The account does not have an Xbox profile (create one at xbox.com)\n" +
|
|
168
|
-
" 2. The account does not own Minecraft Bedrock Edition\n" +
|
|
169
|
-
" 3. The account needs to accept Xbox/Minecraft terms of service");
|
|
392
|
+
throw new Error("Minecraft Bedrock authentication failed (401).\n" +
|
|
393
|
+
"The account may not have an Xbox profile or Minecraft Bedrock Edition.");
|
|
170
394
|
}
|
|
171
395
|
throw new Error(`Minecraft Bedrock auth failed: ${response.status} - ${text}`);
|
|
172
396
|
}
|
package/dist/shared/auth.js
CHANGED
|
@@ -107,6 +107,7 @@ async function authenticateWithEmailPassword(client) {
|
|
|
107
107
|
password: client.options.password,
|
|
108
108
|
clientPublicKey: client.data.loginData.clientX509,
|
|
109
109
|
cacheDir: client.options.tokensFolder,
|
|
110
|
+
proxy: client.options.proxy,
|
|
110
111
|
});
|
|
111
112
|
const profile = {
|
|
112
113
|
name: tokens.gamertag,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "baltica",
|
|
3
3
|
"description": "Library for Minecraft Bedrock Edition community developers.",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.22",
|
|
5
5
|
"minecraft": "1.21.130",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"license": "MIT",
|
|
@@ -21,18 +21,19 @@
|
|
|
21
21
|
}
|
|
22
22
|
],
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@sanctumterra/raknet": "^1.4.
|
|
24
|
+
"@sanctumterra/raknet": "^1.4.11",
|
|
25
25
|
"@serenityjs/binarystream": "^3.0.10",
|
|
26
26
|
"@serenityjs/protocol": "^0.8.17",
|
|
27
|
-
"
|
|
27
|
+
"fetch-socks": "^1.3.2",
|
|
28
28
|
"jose": "^5.10.0",
|
|
29
29
|
"prismarine-auth": "^2.7.0",
|
|
30
|
+
"undici": "^7.18.2",
|
|
30
31
|
"uuid-1345": "^1.0.2"
|
|
31
32
|
},
|
|
32
33
|
"devDependencies": {
|
|
33
34
|
"@biomejs/biome": "1.9.4",
|
|
34
|
-
"@types/node": "^22.19.
|
|
35
|
+
"@types/node": "^22.19.7",
|
|
35
36
|
"@types/uuid-1345": "^0.99.25",
|
|
36
37
|
"typescript": "^5.9.3"
|
|
37
38
|
}
|
|
38
|
-
}
|
|
39
|
+
}
|