From 48bf0942e98266745b451ca996777d5e2c74528f Mon Sep 17 00:00:00 2001 From: RK_01 <50594595+RaphiMC@users.noreply.github.com> Date: Sun, 16 Nov 2025 22:43:39 +0200 Subject: [PATCH] Updated to MinecraftAuth 5 (#5989) --- .../java/org/geysermc/geyser/GeyserImpl.java | 6 ++- .../geyser/session/GeyserSession.java | 45 +++++++++-------- .../PendingMicrosoftAuthentication.java | 35 ++++++------- .../geyser/util/LoginEncryptionUtils.java | 4 +- .../geyser/util/MinecraftAuthLogger.java | 49 ------------------- gradle/libs.versions.toml | 2 +- 6 files changed, 46 insertions(+), 95 deletions(-) delete mode 100644 core/src/main/java/org/geysermc/geyser/util/MinecraftAuthLogger.java diff --git a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java index 86d80ee75..0785cdc1f 100644 --- a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java +++ b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java @@ -38,6 +38,8 @@ import lombok.Getter; import lombok.Setter; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; +import net.raphimc.minecraftauth.msa.data.MsaConstants; +import net.raphimc.minecraftauth.msa.model.MsaApplicationConfig; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; @@ -145,9 +147,9 @@ public class GeyserImpl implements GeyserApi, EventRegistrar { public static final boolean IS_DEV = BuildData.isDevBuild(); /** - * Oauth client ID for Microsoft authentication + * Oauth config for Microsoft authentication */ - public static final String OAUTH_CLIENT_ID = "204cefd1-4818-4de1-b98d-513fae875d88"; + public static final MsaApplicationConfig OAUTH_CONFIG = new MsaApplicationConfig("204cefd1-4818-4de1-b98d-513fae875d88", MsaConstants.SCOPE_OFFLINE_ACCESS); private static final Pattern IP_REGEX = Pattern.compile("\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\b"); diff --git a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java index f4e4e5bb3..5d4cd7816 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -41,10 +41,11 @@ import lombok.Setter; import lombok.experimental.Accessors; import net.kyori.adventure.key.Key; import net.kyori.adventure.text.Component; -import net.raphimc.minecraftauth.responsehandler.exception.MinecraftRequestException; -import net.raphimc.minecraftauth.step.java.StepMCProfile; -import net.raphimc.minecraftauth.step.java.StepMCToken; -import net.raphimc.minecraftauth.step.java.session.StepFullJavaSession; +import net.raphimc.minecraftauth.java.JavaAuthManager; +import net.raphimc.minecraftauth.java.exception.MinecraftProfileNotFoundException; +import net.raphimc.minecraftauth.java.model.MinecraftProfile; +import net.raphimc.minecraftauth.java.model.MinecraftToken; +import net.raphimc.minecraftauth.util.MinecraftAuth4To5Migrator; import org.checkerframework.checker.index.qual.NonNegative; import org.checkerframework.checker.index.qual.Positive; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; @@ -113,7 +114,6 @@ import org.geysermc.cumulus.form.util.FormBuilder; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.api.bedrock.camera.CameraData; import org.geysermc.geyser.api.bedrock.camera.CameraShake; -import org.geysermc.geyser.input.InputLocksFlag; import org.geysermc.geyser.api.connection.GeyserConnection; import org.geysermc.geyser.api.entity.EntityData; import org.geysermc.geyser.api.entity.type.GeyserEntity; @@ -143,6 +143,7 @@ import org.geysermc.geyser.erosion.GeyserboundHandshakePacketHandler; import org.geysermc.geyser.event.type.SessionDisconnectEventImpl; import org.geysermc.geyser.impl.camera.CameraDefinitions; import org.geysermc.geyser.impl.camera.GeyserCameraData; +import org.geysermc.geyser.input.InputLocksFlag; import org.geysermc.geyser.inventory.Inventory; import org.geysermc.geyser.inventory.InventoryHolder; import org.geysermc.geyser.inventory.LecternContainer; @@ -196,7 +197,6 @@ import org.geysermc.geyser.util.EntityUtils; import org.geysermc.geyser.util.InventoryUtils; import org.geysermc.geyser.util.LoginEncryptionUtils; import org.geysermc.geyser.util.MathUtils; -import org.geysermc.geyser.util.MinecraftAuthLogger; import org.geysermc.mcprotocollib.auth.GameProfile; import org.geysermc.mcprotocollib.network.BuiltinFlags; import org.geysermc.mcprotocollib.network.ClientSession; @@ -925,23 +925,28 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { loggingIn = true; CompletableFuture.supplyAsync(() -> { - StepFullJavaSession step = PendingMicrosoftAuthentication.AUTH_FLOW.apply(true, 30); - StepFullJavaSession.FullJavaSession response; + JavaAuthManager authManager; + MinecraftProfile mcProfile; + MinecraftToken mcToken; try { - response = step.refresh(MinecraftAuthLogger.INSTANCE, PendingMicrosoftAuthentication.AUTH_CLIENT, step.fromJson(GSON.fromJson(authChain, JsonObject.class))); + JsonObject parsedAuthChain = GSON.fromJson(authChain, JsonObject.class); + if (parsedAuthChain.has("mcProfile")) { // Old Minecraft v4 auth chain + parsedAuthChain = MinecraftAuth4To5Migrator.migrateJavaSave(parsedAuthChain, GeyserImpl.OAUTH_CONFIG); + } + + authManager = JavaAuthManager.fromJson(PendingMicrosoftAuthentication.AUTH_CLIENT, parsedAuthChain); + mcProfile = authManager.getMinecraftProfile().getUpToDate(); + mcToken = authManager.getMinecraftToken().getUpToDate(); } catch (Exception e) { geyser.getLogger().error("Error while attempting to use auth chain for " + bedrockUsername() + "!", e); return Boolean.FALSE; } - StepMCProfile.MCProfile mcProfile = response.getMcProfile(); - StepMCToken.MCToken mcToken = mcProfile.getMcToken(); - protocol = new MinecraftProtocol( new GameProfile(mcProfile.getId(), mcProfile.getName()), - mcToken.getAccessToken() + mcToken.getToken() ); - geyser.saveAuthChain(bedrockUsername(), GSON.toJson(step.toJson(response))); + geyser.saveAuthChain(bedrockUsername(), GSON.toJson(JavaAuthManager.toJson(authManager))); return Boolean.TRUE; }).whenComplete((successful, ex) -> { if (this.closed) { @@ -1009,9 +1014,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { return task.getAuthentication().handle((result, ex) -> { if (ex != null) { geyser.getLogger().error("Failed to log in with Microsoft code!", ex); - if (ex instanceof CompletionException ce - && ce.getCause() instanceof MinecraftRequestException mre - && mre.getResponse().getStatusCode() == 404) { + if (ex instanceof CompletionException ce && ce.getCause() instanceof MinecraftProfileNotFoundException) { // Player is trying to join with a Microsoft account that doesn't have Java Edition purchased disconnect(GeyserLocale.getPlayerLocaleString("geyser.network.remote.invalid_account", locale())); } else { @@ -1020,12 +1023,12 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { return false; } - StepMCProfile.MCProfile mcProfile = result.session().getMcProfile(); - StepMCToken.MCToken mcToken = mcProfile.getMcToken(); + MinecraftProfile mcProfile = result.getMinecraftProfile().getCached(); + MinecraftToken mcToken = result.getMinecraftToken().getCached(); this.protocol = new MinecraftProtocol( new GameProfile(mcProfile.getId(), mcProfile.getName()), - mcToken.getAccessToken() + mcToken.getToken() ); try { @@ -1036,7 +1039,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { } // Save our auth chain for later use - geyser.saveAuthChain(bedrockUsername(), GSON.toJson(result.step().toJson(result.session()))); + geyser.saveAuthChain(bedrockUsername(), GSON.toJson(JavaAuthManager.toJson(result))); return true; }).getNow(false); } diff --git a/core/src/main/java/org/geysermc/geyser/session/PendingMicrosoftAuthentication.java b/core/src/main/java/org/geysermc/geyser/session/PendingMicrosoftAuthentication.java index 7d0e2fbf9..fbbc034d3 100644 --- a/core/src/main/java/org/geysermc/geyser/session/PendingMicrosoftAuthentication.java +++ b/core/src/main/java/org/geysermc/geyser/session/PendingMicrosoftAuthentication.java @@ -33,20 +33,21 @@ import lombok.Setter; import lombok.SneakyThrows; import net.lenni0451.commons.httpclient.HttpClient; import net.raphimc.minecraftauth.MinecraftAuth; -import net.raphimc.minecraftauth.step.java.session.StepFullJavaSession; -import net.raphimc.minecraftauth.step.msa.StepMsaDeviceCode; -import net.raphimc.minecraftauth.util.MicrosoftConstants; +import net.raphimc.minecraftauth.java.JavaAuthManager; +import net.raphimc.minecraftauth.msa.data.MsaConstants; +import net.raphimc.minecraftauth.msa.model.MsaApplicationConfig; +import net.raphimc.minecraftauth.msa.model.MsaDeviceCode; +import net.raphimc.minecraftauth.msa.model.MsaToken; +import net.raphimc.minecraftauth.msa.service.impl.DeviceCodeMsaAuthService; import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserLogger; -import org.geysermc.geyser.util.MinecraftAuthLogger; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; -import java.util.function.BiFunction; import java.util.function.Consumer; /** @@ -55,14 +56,6 @@ import java.util.function.Consumer; */ public class PendingMicrosoftAuthentication { public static final HttpClient AUTH_CLIENT = MinecraftAuth.createHttpClient(); - public static final BiFunction AUTH_FLOW = (offlineAccess, timeoutSec) -> MinecraftAuth.builder() - .withClientId(GeyserImpl.OAUTH_CLIENT_ID) - .withScope(offlineAccess ? "XboxLive.signin XboxLive.offline_access" : "XboxLive.signin") - .withTimeout(timeoutSec) - .deviceCode() - .withoutDeviceToken() - .regularAuthentication(MicrosoftConstants.JAVA_XSTS_RELYING_PARTY) - .buildMinecraftJavaProfileStep(false); /** * For GeyserConnect usage. */ @@ -100,7 +93,7 @@ public class PendingMicrosoftAuthentication { private final String userKey; private final int timeoutSec; @Getter - private CompletableFuture authentication; + private CompletableFuture authentication; private AuthenticationTask(String userKey, int timeoutSec) { this.userKey = userKey; @@ -124,11 +117,16 @@ public class PendingMicrosoftAuthentication { authentications.invalidate(userKey); } - public CompletableFuture performLoginAttempt(boolean offlineAccess, Consumer deviceCodeConsumer) { + public CompletableFuture performLoginAttempt(boolean offlineAccess, Consumer deviceCodeConsumer) { + MsaApplicationConfig applicationConfig = GeyserImpl.OAUTH_CONFIG.withScope(offlineAccess ? MsaConstants.SCOPE_OFFLINE_ACCESS : MsaConstants.SCOPE_NO_OFFLINE_ACCESS); + DeviceCodeMsaAuthService authService = new DeviceCodeMsaAuthService(AUTH_CLIENT, applicationConfig, deviceCodeConsumer, timeoutSec * 1000); return authentication = CompletableFuture.supplyAsync(() -> { try { - StepFullJavaSession step = AUTH_FLOW.apply(offlineAccess, timeoutSec); - return new StepChainResult(step, step.getFromInput(MinecraftAuthLogger.INSTANCE, AUTH_CLIENT, new StepMsaDeviceCode.MsaDeviceCodeCallback(deviceCodeConsumer))); + MsaToken msaToken = authService.acquireToken(); + JavaAuthManager authManager = JavaAuthManager.create(AUTH_CLIENT).msaApplicationConfig(applicationConfig).login(msaToken); + authManager.getMinecraftToken().refresh(); // Preload the Minecraft token + authManager.getMinecraftProfile().refresh(); // Preload the Minecraft profile + return authManager; } catch (Exception e) { throw new CompletionException(e); } @@ -154,7 +152,4 @@ public class PendingMicrosoftAuthentication { super(userKey, timeoutSec); } } - - public record StepChainResult(StepFullJavaSession step, StepFullJavaSession.FullJavaSession session) { - } } diff --git a/core/src/main/java/org/geysermc/geyser/util/LoginEncryptionUtils.java b/core/src/main/java/org/geysermc/geyser/util/LoginEncryptionUtils.java index fa58381ec..0741ba225 100644 --- a/core/src/main/java/org/geysermc/geyser/util/LoginEncryptionUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/LoginEncryptionUtils.java @@ -28,7 +28,7 @@ package org.geysermc.geyser.util; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import net.raphimc.minecraftauth.step.msa.StepMsaDeviceCode; +import net.raphimc.minecraftauth.msa.model.MsaDeviceCode; import org.cloudburstmc.protocol.bedrock.data.auth.AuthPayload; import org.cloudburstmc.protocol.bedrock.data.auth.CertificateChainPayload; import org.cloudburstmc.protocol.bedrock.data.auth.TokenPayload; @@ -215,7 +215,7 @@ public class LoginEncryptionUtils { /** * Shows the code that a user must input into their browser */ - public static void buildAndShowMicrosoftCodeWindow(GeyserSession session, StepMsaDeviceCode.MsaDeviceCode msCode) { + public static void buildAndShowMicrosoftCodeWindow(GeyserSession session, MsaDeviceCode msCode) { String locale = session.locale(); StringBuilder message = new StringBuilder("%xbox.signin.website\n") diff --git a/core/src/main/java/org/geysermc/geyser/util/MinecraftAuthLogger.java b/core/src/main/java/org/geysermc/geyser/util/MinecraftAuthLogger.java deleted file mode 100644 index 4e928d47e..000000000 --- a/core/src/main/java/org/geysermc/geyser/util/MinecraftAuthLogger.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2024 GeyserMC. http://geysermc.org - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * @author GeyserMC - * @link https://github.com/GeyserMC/Geyser - */ - -package org.geysermc.geyser.util; - -import net.raphimc.minecraftauth.util.logging.ILogger; -import org.geysermc.geyser.GeyserImpl; - -public class MinecraftAuthLogger implements ILogger { - - public static final MinecraftAuthLogger INSTANCE = new MinecraftAuthLogger(); - - @Override - public void info(String message) { - GeyserImpl.getInstance().getLogger().debug(message); - } - - @Override - public void warn(String message) { - GeyserImpl.getInstance().getLogger().warning(message); - } - - @Override - public void error(String message) { - GeyserImpl.getInstance().getLogger().error(message); - } -} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c9ed339a9..8deff33ad 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -13,7 +13,7 @@ protocol-connection = "3.0.0.Beta10-20251014.180344-2" protocol-common = "3.0.0.Beta10-20251014.180344-2" protocol-codec = "3.0.0.Beta10-20251014.180344-2" raknet = "1.0.0.CR3-20250811.214335-20" -minecraftauth = "4.1.1" +minecraftauth = "5.0.0" mcprotocollib = "1.21.9-20251029.184056-18" adventure = "4.25.0" adventure-platform = "4.4.1"