Skip to content

Commit 7fd5f20

Browse files
author
Simeon Kummer
committed
Fix invalid session and Windows CMD.
1 parent 0a30f04 commit 7fd5f20

File tree

4 files changed

+141
-5
lines changed

4 files changed

+141
-5
lines changed

src/main.js

Lines changed: 77 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -170,24 +170,59 @@ ipcMain.handle('launch-minecraft', async (event, { uuid, name }) => {
170170
if (fs.existsSync(accountsPath)) {
171171
try {
172172
const accountData = JSON.parse(fs.readFileSync(accountsPath, 'utf8'));
173+
console.log('Loaded account data:', { uuid: accountData.uuid, name: accountData.name, hasRefreshToken: !!accountData.refreshToken });
173174

174175
// Check if this is a Microsoft account with a refresh token
175176
if (accountData.refreshToken) {
176177
try {
178+
console.log('Attempting to refresh access token...');
177179
// Use refresh token to get fresh access token
178180
const authManager = new Auth("select_account");
179181
const xbox = await authManager.refresh(accountData.refreshToken);
180182
const minecraftProfile = await xbox.getMinecraft();
181183
accessToken = minecraftProfile.access_token;
182-
console.log('Got fresh access token using refresh token');
184+
185+
console.log('Successfully refreshed access token');
186+
console.log('Token length:', accessToken ? accessToken.length : 0);
187+
console.log('Profile info:', {
188+
id: minecraftProfile.profile.id,
189+
name: minecraftProfile.profile.name
190+
});
191+
192+
// Validate that the token is not empty or malformed
193+
if (!accessToken || accessToken.length < 10) {
194+
throw new Error('Received invalid or empty access token');
195+
}
196+
197+
// Update the refresh token in case it changed
198+
const updatedAccountData = {
199+
...accountData,
200+
refreshToken: xbox.save()
201+
};
202+
fs.writeFileSync(accountsPath, JSON.stringify(updatedAccountData, null, 2));
203+
console.log('Updated refresh token in account data');
204+
183205
} catch (error) {
184-
console.warn('Failed to refresh access token, launching in offline mode:', error);
185-
// Continue without token - will work for offline servers
206+
console.error('Failed to refresh access token:', error);
207+
console.warn('Launching in offline mode due to token refresh failure');
208+
accessToken = null;
209+
210+
// If refresh fails, the account might need re-authentication
211+
// You could optionally clear the refresh token here or prompt for re-login
212+
if (error.message && error.message.includes('invalid_grant')) {
213+
console.warn('Refresh token is invalid - user may need to re-authenticate');
214+
// Send a message to the renderer to prompt for re-authentication
215+
mainWindow.webContents.send('auth-expired');
216+
}
186217
}
218+
} else {
219+
console.log('No refresh token found - this is a cracked account or offline mode');
187220
}
188221
} catch (error) {
189222
console.warn('Could not load account data:', error);
190223
}
224+
} else {
225+
console.log('No account data file found');
191226
}
192227

193228
// Check if BlitzClient directory exists
@@ -251,4 +286,43 @@ ipcMain.handle('launch-minecraft', async (event, { uuid, name }) => {
251286
ipcMain.handle('open-external-link', async (event, url) => {
252287
await shell.openExternal(url);
253288
return true;
289+
});
290+
291+
ipcMain.handle('re-authenticate', async () => {
292+
try {
293+
// Clear existing account data
294+
const accountsPath = path.join(path.dirname(INSTALL_DIR), 'accounts.json');
295+
if (fs.existsSync(accountsPath)) {
296+
fs.unlinkSync(accountsPath);
297+
}
298+
299+
// Create a new Auth manager with select_account prompt
300+
const authManager = new Auth("select_account");
301+
302+
// Launch using 'electron' as the GUI framework
303+
const xbox = await authManager.launch("electron");
304+
305+
// Get the Minecraft profile
306+
const minecraftProfile = await xbox.getMinecraft();
307+
308+
// Extract necessary data from the Minecraft profile
309+
const accountData = {
310+
uuid: minecraftProfile.profile.id,
311+
name: minecraftProfile.profile.name,
312+
refreshToken: xbox.save()
313+
};
314+
315+
// Save account data
316+
const accountsDirectory = path.dirname(INSTALL_DIR);
317+
if (!fs.existsSync(accountsDirectory)) {
318+
fs.mkdirSync(accountsDirectory, { recursive: true });
319+
}
320+
321+
fs.writeFileSync(accountsPath, JSON.stringify(accountData, null, 2));
322+
323+
return accountData;
324+
} catch (error) {
325+
console.error('Re-authentication error:', error);
326+
throw error;
327+
}
254328
});

src/minecraftStarter.js

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ const os = require('os');
44
const { spawn } = require('child_process');
55
const axios = require('axios');
66
const extract = require('extract-zip');
7+
const { access, constants } = require('fs').promises;
78

89
async function fileExists(path) {
910
try {
@@ -321,6 +322,9 @@ class MinecraftStarter {
321322
if (accessToken) {
322323
args.push('--token');
323324
args.push(accessToken);
325+
console.log('Launching with access token for online server support');
326+
} else {
327+
console.log('Launching without access token (offline/cracked mode)');
324328
}
325329

326330
// If minecraft directory exists, append --launch-only
@@ -334,7 +338,19 @@ class MinecraftStarter {
334338
detached: true
335339
};
336340

337-
spawnOptions.stdio = 'inherit';
341+
// Configure stdio based on platform to hide command prompt
342+
if (process.platform === 'win32') {
343+
// On Windows, use 'ignore' to completely hide the console window
344+
spawnOptions.stdio = 'ignore';
345+
// Also set windowsHide to true for extra protection
346+
spawnOptions.windowsHide = true;
347+
} else {
348+
// On macOS and Linux, use 'pipe' to capture output without showing terminal
349+
spawnOptions.stdio = ['ignore', 'pipe', 'pipe'];
350+
}
351+
352+
console.log('Launching Minecraft with args:', args);
353+
console.log('Spawn options:', spawnOptions);
338354

339355
// Spawn the process
340356
const process = spawn(args[0], args.slice(1), spawnOptions);
@@ -343,6 +359,19 @@ class MinecraftStarter {
343359
console.error(`Error running executable: ${error.message}`);
344360
});
345361

362+
// On non-Windows platforms, handle stdout/stderr if they exist
363+
if (process.stdout) {
364+
process.stdout.on('data', (data) => {
365+
console.log(`Minecraft stdout: ${data}`);
366+
});
367+
}
368+
369+
if (process.stderr) {
370+
process.stderr.on('data', (data) => {
371+
console.error(`Minecraft stderr: ${data}`);
372+
});
373+
}
374+
346375
process.unref();
347376
return process;
348377
} catch (error) {

src/preload.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ contextBridge.exposeInMainWorld(
77
// Account management
88
loginMicrosoft: () => ipcRenderer.invoke('login-microsoft'),
99
loginCracked: () => ipcRenderer.invoke('login-cracked'),
10+
reAuthenticate: () => ipcRenderer.invoke('re-authenticate'),
1011

1112
// Minecraft launch
1213
launchMinecraft: (data) => ipcRenderer.invoke('launch-minecraft', data),
@@ -17,7 +18,7 @@ contextBridge.exposeInMainWorld(
1718
// IPC event listeners
1819
on: (channel, callback) => {
1920
// Whitelist of valid channels
20-
const validChannels = ['account-loaded', 'device-code', 'minecraft-closed', 'minecraft-status'];
21+
const validChannels = ['account-loaded', 'device-code', 'minecraft-closed', 'minecraft-status', 'auth-expired'];
2122
if (validChannels.includes(channel)) {
2223
// Remove the event listener when it's no longer needed
2324
const subscription = (event, ...args) => callback(...args);

src/renderer.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,17 @@ window.api.on('minecraft-status', (data) => {
6060
}
6161
});
6262

63+
// Listen for authentication expiration
64+
window.api.on('auth-expired', () => {
65+
showAlert('warning', 'Session Expired', 'Your Microsoft account session has expired. Please re-authenticate to join online servers.');
66+
67+
// Optionally, you could automatically trigger re-authentication
68+
// or show a button to re-authenticate
69+
if (confirm('Would you like to re-authenticate now?')) {
70+
handleReAuthentication();
71+
}
72+
});
73+
6374
// Functions
6475
function showAlert(type, title, message) {
6576
// Simple alert using native dialog
@@ -126,6 +137,27 @@ async function handleCrackedLogin() {
126137
}
127138
}
128139

140+
async function handleReAuthentication() {
141+
try {
142+
statusMessage.textContent = 'Re-authenticating...';
143+
144+
const data = await window.api.reAuthenticate();
145+
146+
userData = {
147+
uuid: data.uuid,
148+
name: data.name
149+
};
150+
151+
statusMessage.textContent = '';
152+
showAlert('info', 'Re-authentication Successful', `Welcome back, ${data.name}!`);
153+
154+
} catch (error) {
155+
console.error('Re-authentication error:', error);
156+
statusMessage.textContent = '';
157+
showAlert('error', 'Re-authentication Error', error.message || 'Failed to re-authenticate. Please try again.');
158+
}
159+
}
160+
129161
function showMainSection() {
130162
loginSection.style.display = 'none';
131163
mainSection.style.display = 'flex';

0 commit comments

Comments
 (0)