diff --git a/LaunchServer/source/auth/handler/AuthHandler.java b/LaunchServer/source/auth/handler/AuthHandler.java index ab32acc..3268f57 100644 --- a/LaunchServer/source/auth/handler/AuthHandler.java +++ b/LaunchServer/source/auth/handler/AuthHandler.java @@ -19,22 +19,26 @@ static { + // Default Handlers registerHandler("memory", MemoryAuthHandler::new); registerHandler("delegate", DelegateAuthHandler::new); - registerHandler("mojang", MojangAuthHandler::new); - registerHandler("authlib", AuthlibAuthHandler::new); - registerHandler("minesocial", MineSocialAuthHandler::new); - registerHandler("elyby", ElyByAuthHandler::new); - - // Auth handler that doesn't do nothing :D registerHandler("binaryFile", BinaryFileAuthHandler::new); registerHandler("textFile", TextFileAuthHandler::new); + registerHandler("json", JsonAuthHandler::new); + + // SQL and NoSQL Handlers registerHandler("mysql", MySQLAuthHandler::new); registerHandler("mysql-8", MySQL8AuthHandler::new); registerHandler("mariadb", MariaDBAuthHandler::new); registerHandler("postgresql", PostgreSQLAuthHandler::new); - registerHandler("json", JsonAuthHandler::new); registerHandler("sqlite", SQLiteAuthHandler::new); + + // Authlib Handlers + registerHandler("authlib", AuthlibAuthHandler::new); + registerHandler("authlib-injector", AuthlibInjectorAuthHandler::new); + registerHandler("mojang", MojangAuthHandler::new); + registerHandler("minesocial", MineSocialAuthHandler::new); + registerHandler("elyby", ElyByAuthHandler::new); } @LauncherAPI diff --git a/LaunchServer/source/auth/handler/AuthlibInjectorAuthHandler.java b/LaunchServer/source/auth/handler/AuthlibInjectorAuthHandler.java new file mode 100644 index 0000000..aa8e832 --- /dev/null +++ b/LaunchServer/source/auth/handler/AuthlibInjectorAuthHandler.java @@ -0,0 +1,111 @@ +package launchserver.auth.handler; + +import com.eclipsesource.json.Json; +import com.eclipsesource.json.JsonObject; +import launcher.helper.IOHelper; +import launcher.serialize.config.entry.BlockConfigEntry; +import launcher.serialize.config.entry.StringConfigEntry; +import launchserver.auth.provider.AuthProviderResult; +import launchserver.auth.provider.AuthlibAuthProviderResult; +import launchserver.helpers.HTTPRequestHelper; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +public class AuthlibInjectorAuthHandler extends AuthHandler +{ + private static java.net.URL URL_join, URL_hasJoin; + private static String urlApiInjector; + + public final HashMap usernameToUUID = new HashMap<>(); + + AuthlibInjectorAuthHandler(BlockConfigEntry block) + { + super(block); + urlApiInjector = block.getEntryValue("urlApiInjector", StringConfigEntry.class); + + try + { + URL_join = new URL(urlApiInjector + "/sessionserver/session/minecraft/join"); + URL_hasJoin = new URL(urlApiInjector + "/sessionserver/session/minecraft/hasJoined"); + } + catch (MalformedURLException e) + { + throw new InternalError(e); + } + } + + @Override + public UUID auth(AuthProviderResult authResult) { + if (authResult instanceof AuthlibAuthProviderResult) { + AuthlibAuthProviderResult result = (AuthlibAuthProviderResult) authResult; + usernameToUUID.put(result.username, result.uuid); + return result.uuid; + } + return null; + } + + @Override + public UUID checkServer(String username, String serverID) + { + JsonObject uuidResponse; + try { + URL uuidURL = new URL(URL_hasJoin + "?username=" + IOHelper.urlEncode(username) + "&serverId=" + IOHelper.urlEncode(serverID)); + uuidResponse = HTTPRequestHelper.makeAuthlibRequest(uuidURL, null, "Authlib-Injector"); + } + catch (IOException e) + { + throw new IllegalArgumentException("Empty UUID response"); + } + if (uuidResponse.get("error") != null) + { + throw new IllegalArgumentException(String.valueOf(uuidResponse.get("errorMessage"))); + } + if (uuidResponse.get("id") == null) + { + throw new IllegalArgumentException("Empty UUID response"); + } + return UUID.fromString(uuidResponse.get("id").asString().replaceFirst("(\\w{8})(\\w{4})(\\w{4})(\\w{4})(\\w{12})", "$1-$2-$3-$4-$5")); + } + + @Override + public void close() { + } + + @Override + public boolean joinServer(String username, String accessToken, String serverID) throws IOException { + JsonObject request = Json.object(). + add("accessToken", accessToken). + add("selectedProfile", usernameToUUID(username).toString().replace("-", "")). + add("serverId", serverID); + + int response = HTTPRequestHelper.authJoinRequest(URL_join, request, "AuthLib"); + + if (200 <= response && response < 300 ) + { + return true; + } + else + { + authError("Empty Authlib-Injector Handler response"); + } + return false; + } + + @Override + public UUID usernameToUUID(String username) { + return usernameToUUID.get(username); + } + + @Override + public String uuidToUsername(UUID uuid) { + for (Map.Entry entry : usernameToUUID.entrySet()) { + if (entry.getValue().equals(uuid)) return entry.getKey(); + } + return null; + } +} diff --git a/LaunchServer/source/auth/provider/AuthProvider.java b/LaunchServer/source/auth/provider/AuthProvider.java index 2dd9d0a..b3121a6 100644 --- a/LaunchServer/source/auth/provider/AuthProvider.java +++ b/LaunchServer/source/auth/provider/AuthProvider.java @@ -17,26 +17,31 @@ static { + // Default Providers registerProvider("accept", AcceptAuthProvider::new); registerProvider("reject", RejectAuthProvider::new); registerProvider("delegate", DelegateAuthProvider::new); - registerProvider("file", FileAuthProvider::new); - registerProvider("mojang", MojangAuthProvider::new); - registerProvider("authlib", AuthlibAuthProvider::new); - registerProvider("minesocial", MineSocialAuthProvider::new); - registerProvider("elyby", ElyByAuthProvider::new); + registerProvider("json", JsonAuthProvider::new); + registerProvider("request", RequestAuthProvider::new); + + // SQL and NoSQL Providers registerProvider("mysql", MySQLAuthProvider::new); registerProvider("mysql-bcrypt", MySQLBcryptAuthProvider::new); registerProvider("mysql-8", MySQL8AuthProvider::new); registerProvider("mysql-8-bcrypt", MySQL8BcryptAuthProvider::new); registerProvider("mariadb", MariaDBAuthProvider::new); registerProvider("mariadb-bcrypt", MariaDBBcryptAuthProvider::new); - registerProvider("request", RequestAuthProvider::new); registerProvider("postgresql", PostgreSQLAuthProvider::new); registerProvider("postgresql-bcrypt", PostgreSQLBcryptAuthProvider::new); - registerProvider("json", JsonAuthProvider::new); registerProvider("sqlite", SQLiteAuthProvider::new); + + // Authlib Providers + registerProvider("authlib", AuthlibAuthProvider::new); + registerProvider("authlib-injector", AuthlibInjectorAuthProvider::new); + registerProvider("mojang", MojangAuthProvider::new); + registerProvider("minesocial", MineSocialAuthProvider::new); + registerProvider("elyby", ElyByAuthProvider::new); } @LauncherAPI diff --git a/LaunchServer/source/auth/provider/AuthlibInjectorAuthProvider.java b/LaunchServer/source/auth/provider/AuthlibInjectorAuthProvider.java new file mode 100644 index 0000000..4705a30 --- /dev/null +++ b/LaunchServer/source/auth/provider/AuthlibInjectorAuthProvider.java @@ -0,0 +1,71 @@ +package launchserver.auth.provider; + +import com.eclipsesource.json.Json; +import com.eclipsesource.json.JsonObject; +import com.eclipsesource.json.JsonValue; +import launcher.serialize.config.entry.BlockConfigEntry; +import launcher.serialize.config.entry.StringConfigEntry; +import launchserver.helpers.HTTPRequestHelper; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.UUID; +import java.util.regex.Pattern; + +public class AuthlibInjectorAuthProvider extends AuthProvider +{ + private static final Pattern UUID_REGEX = Pattern.compile("(\\w{8})(\\w{4})(\\w{4})(\\w{4})(\\w{12})"); + private static URL URL; + private static String urlApiInjector; + + AuthlibInjectorAuthProvider(BlockConfigEntry block) + { + super(block); + urlApiInjector = block.getEntryValue("urlApiInjector", StringConfigEntry.class); + try + { + URL = new URL(urlApiInjector + "/authserver/authenticate"); + } + catch (MalformedURLException e) + { + throw new InternalError(e); + } + } + + @Override + public AuthProviderResult auth(String login, String password, String ip) throws Throwable + { + // https://wiki.vg/Authentication#Payload + JsonObject request = Json.object(). + add("agent", Json.object().add("name", "Minecraft").add("version", 1)). + add("username", login).add("password", password); + + // Verify there's no error + JsonObject response = HTTPRequestHelper.makeAuthlibRequest(URL, request, "Authlib-Injector"); + if (response == null) + { + authError("Empty Authlib-Injector response"); + } + JsonValue errorMessage = response.get("errorMessage"); + if (errorMessage != null) + { + authError(errorMessage.asString()); + } + + // Parse JSON data + JsonObject selectedProfile = response.get("selectedProfile").asObject(); + String username = selectedProfile.get("name").asString(); + String accessToken = response.get("clientToken").asString(); + UUID uuid = UUID.fromString(UUID_REGEX.matcher(selectedProfile.get("id").asString()).replaceFirst("$1-$2-$3-$4-$5")); + String launcherToken = response.get("accessToken").asString(); + + // We're done + return new AuthlibAuthProviderResult(username, accessToken, uuid, launcherToken); + } + + @Override + public void close() + { + // Do nothing + } +} diff --git a/LaunchServer/source/texture/AuthlibInjectorTextureProvider.java b/LaunchServer/source/texture/AuthlibInjectorTextureProvider.java new file mode 100644 index 0000000..2228891 --- /dev/null +++ b/LaunchServer/source/texture/AuthlibInjectorTextureProvider.java @@ -0,0 +1,37 @@ +package launchserver.texture; + +import launcher.client.PlayerProfile.Texture; +import launcher.serialize.config.entry.BlockConfigEntry; +import launcher.serialize.config.entry.StringConfigEntry; + +import java.util.UUID; + +public class AuthlibInjectorTextureProvider extends TextureProvider +{ + private final String urlApiInjector; + protected CacheTextureProvider cacheTextureProvider; + + public AuthlibInjectorTextureProvider(BlockConfigEntry block) + { + super(block); + urlApiInjector = block.getEntryValue("urlApiInjector", StringConfigEntry.class); + } + + @Override + public void close() + { + // Do nothing + } + + @Override + public synchronized Texture getSkinTexture(UUID uuid, String username) + { + return cacheTextureProvider.getCached(uuid, username, urlApiInjector + "/api/profiles/minecraft", urlApiInjector + "/sessionserver/session/minecraft/profile/", "Authlib-Injector").skin; + } + + @Override + public synchronized Texture getCloakTexture(UUID uuid, String username) + { + return cacheTextureProvider.getCached(uuid, username, urlApiInjector + "/api/profiles/minecraft", urlApiInjector + "/sessionserver/session/minecraft/profile/", "Authlib-Injector").cloak; + } +} diff --git a/LaunchServer/source/texture/TextureProvider.java b/LaunchServer/source/texture/TextureProvider.java index b8892a0..bd27755 100644 --- a/LaunchServer/source/texture/TextureProvider.java +++ b/LaunchServer/source/texture/TextureProvider.java @@ -18,15 +18,18 @@ static { + // Default TextureProviders registerProvider("void", VoidTextureProvider::new); registerProvider("delegate", DelegateTextureProvider::new); + registerProvider("request", RequestTextureProvider::new); + + // Authlib TextureProviders registerProvider("authlib", AuthlibTextureProvider::new); + registerProvider("authlib-injector", AuthlibInjectorTextureProvider::new); registerProvider("minesocial", MineSocialTextureProvider::new); registerProvider("elyby", ElyByTextureProvider::new); - - // Auth providers that doesn't do nothing :D registerProvider("mojang", MojangTextureProvider::new); - registerProvider("request", RequestTextureProvider::new); + } @LauncherAPI