3
0
Mirror von https://github.com/GeyserMC/Geyser.git synchronisiert 2024-11-19 22:40:18 +01:00

Initial work on porting to MinecraftAuth

Dieser Commit ist enthalten in:
AlexProgrammerDE 2024-06-20 22:26:54 +02:00
Ursprung fc529a661c
Commit 8c89615922
17 geänderte Dateien mit 135 neuen und 197 gelöschten Zeilen

Datei anzeigen

@ -25,7 +25,7 @@ dependencies {
shadow(libs.protocol.connection) { isTransitive = false } shadow(libs.protocol.connection) { isTransitive = false }
shadow(libs.protocol.common) { isTransitive = false } shadow(libs.protocol.common) { isTransitive = false }
shadow(libs.protocol.codec) { isTransitive = false } shadow(libs.protocol.codec) { isTransitive = false }
shadow(libs.mcauthlib) { isTransitive = false } shadow(libs.minecraftauth) { isTransitive = false }
shadow(libs.raknet) { isTransitive = false } shadow(libs.raknet) { isTransitive = false }
// Consequences of shading + relocating mcauthlib: shadow/relocate mcpl! // Consequences of shading + relocating mcauthlib: shadow/relocate mcpl!

Datei anzeigen

@ -25,11 +25,10 @@ dependencies {
api(libs.bundles.protocol) api(libs.bundles.protocol)
api(libs.mcauthlib) api(libs.minecraftauth)
api(libs.mcprotocollib) { api(libs.mcprotocollib) {
exclude("io.netty", "netty-all") exclude("io.netty", "netty-all")
exclude("com.github.GeyserMC", "packetlib") exclude("net.raphimc", "MinecraftAuth")
exclude("com.github.GeyserMC", "mcauthlib")
} }
implementation(libs.raknet) { implementation(libs.raknet) {

Datei anzeigen

@ -39,7 +39,7 @@ 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";
static final String SAVED_REFRESH_TOKEN_FILE = "saved-refresh-tokens.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";

Datei anzeigen

@ -160,7 +160,7 @@ public class GeyserImpl implements GeyserApi {
private PendingMicrosoftAuthentication pendingMicrosoftAuthentication; private PendingMicrosoftAuthentication pendingMicrosoftAuthentication;
@Getter(AccessLevel.NONE) @Getter(AccessLevel.NONE)
private Map<String, String> savedRefreshTokens; private Map<String, String> savedAuthChains;
@Getter @Getter
private static GeyserImpl instance; private static GeyserImpl instance;
@ -527,37 +527,37 @@ public class GeyserImpl implements GeyserApi {
if (config.getRemote().authType() == AuthType.ONLINE) { if (config.getRemote().authType() == AuthType.ONLINE) {
// 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
savedRefreshTokens = new ConcurrentHashMap<>(); savedAuthChains = new ConcurrentHashMap<>();
File tokensFile = bootstrap.getSavedUserLoginsFolder().resolve(Constants.SAVED_REFRESH_TOKEN_FILE).toFile(); File authChainsFile = bootstrap.getSavedUserLoginsFolder().resolve(Constants.SAVED_AUTH_CHAINS_FILE).toFile();
if (tokensFile.exists()) { if (authChainsFile.exists()) {
TypeReference<Map<String, String>> type = new TypeReference<>() { }; TypeReference<Map<String, String>> type = new TypeReference<>() { };
Map<String, String> refreshTokenFile = null; Map<String, String> authChainFile = null;
try { try {
refreshTokenFile = JSON_MAPPER.readValue(tokensFile, type); authChainFile = JSON_MAPPER.readValue(authChainsFile, type);
} catch (IOException e) { } catch (IOException e) {
logger.error("Cannot load saved user tokens!", e); logger.error("Cannot load saved user tokens!", e);
} }
if (refreshTokenFile != null) { if (authChainFile != null) {
List<String> validUsers = config.getSavedUserLogins(); List<String> validUsers = config.getSavedUserLogins();
boolean doWrite = false; boolean doWrite = false;
for (Map.Entry<String, String> entry : refreshTokenFile.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)) {
// Perform a write to this file to purge the now-unused name // Perform a write to this file to purge the now-unused name
doWrite = true; doWrite = true;
continue; continue;
} }
savedRefreshTokens.put(user, entry.getValue()); savedAuthChains.put(user, entry.getValue());
} }
if (doWrite) { if (doWrite) {
scheduleRefreshTokensWrite(); scheduleAuthChainsWrite();
} }
} }
} }
} else { } else {
savedRefreshTokens = null; savedAuthChains = null;
} }
newsHandler.handleNews(null, NewsItemAction.ON_SERVER_STARTED); newsHandler.handleNews(null, NewsItemAction.ON_SERVER_STARTED);
@ -808,11 +808,11 @@ public class GeyserImpl implements GeyserApi {
} }
@Nullable @Nullable
public String refreshTokenFor(@NonNull String bedrockName) { public String authChainFor(@NonNull String bedrockName) {
return savedRefreshTokens.get(bedrockName); return savedAuthChains.get(bedrockName);
} }
public void saveRefreshToken(@NonNull String bedrockName, @NonNull String refreshToken) { public void saveAuthChain(@NonNull String bedrockName, @NonNull String authChain) {
if (!getConfig().getSavedUserLogins().contains(bedrockName)) { if (!getConfig().getSavedUserLogins().contains(bedrockName)) {
// Do not save this login // Do not save this login
return; return;
@ -820,20 +820,20 @@ public class GeyserImpl implements GeyserApi {
// We can safely overwrite old instances because MsaAuthenticationService#getLoginResponseFromRefreshToken // We can safely overwrite old instances because MsaAuthenticationService#getLoginResponseFromRefreshToken
// refreshes the token for us // refreshes the token for us
if (!Objects.equals(refreshToken, savedRefreshTokens.put(bedrockName, refreshToken))) { if (!Objects.equals(authChain, savedAuthChains.put(bedrockName, authChain))) {
scheduleRefreshTokensWrite(); scheduleAuthChainsWrite();
} }
} }
private void scheduleRefreshTokensWrite() { private void scheduleAuthChainsWrite() {
scheduledThread.execute(() -> { scheduledThread.execute(() -> {
// Ensure all writes are handled on the same thread // Ensure all writes are handled on the same thread
File savedTokens = getBootstrap().getSavedUserLoginsFolder().resolve(Constants.SAVED_REFRESH_TOKEN_FILE).toFile(); File savedAuthChains = getBootstrap().getSavedUserLoginsFolder().resolve(Constants.SAVED_AUTH_CHAINS_FILE).toFile();
TypeReference<Map<String, String>> type = new TypeReference<>() { }; TypeReference<Map<String, String>> type = new TypeReference<>() { };
try (FileWriter writer = new FileWriter(savedTokens)) { try (FileWriter writer = new FileWriter(savedAuthChains)) {
JSON_MAPPER.writerFor(type) JSON_MAPPER.writerFor(type)
.withDefaultPrettyPrinter() .withDefaultPrettyPrinter()
.writeValue(writer, savedRefreshTokens); .writeValue(writer, this.savedAuthChains);
} catch (IOException e) { } catch (IOException e) {
getLogger().error("Unable to write saved refresh tokens!", e); getLogger().error("Unable to write saved refresh tokens!", e);
} }

Datei anzeigen

@ -25,7 +25,7 @@
package org.geysermc.geyser.item.type; package org.geysermc.geyser.item.type;
import com.github.steveice10.mc.auth.data.GameProfile; import org.geysermc.mcprotocollib.auth.GameProfile;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
import org.geysermc.geyser.level.block.type.Block; import org.geysermc.geyser.level.block.type.Block;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;

Datei anzeigen

@ -25,7 +25,7 @@
package org.geysermc.geyser.level.block.type; package org.geysermc.geyser.level.block.type;
import com.github.steveice10.mc.auth.data.GameProfile; import org.geysermc.mcprotocollib.auth.GameProfile;
import org.cloudburstmc.math.vector.Vector3i; import org.cloudburstmc.math.vector.Vector3i;
import org.cloudburstmc.nbt.NbtMap; import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.nbt.NbtMapBuilder; import org.cloudburstmc.nbt.NbtMapBuilder;

Datei anzeigen

@ -274,10 +274,10 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
private boolean couldLoginUserByName(String bedrockUsername) { private boolean couldLoginUserByName(String bedrockUsername) {
if (geyser.getConfig().getSavedUserLogins().contains(bedrockUsername)) { if (geyser.getConfig().getSavedUserLogins().contains(bedrockUsername)) {
String refreshToken = geyser.refreshTokenFor(bedrockUsername); String authChain = geyser.authChainFor(bedrockUsername);
if (refreshToken != null) { if (authChain != null) {
geyser.getLogger().info(GeyserLocale.getLocaleStringLog("geyser.auth.stored_credentials", session.getAuthData().name())); geyser.getLogger().info(GeyserLocale.getLocaleStringLog("geyser.auth.stored_credentials", session.getAuthData().name()));
session.authenticateWithRefreshToken(refreshToken); session.authenticateWithAuthChain(authChain);
return true; return true;
} }
} }

Datei anzeigen

@ -25,9 +25,12 @@
package org.geysermc.geyser.session; package org.geysermc.geyser.session;
import com.github.steveice10.mc.auth.data.GameProfile; import com.google.gson.Gson;
import com.github.steveice10.mc.auth.exception.request.RequestException; import com.google.gson.JsonObject;
import com.github.steveice10.mc.auth.service.MsaAuthenticationService; 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.Channel;
import io.netty.channel.EventLoop; import io.netty.channel.EventLoop;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
@ -554,6 +557,8 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
@Setter @Getter @Setter @Getter
private Map<String, byte[]> cookies = new Object2ObjectOpenHashMap<>(); private Map<String, byte[]> cookies = new Object2ObjectOpenHashMap<>();
private final Gson gson = new Gson();
private final GeyserCameraData cameraData; private final GeyserCameraData cameraData;
private final GeyserEntityData entityData; private final GeyserEntityData entityData;
@ -691,7 +696,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
} }
} }
public void authenticateWithRefreshToken(String refreshToken) { public void authenticateWithAuthChain(String authChain) {
if (loggedIn) { if (loggedIn) {
geyser.getLogger().severe(GeyserLocale.getLocaleStringLog("geyser.auth.already_loggedin", getAuthData().name())); geyser.getLogger().severe(GeyserLocale.getLocaleStringLog("geyser.auth.already_loggedin", getAuthData().name()));
return; return;
@ -700,24 +705,23 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
loggingIn = true; loggingIn = true;
CompletableFuture.supplyAsync(() -> { CompletableFuture.supplyAsync(() -> {
MsaAuthenticationService service = new MsaAuthenticationService(GeyserImpl.OAUTH_CLIENT_ID); StepFullJavaSession step = PendingMicrosoftAuthentication.AUTH_FLOW.apply(true, 30);
service.setRefreshToken(refreshToken); StepFullJavaSession.FullJavaSession response;
try { try {
service.login(); response = step.refresh(PendingMicrosoftAuthentication.AUTH_CLIENT, step.fromJson(gson.fromJson(authChain, JsonObject.class)));
} catch (RequestException 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 refresh token for " + bedrockUsername() + "!", e);
return Boolean.FALSE; return Boolean.FALSE;
} }
GameProfile profile = service.getSelectedProfile(); StepMCProfile.MCProfile mcProfile = response.getMcProfile();
if (profile == null) { StepMCToken.MCToken mcToken = mcProfile.getMcToken();
// Java account is offline
disconnect(GeyserLocale.getPlayerLocaleString("geyser.network.remote.invalid_account", clientData.getLanguageCode()));
return null;
}
protocol = new MinecraftProtocol(profile, service.getAccessToken()); protocol = new MinecraftProtocol(
geyser.saveRefreshToken(bedrockUsername(), service.getRefreshToken()); new GameProfile(mcProfile.getId(), mcProfile.getName()),
mcToken.getAccessToken()
);
geyser.saveAuthChain(bedrockUsername(), gson.toJson(step.toJson(response)));
return Boolean.TRUE; return Boolean.TRUE;
}).whenComplete((successful, ex) -> { }).whenComplete((successful, ex) -> {
if (this.closed) { if (this.closed) {
@ -762,25 +766,15 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
final PendingMicrosoftAuthentication.AuthenticationTask task = geyser.getPendingMicrosoftAuthentication().getOrCreateTask( final PendingMicrosoftAuthentication.AuthenticationTask task = geyser.getPendingMicrosoftAuthentication().getOrCreateTask(
getAuthData().xuid() getAuthData().xuid()
); );
task.setOnline(true);
task.resetTimer();
if (task.getAuthentication().isDone()) { if (task.getAuthentication().isDone()) {
onMicrosoftLoginComplete(task); onMicrosoftLoginComplete(task);
} else { } else {
task.getCode(offlineAccess).whenComplete((response, ex) -> { task.resetRunningFlow();
boolean connected = !closed; task.performLoginAttempt(offlineAccess, code -> {
if (ex != null) { if (!closed) {
if (connected) { LoginEncryptionUtils.buildAndShowMicrosoftCodeWindow(this, code);
geyser.getLogger().error("Failed to get Microsoft auth code", ex);
disconnect(ex.toString());
}
task.cleanup(); // error getting auth code -> clean up immediately
} else if (connected) {
LoginEncryptionUtils.buildAndShowMicrosoftCodeWindow(this, response);
task.getAuthentication().whenComplete((r, $) -> onMicrosoftLoginComplete(task));
} }
}); }).handle((r, e) -> onMicrosoftLoginComplete(task));
} }
} }
@ -792,36 +786,31 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
return false; return false;
} }
task.cleanup(); // player is online -> remove pending authentication immediately task.cleanup(); // player is online -> remove pending authentication immediately
Throwable ex = task.getLoginException(); return task.getAuthentication().handle((response, ex) -> {
if (ex != null) { if (ex != null) {
geyser.getLogger().error("Failed to log in with Microsoft code!", ex); geyser.getLogger().error("Failed to log in with Microsoft code!", ex);
disconnect(ex.toString()); disconnect(ex.toString());
} else { return false;
MsaAuthenticationService service = task.getMsaAuthenticationService(); }
GameProfile selectedProfile = service.getSelectedProfile();
if (selectedProfile == null) {
disconnect(GeyserLocale.getPlayerLocaleString(
"geyser.network.remote.invalid_account",
clientData.getLanguageCode()
));
} else {
this.protocol = new MinecraftProtocol(
selectedProfile,
service.getAccessToken()
);
try {
connectDownstream();
} catch (Throwable t) {
t.printStackTrace();
return false;
}
// Save our refresh token for later use StepMCProfile.MCProfile mcProfile = response.getMcProfile();
geyser.saveRefreshToken(bedrockUsername(), service.getRefreshToken()); StepMCToken.MCToken mcToken = mcProfile.getMcToken();
return true;
} this.protocol = new MinecraftProtocol(
} new GameProfile(mcProfile.getId(), mcProfile.getName()),
return false; mcToken.getAccessToken()
);
try {
connectDownstream();
} catch (Throwable t) {
t.printStackTrace();
return false;
}
// Save our refresh token for later use
geyser.saveAuthChain(bedrockUsername(), mcToken.getXblXsts().getInitialXblSession().getMsaToken().getRefreshToken());
return true;
}).join();
} }
/** /**
@ -1099,7 +1088,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
if (authData != null) { if (authData != null) {
PendingMicrosoftAuthentication.AuthenticationTask task = geyser.getPendingMicrosoftAuthentication().getTask(authData.xuid()); PendingMicrosoftAuthentication.AuthenticationTask task = geyser.getPendingMicrosoftAuthentication().getTask(authData.xuid());
if (task != null) { if (task != null) {
task.setOnline(false); task.resetRunningFlow();
} }
} }
} }

Datei anzeigen

@ -25,27 +25,41 @@
package org.geysermc.geyser.session; package org.geysermc.geyser.session;
import com.github.steveice10.mc.auth.exception.request.AuthPendingException;
import com.github.steveice10.mc.auth.exception.request.RequestException;
import com.github.steveice10.mc.auth.service.MsaAuthenticationService;
import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader; import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache; import com.google.common.cache.LoadingCache;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import lombok.SneakyThrows; 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 org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.GeyserLogger; import org.geysermc.geyser.GeyserLogger;
import java.io.Serial; import java.io.Serial;
import java.util.concurrent.*; import java.util.concurrent.*;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
/** /**
* Pending Microsoft authentication task cache. * Pending Microsoft authentication task cache.
* It permits user to exit the server while they authorize Geyser to access their Microsoft account. * It permits user to exit the server while they authorize Geyser to access their Microsoft account.
*/ */
public class PendingMicrosoftAuthentication { 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.signin XboxLive.offline_access")
.withTimeout(timeoutSec)
.deviceCode()
.withoutDeviceToken()
.regularAuthentication(MicrosoftConstants.JAVA_XSTS_RELYING_PARTY)
.buildMinecraftJavaProfileStep(false);
/** /**
* For GeyserConnect usage. * For GeyserConnect usage.
*/ */
@ -57,8 +71,8 @@ public class PendingMicrosoftAuthentication {
.build(new CacheLoader<>() { .build(new CacheLoader<>() {
@Override @Override
public AuthenticationTask load(@NonNull String userKey) { public AuthenticationTask load(@NonNull String userKey) {
return storeServerInformation ? new ProxyAuthenticationTask(userKey, timeoutSeconds * 1000L) return storeServerInformation ? new ProxyAuthenticationTask(userKey, timeoutSeconds)
: new AuthenticationTask(userKey, timeoutSeconds * 1000L); : new AuthenticationTask(userKey, timeoutSeconds);
} }
}); });
} }
@ -80,37 +94,19 @@ public class PendingMicrosoftAuthentication {
public class AuthenticationTask { public class AuthenticationTask {
private static final Executor DELAYED_BY_ONE_SECOND = CompletableFuture.delayedExecutor(1, TimeUnit.SECONDS); private static final Executor DELAYED_BY_ONE_SECOND = CompletableFuture.delayedExecutor(1, TimeUnit.SECONDS);
@Getter
private final MsaAuthenticationService msaAuthenticationService = new MsaAuthenticationService(GeyserImpl.OAUTH_CLIENT_ID);
private final String userKey; private final String userKey;
private final long timeoutMs; private final int timeoutSec;
private long remainingTimeMs;
@Setter
private boolean online = true;
@Getter @Getter
private final CompletableFuture<MsaAuthenticationService> authentication; private CompletableFuture<StepFullJavaSession.FullJavaSession> authentication;
@Getter private AuthenticationTask(String userKey, int timeoutSec) {
private volatile Throwable loginException;
private AuthenticationTask(String userKey, long timeoutMs) {
this.userKey = userKey; this.userKey = userKey;
this.timeoutMs = timeoutMs; this.timeoutSec = timeoutSec;
this.remainingTimeMs = timeoutMs;
this.authentication = new CompletableFuture<>();
this.authentication.whenComplete((r, ex) -> {
this.loginException = ex;
// avoid memory leak, in case player doesn't connect again
CompletableFuture.delayedExecutor(timeoutMs, TimeUnit.MILLISECONDS).execute(this::cleanup);
});
} }
public void resetTimer() { public void resetRunningFlow() {
this.remainingTimeMs = this.timeoutMs; // Interrupt the current flow
this.authentication.cancel(true);
} }
public void cleanup() { public void cleanup() {
@ -121,52 +117,18 @@ public class PendingMicrosoftAuthentication {
authentications.invalidate(userKey); authentications.invalidate(userKey);
} }
public CompletableFuture<MsaAuthenticationService.MsCodeResponse> getCode(boolean offlineAccess) { public CompletableFuture<StepFullJavaSession.FullJavaSession> performLoginAttempt(boolean offlineAccess, Consumer<StepMsaDeviceCode.MsaDeviceCode> deviceCodeConsumer) {
// Request the code return authentication = CompletableFuture.supplyAsync(() -> {
CompletableFuture<MsaAuthenticationService.MsCodeResponse> code = CompletableFuture.supplyAsync(
() -> tryGetCode(offlineAccess));
// Once the code is received, continuously try to request the access token, profile, etc
code.thenRun(() -> performLoginAttempt(System.currentTimeMillis()));
return code;
}
/**
* @param offlineAccess whether we want a refresh token for later use.
*/
private MsaAuthenticationService.MsCodeResponse tryGetCode(boolean offlineAccess) throws CompletionException {
try {
return msaAuthenticationService.getAuthCode(offlineAccess);
} catch (RequestException e) {
throw new CompletionException(e);
}
}
private void performLoginAttempt(long lastAttempt) {
CompletableFuture.runAsync(() -> {
try { try {
msaAuthenticationService.login(); return AUTH_FLOW.apply(offlineAccess, timeoutSec)
} catch (AuthPendingException e) { .getFromInput(AUTH_CLIENT, new StepMsaDeviceCode.MsaDeviceCodeCallback(deviceCodeConsumer));
long currentAttempt = System.currentTimeMillis();
if (!online) {
// decrement timer only when player's offline
remainingTimeMs -= currentAttempt - lastAttempt;
if (remainingTimeMs <= 0L) {
// time's up
authentication.completeExceptionally(new TaskTimeoutException());
cleanup();
return;
}
}
// try again in 1 second
performLoginAttempt(currentAttempt);
return;
} catch (Exception e) { } catch (Exception e) {
authentication.completeExceptionally(e); throw new CompletionException(e);
return;
} }
// login successful }, DELAYED_BY_ONE_SECOND).whenComplete((r, ex) -> {
authentication.complete(msaAuthenticationService); // avoid memory leak, in case player doesn't connect again
}, DELAYED_BY_ONE_SECOND); CompletableFuture.delayedExecutor(timeoutSec, TimeUnit.SECONDS).execute(this::cleanup);
});
} }
@Override @Override
@ -181,8 +143,8 @@ public class PendingMicrosoftAuthentication {
private String server; private String server;
private int port; private int port;
private ProxyAuthenticationTask(String userKey, long timeoutMs) { private ProxyAuthenticationTask(String userKey, int timeoutSec) {
super(userKey, timeoutMs); super(userKey, timeoutSec);
} }
} }

Datei anzeigen

@ -25,11 +25,10 @@
package org.geysermc.geyser.skin; package org.geysermc.geyser.skin;
import com.github.steveice10.mc.auth.data.GameProfile; import org.geysermc.mcprotocollib.auth.GameProfile;
import com.github.steveice10.mc.auth.data.GameProfile.Texture; import org.geysermc.mcprotocollib.auth.GameProfile.Texture;
import com.github.steveice10.mc.auth.data.GameProfile.TextureModel; import org.geysermc.mcprotocollib.auth.GameProfile.TextureModel;
import com.github.steveice10.mc.auth.data.GameProfile.TextureType; import org.geysermc.mcprotocollib.auth.GameProfile.TextureType;
import com.github.steveice10.mc.auth.exception.property.PropertyException;
import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader; import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache; import com.google.common.cache.LoadingCache;
@ -113,12 +112,7 @@ public class FakeHeadProvider {
return; return;
} }
Map<TextureType, Texture> textures = null; Map<TextureType, Texture> textures = profile.getTextures(false);
try {
textures = profile.getTextures(false);
} catch (PropertyException e) {
session.getGeyser().getLogger().debug("Failed to get textures from GameProfile: " + e);
}
if (textures == null || textures.isEmpty()) { if (textures == null || textures.isEmpty()) {
loadHead(session, entity, profile.getName()); loadHead(session, entity, profile.getName());

Datei anzeigen

@ -25,10 +25,9 @@
package org.geysermc.geyser.translator.item; package org.geysermc.geyser.translator.item;
import com.github.steveice10.mc.auth.data.GameProfile; import org.geysermc.mcprotocollib.auth.GameProfile;
import com.github.steveice10.mc.auth.data.GameProfile.Texture; import org.geysermc.mcprotocollib.auth.GameProfile.Texture;
import com.github.steveice10.mc.auth.data.GameProfile.TextureType; import org.geysermc.mcprotocollib.auth.GameProfile.TextureType;
import com.github.steveice10.mc.auth.exception.property.PropertyException;
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 org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
@ -465,12 +464,7 @@ public final class ItemTranslator {
GameProfile profile = components.get(DataComponentType.PROFILE); GameProfile profile = components.get(DataComponentType.PROFILE);
if (profile != null) { if (profile != null) {
Map<TextureType, Texture> textures = null; Map<TextureType, Texture> textures = profile.getTextures(false);
try {
textures = profile.getTextures(false);
} catch (PropertyException e) {
GeyserImpl.getInstance().getLogger().debug("Failed to get textures from GameProfile: " + e);
}
if (textures == null || textures.isEmpty()) { if (textures == null || textures.isEmpty()) {
return null; return null;

Datei anzeigen

@ -46,7 +46,7 @@ public class BedrockSetLocalPlayerAsInitializedTranslator extends PacketTranslat
if (session.remoteServer().authType() == AuthType.ONLINE) { if (session.remoteServer().authType() == AuthType.ONLINE) {
if (!session.isLoggedIn()) { if (!session.isLoggedIn()) {
if (session.getGeyser().getConfig().getSavedUserLogins().contains(session.bedrockUsername())) { if (session.getGeyser().getConfig().getSavedUserLogins().contains(session.bedrockUsername())) {
if (session.getGeyser().refreshTokenFor(session.bedrockUsername()) == null) { if (session.getGeyser().authChainFor(session.bedrockUsername()) == null) {
LoginEncryptionUtils.buildAndShowConsentWindow(session); LoginEncryptionUtils.buildAndShowConsentWindow(session);
} else { } else {
// If the refresh token is not null and we're here, then the refresh token expired // If the refresh token is not null and we're here, then the refresh token expired

Datei anzeigen

@ -25,7 +25,7 @@
package org.geysermc.geyser.translator.protocol.java; package org.geysermc.geyser.translator.protocol.java;
import com.github.steveice10.mc.auth.data.GameProfile; import org.geysermc.mcprotocollib.auth.GameProfile;
import net.kyori.adventure.key.Key; import net.kyori.adventure.key.Key;
import org.geysermc.geyser.api.network.AuthType; import org.geysermc.geyser.api.network.AuthType;
import org.geysermc.geyser.entity.type.player.PlayerEntity; import org.geysermc.geyser.entity.type.player.PlayerEntity;

Datei anzeigen

@ -25,7 +25,7 @@
package org.geysermc.geyser.translator.protocol.java.entity.player; package org.geysermc.geyser.translator.protocol.java.entity.player;
import com.github.steveice10.mc.auth.data.GameProfile; import org.geysermc.mcprotocollib.auth.GameProfile;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
import org.cloudburstmc.math.vector.Vector3f; import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.packet.PlayerListPacket; import org.cloudburstmc.protocol.bedrock.packet.PlayerListPacket;

Datei anzeigen

@ -28,7 +28,7 @@ package org.geysermc.geyser.util;
import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.steveice10.mc.auth.service.MsaAuthenticationService; import net.raphimc.minecraftauth.step.msa.StepMsaDeviceCode;
import org.cloudburstmc.protocol.bedrock.packet.LoginPacket; import org.cloudburstmc.protocol.bedrock.packet.LoginPacket;
import org.cloudburstmc.protocol.bedrock.packet.ServerToClientHandshakePacket; import org.cloudburstmc.protocol.bedrock.packet.ServerToClientHandshakePacket;
import org.cloudburstmc.protocol.bedrock.util.ChainValidationResult; import org.cloudburstmc.protocol.bedrock.util.ChainValidationResult;
@ -203,7 +203,7 @@ public class LoginEncryptionUtils {
/** /**
* Shows the code that a user must input into their browser * Shows the code that a user must input into their browser
*/ */
public static void buildAndShowMicrosoftCodeWindow(GeyserSession session, MsaAuthenticationService.MsCodeResponse msCode) { public static void buildAndShowMicrosoftCodeWindow(GeyserSession session, StepMsaDeviceCode.MsaDeviceCode msCode) {
String locale = session.locale(); String locale = session.locale();
StringBuilder message = new StringBuilder("%xbox.signin.website\n") StringBuilder message = new StringBuilder("%xbox.signin.website\n")
@ -212,7 +212,7 @@ public class LoginEncryptionUtils {
.append(ChatColor.RESET) .append(ChatColor.RESET)
.append("\n%xbox.signin.enterCode\n") .append("\n%xbox.signin.enterCode\n")
.append(ChatColor.GREEN) .append(ChatColor.GREEN)
.append(msCode.user_code); .append(msCode.getUserCode());
int timeout = session.getGeyser().getConfig().getPendingAuthenticationTimeout(); int timeout = session.getGeyser().getConfig().getPendingAuthenticationTimeout();
if (timeout != 0) { if (timeout != 0) {
message.append("\n\n") message.append("\n\n")

@ -1 +1 @@
Subproject commit 23cb22f9ceeb7f24b896a69a711944d7f3e756ed Subproject commit ff44a32574d0cac242aa99d8f97af0b5c636c0cf

Datei anzeigen

@ -12,8 +12,8 @@ gson = "2.3.1" # Provided by Spigot 1.8.8
websocket = "1.5.1" websocket = "1.5.1"
protocol = "3.0.0.Beta2-20240616.144648-10" protocol = "3.0.0.Beta2-20240616.144648-10"
raknet = "1.0.0.CR3-20240416.144209-1" raknet = "1.0.0.CR3-20240416.144209-1"
mcauthlib = "e5b0bcc" minecraftauth = "4.0.2"
mcprotocollib = "1.21-20240616.154144-5" mcprotocollib = "1.21-20240618.151504-8"
adventure = "4.14.0" adventure = "4.14.0"
adventure-platform = "4.3.0" adventure-platform = "4.3.0"
junit = "5.9.2" junit = "5.9.2"
@ -107,7 +107,7 @@ commodore = { group = "me.lucko", name = "commodore", version.ref = "commodore"
guava = { group = "com.google.guava", name = "guava", version.ref = "guava" } guava = { group = "com.google.guava", name = "guava", version.ref = "guava" }
gson = { group = "com.google.code.gson", name = "gson", version.ref = "gson" } gson = { group = "com.google.code.gson", name = "gson", version.ref = "gson" }
junit = { group = "org.junit.jupiter", name = "junit-jupiter", version.ref = "junit" } junit = { group = "org.junit.jupiter", name = "junit-jupiter", version.ref = "junit" }
mcauthlib = { group = "com.github.GeyserMC", name = "MCAuthLib", version.ref = "mcauthlib" } minecraftauth = { group = "net.raphimc", name = "MinecraftAuth", version.ref = "minecraftauth" }
mcprotocollib = { group = "org.geysermc.mcprotocollib", name = "protocol", version.ref = "mcprotocollib" } mcprotocollib = { group = "org.geysermc.mcprotocollib", name = "protocol", version.ref = "mcprotocollib" }
raknet = { group = "org.cloudburstmc.netty", name = "netty-transport-raknet", version.ref = "raknet" } raknet = { group = "org.cloudburstmc.netty", name = "netty-transport-raknet", version.ref = "raknet" }
terminalconsoleappender = { group = "net.minecrell", name = "terminalconsoleappender", version.ref = "terminalconsoleappender" } terminalconsoleappender = { group = "net.minecrell", name = "terminalconsoleappender", version.ref = "terminalconsoleappender" }