2018-09-17 10:07:32 +03:00
|
|
|
package com.mojang.authlib.yggdrasil;
|
|
|
|
|
2019-06-03 10:58:10 +03:00
|
|
|
import java.net.InetAddress;
|
|
|
|
import java.util.Base64;
|
|
|
|
import java.util.EnumMap;
|
|
|
|
import java.util.Map;
|
|
|
|
import java.util.UUID;
|
|
|
|
|
2018-09-17 10:07:32 +03:00
|
|
|
import com.google.common.collect.Iterables;
|
|
|
|
import com.google.gson.JsonElement;
|
|
|
|
import com.google.gson.JsonObject;
|
|
|
|
import com.google.gson.JsonParser;
|
|
|
|
import com.mojang.authlib.AuthenticationService;
|
|
|
|
import com.mojang.authlib.GameProfile;
|
|
|
|
import com.mojang.authlib.exceptions.AuthenticationException;
|
|
|
|
import com.mojang.authlib.exceptions.AuthenticationUnavailableException;
|
|
|
|
import com.mojang.authlib.minecraft.BaseMinecraftSessionService;
|
2018-12-06 05:29:34 +03:00
|
|
|
import com.mojang.authlib.minecraft.MinecraftProfileTexture;
|
2018-09-17 10:07:32 +03:00
|
|
|
import com.mojang.authlib.properties.Property;
|
|
|
|
import com.mojang.authlib.properties.PropertyMap;
|
2019-06-03 10:58:10 +03:00
|
|
|
|
2019-06-02 05:03:08 +03:00
|
|
|
import pro.gravit.launcher.Launcher;
|
|
|
|
import pro.gravit.launcher.profiles.PlayerProfile;
|
|
|
|
import pro.gravit.launcher.request.auth.CheckServerRequest;
|
|
|
|
import pro.gravit.launcher.request.auth.JoinServerRequest;
|
|
|
|
import pro.gravit.launcher.request.uuid.ProfileByUUIDRequest;
|
|
|
|
import pro.gravit.utils.helper.IOHelper;
|
|
|
|
import pro.gravit.utils.helper.LogHelper;
|
|
|
|
import pro.gravit.utils.helper.SecurityHelper;
|
2018-09-17 10:07:32 +03:00
|
|
|
|
2019-09-18 00:53:03 +03:00
|
|
|
public class YggdrasilMinecraftSessionService extends BaseMinecraftSessionService {
|
2018-09-17 10:07:32 +03:00
|
|
|
public static final JsonParser JSON_PARSER = new JsonParser();
|
2018-09-25 17:06:13 +03:00
|
|
|
public static final boolean NO_TEXTURES = Boolean.parseBoolean("launcher.com.mojang.authlib.noTextures");
|
2018-09-17 10:07:32 +03:00
|
|
|
|
|
|
|
public static void fillTextureProperties(GameProfile profile, PlayerProfile pp) {
|
2019-08-13 22:11:51 +03:00
|
|
|
boolean debug = LogHelper.isDebugEnabled();
|
|
|
|
if (debug) {
|
2019-08-13 21:02:33 +03:00
|
|
|
LogHelper.debug("fillTextureProperties, Username: '%s'", profile.getName());
|
|
|
|
}
|
2018-09-17 10:07:32 +03:00
|
|
|
if (NO_TEXTURES)
|
2018-09-22 17:33:00 +03:00
|
|
|
return;
|
2018-09-17 10:07:32 +03:00
|
|
|
|
|
|
|
// Fill textures map
|
|
|
|
PropertyMap properties = profile.getProperties();
|
|
|
|
if (pp.skin != null) {
|
2018-09-25 17:06:13 +03:00
|
|
|
properties.put(Launcher.SKIN_URL_PROPERTY, new Property(Launcher.SKIN_URL_PROPERTY, pp.skin.url, ""));
|
|
|
|
properties.put(Launcher.SKIN_DIGEST_PROPERTY, new Property(Launcher.SKIN_DIGEST_PROPERTY, SecurityHelper.toHex(pp.skin.digest), ""));
|
2019-08-13 22:11:51 +03:00
|
|
|
if (debug) {
|
2019-08-13 21:02:33 +03:00
|
|
|
LogHelper.debug("fillTextureProperties, Has skin texture for username '%s'", profile.getName());
|
|
|
|
}
|
2018-09-17 10:07:32 +03:00
|
|
|
}
|
|
|
|
if (pp.cloak != null) {
|
2018-09-25 17:06:13 +03:00
|
|
|
properties.put(Launcher.CLOAK_URL_PROPERTY, new Property(Launcher.CLOAK_URL_PROPERTY, pp.cloak.url, ""));
|
|
|
|
properties.put(Launcher.CLOAK_DIGEST_PROPERTY, new Property(Launcher.CLOAK_DIGEST_PROPERTY, SecurityHelper.toHex(pp.cloak.digest), ""));
|
2019-08-13 22:11:51 +03:00
|
|
|
if (debug) {
|
2019-08-13 21:02:33 +03:00
|
|
|
LogHelper.debug("fillTextureProperties, Has cloak texture for username '%s'", profile.getName());
|
|
|
|
}
|
2018-09-17 10:07:32 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-25 17:06:13 +03:00
|
|
|
private static void getTexturesMojang(Map<MinecraftProfileTexture.Type, MinecraftProfileTexture> textures, String texturesBase64, GameProfile profile) {
|
2018-09-17 10:07:32 +03:00
|
|
|
// Decode textures payload
|
|
|
|
JsonObject texturesJSON;
|
|
|
|
try {
|
|
|
|
byte[] decoded = Base64.getDecoder().decode(texturesBase64);
|
|
|
|
texturesJSON = JSON_PARSER.parse(new String(decoded, IOHelper.UNICODE_CHARSET)).getAsJsonObject().getAsJsonObject("textures");
|
|
|
|
} catch (Exception ignored) {
|
|
|
|
LogHelper.error("Could not decode textures payload, Username: '%s', UUID: '%s'", profile.getName(), profile.getUUID());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fetch textures from textures JSON
|
2018-09-25 17:06:13 +03:00
|
|
|
for (MinecraftProfileTexture.Type type : MinecraftProfileTexture.PROFILE_TEXTURE_TYPES) {
|
2018-09-17 10:07:32 +03:00
|
|
|
if (textures.containsKey(type))
|
2018-09-22 17:33:00 +03:00
|
|
|
continue; // Overriden by launcher
|
2018-09-17 10:07:32 +03:00
|
|
|
|
|
|
|
// Get texture from JSON
|
|
|
|
JsonElement textureJSON = texturesJSON.get(type.name());
|
|
|
|
if (textureJSON != null && textureJSON.isJsonObject()) {
|
|
|
|
JsonElement urlValue = textureJSON.getAsJsonObject().get("url");
|
|
|
|
if (urlValue.isJsonPrimitive())
|
2018-09-22 17:33:00 +03:00
|
|
|
textures.put(type, new MinecraftProfileTexture(urlValue.getAsString()));
|
2018-09-17 10:07:32 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public static GameProfile toGameProfile(PlayerProfile pp) {
|
|
|
|
GameProfile profile = new GameProfile(pp.uuid, pp.username);
|
|
|
|
fillTextureProperties(profile, pp);
|
|
|
|
return profile;
|
|
|
|
}
|
|
|
|
|
|
|
|
public YggdrasilMinecraftSessionService(AuthenticationService service) {
|
|
|
|
super(service);
|
|
|
|
LogHelper.debug("Patched MinecraftSessionService created");
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public GameProfile fillProfileProperties(GameProfile profile, boolean requireSecure) {
|
|
|
|
// Verify has UUID
|
|
|
|
UUID uuid = profile.getUUID();
|
2019-08-13 22:11:51 +03:00
|
|
|
boolean debug = LogHelper.isDebugEnabled();
|
|
|
|
if (debug) {
|
2019-08-13 21:02:33 +03:00
|
|
|
LogHelper.debug("fillProfileProperties, UUID: %s", uuid);
|
|
|
|
}
|
2018-09-17 10:07:32 +03:00
|
|
|
if (uuid == null)
|
2018-09-22 17:33:00 +03:00
|
|
|
return profile;
|
2018-09-17 10:07:32 +03:00
|
|
|
|
|
|
|
// Make profile request
|
|
|
|
PlayerProfile pp;
|
|
|
|
try {
|
2019-02-10 11:38:48 +03:00
|
|
|
pp = new ProfileByUUIDRequest(uuid).request().playerProfile;
|
2018-09-17 10:07:32 +03:00
|
|
|
} catch (Exception e) {
|
2019-08-13 22:11:51 +03:00
|
|
|
if (debug) {
|
2019-08-13 21:02:33 +03:00
|
|
|
LogHelper.debug("Couldn't fetch profile properties for '%s': %s", profile, e);
|
|
|
|
}
|
2018-09-17 10:07:32 +03:00
|
|
|
return profile;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify is found
|
|
|
|
if (pp == null) {
|
2019-08-13 22:11:51 +03:00
|
|
|
if (debug) {
|
|
|
|
LogHelper.debug("Couldn't fetch profile properties for '%s' as the profile does not exist", profile);
|
|
|
|
}
|
2018-09-17 10:07:32 +03:00
|
|
|
return profile;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create new game profile from player profile
|
2019-08-13 22:11:51 +03:00
|
|
|
if (debug) {
|
2019-08-13 21:02:33 +03:00
|
|
|
LogHelper.debug("Successfully fetched profile properties for '%s'", profile);
|
|
|
|
}
|
2018-09-17 10:07:32 +03:00
|
|
|
fillTextureProperties(profile, pp);
|
|
|
|
return toGameProfile(pp);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2018-09-25 17:06:13 +03:00
|
|
|
public Map<MinecraftProfileTexture.Type, MinecraftProfileTexture> getTextures(GameProfile profile, boolean requireSecure) {
|
2019-08-13 21:02:33 +03:00
|
|
|
if (LogHelper.isDebugEnabled()) {
|
|
|
|
LogHelper.debug("getTextures, Username: '%s', UUID: '%s'", profile.getName(), profile.getUUID());
|
|
|
|
}
|
2018-09-25 17:06:13 +03:00
|
|
|
Map<MinecraftProfileTexture.Type, MinecraftProfileTexture> textures = new EnumMap<>(MinecraftProfileTexture.Type.class);
|
2018-09-17 10:07:32 +03:00
|
|
|
|
|
|
|
// Add textures
|
|
|
|
if (!NO_TEXTURES) {
|
|
|
|
// Add skin URL to textures map
|
2018-09-25 17:06:13 +03:00
|
|
|
Property skinURL = Iterables.getFirst(profile.getProperties().get(Launcher.SKIN_URL_PROPERTY), null);
|
|
|
|
Property skinDigest = Iterables.getFirst(profile.getProperties().get(Launcher.SKIN_DIGEST_PROPERTY), null);
|
2018-09-17 10:07:32 +03:00
|
|
|
if (skinURL != null && skinDigest != null)
|
2018-09-25 17:06:13 +03:00
|
|
|
textures.put(MinecraftProfileTexture.Type.SKIN, new MinecraftProfileTexture(skinURL.getValue(), skinDigest.getValue()));
|
2018-09-17 10:07:32 +03:00
|
|
|
|
|
|
|
// Add cloak URL to textures map
|
2018-09-25 17:06:13 +03:00
|
|
|
Property cloakURL = Iterables.getFirst(profile.getProperties().get(Launcher.CLOAK_URL_PROPERTY), null);
|
|
|
|
Property cloakDigest = Iterables.getFirst(profile.getProperties().get(Launcher.CLOAK_DIGEST_PROPERTY), null);
|
2018-09-17 10:07:32 +03:00
|
|
|
if (cloakURL != null && cloakDigest != null)
|
2018-09-25 17:06:13 +03:00
|
|
|
textures.put(MinecraftProfileTexture.Type.CAPE, new MinecraftProfileTexture(cloakURL.getValue(), cloakDigest.getValue()));
|
2018-09-17 10:07:32 +03:00
|
|
|
|
|
|
|
// Try to find missing textures in textures payload (now always true because launcher is not passing elytra skins)
|
|
|
|
if (textures.size() != MinecraftProfileTexture.PROFILE_TEXTURE_COUNT) {
|
|
|
|
Property texturesMojang = Iterables.getFirst(profile.getProperties().get("textures"), null);
|
|
|
|
if (texturesMojang != null)
|
2018-09-22 17:33:00 +03:00
|
|
|
getTexturesMojang(textures, texturesMojang.getValue(), profile);
|
2018-09-17 10:07:32 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return filled textures
|
|
|
|
return textures;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public GameProfile hasJoinedServer(GameProfile profile, String serverID) throws AuthenticationUnavailableException {
|
|
|
|
String username = profile.getName();
|
2019-08-13 21:02:33 +03:00
|
|
|
if (LogHelper.isDebugEnabled()) {
|
|
|
|
LogHelper.debug("checkServer, Username: '%s', Server ID: %s", username, serverID);
|
|
|
|
}
|
2018-09-17 10:07:32 +03:00
|
|
|
|
|
|
|
// Make checkServer request
|
|
|
|
PlayerProfile pp;
|
|
|
|
try {
|
2019-02-10 11:38:48 +03:00
|
|
|
pp = new CheckServerRequest(username, serverID).request().playerProfile;
|
2018-09-17 10:07:32 +03:00
|
|
|
} catch (Exception e) {
|
|
|
|
LogHelper.error(e);
|
|
|
|
throw new AuthenticationUnavailableException(e);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return profile if found
|
|
|
|
return pp == null ? null : toGameProfile(pp);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public GameProfile hasJoinedServer(GameProfile profile, String serverID, InetAddress address) throws AuthenticationUnavailableException {
|
|
|
|
return hasJoinedServer(profile, serverID);
|
|
|
|
}
|
2018-09-22 17:33:00 +03:00
|
|
|
|
2018-09-19 15:03:52 +03:00
|
|
|
public YggdrasilAuthenticationService getAuthenticationService() {
|
2018-09-22 17:33:00 +03:00
|
|
|
return (YggdrasilAuthenticationService) super.getAuthenticationService();
|
2018-09-19 15:03:52 +03:00
|
|
|
}
|
2018-09-22 17:33:00 +03:00
|
|
|
|
2018-09-17 10:07:32 +03:00
|
|
|
@Override
|
|
|
|
public void joinServer(GameProfile profile, String accessToken, String serverID) throws AuthenticationException {
|
|
|
|
|
|
|
|
// Join server
|
|
|
|
String username = profile.getName();
|
2019-08-13 21:02:33 +03:00
|
|
|
if (LogHelper.isDebugEnabled()) {
|
|
|
|
LogHelper.debug("joinServer, Username: '%s', Access token: %s, Server ID: %s", username, accessToken, serverID);
|
|
|
|
}
|
2018-09-17 10:07:32 +03:00
|
|
|
|
|
|
|
// Make joinServer request
|
|
|
|
boolean success;
|
|
|
|
try {
|
2019-02-10 11:38:48 +03:00
|
|
|
success = new JoinServerRequest(username, accessToken, serverID).request().allow;
|
2018-09-17 10:07:32 +03:00
|
|
|
} catch (Exception e) {
|
|
|
|
throw new AuthenticationUnavailableException(e);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify is success
|
|
|
|
if (!success)
|
2018-09-22 17:33:00 +03:00
|
|
|
throw new AuthenticationException("Bad Login (Clientside)");
|
2018-09-17 10:07:32 +03:00
|
|
|
}
|
|
|
|
}
|