launcher: refresh Microsoft session right before launch

Microsoft access tokens have a ~1 hour TTL. The launcher cached the
MSession once at sign-in and re-used it on every Play click, so leaving
the launcher open for >1 hour and then hitting Play sent stale
credentials to MC -- Mojang's session check on the server then rejected
the join with "Invalid session".

Fix: in DoLaunchAsync, before assembling the MC launch command, call
_auth.TryAuthenticateSilentlyAsync(). XboxAuthNet's silent flow uses
the cached refresh token (~14 day TTL) to mint a fresh access token
transparently. On success ApplySession swaps in the new MSession and
we launch with valid credentials. On failure (refresh token also
stale) the launcher prompts the player to sign in again -- they keep
the launcher open, hit Sign In, no MC restart needed.

No new dependencies, no UI change beyond the brief "Refreshing
session..." status line, no perf cost beyond the ~500ms HTTP roundtrip
to Microsoft each launch.

Bump the launcher version in launcher/ModpackLauncher.csproj before
publishing so old launcher installs see the upgrade banner.
This commit is contained in:
2026-05-23 16:31:17 +00:00
parent 67870f0648
commit 8e00526d27
+26
View File
@@ -797,6 +797,32 @@ public partial class MainWindow : Window
try try
{ {
SetBusy(true); SetBusy(true);
// ─── Refresh the Microsoft session before launch. ─────────────────
// _session was set at sign-in; the access token inside is short-lived
// (~1 hour). If the launcher's been open longer than that, MC will
// send stale credentials to the server and Mojang's session check
// rejects the join ("Invalid session"). XboxAuthNet's silent flow
// uses the cached refresh token (~14 day TTL) to mint a fresh
// access token transparently.
UpdateStatus("Refreshing session...", "");
var freshSession = await _auth.TryAuthenticateSilentlyAsync();
if (freshSession != null)
{
ApplySession(freshSession);
AppendLog("[auth] Session refreshed before launch.");
}
else
{
// Refresh token also stale -- need full interactive re-login.
// Don't try inline (player would lose context); prompt them to
// hit Sign In again.
AppendLog("[auth] Silent refresh returned no session; sign-in required.");
UpdateStatus("Session expired", "Please sign in again to continue.");
ClearSession();
return;
}
var progress = new Progress<ProgressReport>(OnProgress); var progress = new Progress<ProgressReport>(OnProgress);
var installDir = GetInstallDir(); var installDir = GetInstallDir();
_launch ??= new LaunchService(installDir); _launch ??= new LaunchService(installDir);