mirror of
https://github.com/GeyserMC/Geyser.git
synced 2024-08-14 23:57:35 +00:00
Add migration from refresh tokens to auth chains
This commit is contained in:
parent
59495d8cc1
commit
450af559b5
4 changed files with 86 additions and 6 deletions
|
|
@ -39,6 +39,8 @@ public final class Constants {
|
||||||
public static final String GEYSER_DOWNLOAD_LOCATION = "https://geysermc.org/download";
|
public static final String GEYSER_DOWNLOAD_LOCATION = "https://geysermc.org/download";
|
||||||
public static final String UPDATE_PERMISSION = "geyser.update";
|
public static final String UPDATE_PERMISSION = "geyser.update";
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
static final String SAVED_REFRESH_TOKEN_FILE = "saved-refresh-tokens.json";
|
||||||
static final String SAVED_AUTH_CHAINS_FILE = "saved-auth-chains.json";
|
static final String SAVED_AUTH_CHAINS_FILE = "saved-auth-chains.json";
|
||||||
|
|
||||||
public static final String GEYSER_CUSTOM_NAMESPACE = "geyser_custom";
|
public static final String GEYSER_CUSTOM_NAMESPACE = "geyser_custom";
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ import com.fasterxml.jackson.core.JsonParser;
|
||||||
import com.fasterxml.jackson.core.type.TypeReference;
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.google.gson.Gson;
|
||||||
import io.netty.channel.epoll.Epoll;
|
import io.netty.channel.epoll.Epoll;
|
||||||
import io.netty.util.NettyRuntime;
|
import io.netty.util.NettyRuntime;
|
||||||
import io.netty.util.concurrent.DefaultThreadFactory;
|
import io.netty.util.concurrent.DefaultThreadFactory;
|
||||||
|
|
@ -38,6 +39,8 @@ import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
import net.kyori.adventure.text.format.NamedTextColor;
|
import net.kyori.adventure.text.format.NamedTextColor;
|
||||||
|
import net.raphimc.minecraftauth.step.java.session.StepFullJavaSession;
|
||||||
|
import net.raphimc.minecraftauth.step.msa.StepRefreshTokenMsaCode;
|
||||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
@ -55,7 +58,11 @@ import org.geysermc.geyser.api.GeyserApi;
|
||||||
import org.geysermc.geyser.api.command.CommandSource;
|
import org.geysermc.geyser.api.command.CommandSource;
|
||||||
import org.geysermc.geyser.api.event.EventBus;
|
import org.geysermc.geyser.api.event.EventBus;
|
||||||
import org.geysermc.geyser.api.event.EventRegistrar;
|
import org.geysermc.geyser.api.event.EventRegistrar;
|
||||||
import org.geysermc.geyser.api.event.lifecycle.*;
|
import org.geysermc.geyser.api.event.lifecycle.GeyserPostInitializeEvent;
|
||||||
|
import org.geysermc.geyser.api.event.lifecycle.GeyserPostReloadEvent;
|
||||||
|
import org.geysermc.geyser.api.event.lifecycle.GeyserPreInitializeEvent;
|
||||||
|
import org.geysermc.geyser.api.event.lifecycle.GeyserPreReloadEvent;
|
||||||
|
import org.geysermc.geyser.api.event.lifecycle.GeyserShutdownEvent;
|
||||||
import org.geysermc.geyser.api.network.AuthType;
|
import org.geysermc.geyser.api.network.AuthType;
|
||||||
import org.geysermc.geyser.api.network.BedrockListener;
|
import org.geysermc.geyser.api.network.BedrockListener;
|
||||||
import org.geysermc.geyser.api.network.RemoteServer;
|
import org.geysermc.geyser.api.network.RemoteServer;
|
||||||
|
|
@ -85,7 +92,13 @@ import org.geysermc.geyser.skin.SkinProvider;
|
||||||
import org.geysermc.geyser.text.GeyserLocale;
|
import org.geysermc.geyser.text.GeyserLocale;
|
||||||
import org.geysermc.geyser.text.MinecraftLocale;
|
import org.geysermc.geyser.text.MinecraftLocale;
|
||||||
import org.geysermc.geyser.translator.text.MessageTranslator;
|
import org.geysermc.geyser.translator.text.MessageTranslator;
|
||||||
import org.geysermc.geyser.util.*;
|
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.NewsHandler;
|
||||||
|
import org.geysermc.geyser.util.VersionCheckUtils;
|
||||||
|
import org.geysermc.geyser.util.WebUtils;
|
||||||
import org.geysermc.mcprotocollib.network.tcp.TcpSession;
|
import org.geysermc.mcprotocollib.network.tcp.TcpSession;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
@ -97,7 +110,14 @@ import java.net.UnknownHostException;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.security.Key;
|
import java.security.Key;
|
||||||
import java.text.DecimalFormat;
|
import java.text.DecimalFormat;
|
||||||
import java.util.*;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.UUID;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
|
|
@ -536,6 +556,54 @@ public class GeyserImpl implements GeyserApi {
|
||||||
// May be written/read to on multiple threads from each GeyserSession as well as writing the config
|
// May be written/read to on multiple threads from each GeyserSession as well as writing the config
|
||||||
savedAuthChains = new ConcurrentHashMap<>();
|
savedAuthChains = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
boolean doWrite = false;
|
||||||
|
|
||||||
|
// 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<Map<String, String>> type = new TypeReference<>() { };
|
||||||
|
Map<String, String> authChainFile = null;
|
||||||
|
try {
|
||||||
|
authChainFile = JSON_MAPPER.readValue(refreshTokensFile, type);
|
||||||
|
} catch (IOException e) {
|
||||||
|
// ignored - we'll just delete this file :))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (authChainFile != null) {
|
||||||
|
List<String> validUsers = config.getSavedUserLogins();
|
||||||
|
final Gson gson = new Gson();
|
||||||
|
for (Map.Entry<String, String> entry : authChainFile.entrySet()) {
|
||||||
|
String user = entry.getKey();
|
||||||
|
if (!validUsers.contains(user)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Migrate refresh tokens to auth chains
|
||||||
|
try {
|
||||||
|
StepFullJavaSession javaSession = PendingMicrosoftAuthentication.REFRESH_TOKEN_UPDATE_AUTH_FLOW.apply(true, 10);
|
||||||
|
StepFullJavaSession.FullJavaSession fullJavaSession = javaSession.getFromInput(
|
||||||
|
PendingMicrosoftAuthentication.AUTH_CLIENT,
|
||||||
|
new StepRefreshTokenMsaCode.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().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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally: Delete it. Goodbye!
|
||||||
|
//refreshTokensFile.delete();
|
||||||
|
}
|
||||||
|
|
||||||
File authChainsFile = bootstrap.getSavedUserLoginsFolder().resolve(Constants.SAVED_AUTH_CHAINS_FILE).toFile();
|
File authChainsFile = bootstrap.getSavedUserLoginsFolder().resolve(Constants.SAVED_AUTH_CHAINS_FILE).toFile();
|
||||||
if (authChainsFile.exists()) {
|
if (authChainsFile.exists()) {
|
||||||
TypeReference<Map<String, String>> type = new TypeReference<>() { };
|
TypeReference<Map<String, String>> type = new TypeReference<>() { };
|
||||||
|
|
@ -548,7 +616,6 @@ public class GeyserImpl implements GeyserApi {
|
||||||
}
|
}
|
||||||
if (authChainFile != null) {
|
if (authChainFile != null) {
|
||||||
List<String> validUsers = config.getSavedUserLogins();
|
List<String> validUsers = config.getSavedUserLogins();
|
||||||
boolean doWrite = false;
|
|
||||||
for (Map.Entry<String, String> entry : authChainFile.entrySet()) {
|
for (Map.Entry<String, String> entry : authChainFile.entrySet()) {
|
||||||
String user = entry.getKey();
|
String user = entry.getKey();
|
||||||
if (!validUsers.contains(user)) {
|
if (!validUsers.contains(user)) {
|
||||||
|
|
|
||||||
|
|
@ -709,7 +709,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
||||||
try {
|
try {
|
||||||
response = step.refresh(PendingMicrosoftAuthentication.AUTH_CLIENT, step.fromJson(gson.fromJson(authChain, JsonObject.class)));
|
response = step.refresh(PendingMicrosoftAuthentication.AUTH_CLIENT, step.fromJson(gson.fromJson(authChain, JsonObject.class)));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
geyser.getLogger().error("Error while attempting to use refresh token for " + bedrockUsername() + "!", e);
|
geyser.getLogger().error("Error while attempting to use auth chain for " + bedrockUsername() + "!", e);
|
||||||
return Boolean.FALSE;
|
return Boolean.FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ import net.lenni0451.commons.httpclient.HttpClient;
|
||||||
import net.raphimc.minecraftauth.MinecraftAuth;
|
import net.raphimc.minecraftauth.MinecraftAuth;
|
||||||
import net.raphimc.minecraftauth.step.java.session.StepFullJavaSession;
|
import net.raphimc.minecraftauth.step.java.session.StepFullJavaSession;
|
||||||
import net.raphimc.minecraftauth.step.msa.StepMsaDeviceCode;
|
import net.raphimc.minecraftauth.step.msa.StepMsaDeviceCode;
|
||||||
|
import net.raphimc.minecraftauth.step.msa.StepRefreshTokenMsaCode;
|
||||||
import net.raphimc.minecraftauth.util.MicrosoftConstants;
|
import net.raphimc.minecraftauth.util.MicrosoftConstants;
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
import org.geysermc.geyser.GeyserImpl;
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
|
|
@ -58,6 +59,16 @@ public class PendingMicrosoftAuthentication {
|
||||||
.withoutDeviceToken()
|
.withoutDeviceToken()
|
||||||
.regularAuthentication(MicrosoftConstants.JAVA_XSTS_RELYING_PARTY)
|
.regularAuthentication(MicrosoftConstants.JAVA_XSTS_RELYING_PARTY)
|
||||||
.buildMinecraftJavaProfileStep(false);
|
.buildMinecraftJavaProfileStep(false);
|
||||||
|
|
||||||
|
public static final BiFunction<Boolean, Integer, StepFullJavaSession> 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.
|
* For GeyserConnect usage.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue