1
0
mirror of https://github.com/GeyserMC/Geyser.git synced 2025-12-19 14:59:27 +00:00

Updated to MinecraftAuth 5 (#5989)

This commit is contained in:
RK_01
2025-11-16 22:43:39 +02:00
committed by GitHub
parent a58fa79805
commit 48bf0942e9
6 changed files with 46 additions and 95 deletions

View File

@@ -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");

View File

@@ -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);
}

View File

@@ -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<Boolean, Integer, StepFullJavaSession> 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<StepChainResult> authentication;
private CompletableFuture<JavaAuthManager> authentication;
private AuthenticationTask(String userKey, int timeoutSec) {
this.userKey = userKey;
@@ -124,11 +117,16 @@ public class PendingMicrosoftAuthentication {
authentications.invalidate(userKey);
}
public CompletableFuture<StepChainResult> performLoginAttempt(boolean offlineAccess, Consumer<StepMsaDeviceCode.MsaDeviceCode> deviceCodeConsumer) {
public CompletableFuture<JavaAuthManager> performLoginAttempt(boolean offlineAccess, Consumer<MsaDeviceCode> 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) {
}
}

View File

@@ -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")

View File

@@ -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);
}
}

View File

@@ -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"