From 491737202dbc4c538bd7db91e9807fc491334fc5 Mon Sep 17 00:00:00 2001 From: Blue Kelp Date: Thu, 1 Aug 2019 19:08:50 -0700 Subject: [PATCH 1/9] refactor auth code out --- .../network/AuthenticationUtils.java | 89 +++++++++++++++++++ .../network/UpstreamPacketHandler.java | 74 +-------------- 2 files changed, 90 insertions(+), 73 deletions(-) create mode 100644 connector/src/main/java/org/geysermc/connector/network/AuthenticationUtils.java diff --git a/connector/src/main/java/org/geysermc/connector/network/AuthenticationUtils.java b/connector/src/main/java/org/geysermc/connector/network/AuthenticationUtils.java new file mode 100644 index 000000000..052fb001a --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/AuthenticationUtils.java @@ -0,0 +1,89 @@ +package org.geysermc.connector.network; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.JsonNodeType; +import com.nimbusds.jose.JWSObject; +import com.nukkitx.protocol.bedrock.packet.LoginPacket; +import com.nukkitx.protocol.bedrock.packet.ServerToClientHandshakePacket; +import com.nukkitx.protocol.bedrock.util.EncryptionUtils; +import net.minidev.json.JSONObject; +import org.geysermc.connector.GeyserConnector; +import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.session.auth.BedrockAuthData; +import org.geysermc.connector.utils.LoginEncryptionUtils; + +import javax.crypto.SecretKey; +import java.io.IOException; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.PublicKey; +import java.security.interfaces.ECPublicKey; +import java.security.spec.ECGenParameterSpec; +import java.util.UUID; + +public class AuthenticationUtils { + public static void encryptPlayerConnection(GeyserConnector connector, GeyserSession session, LoginPacket loginPacket) { + JsonNode certData; + try { + certData = LoginEncryptionUtils.JSON_MAPPER.readTree(loginPacket.getChainData().toByteArray()); + } catch (IOException ex) { + throw new RuntimeException("Certificate JSON can not be read."); + } + + JsonNode certChainData = certData.get("chain"); + if (certChainData.getNodeType() != JsonNodeType.ARRAY) { + throw new RuntimeException("Certificate data is not valid"); + } + + encryptConnectionWithCert(connector, session, loginPacket.getSkinData().toString(), certData); + } + + private static void encryptConnectionWithCert(GeyserConnector connector, GeyserSession session, String playerSkin, JsonNode certChainData) { + try { + boolean validChain = LoginEncryptionUtils.validateChainData(certChainData); + + connector.getLogger().debug(String.format("Is player data valid? %s", validChain)); + + JWSObject jwt = JWSObject.parse(certChainData.get(certChainData.size() - 1).asText()); + JsonNode payload = LoginEncryptionUtils.JSON_MAPPER.readTree(jwt.getPayload().toBytes()); + + if (payload.get("extraData").getNodeType() != JsonNodeType.OBJECT) { + throw new RuntimeException("AuthData was not found!"); + } + + JSONObject extraData = (JSONObject) jwt.getPayload().toJSONObject().get("extraData"); + session.setAuthenticationData(new BedrockAuthData(extraData.getAsString("displayName"), UUID.fromString(extraData.getAsString("identity")), extraData.getAsString("XUID"))); + + if (payload.get("identityPublicKey").getNodeType() != JsonNodeType.STRING) { + throw new RuntimeException("Identity Public Key was not found!"); + } + + ECPublicKey identityPublicKey = EncryptionUtils.generateKey(payload.get("identityPublicKey").textValue()); + JWSObject clientJwt = JWSObject.parse(playerSkin); + EncryptionUtils.verifyJwt(clientJwt, identityPublicKey); + + if (EncryptionUtils.canUseEncryption()) { + AuthenticationUtils.startEncryptionHandshake(session, identityPublicKey); + } + } catch (Exception ex) { + session.disconnect("disconnectionScreen.internalError.cantConnect"); + throw new RuntimeException("Unable to complete login", ex); + } + } + + private static void startEncryptionHandshake(GeyserSession session, PublicKey key) throws Exception { + KeyPairGenerator generator = KeyPairGenerator.getInstance("EC"); + generator.initialize(new ECGenParameterSpec("secp384r1")); + KeyPair serverKeyPair = generator.generateKeyPair(); + + byte[] token = EncryptionUtils.generateRandomToken(); + SecretKey encryptionKey = EncryptionUtils.getSecretKey(serverKeyPair.getPrivate(), key, token); + session.getUpstream().enableEncryption(encryptionKey); + + ServerToClientHandshakePacket packet = new ServerToClientHandshakePacket(); + packet.setJwt(EncryptionUtils.createHandshakeJwt(serverKeyPair, token).serialize()); + session.getUpstream().sendPacketImmediately(packet); + } + + +} diff --git a/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java b/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java index 6309aeeac..6cbb41190 100644 --- a/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java +++ b/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java @@ -25,13 +25,8 @@ package org.geysermc.connector.network; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.JsonNodeType; -import com.nimbusds.jose.JWSObject; import com.nukkitx.protocol.bedrock.BedrockPacket; import com.nukkitx.protocol.bedrock.packet.*; -import com.nukkitx.protocol.bedrock.util.EncryptionUtils; -import net.minidev.json.JSONObject; import org.geysermc.api.events.player.PlayerFormResponseEvent; import org.geysermc.api.window.CustomFormBuilder; import org.geysermc.api.window.CustomFormWindow; @@ -41,19 +36,8 @@ import org.geysermc.api.window.component.LabelComponent; import org.geysermc.api.window.response.CustomFormResponse; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.network.session.auth.BedrockAuthData; import org.geysermc.connector.network.session.cache.WindowCache; import org.geysermc.connector.network.translators.Registry; -import org.geysermc.connector.utils.LoginEncryptionUtils; - -import javax.crypto.SecretKey; -import java.io.IOException; -import java.security.KeyPair; -import java.security.KeyPairGenerator; -import java.security.PublicKey; -import java.security.interfaces.ECPublicKey; -import java.security.spec.ECGenParameterSpec; -import java.util.UUID; public class UpstreamPacketHandler extends LoggingPacketHandler { @@ -75,49 +59,7 @@ public class UpstreamPacketHandler extends LoggingPacketHandler { return true; } - JsonNode certData; - try { - certData = LoginEncryptionUtils.JSON_MAPPER.readTree(loginPacket.getChainData().toByteArray()); - } catch (IOException ex) { - throw new RuntimeException("Certificate JSON can not be read."); - } - - JsonNode certChainData = certData.get("chain"); - if (certChainData.getNodeType() != JsonNodeType.ARRAY) { - throw new RuntimeException("Certificate data is not valid"); - } - - boolean validChain; - try { - validChain = LoginEncryptionUtils.validateChainData(certChainData); - - connector.getLogger().debug(String.format("Is player data valid? %s", validChain)); - - JWSObject jwt = JWSObject.parse(certChainData.get(certChainData.size() - 1).asText()); - JsonNode payload = LoginEncryptionUtils.JSON_MAPPER.readTree(jwt.getPayload().toBytes()); - - if (payload.get("extraData").getNodeType() != JsonNodeType.OBJECT) { - throw new RuntimeException("AuthData was not found!"); - } - - JSONObject extraData = (JSONObject) jwt.getPayload().toJSONObject().get("extraData"); - session.setAuthenticationData(new BedrockAuthData(extraData.getAsString("displayName"), UUID.fromString(extraData.getAsString("identity")), extraData.getAsString("XUID"))); - - if (payload.get("identityPublicKey").getNodeType() != JsonNodeType.STRING) { - throw new RuntimeException("Identity Public Key was not found!"); - } - - ECPublicKey identityPublicKey = EncryptionUtils.generateKey(payload.get("identityPublicKey").textValue()); - JWSObject clientJwt = JWSObject.parse(loginPacket.getSkinData().toString()); - EncryptionUtils.verifyJwt(clientJwt, identityPublicKey); - - if (EncryptionUtils.canUseEncryption()) { - startEncryptionHandshake(identityPublicKey); - } - } catch (Exception ex) { - session.disconnect("disconnectionScreen.internalError.cantConnect"); - throw new RuntimeException("Unable to complete login", ex); - } + AuthenticationUtils.encryptPlayerConnection(connector, session, loginPacket); PlayStatusPacket playStatus = new PlayStatusPacket(); playStatus.setStatus(PlayStatusPacket.Status.LOGIN_SUCCESS); @@ -196,20 +138,6 @@ public class UpstreamPacketHandler extends LoggingPacketHandler { return false; } - private void startEncryptionHandshake(PublicKey key) throws Exception { - KeyPairGenerator generator = KeyPairGenerator.getInstance("EC"); - generator.initialize(new ECGenParameterSpec("secp384r1")); - KeyPair serverKeyPair = generator.generateKeyPair(); - - byte[] token = EncryptionUtils.generateRandomToken(); - SecretKey encryptionKey = EncryptionUtils.getSecretKey(serverKeyPair.getPrivate(), key, token); - session.getUpstream().enableEncryption(encryptionKey); - - ServerToClientHandshakePacket packet = new ServerToClientHandshakePacket(); - packet.setJwt(EncryptionUtils.createHandshakeJwt(serverKeyPair, token).serialize()); - session.getUpstream().sendPacketImmediately(packet); - } - @Override public boolean handle(AnimatePacket packet) { return translateAndDefault(packet); From 9bebfd5ded06db89c71df7f0ba5f3360326c30e2 Mon Sep 17 00:00:00 2001 From: Blue Kelp Date: Thu, 1 Aug 2019 19:59:13 -0700 Subject: [PATCH 2/9] pull out auth window code into util class --- .../network/UpstreamPacketHandler.java | 43 ++------------- .../AuthenticationUtils.java | 54 ++++++++++++++++++- 2 files changed, 55 insertions(+), 42 deletions(-) rename connector/src/main/java/org/geysermc/connector/{network => utils}/AuthenticationUtils.java (61%) diff --git a/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java b/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java index 6cbb41190..2c52de7de 100644 --- a/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java +++ b/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java @@ -27,17 +27,10 @@ package org.geysermc.connector.network; import com.nukkitx.protocol.bedrock.BedrockPacket; import com.nukkitx.protocol.bedrock.packet.*; -import org.geysermc.api.events.player.PlayerFormResponseEvent; -import org.geysermc.api.window.CustomFormBuilder; -import org.geysermc.api.window.CustomFormWindow; -import org.geysermc.api.window.FormWindow; -import org.geysermc.api.window.component.InputComponent; -import org.geysermc.api.window.component.LabelComponent; -import org.geysermc.api.window.response.CustomFormResponse; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.network.session.cache.WindowCache; import org.geysermc.connector.network.translators.Registry; +import org.geysermc.connector.utils.AuthenticationUtils; public class UpstreamPacketHandler extends LoggingPacketHandler { @@ -95,44 +88,14 @@ public class UpstreamPacketHandler extends LoggingPacketHandler { @Override public boolean handle(ModalFormResponsePacket packet) { connector.getLogger().debug("Handled packet: " + packet.getClass().getSimpleName()); - WindowCache windowCache = session.getWindowCache(); - if (!windowCache.getWindows().containsKey(packet.getFormId())) - return false; - - FormWindow window = windowCache.getWindows().remove(packet.getFormId()); - window.setResponse(packet.getFormData().trim()); - - if (session.isLoggedIn()) { - PlayerFormResponseEvent event = new PlayerFormResponseEvent(session, packet.getFormId(), window); - connector.getPluginManager().runEvent(event); - } else { - if (window instanceof CustomFormWindow) { - CustomFormWindow customFormWindow = (CustomFormWindow) window; - if (!customFormWindow.getTitle().equals("Login")) - return false; - - CustomFormResponse response = (CustomFormResponse) customFormWindow.getResponse(); - session.authenticate(response.getInputResponses().get(2), response.getInputResponses().get(3)); - - // Clear windows so authentication data isn't accidentally cached - windowCache.getWindows().clear(); - } - } - return true; + return AuthenticationUtils.authenticateFromForm(session, connector, packet.getFormData()); } @Override public boolean handle(MovePlayerPacket packet) { connector.getLogger().debug("Handled packet: " + packet.getClass().getSimpleName()); if (!session.isLoggedIn()) { - CustomFormWindow window = new CustomFormBuilder("Login") - .addComponent(new LabelComponent("Minecraft: Java Edition account authentication.")) - .addComponent(new LabelComponent("Enter the credentials for your Minecraft: Java Edition account below.")) - .addComponent(new InputComponent("Email/Username", "account@geysermc.org", "")) - .addComponent(new InputComponent("Password", "123456", "")) - .build(); - - session.sendForm(window, 1); + AuthenticationUtils.showLoginWindow(session); return true; } return false; diff --git a/connector/src/main/java/org/geysermc/connector/network/AuthenticationUtils.java b/connector/src/main/java/org/geysermc/connector/utils/AuthenticationUtils.java similarity index 61% rename from connector/src/main/java/org/geysermc/connector/network/AuthenticationUtils.java rename to connector/src/main/java/org/geysermc/connector/utils/AuthenticationUtils.java index 052fb001a..226dcb7fc 100644 --- a/connector/src/main/java/org/geysermc/connector/network/AuthenticationUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/AuthenticationUtils.java @@ -1,4 +1,4 @@ -package org.geysermc.connector.network; +package org.geysermc.connector.utils; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.JsonNodeType; @@ -7,10 +7,17 @@ import com.nukkitx.protocol.bedrock.packet.LoginPacket; import com.nukkitx.protocol.bedrock.packet.ServerToClientHandshakePacket; import com.nukkitx.protocol.bedrock.util.EncryptionUtils; import net.minidev.json.JSONObject; +import org.geysermc.api.events.player.PlayerFormResponseEvent; +import org.geysermc.api.window.CustomFormBuilder; +import org.geysermc.api.window.CustomFormWindow; +import org.geysermc.api.window.FormWindow; +import org.geysermc.api.window.component.InputComponent; +import org.geysermc.api.window.component.LabelComponent; +import org.geysermc.api.window.response.CustomFormResponse; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.session.auth.BedrockAuthData; -import org.geysermc.connector.utils.LoginEncryptionUtils; +import org.geysermc.connector.network.session.cache.WindowCache; import javax.crypto.SecretKey; import java.io.IOException; @@ -85,5 +92,48 @@ public class AuthenticationUtils { session.getUpstream().sendPacketImmediately(packet); } + private static int AUTH_FORM_ID = 1337; + + public static void showLoginWindow(GeyserSession session) { + CustomFormWindow window = new CustomFormBuilder("Login") + .addComponent(new LabelComponent("Minecraft: Java Edition account authentication.")) + .addComponent(new LabelComponent("Enter the credentials for your Minecraft: Java Edition account below.")) + .addComponent(new InputComponent("Email/Username", "account@geysermc.org", "")) + .addComponent(new InputComponent("Password", "123456", "")) + .build(); + + session.sendForm(window, AUTH_FORM_ID); + } + + public static boolean authenticateFromForm(GeyserSession session, GeyserConnector connector, String formData) { + WindowCache windowCache = session.getWindowCache(); + if (!windowCache.getWindows().containsKey(AUTH_FORM_ID)) + return false; + + FormWindow window = windowCache.getWindows().remove(AUTH_FORM_ID); + window.setResponse(formData.trim()); + + if (session.isLoggedIn()) { + PlayerFormResponseEvent event = new PlayerFormResponseEvent(session, AUTH_FORM_ID, window); + connector.getPluginManager().runEvent(event); + } else { + if (window instanceof CustomFormWindow) { + CustomFormWindow customFormWindow = (CustomFormWindow) window; + if (!customFormWindow.getTitle().equals("Login")) + return false; + + CustomFormResponse response = (CustomFormResponse) customFormWindow.getResponse(); + String username = response.getInputResponses().get(2); + String password = response.getInputResponses().get(3); + + session.authenticate(username, password); + + // TODO should we clear the window cache in all cases or just if not already logged in? + // Clear windows so authentication data isn't accidentally cached + windowCache.getWindows().clear(); + } + } + return true; + } } From 6f5c9a535eec91df9e8af5c1b7c6e00e16261d8f Mon Sep 17 00:00:00 2001 From: Blue Kelp Date: Thu, 1 Aug 2019 20:02:17 -0700 Subject: [PATCH 3/9] consolidate with existing utils class --- .../network/UpstreamPacketHandler.java | 8 +- .../connector/utils/AuthenticationUtils.java | 139 ------------------ .../connector/utils/LoginEncryptionUtils.java | 134 ++++++++++++++++- 3 files changed, 135 insertions(+), 146 deletions(-) delete mode 100644 connector/src/main/java/org/geysermc/connector/utils/AuthenticationUtils.java diff --git a/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java b/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java index 2c52de7de..4fbb520d9 100644 --- a/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java +++ b/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java @@ -30,7 +30,7 @@ import com.nukkitx.protocol.bedrock.packet.*; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.Registry; -import org.geysermc.connector.utils.AuthenticationUtils; +import org.geysermc.connector.utils.LoginEncryptionUtils; public class UpstreamPacketHandler extends LoggingPacketHandler { @@ -52,7 +52,7 @@ public class UpstreamPacketHandler extends LoggingPacketHandler { return true; } - AuthenticationUtils.encryptPlayerConnection(connector, session, loginPacket); + LoginEncryptionUtils.encryptPlayerConnection(connector, session, loginPacket); PlayStatusPacket playStatus = new PlayStatusPacket(); playStatus.setStatus(PlayStatusPacket.Status.LOGIN_SUCCESS); @@ -88,14 +88,14 @@ public class UpstreamPacketHandler extends LoggingPacketHandler { @Override public boolean handle(ModalFormResponsePacket packet) { connector.getLogger().debug("Handled packet: " + packet.getClass().getSimpleName()); - return AuthenticationUtils.authenticateFromForm(session, connector, packet.getFormData()); + return LoginEncryptionUtils.authenticateFromForm(session, connector, packet.getFormData()); } @Override public boolean handle(MovePlayerPacket packet) { connector.getLogger().debug("Handled packet: " + packet.getClass().getSimpleName()); if (!session.isLoggedIn()) { - AuthenticationUtils.showLoginWindow(session); + LoginEncryptionUtils.showLoginWindow(session); return true; } return false; diff --git a/connector/src/main/java/org/geysermc/connector/utils/AuthenticationUtils.java b/connector/src/main/java/org/geysermc/connector/utils/AuthenticationUtils.java deleted file mode 100644 index 226dcb7fc..000000000 --- a/connector/src/main/java/org/geysermc/connector/utils/AuthenticationUtils.java +++ /dev/null @@ -1,139 +0,0 @@ -package org.geysermc.connector.utils; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.JsonNodeType; -import com.nimbusds.jose.JWSObject; -import com.nukkitx.protocol.bedrock.packet.LoginPacket; -import com.nukkitx.protocol.bedrock.packet.ServerToClientHandshakePacket; -import com.nukkitx.protocol.bedrock.util.EncryptionUtils; -import net.minidev.json.JSONObject; -import org.geysermc.api.events.player.PlayerFormResponseEvent; -import org.geysermc.api.window.CustomFormBuilder; -import org.geysermc.api.window.CustomFormWindow; -import org.geysermc.api.window.FormWindow; -import org.geysermc.api.window.component.InputComponent; -import org.geysermc.api.window.component.LabelComponent; -import org.geysermc.api.window.response.CustomFormResponse; -import org.geysermc.connector.GeyserConnector; -import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.network.session.auth.BedrockAuthData; -import org.geysermc.connector.network.session.cache.WindowCache; - -import javax.crypto.SecretKey; -import java.io.IOException; -import java.security.KeyPair; -import java.security.KeyPairGenerator; -import java.security.PublicKey; -import java.security.interfaces.ECPublicKey; -import java.security.spec.ECGenParameterSpec; -import java.util.UUID; - -public class AuthenticationUtils { - public static void encryptPlayerConnection(GeyserConnector connector, GeyserSession session, LoginPacket loginPacket) { - JsonNode certData; - try { - certData = LoginEncryptionUtils.JSON_MAPPER.readTree(loginPacket.getChainData().toByteArray()); - } catch (IOException ex) { - throw new RuntimeException("Certificate JSON can not be read."); - } - - JsonNode certChainData = certData.get("chain"); - if (certChainData.getNodeType() != JsonNodeType.ARRAY) { - throw new RuntimeException("Certificate data is not valid"); - } - - encryptConnectionWithCert(connector, session, loginPacket.getSkinData().toString(), certData); - } - - private static void encryptConnectionWithCert(GeyserConnector connector, GeyserSession session, String playerSkin, JsonNode certChainData) { - try { - boolean validChain = LoginEncryptionUtils.validateChainData(certChainData); - - connector.getLogger().debug(String.format("Is player data valid? %s", validChain)); - - JWSObject jwt = JWSObject.parse(certChainData.get(certChainData.size() - 1).asText()); - JsonNode payload = LoginEncryptionUtils.JSON_MAPPER.readTree(jwt.getPayload().toBytes()); - - if (payload.get("extraData").getNodeType() != JsonNodeType.OBJECT) { - throw new RuntimeException("AuthData was not found!"); - } - - JSONObject extraData = (JSONObject) jwt.getPayload().toJSONObject().get("extraData"); - session.setAuthenticationData(new BedrockAuthData(extraData.getAsString("displayName"), UUID.fromString(extraData.getAsString("identity")), extraData.getAsString("XUID"))); - - if (payload.get("identityPublicKey").getNodeType() != JsonNodeType.STRING) { - throw new RuntimeException("Identity Public Key was not found!"); - } - - ECPublicKey identityPublicKey = EncryptionUtils.generateKey(payload.get("identityPublicKey").textValue()); - JWSObject clientJwt = JWSObject.parse(playerSkin); - EncryptionUtils.verifyJwt(clientJwt, identityPublicKey); - - if (EncryptionUtils.canUseEncryption()) { - AuthenticationUtils.startEncryptionHandshake(session, identityPublicKey); - } - } catch (Exception ex) { - session.disconnect("disconnectionScreen.internalError.cantConnect"); - throw new RuntimeException("Unable to complete login", ex); - } - } - - private static void startEncryptionHandshake(GeyserSession session, PublicKey key) throws Exception { - KeyPairGenerator generator = KeyPairGenerator.getInstance("EC"); - generator.initialize(new ECGenParameterSpec("secp384r1")); - KeyPair serverKeyPair = generator.generateKeyPair(); - - byte[] token = EncryptionUtils.generateRandomToken(); - SecretKey encryptionKey = EncryptionUtils.getSecretKey(serverKeyPair.getPrivate(), key, token); - session.getUpstream().enableEncryption(encryptionKey); - - ServerToClientHandshakePacket packet = new ServerToClientHandshakePacket(); - packet.setJwt(EncryptionUtils.createHandshakeJwt(serverKeyPair, token).serialize()); - session.getUpstream().sendPacketImmediately(packet); - } - - private static int AUTH_FORM_ID = 1337; - - public static void showLoginWindow(GeyserSession session) { - CustomFormWindow window = new CustomFormBuilder("Login") - .addComponent(new LabelComponent("Minecraft: Java Edition account authentication.")) - .addComponent(new LabelComponent("Enter the credentials for your Minecraft: Java Edition account below.")) - .addComponent(new InputComponent("Email/Username", "account@geysermc.org", "")) - .addComponent(new InputComponent("Password", "123456", "")) - .build(); - - session.sendForm(window, AUTH_FORM_ID); - } - - public static boolean authenticateFromForm(GeyserSession session, GeyserConnector connector, String formData) { - WindowCache windowCache = session.getWindowCache(); - if (!windowCache.getWindows().containsKey(AUTH_FORM_ID)) - return false; - - FormWindow window = windowCache.getWindows().remove(AUTH_FORM_ID); - window.setResponse(formData.trim()); - - if (session.isLoggedIn()) { - PlayerFormResponseEvent event = new PlayerFormResponseEvent(session, AUTH_FORM_ID, window); - connector.getPluginManager().runEvent(event); - } else { - if (window instanceof CustomFormWindow) { - CustomFormWindow customFormWindow = (CustomFormWindow) window; - if (!customFormWindow.getTitle().equals("Login")) - return false; - - CustomFormResponse response = (CustomFormResponse) customFormWindow.getResponse(); - String username = response.getInputResponses().get(2); - String password = response.getInputResponses().get(3); - - session.authenticate(username, password); - - // TODO should we clear the window cache in all cases or just if not already logged in? - // Clear windows so authentication data isn't accidentally cached - windowCache.getWindows().clear(); - } - } - return true; - } - -} diff --git a/connector/src/main/java/org/geysermc/connector/utils/LoginEncryptionUtils.java b/connector/src/main/java/org/geysermc/connector/utils/LoginEncryptionUtils.java index 47584fcc2..868d7b490 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/LoginEncryptionUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/LoginEncryptionUtils.java @@ -6,15 +6,35 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.JsonNodeType; import com.nimbusds.jose.JWSObject; import com.nukkitx.network.util.Preconditions; +import com.nukkitx.protocol.bedrock.packet.LoginPacket; +import com.nukkitx.protocol.bedrock.packet.ServerToClientHandshakePacket; import com.nukkitx.protocol.bedrock.util.EncryptionUtils; +import net.minidev.json.JSONObject; +import org.geysermc.api.events.player.PlayerFormResponseEvent; +import org.geysermc.api.window.CustomFormBuilder; +import org.geysermc.api.window.CustomFormWindow; +import org.geysermc.api.window.FormWindow; +import org.geysermc.api.window.component.InputComponent; +import org.geysermc.api.window.component.LabelComponent; +import org.geysermc.api.window.response.CustomFormResponse; +import org.geysermc.connector.GeyserConnector; +import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.session.auth.BedrockAuthData; +import org.geysermc.connector.network.session.cache.WindowCache; +import javax.crypto.SecretKey; +import java.io.IOException; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.PublicKey; import java.security.interfaces.ECPublicKey; +import java.security.spec.ECGenParameterSpec; +import java.util.UUID; public class LoginEncryptionUtils { + private static final ObjectMapper JSON_MAPPER = new ObjectMapper().disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); - public static final ObjectMapper JSON_MAPPER = new ObjectMapper().disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); - - public static boolean validateChainData(JsonNode data) throws Exception { + private static boolean validateChainData(JsonNode data) throws Exception { ECPublicKey lastKey = null; boolean validChain = false; for (JsonNode node : data) { @@ -35,4 +55,112 @@ public class LoginEncryptionUtils { } return validChain; } + + public static void encryptPlayerConnection(GeyserConnector connector, GeyserSession session, LoginPacket loginPacket) { + JsonNode certData; + try { + certData = JSON_MAPPER.readTree(loginPacket.getChainData().toByteArray()); + } catch (IOException ex) { + throw new RuntimeException("Certificate JSON can not be read."); + } + + JsonNode certChainData = certData.get("chain"); + if (certChainData.getNodeType() != JsonNodeType.ARRAY) { + throw new RuntimeException("Certificate data is not valid"); + } + + encryptConnectionWithCert(connector, session, loginPacket.getSkinData().toString(), certData); + } + + private static void encryptConnectionWithCert(GeyserConnector connector, GeyserSession session, String playerSkin, JsonNode certChainData) { + try { + boolean validChain = validateChainData(certChainData); + + connector.getLogger().debug(String.format("Is player data valid? %s", validChain)); + + JWSObject jwt = JWSObject.parse(certChainData.get(certChainData.size() - 1).asText()); + JsonNode payload = JSON_MAPPER.readTree(jwt.getPayload().toBytes()); + + if (payload.get("extraData").getNodeType() != JsonNodeType.OBJECT) { + throw new RuntimeException("AuthData was not found!"); + } + + JSONObject extraData = (JSONObject) jwt.getPayload().toJSONObject().get("extraData"); + session.setAuthenticationData(new BedrockAuthData(extraData.getAsString("displayName"), UUID.fromString(extraData.getAsString("identity")), extraData.getAsString("XUID"))); + + if (payload.get("identityPublicKey").getNodeType() != JsonNodeType.STRING) { + throw new RuntimeException("Identity Public Key was not found!"); + } + + ECPublicKey identityPublicKey = EncryptionUtils.generateKey(payload.get("identityPublicKey").textValue()); + JWSObject clientJwt = JWSObject.parse(playerSkin); + EncryptionUtils.verifyJwt(clientJwt, identityPublicKey); + + if (EncryptionUtils.canUseEncryption()) { + LoginEncryptionUtils.startEncryptionHandshake(session, identityPublicKey); + } + } catch (Exception ex) { + session.disconnect("disconnectionScreen.internalError.cantConnect"); + throw new RuntimeException("Unable to complete login", ex); + } + } + + private static void startEncryptionHandshake(GeyserSession session, PublicKey key) throws Exception { + KeyPairGenerator generator = KeyPairGenerator.getInstance("EC"); + generator.initialize(new ECGenParameterSpec("secp384r1")); + KeyPair serverKeyPair = generator.generateKeyPair(); + + byte[] token = EncryptionUtils.generateRandomToken(); + SecretKey encryptionKey = EncryptionUtils.getSecretKey(serverKeyPair.getPrivate(), key, token); + session.getUpstream().enableEncryption(encryptionKey); + + ServerToClientHandshakePacket packet = new ServerToClientHandshakePacket(); + packet.setJwt(EncryptionUtils.createHandshakeJwt(serverKeyPair, token).serialize()); + session.getUpstream().sendPacketImmediately(packet); + } + + private static int AUTH_FORM_ID = 1337; + + public static void showLoginWindow(GeyserSession session) { + CustomFormWindow window = new CustomFormBuilder("Login") + .addComponent(new LabelComponent("Minecraft: Java Edition account authentication.")) + .addComponent(new LabelComponent("Enter the credentials for your Minecraft: Java Edition account below.")) + .addComponent(new InputComponent("Email/Username", "account@geysermc.org", "")) + .addComponent(new InputComponent("Password", "123456", "")) + .build(); + + session.sendForm(window, AUTH_FORM_ID); + } + + public static boolean authenticateFromForm(GeyserSession session, GeyserConnector connector, String formData) { + WindowCache windowCache = session.getWindowCache(); + if (!windowCache.getWindows().containsKey(AUTH_FORM_ID)) + return false; + + FormWindow window = windowCache.getWindows().remove(AUTH_FORM_ID); + window.setResponse(formData.trim()); + + if (session.isLoggedIn()) { + PlayerFormResponseEvent event = new PlayerFormResponseEvent(session, AUTH_FORM_ID, window); + connector.getPluginManager().runEvent(event); + } else { + if (window instanceof CustomFormWindow) { + CustomFormWindow customFormWindow = (CustomFormWindow) window; + if (!customFormWindow.getTitle().equals("Login")) + return false; + + CustomFormResponse response = (CustomFormResponse) customFormWindow.getResponse(); + String username = response.getInputResponses().get(2); + String password = response.getInputResponses().get(3); + + session.authenticate(username, password); + + // TODO should we clear the window cache in all cases or just if not already logged in? + // Clear windows so authentication data isn't accidentally cached + windowCache.getWindows().clear(); + } + } + return true; + } + } From 168778026a091c42891cef2b2eddb4ea1d509c1f Mon Sep 17 00:00:00 2001 From: Blue Kelp Date: Thu, 1 Aug 2019 21:16:17 -0700 Subject: [PATCH 4/9] refactor file utils for reuse --- .../geysermc/connector/GeyserConnector.java | 20 +++-------- .../geysermc/connector/utils/FileUtils.java | 35 +++++++++++++++++++ 2 files changed, 40 insertions(+), 15 deletions(-) create mode 100644 connector/src/main/java/org/geysermc/connector/utils/FileUtils.java diff --git a/connector/src/main/java/org/geysermc/connector/GeyserConnector.java b/connector/src/main/java/org/geysermc/connector/GeyserConnector.java index 137a6238f..639f53709 100644 --- a/connector/src/main/java/org/geysermc/connector/GeyserConnector.java +++ b/connector/src/main/java/org/geysermc/connector/GeyserConnector.java @@ -25,8 +25,6 @@ package org.geysermc.connector; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import com.nukkitx.protocol.bedrock.BedrockPacketCodec; import com.nukkitx.protocol.bedrock.BedrockServer; import com.nukkitx.protocol.bedrock.v361.Bedrock_v361; @@ -47,9 +45,11 @@ import org.geysermc.connector.network.translators.TranslatorsInit; import org.geysermc.connector.plugin.GeyserPluginLoader; import org.geysermc.connector.plugin.GeyserPluginManager; import org.geysermc.connector.thread.PingPassthroughThread; +import org.geysermc.connector.utils.FileUtils; import org.geysermc.connector.utils.Toolbox; -import java.io.*; +import java.io.File; +import java.io.IOException; import java.net.InetSocketAddress; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; @@ -112,19 +112,9 @@ public class GeyserConnector implements Connector { logger.info("******************************************"); try { - File configFile = new File("config.yml"); - if (!configFile.exists()) { - FileOutputStream fos = new FileOutputStream(configFile); - InputStream is = GeyserConnector.class.getResourceAsStream("/config.yml"); - int data; - while ((data = is.read()) != -1) - fos.write(data); - is.close(); - fos.close(); - } + File configFile = FileUtils.fileOrCopiedFromResource("config.yml"); - ObjectMapper objectMapper = new ObjectMapper(new YAMLFactory()); - config = objectMapper.readValue(configFile, GeyserConfiguration.class); + config = FileUtils.loadConfig(configFile, GeyserConfiguration.class); } catch (IOException ex) { logger.severe("Failed to create config.yml! Make sure it's up to date and writable!"); shutdown(); diff --git a/connector/src/main/java/org/geysermc/connector/utils/FileUtils.java b/connector/src/main/java/org/geysermc/connector/utils/FileUtils.java new file mode 100644 index 000000000..435bd603a --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/utils/FileUtils.java @@ -0,0 +1,35 @@ +package org.geysermc.connector.utils; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; +import org.geysermc.connector.GeyserConnector; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; + +public class FileUtils { + public static T loadConfig(File src, Class valueType) throws IOException { + ObjectMapper objectMapper = new ObjectMapper(new YAMLFactory()); + return objectMapper.readValue(src, valueType); + } + + public static File fileOrCopiedFromResource(String name) throws IOException { + File file = new File(name); + if (!file.exists()) { + FileOutputStream fos = new FileOutputStream(file); + InputStream is = GeyserConnector.class.getResourceAsStream("/" + name); // resources need leading "/" prefix + + int data; + while ((data = is.read()) != -1) + fos.write(data); + is.close(); + fos.close(); + } + + return file; + } + + +} From f62aa390d25b5e404405f6fe31d6d86f7a43450e Mon Sep 17 00:00:00 2001 From: Blue Kelp Date: Fri, 2 Aug 2019 13:15:32 -0700 Subject: [PATCH 5/9] support auto-login of configured users - fall back to window/form prompt if user is not configured --- .../geysermc/connector/GeyserConnector.java | 2 +- .../configuration/GeyserConfiguration.java | 4 +++ .../configuration/UserAuthenticationInfo.java | 31 +++++++++++++++++++ .../network/UpstreamPacketHandler.java | 22 ++++++++++++- .../connector/utils/LoginEncryptionUtils.java | 4 +-- connector/src/main/resources/config.yml | 13 ++++++++ 6 files changed, 72 insertions(+), 4 deletions(-) create mode 100644 connector/src/main/java/org/geysermc/connector/configuration/UserAuthenticationInfo.java diff --git a/connector/src/main/java/org/geysermc/connector/GeyserConnector.java b/connector/src/main/java/org/geysermc/connector/GeyserConnector.java index 639f53709..5f6f39a67 100644 --- a/connector/src/main/java/org/geysermc/connector/GeyserConnector.java +++ b/connector/src/main/java/org/geysermc/connector/GeyserConnector.java @@ -116,7 +116,7 @@ public class GeyserConnector implements Connector { config = FileUtils.loadConfig(configFile, GeyserConfiguration.class); } catch (IOException ex) { - logger.severe("Failed to create config.yml! Make sure it's up to date and writable!"); + logger.severe("Failed to read/create config.yml! Make sure it's up to date and/or readable+writable!"); shutdown(); } diff --git a/connector/src/main/java/org/geysermc/connector/configuration/GeyserConfiguration.java b/connector/src/main/java/org/geysermc/connector/configuration/GeyserConfiguration.java index fad955051..6d6185f05 100644 --- a/connector/src/main/java/org/geysermc/connector/configuration/GeyserConfiguration.java +++ b/connector/src/main/java/org/geysermc/connector/configuration/GeyserConfiguration.java @@ -28,12 +28,16 @@ package org.geysermc.connector.configuration; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Getter; +import java.util.Map; + @Getter public class GeyserConfiguration { private BedrockConfiguration bedrock; private RemoteConfiguration remote; + private Map userAuths; + @JsonProperty("ping-passthrough") private boolean pingPassthrough; diff --git a/connector/src/main/java/org/geysermc/connector/configuration/UserAuthenticationInfo.java b/connector/src/main/java/org/geysermc/connector/configuration/UserAuthenticationInfo.java new file mode 100644 index 000000000..e8ce14243 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/configuration/UserAuthenticationInfo.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2019 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.connector.configuration; + +public class UserAuthenticationInfo { + public String email; + public String password; +} \ No newline at end of file diff --git a/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java b/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java index 4fbb520d9..df1504403 100644 --- a/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java +++ b/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java @@ -28,6 +28,7 @@ package org.geysermc.connector.network; import com.nukkitx.protocol.bedrock.BedrockPacket; import com.nukkitx.protocol.bedrock.packet.*; import org.geysermc.connector.GeyserConnector; +import org.geysermc.connector.configuration.UserAuthenticationInfo; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.Registry; import org.geysermc.connector.utils.LoginEncryptionUtils; @@ -91,11 +92,30 @@ public class UpstreamPacketHandler extends LoggingPacketHandler { return LoginEncryptionUtils.authenticateFromForm(session, connector, packet.getFormData()); } + private boolean couldLoginUserByName(String bedrockUsername) { + if (connector.getConfig().getUserAuths() != null) { + UserAuthenticationInfo info = connector.getConfig().getUserAuths().get(bedrockUsername); + + if (info != null) { + connector.getLogger().debug("using stored credentials for bedrock user " + session.getAuthenticationData().getName()); + session.authenticate(info.email, info.password); + return true; + } + } + + return false; + } + @Override public boolean handle(MovePlayerPacket packet) { connector.getLogger().debug("Handled packet: " + packet.getClass().getSimpleName()); if (!session.isLoggedIn()) { - LoginEncryptionUtils.showLoginWindow(session); + // TODO it is safer to key authentication on something that won't change (UUID, not username) + if (!couldLoginUserByName(session.getAuthenticationData().getName())) { + LoginEncryptionUtils.showLoginWindow(session); + } + // else we were able to log the user in + return true; } return false; diff --git a/connector/src/main/java/org/geysermc/connector/utils/LoginEncryptionUtils.java b/connector/src/main/java/org/geysermc/connector/utils/LoginEncryptionUtils.java index 868d7b490..48e5d5150 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/LoginEncryptionUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/LoginEncryptionUtils.java @@ -150,10 +150,10 @@ public class LoginEncryptionUtils { return false; CustomFormResponse response = (CustomFormResponse) customFormWindow.getResponse(); - String username = response.getInputResponses().get(2); + String email = response.getInputResponses().get(2); String password = response.getInputResponses().get(3); - session.authenticate(username, password); + session.authenticate(email, password); // TODO should we clear the window cache in all cases or just if not already logged in? // Clear windows so authentication data isn't accidentally cached diff --git a/connector/src/main/resources/config.yml b/connector/src/main/resources/config.yml index 885e5cf7c..504071773 100644 --- a/connector/src/main/resources/config.yml +++ b/connector/src/main/resources/config.yml @@ -21,6 +21,19 @@ remote: port: 25565 online-mode: false +## the Xbox/MCPE username is the key for the Java server auth-info +## this allows automatic configuration/login to the remote Java server +## if you are brave/stupid enough to put your Mojang account info into +## a config file +#userAuths: +# bluekelp2: # MCPE/Xbox username +# email: not_really_my_email_address_mr_minecrafter53267@gmail.com # Mojang account email address +# password: "this isn't really my password" +# +# herpderp40300499303040503030300500293858393589: +# email: herpderp@derpherp.com +# password: dooooo + # Relay the MOTD, player count and max players from the remote server ping-passthrough: false From 5f93b5bec49cf3804002e271925c5acc7ca56a0a Mon Sep 17 00:00:00 2001 From: Blue Kelp Date: Fri, 2 Aug 2019 13:54:40 -0700 Subject: [PATCH 6/9] null safety on auth/handshake errors --- .../connector/network/session/GeyserSession.java | 8 ++++++-- .../geysermc/connector/utils/LoginEncryptionUtils.java | 9 ++++++--- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java index 884134948..b7ca7a2f5 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java @@ -158,8 +158,12 @@ public class GeyserSession implements PlayerSession, Player { public void disconnect(String reason) { if (!closed) { loggedIn = false; - downstream.getSession().disconnect(reason); - upstream.disconnect(reason); + if (downstream != null && downstream.getSession() != null) { + downstream.getSession().disconnect(reason); + } + if (upstream != null) { + upstream.disconnect(reason); + } } } diff --git a/connector/src/main/java/org/geysermc/connector/utils/LoginEncryptionUtils.java b/connector/src/main/java/org/geysermc/connector/utils/LoginEncryptionUtils.java index 48e5d5150..54ab46135 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/LoginEncryptionUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/LoginEncryptionUtils.java @@ -150,10 +150,13 @@ public class LoginEncryptionUtils { return false; CustomFormResponse response = (CustomFormResponse) customFormWindow.getResponse(); - String email = response.getInputResponses().get(2); - String password = response.getInputResponses().get(3); - session.authenticate(email, password); + if (response != null) { + String email = response.getInputResponses().get(2); + String password = response.getInputResponses().get(3); + + session.authenticate(email, password); + } // TODO should we clear the window cache in all cases or just if not already logged in? // Clear windows so authentication data isn't accidentally cached From bf7c74e072107bc464db090d25895d7fee64f5fb Mon Sep 17 00:00:00 2001 From: Blue Kelp Date: Fri, 2 Aug 2019 13:55:06 -0700 Subject: [PATCH 7/9] fix cert/handshake error --- .../java/org/geysermc/connector/utils/LoginEncryptionUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/connector/src/main/java/org/geysermc/connector/utils/LoginEncryptionUtils.java b/connector/src/main/java/org/geysermc/connector/utils/LoginEncryptionUtils.java index 54ab46135..a68e8b5cc 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/LoginEncryptionUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/LoginEncryptionUtils.java @@ -69,7 +69,7 @@ public class LoginEncryptionUtils { throw new RuntimeException("Certificate data is not valid"); } - encryptConnectionWithCert(connector, session, loginPacket.getSkinData().toString(), certData); + encryptConnectionWithCert(connector, session, loginPacket.getSkinData().toString(), certChainData); } private static void encryptConnectionWithCert(GeyserConnector connector, GeyserSession session, String playerSkin, JsonNode certChainData) { From ae9d51c8b706498cb002d79ec7ced57265ca54e9 Mon Sep 17 00:00:00 2001 From: Blue Kelp Date: Fri, 2 Aug 2019 13:55:26 -0700 Subject: [PATCH 8/9] log user auto-logins and note to improve --- .../geysermc/connector/network/UpstreamPacketHandler.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java b/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java index df1504403..7a0f46162 100644 --- a/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java +++ b/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java @@ -97,8 +97,11 @@ public class UpstreamPacketHandler extends LoggingPacketHandler { UserAuthenticationInfo info = connector.getConfig().getUserAuths().get(bedrockUsername); if (info != null) { - connector.getLogger().debug("using stored credentials for bedrock user " + session.getAuthenticationData().getName()); + connector.getLogger().info("using stored credentials for bedrock user " + session.getAuthenticationData().getName()); session.authenticate(info.email, info.password); + + // TODO send a message to bedrock user telling them they are connected (if nothing like a motd + // somes from the Java server w/in a few seconds) return true; } } From fda221c12c073e904c5b1fb8afe78509681f15b2 Mon Sep 17 00:00:00 2001 From: Blue Kelp Date: Fri, 2 Aug 2019 14:01:01 -0700 Subject: [PATCH 9/9] fixup example xbox username --- connector/src/main/resources/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/connector/src/main/resources/config.yml b/connector/src/main/resources/config.yml index 504071773..72ddf1864 100644 --- a/connector/src/main/resources/config.yml +++ b/connector/src/main/resources/config.yml @@ -26,7 +26,7 @@ remote: ## if you are brave/stupid enough to put your Mojang account info into ## a config file #userAuths: -# bluekelp2: # MCPE/Xbox username +# bluerkelp2: # MCPE/Xbox username # email: not_really_my_email_address_mr_minecrafter53267@gmail.com # Mojang account email address # password: "this isn't really my password" #