From 2d8042909376a651f4391440f7a18e00fb8f0c7a Mon Sep 17 00:00:00 2001 From: onebeastchris Date: Thu, 18 Jul 2024 02:36:37 +0200 Subject: [PATCH] Attempt to implement auth saving --- .../java/org/geysermc/geyser/GeyserImpl.java | 26 +++--- .../geyser/session/GeyserSession.java | 86 ++++++++++++++++--- .../PendingMicrosoftAuthentication.java | 15 ++-- .../geyser/util/MinecraftAuthLogger.java | 49 +++++++++++ gradle/libs.versions.toml | 2 +- 5 files changed, 144 insertions(+), 34 deletions(-) create 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 9f45fdd60..e29457f3a 100644 --- a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java +++ b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java @@ -40,7 +40,7 @@ import lombok.Setter; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import net.raphimc.minecraftauth.step.java.session.StepFullJavaSession; -import net.raphimc.minecraftauth.step.msa.StepRefreshTokenMsaCode; +import net.raphimc.minecraftauth.step.msa.StepMsaToken; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; @@ -96,6 +96,7 @@ import org.geysermc.geyser.util.AssetUtils; import org.geysermc.geyser.util.CooldownUtils; import org.geysermc.geyser.util.DimensionUtils; import org.geysermc.geyser.util.Metrics; +import org.geysermc.geyser.util.MinecraftAuthLogger; import org.geysermc.geyser.util.NewsHandler; import org.geysermc.geyser.util.VersionCheckUtils; import org.geysermc.geyser.util.WebUtils; @@ -556,24 +557,22 @@ public class GeyserImpl implements GeyserApi { // May be written/read to on multiple threads from each GeyserSession as well as writing the config savedAuthChains = new ConcurrentHashMap<>(); - boolean doWrite = false; - - // Remove after a while - just a migration help + // TODO Remove after a while - just a migration help //noinspection deprecation File refreshTokensFile = bootstrap.getSavedUserLoginsFolder().resolve(Constants.SAVED_REFRESH_TOKEN_FILE).toFile(); if (refreshTokensFile.exists()) { TypeReference> type = new TypeReference<>() { }; - Map authChainFile = null; + Map refreshTokens = null; try { - authChainFile = JSON_MAPPER.readValue(refreshTokensFile, type); + refreshTokens = JSON_MAPPER.readValue(refreshTokensFile, type); } catch (IOException e) { // ignored - we'll just delete this file :)) } - if (authChainFile != null) { + if (refreshTokens != null) { List validUsers = config.getSavedUserLogins(); final Gson gson = new Gson(); - for (Map.Entry entry : authChainFile.entrySet()) { + for (Map.Entry entry : refreshTokens.entrySet()) { String user = entry.getKey(); if (!validUsers.contains(user)) { continue; @@ -581,27 +580,29 @@ public class GeyserImpl implements GeyserApi { // Migrate refresh tokens to auth chains try { - StepFullJavaSession javaSession = PendingMicrosoftAuthentication.REFRESH_TOKEN_UPDATE_AUTH_FLOW.apply(true, 10); + StepFullJavaSession javaSession = PendingMicrosoftAuthentication.AUTH_FLOW.apply(false, 10); StepFullJavaSession.FullJavaSession fullJavaSession = javaSession.getFromInput( + MinecraftAuthLogger.INSTANCE, PendingMicrosoftAuthentication.AUTH_CLIENT, - new StepRefreshTokenMsaCode.RefreshToken(entry.getValue()) + new StepMsaToken.RefreshToken(entry.getValue()) ); String authChain = gson.toJson(javaSession.toJson(fullJavaSession)); GeyserImpl.getInstance().getLogger().warning("new auth chain: " + authChain); savedAuthChains.put(user, authChain); } catch (Exception e) { + GeyserImpl.getInstance().getLogger().severe(e.getMessage(), e); GeyserImpl.getInstance().getLogger().warning("Could not migrate " + entry.getKey() + " to an auth chain! " + "They will need to sign in the next time they join Geyser."); } // Ensure the new additions are written to the file - doWrite = true; + scheduleAuthChainsWrite(); } } // Finally: Delete it. Goodbye! - //refreshTokensFile.delete(); + refreshTokensFile.delete(); } File authChainsFile = bootstrap.getSavedUserLoginsFolder().resolve(Constants.SAVED_AUTH_CHAINS_FILE).toFile(); @@ -614,6 +615,7 @@ public class GeyserImpl implements GeyserApi { } catch (IOException e) { logger.error("Cannot load saved user tokens!", e); } + boolean doWrite = false; if (authChainFile != null) { List validUsers = config.getSavedUserLogins(); for (Map.Entry entry : authChainFile.entrySet()) { 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 8527c2f49..254070563 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -27,10 +27,6 @@ package org.geysermc.geyser.session; import com.google.gson.Gson; import com.google.gson.JsonObject; -import net.raphimc.minecraftauth.step.java.StepMCProfile; -import net.raphimc.minecraftauth.step.java.StepMCToken; -import net.raphimc.minecraftauth.step.java.session.StepFullJavaSession; -import org.geysermc.mcprotocollib.auth.GameProfile; import io.netty.channel.Channel; import io.netty.channel.EventLoop; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; @@ -44,22 +40,60 @@ import lombok.Getter; import lombok.Setter; import lombok.experimental.Accessors; import net.kyori.adventure.key.Key; +import net.raphimc.minecraftauth.step.java.StepMCProfile; +import net.raphimc.minecraftauth.step.java.StepMCToken; +import net.raphimc.minecraftauth.step.java.session.StepFullJavaSession; import org.checkerframework.checker.index.qual.NonNegative; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.common.value.qual.IntRange; -import org.cloudburstmc.math.vector.*; +import org.cloudburstmc.math.vector.Vector2f; +import org.cloudburstmc.math.vector.Vector2i; +import org.cloudburstmc.math.vector.Vector3d; +import org.cloudburstmc.math.vector.Vector3f; +import org.cloudburstmc.math.vector.Vector3i; import org.cloudburstmc.nbt.NbtMap; import org.cloudburstmc.protocol.bedrock.BedrockDisconnectReasons; import org.cloudburstmc.protocol.bedrock.BedrockServerSession; -import org.cloudburstmc.protocol.bedrock.data.*; +import org.cloudburstmc.protocol.bedrock.data.Ability; +import org.cloudburstmc.protocol.bedrock.data.AbilityLayer; +import org.cloudburstmc.protocol.bedrock.data.AuthoritativeMovementMode; +import org.cloudburstmc.protocol.bedrock.data.ChatRestrictionLevel; +import org.cloudburstmc.protocol.bedrock.data.ExperimentData; +import org.cloudburstmc.protocol.bedrock.data.GamePublishSetting; +import org.cloudburstmc.protocol.bedrock.data.GameRuleData; +import org.cloudburstmc.protocol.bedrock.data.GameType; +import org.cloudburstmc.protocol.bedrock.data.PlayerPermission; +import org.cloudburstmc.protocol.bedrock.data.SoundEvent; +import org.cloudburstmc.protocol.bedrock.data.SpawnBiomeType; import org.cloudburstmc.protocol.bedrock.data.command.CommandEnumData; import org.cloudburstmc.protocol.bedrock.data.command.CommandPermission; import org.cloudburstmc.protocol.bedrock.data.command.SoftEnumUpdateType; import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData; -import org.cloudburstmc.protocol.bedrock.packet.*; +import org.cloudburstmc.protocol.bedrock.packet.AvailableEntityIdentifiersPacket; +import org.cloudburstmc.protocol.bedrock.packet.BedrockPacket; +import org.cloudburstmc.protocol.bedrock.packet.BiomeDefinitionListPacket; +import org.cloudburstmc.protocol.bedrock.packet.CameraPresetsPacket; +import org.cloudburstmc.protocol.bedrock.packet.ChunkRadiusUpdatedPacket; +import org.cloudburstmc.protocol.bedrock.packet.CraftingDataPacket; +import org.cloudburstmc.protocol.bedrock.packet.CreativeContentPacket; +import org.cloudburstmc.protocol.bedrock.packet.EmoteListPacket; +import org.cloudburstmc.protocol.bedrock.packet.GameRulesChangedPacket; +import org.cloudburstmc.protocol.bedrock.packet.ItemComponentPacket; +import org.cloudburstmc.protocol.bedrock.packet.LevelSoundEvent2Packet; +import org.cloudburstmc.protocol.bedrock.packet.PlayStatusPacket; +import org.cloudburstmc.protocol.bedrock.packet.SetTimePacket; +import org.cloudburstmc.protocol.bedrock.packet.StartGamePacket; +import org.cloudburstmc.protocol.bedrock.packet.SyncEntityPropertyPacket; +import org.cloudburstmc.protocol.bedrock.packet.TextPacket; +import org.cloudburstmc.protocol.bedrock.packet.TransferPacket; +import org.cloudburstmc.protocol.bedrock.packet.UpdateAbilitiesPacket; +import org.cloudburstmc.protocol.bedrock.packet.UpdateAdventureSettingsPacket; +import org.cloudburstmc.protocol.bedrock.packet.UpdateAttributesPacket; +import org.cloudburstmc.protocol.bedrock.packet.UpdateClientInputLocksPacket; +import org.cloudburstmc.protocol.bedrock.packet.UpdateSoftEnumPacket; import org.cloudburstmc.protocol.common.util.OptionalBoolean; import org.geysermc.api.util.BedrockPlatform; import org.geysermc.api.util.InputMode; @@ -109,7 +143,22 @@ import org.geysermc.geyser.registry.type.BlockMappings; import org.geysermc.geyser.registry.type.ItemMappings; import org.geysermc.geyser.session.auth.AuthData; import org.geysermc.geyser.session.auth.BedrockClientData; -import org.geysermc.geyser.session.cache.*; +import org.geysermc.geyser.session.cache.AdvancementsCache; +import org.geysermc.geyser.session.cache.BookEditCache; +import org.geysermc.geyser.session.cache.ChunkCache; +import org.geysermc.geyser.session.cache.EntityCache; +import org.geysermc.geyser.session.cache.EntityEffectCache; +import org.geysermc.geyser.session.cache.FormCache; +import org.geysermc.geyser.session.cache.LodestoneCache; +import org.geysermc.geyser.session.cache.PistonCache; +import org.geysermc.geyser.session.cache.PreferencesCache; +import org.geysermc.geyser.session.cache.RegistryCache; +import org.geysermc.geyser.session.cache.SkullCache; +import org.geysermc.geyser.session.cache.StructureBlockCache; +import org.geysermc.geyser.session.cache.TagCache; +import org.geysermc.geyser.session.cache.TeleportCache; +import org.geysermc.geyser.session.cache.WorldBorder; +import org.geysermc.geyser.session.cache.WorldCache; import org.geysermc.geyser.skin.FloodgateSkinUploader; import org.geysermc.geyser.text.GeyserLocale; import org.geysermc.geyser.text.MinecraftLocale; @@ -119,9 +168,15 @@ import org.geysermc.geyser.util.ChunkUtils; import org.geysermc.geyser.util.DimensionUtils; import org.geysermc.geyser.util.EntityUtils; import org.geysermc.geyser.util.LoginEncryptionUtils; +import org.geysermc.geyser.util.MinecraftAuthLogger; +import org.geysermc.mcprotocollib.auth.GameProfile; import org.geysermc.mcprotocollib.network.BuiltinFlags; import org.geysermc.mcprotocollib.network.Session; -import org.geysermc.mcprotocollib.network.event.session.*; +import org.geysermc.mcprotocollib.network.event.session.ConnectedEvent; +import org.geysermc.mcprotocollib.network.event.session.DisconnectedEvent; +import org.geysermc.mcprotocollib.network.event.session.PacketErrorEvent; +import org.geysermc.mcprotocollib.network.event.session.PacketSendingEvent; +import org.geysermc.mcprotocollib.network.event.session.SessionAdapter; import org.geysermc.mcprotocollib.network.packet.Packet; import org.geysermc.mcprotocollib.network.tcp.TcpClientSession; import org.geysermc.mcprotocollib.network.tcp.TcpSession; @@ -156,7 +211,16 @@ import java.net.ConnectException; import java.net.InetSocketAddress; import java.nio.charset.StandardCharsets; import java.time.Instant; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.BitSet; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Queue; +import java.util.Set; +import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ScheduledFuture; @@ -707,7 +771,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { StepFullJavaSession step = PendingMicrosoftAuthentication.AUTH_FLOW.apply(true, 30); StepFullJavaSession.FullJavaSession response; try { - response = step.refresh(PendingMicrosoftAuthentication.AUTH_CLIENT, step.fromJson(gson.fromJson(authChain, JsonObject.class))); + response = step.refresh(MinecraftAuthLogger.INSTANCE, PendingMicrosoftAuthentication.AUTH_CLIENT, step.fromJson(gson.fromJson(authChain, JsonObject.class))); } catch (Exception e) { geyser.getLogger().error("Error while attempting to use auth chain for " + bedrockUsername() + "!", e); return Boolean.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 8f2261c45..f82a2a9ef 100644 --- a/core/src/main/java/org/geysermc/geyser/session/PendingMicrosoftAuthentication.java +++ b/core/src/main/java/org/geysermc/geyser/session/PendingMicrosoftAuthentication.java @@ -35,13 +35,16 @@ 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.step.msa.StepRefreshTokenMsaCode; import net.raphimc.minecraftauth.util.MicrosoftConstants; import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserLogger; -import java.util.concurrent.*; +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; @@ -60,14 +63,6 @@ public class PendingMicrosoftAuthentication { .regularAuthentication(MicrosoftConstants.JAVA_XSTS_RELYING_PARTY) .buildMinecraftJavaProfileStep(false); - public static final BiFunction REFRESH_TOKEN_UPDATE_AUTH_FLOW = (offlineAccess, timeoutSec) -> MinecraftAuth.builder() - .withClientId(GeyserImpl.OAUTH_CLIENT_ID) - .withScope(offlineAccess ? "XboxLive.signin XboxLive.offline_access" : "XboxLive.signin") - .withTimeout(timeoutSec) - .customMsaCodeStep(StepRefreshTokenMsaCode::new) - .withoutDeviceToken() - .regularAuthentication(MicrosoftConstants.JAVA_XSTS_RELYING_PARTY) - .buildMinecraftJavaProfileStep(false); /** * For GeyserConnect usage. diff --git a/core/src/main/java/org/geysermc/geyser/util/MinecraftAuthLogger.java b/core/src/main/java/org/geysermc/geyser/util/MinecraftAuthLogger.java new file mode 100644 index 000000000..4e928d47e --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/util/MinecraftAuthLogger.java @@ -0,0 +1,49 @@ +/* + * 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 e5ad0f240..0c2f99e6e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -13,7 +13,7 @@ websocket = "1.5.1" protocol = "3.0.0.Beta2-20240704.153116-14" raknet = "1.0.0.CR3-20240416.144209-1" minecraftauth = "4.0.3-SNAPSHOT" -mcprotocollib = "1.21-20240618.151504-8" +mcprotocollib = "1.21-20240711.182247-12" adventure = "4.14.0" adventure-platform = "4.3.0" junit = "5.9.2"