diff --git a/build.gradle b/build.gradle index 6811fde..7fdcd88 100644 --- a/build.gradle +++ b/build.gradle @@ -56,7 +56,6 @@ dependencies { impl("meteordevelopment:discord-ipc:1.1") - impl(name: "xauthlib-1.0.0") impl 'org.java-websocket:Java-WebSocket:1.5.3' configurations.impl.dependencies.each { diff --git a/lib/xauthlib-1.0.0.jar b/lib/xauthlib-1.0.0.jar deleted file mode 100644 index 7b410f3..0000000 Binary files a/lib/xauthlib-1.0.0.jar and /dev/null differ diff --git a/src/main/java/me/x150/authlib/AccountUtils.java b/src/main/java/me/x150/authlib/AccountUtils.java new file mode 100644 index 0000000..e6c2ec3 --- /dev/null +++ b/src/main/java/me/x150/authlib/AccountUtils.java @@ -0,0 +1,48 @@ +/* + * This file is part of the Meteor Client distribution (https://github.com/MeteorDevelopment/meteor-client/). + * Copyright (c) 2021 Meteor Development. + */ + +package me.x150.authlib; + +import com.google.gson.Gson; +import com.mojang.authlib.yggdrasil.YggdrasilMinecraftSessionService; + +import java.lang.reflect.Field; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Base64; + +public class AccountUtils { + public static void setBaseUrl(YggdrasilMinecraftSessionService service, String url) { + try { + Field field = service.getClass().getDeclaredField("baseUrl"); + field.setAccessible(true); + field.set(service, url); + } catch (IllegalAccessException | NoSuchFieldException e) { + e.printStackTrace(); + } + } + + public static void setJoinUrl(YggdrasilMinecraftSessionService service, String url) { + try { + Field field = service.getClass().getDeclaredField("joinUrl"); + field.setAccessible(true); + field.set(service, new URL(url)); + } catch (IllegalAccessException | NoSuchFieldException | MalformedURLException e) { + e.printStackTrace(); + } + } + + public static void setCheckUrl(YggdrasilMinecraftSessionService service, String url) { + try { + Field field = service.getClass().getDeclaredField("checkUrl"); + field.setAccessible(true); + field.set(service, new URL(url)); + } catch (IllegalAccessException | NoSuchFieldException | MalformedURLException e) { + e.printStackTrace(); + } + } + + +} diff --git a/src/main/java/me/x150/authlib/exception/AuthFailureException.java b/src/main/java/me/x150/authlib/exception/AuthFailureException.java new file mode 100644 index 0000000..909ea1a --- /dev/null +++ b/src/main/java/me/x150/authlib/exception/AuthFailureException.java @@ -0,0 +1,12 @@ +// +// Source code recreated from a .class file by IntelliJ IDEA +// (powered by FernFlower decompiler) +// + +package me.x150.authlib.exception; + +public class AuthFailureException extends RuntimeException { + public AuthFailureException(String message) { + super(message); + } +} diff --git a/src/main/java/me/x150/authlib/login/altening/AlteningAuth.java b/src/main/java/me/x150/authlib/login/altening/AlteningAuth.java new file mode 100644 index 0000000..a042acb --- /dev/null +++ b/src/main/java/me/x150/authlib/login/altening/AlteningAuth.java @@ -0,0 +1,63 @@ +package me.x150.authlib.login.altening; + +import com.mojang.authlib.Agent; +import com.mojang.authlib.Environment; +import com.mojang.authlib.exceptions.AuthenticationException; +import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService; +import com.mojang.authlib.yggdrasil.YggdrasilMinecraftSessionService; +import com.mojang.authlib.yggdrasil.YggdrasilUserAuthentication; +import me.x150.authlib.AccountUtils; +import me.x150.authlib.login.mojang.MinecraftToken; +import net.minecraft.client.util.Session; +import net.shadow.client.ShadowMain; +import net.shadow.client.mixin.IMinecraftClientAccessor; + +import java.util.Optional; + +public class AlteningAuth { + private static final String AUTH = "http://authserver.thealtening.com"; + private static final String ACCOUNT = "https://api.mojang.com"; + private static final String SESSION = "http://sessionserver.thealtening.com"; + private static final String SERVICES = "https://api.minecraftservices.com"; + String username; + String uuid; + String token; + private YggdrasilUserAuthentication getAuth() { + YggdrasilUserAuthentication yggdrasilUserAuthentication = (YggdrasilUserAuthentication)new YggdrasilAuthenticationService(((IMinecraftClientAccessor) ShadowMain.client).getProxy(), "", Environment.create((String)"http://authserver.thealtening.com", (String)"https://api.mojang.com", (String)"http://sessionserver.thealtening.com", (String)"https://api.minecraftservices.com", (String)"The Altening")).createUserAuthentication(Agent.MINECRAFT); + yggdrasilUserAuthentication.setUsername(token); + yggdrasilUserAuthentication.setPassword("(REAL)"); + return yggdrasilUserAuthentication; + } + public AlteningAuth(String token) { + this.token = token; + } + public boolean fetchInfo() { + YggdrasilUserAuthentication yggdrasilUserAuthentication = this.getAuth(); + try { + yggdrasilUserAuthentication.logIn(); + this.username = yggdrasilUserAuthentication.getSelectedProfile().getName(); + this.uuid = yggdrasilUserAuthentication.getSelectedProfile().getId().toString(); + return true; + } + catch (AuthenticationException authenticationException) { + return false; + } + } + public MinecraftToken login() { + fetchInfo(); + YggdrasilMinecraftSessionService yggdrasilMinecraftSessionService = (YggdrasilMinecraftSessionService) ShadowMain.client.getSessionService(); + AccountUtils.setBaseUrl(yggdrasilMinecraftSessionService, SESSION + "/session/minecraft/"); + AccountUtils.setJoinUrl(yggdrasilMinecraftSessionService, SESSION + "/session/minecraft/join"); + AccountUtils.setCheckUrl(yggdrasilMinecraftSessionService, SESSION + "/session/minecraft/hasJoined"); + YggdrasilUserAuthentication yggdrasilUserAuthentication = this.getAuth(); + try { + yggdrasilUserAuthentication.logIn(); + ((IMinecraftClientAccessor) ShadowMain.client).setSession(new Session(yggdrasilUserAuthentication.getSelectedProfile().getName(), yggdrasilUserAuthentication.getSelectedProfile().getId().toString(), yggdrasilUserAuthentication.getAuthenticatedToken(), Optional.empty(), Optional.empty(), Session.AccountType.MOJANG)); + this.username = yggdrasilUserAuthentication.getSelectedProfile().getName(); + return new MinecraftToken(yggdrasilUserAuthentication.getAuthenticatedToken(),username); + } + catch (AuthenticationException authenticationException) { + return null; + } + } +} diff --git a/src/main/java/me/x150/authlib/login/microsoft/MicrosoftAuthenticator.java b/src/main/java/me/x150/authlib/login/microsoft/MicrosoftAuthenticator.java new file mode 100644 index 0000000..227fc09 --- /dev/null +++ b/src/main/java/me/x150/authlib/login/microsoft/MicrosoftAuthenticator.java @@ -0,0 +1,297 @@ + + +package me.x150.authlib.login.microsoft; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLConnection; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.StringJoiner; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import me.x150.authlib.exception.AuthFailureException; +import me.x150.authlib.struct.Authenticator; + +public class MicrosoftAuthenticator extends Authenticator { + protected final String clientId = "00000000402b5328"; + protected final String scopeUrl = "service::user.auth.xboxlive.com::MBI_SSL"; + protected String loginUrl; + protected String loginCookie; + protected String loginPPFT; + + public MicrosoftAuthenticator() { + } + + public XboxToken login(String email, String password) { + MicrosoftToken microsoftToken = this.generateTokenPair(this.generateLoginCode(email, password)); + XboxLiveToken xboxLiveToken = this.generateXboxTokenPair(microsoftToken); + return this.generateXboxTokenPair(xboxLiveToken); + } + + private String generateLoginCode(String email, String password) { + try { + URL url = new URL("https://login.live.com/oauth20_authorize.srf?redirect_uri=https://login.live.com/oauth20_desktop.srf&scope=service::user.auth.xboxlive.com::MBI_SSL&display=touch&response_type=code&locale=en&client_id=00000000402b5328"); + HttpURLConnection httpURLConnection = (HttpURLConnection)url.openConnection(); + InputStream inputStream = httpURLConnection.getResponseCode() == 200 ? httpURLConnection.getInputStream() : httpURLConnection.getErrorStream(); + this.loginCookie = httpURLConnection.getHeaderField("set-cookie"); + String responseData = (String)(new BufferedReader(new InputStreamReader(inputStream))).lines().collect(Collectors.joining()); + Matcher bodyMatcher = Pattern.compile("sFTTag:[ ]?'.*value=\"(.*)\"/>'").matcher(responseData); + if (bodyMatcher.find()) { + this.loginPPFT = bodyMatcher.group(1); + bodyMatcher = Pattern.compile("urlPost:[ ]?'(.+?(?='))").matcher(responseData); + if (!bodyMatcher.find()) { + throw new AuthFailureException("Authentication error. Could not find 'LOGIN-URL' tag from response!"); + } else { + this.loginUrl = bodyMatcher.group(1); + if (this.loginCookie != null && this.loginPPFT != null && this.loginUrl != null) { + return this.sendCodeData(email, password); + } else { + throw new AuthFailureException("Authentication error. Error in authentication process!"); + } + } + } else { + throw new AuthFailureException("Authentication error. Could not find 'LOGIN-PFTT' tag from response!"); + } + } catch (IOException var8) { + throw new AuthFailureException(String.format("Authentication error. Request could not be made! Cause: '%s'", var8.getMessage())); + } + } + + private String sendCodeData(String email, String password) { + Map requestData = new HashMap(); + requestData.put("login", email); + requestData.put("loginfmt", email); + requestData.put("passwd", password); + requestData.put("PPFT", this.loginPPFT); + String postData = this.encodeURL((Map)requestData); + + String authToken; + try { + byte[] data = postData.getBytes(StandardCharsets.UTF_8); + HttpURLConnection connection = (HttpURLConnection)(new URL(this.loginUrl)).openConnection(); + connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded; charset=utf-8"); + connection.setRequestProperty("Content-Length", String.valueOf(data.length)); + connection.setRequestProperty("Cookie", this.loginCookie); + connection.setDoInput(true); + connection.setDoOutput(true); + OutputStream outputStream = connection.getOutputStream(); + + try { + outputStream.write(data); + } catch (Throwable var12) { + if (outputStream != null) { + try { + outputStream.close(); + } catch (Throwable var11) { + var12.addSuppressed(var11); + } + } + + throw var12; + } + + if (outputStream != null) { + outputStream.close(); + } + + if (connection.getResponseCode() != 200 || connection.getURL().toString().equals(this.loginUrl)) { + throw new AuthFailureException("Authentication error. Username or password is not valid."); + } + + Pattern pattern = Pattern.compile("[?|&]code=([\\w.-]+)"); + Matcher tokenMatcher = pattern.matcher(URLDecoder.decode(connection.getURL().toString(), StandardCharsets.UTF_8.name())); + if (!tokenMatcher.find()) { + throw new AuthFailureException("Authentication error. Could not handle data from response."); + } + + authToken = tokenMatcher.group(1); + } catch (IOException var13) { + throw new AuthFailureException(String.format("Authentication error. Request could not be made! Cause: '%s'", var13.getMessage())); + } + + this.loginUrl = null; + this.loginCookie = null; + this.loginPPFT = null; + return authToken; + } + + private void sendXboxRequest(HttpURLConnection httpURLConnection, JsonObject request, JsonObject properties) throws IOException { + request.add("Properties", properties); + String requestBody = request.toString(); + httpURLConnection.setFixedLengthStreamingMode(requestBody.length()); + httpURLConnection.setRequestProperty("Content-Type", "application/json"); + httpURLConnection.setRequestProperty("Accept", "application/json"); + httpURLConnection.connect(); + OutputStream outputStream = httpURLConnection.getOutputStream(); + + try { + outputStream.write(requestBody.getBytes(StandardCharsets.US_ASCII)); + } catch (Throwable var9) { + if (outputStream != null) { + try { + outputStream.close(); + } catch (Throwable var8) { + var9.addSuppressed(var8); + } + } + + throw var9; + } + + if (outputStream != null) { + outputStream.close(); + } + + } + + private MicrosoftToken generateTokenPair(String authToken) { + try { + Map arguments = new HashMap(); + arguments.put("client_id", "00000000402b5328"); + arguments.put("code", authToken); + arguments.put("grant_type", "authorization_code"); + arguments.put("redirect_uri", "https://login.live.com/oauth20_desktop.srf"); + arguments.put("scope", "service::user.auth.xboxlive.com::MBI_SSL"); + StringJoiner argumentBuilder = new StringJoiner("&"); + Iterator var4 = arguments.entrySet().iterator(); + + while(var4.hasNext()) { + Map.Entry entry = (Map.Entry)var4.next(); + argumentBuilder.add(this.encodeURL((String)entry.getKey()) + "=" + this.encodeURL((String)entry.getValue())); + } + + byte[] data = argumentBuilder.toString().getBytes(StandardCharsets.UTF_8); + URL url = new URL("https://login.live.com/oauth20_token.srf"); + URLConnection urlConnection = url.openConnection(); + HttpURLConnection httpURLConnection = (HttpURLConnection)urlConnection; + httpURLConnection.setRequestMethod("POST"); + httpURLConnection.setDoOutput(true); + httpURLConnection.setFixedLengthStreamingMode(data.length); + httpURLConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); + httpURLConnection.connect(); + OutputStream outputStream = httpURLConnection.getOutputStream(); + + try { + outputStream.write(data); + } catch (Throwable var12) { + if (outputStream != null) { + try { + outputStream.close(); + } catch (Throwable var11) { + var12.addSuppressed(var11); + } + } + + throw var12; + } + + if (outputStream != null) { + outputStream.close(); + } + + JsonObject jsonObject = this.parseResponseData(httpURLConnection); + return new MicrosoftToken(jsonObject.get("access_token").getAsString(), jsonObject.get("refresh_token").getAsString()); + } catch (IOException var13) { + throw new AuthFailureException(String.format("Authentication error. Request could not be made! Cause: '%s'", var13.getMessage())); + } + } + + public XboxLiveToken generateXboxTokenPair(MicrosoftToken microsoftToken) { + try { + URL url = new URL("https://user.auth.xboxlive.com/user/authenticate"); + URLConnection urlConnection = url.openConnection(); + HttpURLConnection httpURLConnection = (HttpURLConnection)urlConnection; + httpURLConnection.setDoOutput(true); + JsonObject request = new JsonObject(); + request.addProperty("RelyingParty", "http://auth.xboxlive.com"); + request.addProperty("TokenType", "JWT"); + JsonObject properties = new JsonObject(); + properties.addProperty("AuthMethod", "RPS"); + properties.addProperty("SiteName", "user.auth.xboxlive.com"); + properties.addProperty("RpsTicket", microsoftToken.getToken()); + this.sendXboxRequest(httpURLConnection, request, properties); + JsonObject jsonObject = this.parseResponseData(httpURLConnection); + String uhs = ((JsonObject)jsonObject.getAsJsonObject("DisplayClaims").getAsJsonArray("xui").get(0)).get("uhs").getAsString(); + return new XboxLiveToken(jsonObject.get("Token").getAsString(), uhs); + } catch (IOException var9) { + throw new AuthFailureException(String.format("Authentication error. Request could not be made! Cause: '%s'", var9.getMessage())); + } + } + + public XboxToken generateXboxTokenPair(XboxLiveToken xboxLiveToken) { + try { + URL url = new URL("https://xsts.auth.xboxlive.com/xsts/authorize"); + URLConnection urlConnection = url.openConnection(); + HttpURLConnection httpURLConnection = (HttpURLConnection)urlConnection; + httpURLConnection.setRequestMethod("POST"); + httpURLConnection.setDoOutput(true); + JsonObject request = new JsonObject(); + request.addProperty("RelyingParty", "rp://api.minecraftservices.com/"); + request.addProperty("TokenType", "JWT"); + JsonObject properties = new JsonObject(); + properties.addProperty("SandboxId", "RETAIL"); + JsonArray userTokens = new JsonArray(); + userTokens.add(xboxLiveToken.getToken()); + properties.add("UserTokens", userTokens); + this.sendXboxRequest(httpURLConnection, request, properties); + if (httpURLConnection.getResponseCode() == 401) { + throw new AuthFailureException("No xbox account was found!"); + } else { + JsonObject jsonObject = this.parseResponseData(httpURLConnection); + String uhs = ((JsonObject)jsonObject.getAsJsonObject("DisplayClaims").getAsJsonArray("xui").get(0)).get("uhs").getAsString(); + return new XboxToken(jsonObject.get("Token").getAsString(), uhs); + } + } catch (IOException var10) { + throw new AuthFailureException(String.format("Authentication error. Request could not be made! Cause: '%s'", var10.getMessage())); + } + } + + public JsonObject parseResponseData(HttpURLConnection httpURLConnection) throws IOException { + BufferedReader bufferedReader; + if (httpURLConnection.getResponseCode() != 200) { + bufferedReader = new BufferedReader(new InputStreamReader(httpURLConnection.getErrorStream())); + } else { + bufferedReader = new BufferedReader(new InputStreamReader(httpURLConnection.getInputStream())); + } + + String lines = (String)bufferedReader.lines().collect(Collectors.joining()); + JsonObject jsonObject = (new JsonParser()).parse(lines).getAsJsonObject(); + if (jsonObject.has("error")) { + throw new AuthFailureException(jsonObject.get("error") + ": " + jsonObject.get("error_description")); + } else { + return jsonObject; + } + } + + private String encodeURL(String url) { + return URLEncoder.encode(url, StandardCharsets.UTF_8); + } + + private String encodeURL(Map map) { + StringBuilder sb = new StringBuilder(); + + Map.Entry entry; + for(Iterator var3 = map.entrySet().iterator(); var3.hasNext(); sb.append(String.format("%s=%s", this.encodeURL((String)entry.getKey()), this.encodeURL((String)entry.getValue())))) { + entry = (Map.Entry)var3.next(); + if (sb.length() > 0) { + sb.append("&"); + } + } + + return sb.toString(); + } +} diff --git a/src/main/java/me/x150/authlib/login/microsoft/MicrosoftToken.java b/src/main/java/me/x150/authlib/login/microsoft/MicrosoftToken.java new file mode 100644 index 0000000..d023025 --- /dev/null +++ b/src/main/java/me/x150/authlib/login/microsoft/MicrosoftToken.java @@ -0,0 +1,26 @@ + + +package me.x150.authlib.login.microsoft; + +import me.x150.authlib.struct.AuthToken; + +public class MicrosoftToken extends AuthToken { + protected String token; + protected String refreshToken; + + public MicrosoftToken() { + } + + public MicrosoftToken(String token, String refreshToken) { + this.token = token; + this.refreshToken = refreshToken; + } + + public String getToken() { + return this.token; + } + + public String getRefreshToken() { + return this.refreshToken; + } +} diff --git a/src/main/java/me/x150/authlib/login/microsoft/XboxLiveToken.java b/src/main/java/me/x150/authlib/login/microsoft/XboxLiveToken.java new file mode 100644 index 0000000..6510f4e --- /dev/null +++ b/src/main/java/me/x150/authlib/login/microsoft/XboxLiveToken.java @@ -0,0 +1,26 @@ + + +package me.x150.authlib.login.microsoft; + +import me.x150.authlib.struct.AuthToken; + +public class XboxLiveToken extends AuthToken { + protected String token; + protected String uhs; + + public XboxLiveToken() { + } + + public XboxLiveToken(String token, String uhs) { + this.token = token; + this.uhs = uhs; + } + + public String getToken() { + return this.token; + } + + public String getUhs() { + return this.uhs; + } +} diff --git a/src/main/java/me/x150/authlib/login/microsoft/XboxToken.java b/src/main/java/me/x150/authlib/login/microsoft/XboxToken.java new file mode 100644 index 0000000..30d51bd --- /dev/null +++ b/src/main/java/me/x150/authlib/login/microsoft/XboxToken.java @@ -0,0 +1,10 @@ +package me.x150.authlib.login.microsoft; + +public class XboxToken extends XboxLiveToken { + public XboxToken() { + } + + public XboxToken(String token, String uhs) { + super(token, uhs); + } +} diff --git a/src/main/java/me/x150/authlib/login/mojang/MinecraftAuthenticator.java b/src/main/java/me/x150/authlib/login/mojang/MinecraftAuthenticator.java new file mode 100644 index 0000000..ce00f1d --- /dev/null +++ b/src/main/java/me/x150/authlib/login/mojang/MinecraftAuthenticator.java @@ -0,0 +1,179 @@ +package me.x150.authlib.login.mojang; + +import com.google.gson.JsonObject; +import com.google.gson.reflect.TypeToken; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLConnection; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; +import me.x150.authlib.exception.AuthFailureException; +import me.x150.authlib.login.altening.AlteningAuth; +import me.x150.authlib.login.microsoft.MicrosoftAuthenticator; +import me.x150.authlib.login.microsoft.XboxToken; +import me.x150.authlib.login.mojang.MinecraftToken; +import me.x150.authlib.login.mojang.profile.MinecraftProfile; +import me.x150.authlib.login.mojang.profile.MinecraftProfileCape; +import me.x150.authlib.login.mojang.profile.MinecraftProfileSkin; +import me.x150.authlib.struct.Authenticator; + +public class MinecraftAuthenticator extends Authenticator { + protected final MicrosoftAuthenticator microsoftAuthenticator = new MicrosoftAuthenticator(); + + public MinecraftAuthenticator() { + } + + public MinecraftToken login(String email, String password) { + try { + URL url = new URL("https://authserver.mojang.com/authenticate"); + URLConnection urlConnection = url.openConnection(); + HttpURLConnection httpURLConnection = (HttpURLConnection)urlConnection; + httpURLConnection.setRequestMethod("POST"); + httpURLConnection.setDoOutput(true); + JsonObject request = new JsonObject(); + JsonObject agent = new JsonObject(); + agent.addProperty("name", "Minecraft"); + agent.addProperty("version", "1"); + request.add("agent", agent); + request.addProperty("username", email); + request.addProperty("password", password); + request.addProperty("requestUser", false); + String requestBody = request.toString(); + httpURLConnection.setFixedLengthStreamingMode(requestBody.length()); + httpURLConnection.setRequestProperty("Content-Type", "application/json"); + httpURLConnection.setRequestProperty("Host", "authserver.mojang.com"); + httpURLConnection.connect(); + OutputStream outputStream = httpURLConnection.getOutputStream(); + + try { + outputStream.write(requestBody.getBytes(StandardCharsets.US_ASCII)); + } catch (Throwable var13) { + if (outputStream != null) { + try { + outputStream.close(); + } catch (Throwable var12) { + var13.addSuppressed(var12); + } + } + + throw var13; + } + + if (outputStream != null) { + outputStream.close(); + } + + JsonObject jsonObject = this.parseResponseData(httpURLConnection); + return new MinecraftToken(jsonObject.get("accessToken").getAsString(), ((JsonObject)jsonObject.get("selectedProfile")).get("name").getAsString()); + } catch (IOException var14) { + throw new AuthFailureException(String.format("Authentication error. Request could not be made! Cause: '%s'", var14.getMessage())); + } + } + + public MinecraftToken loginWithMicrosoft(String email, String password) { + XboxToken xboxToken = this.microsoftAuthenticator.login(email, password); + + try { + URL url = new URL("https://api.minecraftservices.com/authentication/login_with_xbox"); + URLConnection urlConnection = url.openConnection(); + HttpURLConnection httpURLConnection = (HttpURLConnection)urlConnection; + httpURLConnection.setRequestMethod("POST"); + httpURLConnection.setDoOutput(true); + JsonObject request = new JsonObject(); + request.addProperty("identityToken", "XBL3.0 x=" + xboxToken.getUhs() + ";" + xboxToken.getToken()); + String requestBody = request.toString(); + httpURLConnection.setFixedLengthStreamingMode(requestBody.length()); + httpURLConnection.setRequestProperty("Content-Type", "application/json"); + httpURLConnection.setRequestProperty("Host", "api.minecraftservices.com"); + httpURLConnection.connect(); + OutputStream outputStream = httpURLConnection.getOutputStream(); + + try { + outputStream.write(requestBody.getBytes(StandardCharsets.US_ASCII)); + } catch (Throwable var13) { + if (outputStream != null) { + try { + outputStream.close(); + } catch (Throwable var12) { + var13.addSuppressed(var12); + } + } + + throw var13; + } + + if (outputStream != null) { + outputStream.close(); + } + + JsonObject jsonObject = this.microsoftAuthenticator.parseResponseData(httpURLConnection); + return new MinecraftToken(jsonObject.get("access_token").getAsString(), jsonObject.get("username").getAsString()); + } catch (IOException var14) { + throw new AuthFailureException(String.format("Authentication error. Request could not be made! Cause: '%s'", var14.getMessage())); + } + } + public MinecraftToken loginWithAltening(String token) { + AlteningAuth alteningAuth = new AlteningAuth(token); + return alteningAuth.login(); + } + + public MinecraftProfile getGameProfile(MinecraftToken minecraftToken) { + try { + URL url = new URL("https://api.minecraftservices.com/minecraft/profile"); + URLConnection urlConnection = url.openConnection(); + HttpURLConnection httpURLConnection = (HttpURLConnection)urlConnection; + httpURLConnection.setRequestMethod("GET"); + httpURLConnection.setRequestProperty("Authorization", "Bearer " + minecraftToken.getAccessToken()); + httpURLConnection.setRequestProperty("Host", "api.minecraftservices.com"); + httpURLConnection.connect(); + JsonObject jsonObject = this.parseResponseData(httpURLConnection); + UUID uuid = this.generateUUID(jsonObject.get("id").getAsString()); + String name = jsonObject.get("name").getAsString(); + + return new MinecraftProfile(uuid, name); + } catch (IOException var10) { + throw new AuthFailureException(String.format("Authentication error. Request could not be made! Cause: '%s'", var10.getMessage())); + } + } + + public JsonObject parseResponseData(HttpURLConnection httpURLConnection) throws IOException { + BufferedReader bufferedReader; + if (httpURLConnection.getResponseCode() != 200) { + bufferedReader = new BufferedReader(new InputStreamReader(httpURLConnection.getErrorStream())); + } else { + bufferedReader = new BufferedReader(new InputStreamReader(httpURLConnection.getInputStream())); + } + + String lines = (String)bufferedReader.lines().collect(Collectors.joining()); + JsonObject jsonObject = (JsonObject)this.gson.fromJson(lines, JsonObject.class); + if (jsonObject.has("error")) { + throw new AuthFailureException(String.format("Could not find profile!. Error: '%s'", jsonObject.get("errorMessage").getAsString())); + } else { + return jsonObject; + } + } + + public UUID generateUUID(String trimmedUUID) throws IllegalArgumentException { + if (trimmedUUID == null) { + throw new IllegalArgumentException(); + } else { + StringBuilder builder = new StringBuilder(trimmedUUID.trim()); + + try { + builder.insert(20, "-"); + builder.insert(16, "-"); + builder.insert(12, "-"); + builder.insert(8, "-"); + return UUID.fromString(builder.toString()); + } catch (StringIndexOutOfBoundsException var4) { + return null; + } + } + } +} diff --git a/src/main/java/me/x150/authlib/login/mojang/MinecraftToken.java b/src/main/java/me/x150/authlib/login/mojang/MinecraftToken.java new file mode 100644 index 0000000..45a933b --- /dev/null +++ b/src/main/java/me/x150/authlib/login/mojang/MinecraftToken.java @@ -0,0 +1,26 @@ +package me.x150.authlib.login.mojang; + +public class MinecraftToken { + private String accessToken; + private String username; + + public MinecraftToken() { + } + + public MinecraftToken(String accessToken, String username) { + this.accessToken = accessToken; + this.username = username; + } + + public String getAccessToken() { + return this.accessToken; + } + + public String getUsername() { + return this.username; + } + + public String toString() { + return "MinecraftToken{accessToken='" + this.accessToken + '\'' + ", username='" + this.username + '\'' + '}'; + } +} diff --git a/src/main/java/me/x150/authlib/login/mojang/profile/MinecraftProfile.java b/src/main/java/me/x150/authlib/login/mojang/profile/MinecraftProfile.java new file mode 100644 index 0000000..09e2a5d --- /dev/null +++ b/src/main/java/me/x150/authlib/login/mojang/profile/MinecraftProfile.java @@ -0,0 +1,32 @@ +package me.x150.authlib.login.mojang.profile; + +import java.util.List; +import java.util.UUID; + +public class MinecraftProfile { + private UUID uuid; + private String username; + + public MinecraftProfile() { + } + + public MinecraftProfile(UUID uuid, String username) { + this.uuid = uuid; + this.username = username; + + } + + public UUID getUuid() { + return this.uuid; + } + + public String getUsername() { + return this.username; + } + + + + public String toString() { + return "MinecraftProfile{uuid=" + this.uuid + ", username='" + this.username + '\'' + '}'; + } +} diff --git a/src/main/java/me/x150/authlib/login/mojang/profile/MinecraftProfileCape.java b/src/main/java/me/x150/authlib/login/mojang/profile/MinecraftProfileCape.java new file mode 100644 index 0000000..b85cbaf --- /dev/null +++ b/src/main/java/me/x150/authlib/login/mojang/profile/MinecraftProfileCape.java @@ -0,0 +1,12 @@ +package me.x150.authlib.login.mojang.profile; + +import me.x150.authlib.struct.OnlineTexture; + +public class MinecraftProfileCape extends OnlineTexture { + public MinecraftProfileCape() { + } + + public MinecraftProfileCape(String id, String state, String url, String alias) { + super(id, state, url, alias); + } +} diff --git a/src/main/java/me/x150/authlib/login/mojang/profile/MinecraftProfileSkin.java b/src/main/java/me/x150/authlib/login/mojang/profile/MinecraftProfileSkin.java new file mode 100644 index 0000000..9bada9b --- /dev/null +++ b/src/main/java/me/x150/authlib/login/mojang/profile/MinecraftProfileSkin.java @@ -0,0 +1,27 @@ +package me.x150.authlib.login.mojang.profile; + +import me.x150.authlib.struct.OnlineTexture; + +public class MinecraftProfileSkin extends OnlineTexture { + private String variant; + + public MinecraftProfileSkin() { + } + + public MinecraftProfileSkin(String variant) { + this.variant = variant; + } + + public MinecraftProfileSkin(String id, String state, String url, String alias, String variant) { + super(id, state, url, alias); + this.variant = variant; + } + + public String getVariant() { + return this.variant; + } + + public String toString() { + return "MinecraftSkin{id='" + this.getId() + '\'' + ", state='" + this.getState() + '\'' + ", url='" + this.getUrl() + '\'' + ", alias='" + this.getAlias() + '\'' + "variant='" + this.variant + '\'' + '}'; + } +} diff --git a/src/main/java/me/x150/authlib/struct/AuthToken.java b/src/main/java/me/x150/authlib/struct/AuthToken.java new file mode 100644 index 0000000..302c482 --- /dev/null +++ b/src/main/java/me/x150/authlib/struct/AuthToken.java @@ -0,0 +1,6 @@ +package me.x150.authlib.struct; + +public abstract class AuthToken { + public AuthToken() { + } +} diff --git a/src/main/java/me/x150/authlib/struct/Authenticator.java b/src/main/java/me/x150/authlib/struct/Authenticator.java new file mode 100644 index 0000000..55a661f --- /dev/null +++ b/src/main/java/me/x150/authlib/struct/Authenticator.java @@ -0,0 +1,15 @@ +package me.x150.authlib.struct; + +import com.google.gson.Gson; +import java.net.http.HttpClient; +import java.time.Duration; + +public abstract class Authenticator { + protected final Gson gson = new Gson(); + protected final HttpClient client = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(10L)).build(); + + public Authenticator() { + } + + public abstract T login(String var1, String var2); +} diff --git a/src/main/java/me/x150/authlib/struct/OnlineTexture.java b/src/main/java/me/x150/authlib/struct/OnlineTexture.java new file mode 100644 index 0000000..1c5f474 --- /dev/null +++ b/src/main/java/me/x150/authlib/struct/OnlineTexture.java @@ -0,0 +1,38 @@ +package me.x150.authlib.struct; + +public abstract class OnlineTexture { + private String id; + private String state; + private String url; + private String alias; + + public OnlineTexture() { + } + + public OnlineTexture(String id, String state, String url, String alias) { + this.id = id; + this.state = state; + this.url = url; + this.alias = alias; + } + + public String getId() { + return this.id; + } + + public String getState() { + return this.state; + } + + public String getUrl() { + return this.url; + } + + public String getAlias() { + return this.alias; + } + + public String toString() { + return "TextureVariable{id='" + this.id + '\'' + ", state='" + this.state + '\'' + ", url='" + this.url + '\'' + ", alias='" + this.alias + '\'' + '}'; + } +} \ No newline at end of file diff --git a/src/main/java/net/shadow/client/feature/gui/screen/AltManagerScreen.java b/src/main/java/net/shadow/client/feature/gui/screen/AltManagerScreen.java index ee16b02..200061b 100644 --- a/src/main/java/net/shadow/client/feature/gui/screen/AltManagerScreen.java +++ b/src/main/java/net/shadow/client/feature/gui/screen/AltManagerScreen.java @@ -233,10 +233,12 @@ public class AltManagerScreen extends ClientScreen implements FastTickable { new Thread(() -> { this.selectedAlt.login(); isLoggingIn.set(false); - if (!this.selectedAlt.storage.valid) { + // TODO: Actually fix this error instead of janky bypass + if (!this.selectedAlt.storage.valid && this.selectedAlt.storage.type != AddScreenOverlay.AccountType.ALTENING) { HudNotification.create("Failed to log in", 5000, HudNotification.Type.ERROR); return; } + if(this.selectedAlt.storage.type == AddScreenOverlay.AccountType.ALTENING) return; Session newSession = new Session(selectedAlt.storage.cachedName, selectedAlt.storage.cachedUuid.toString(), selectedAlt.storage.accessToken, Optional.empty(), Optional.empty(), Session.AccountType.MOJANG); ((IMinecraftClientAccessor) ShadowMain.client).setSession(newSession); HudNotification.create("Logged into account " + newSession.getUsername(), 5000, HudNotification.Type.INFO); @@ -337,7 +339,7 @@ public class AltManagerScreen extends ClientScreen implements FastTickable { RenderSystem.defaultBlendFunc(); String mail; - if (this.selectedAlt.storage.type != AddScreenOverlay.AccountType.CRACKED) { + if (this.selectedAlt.storage.type != AddScreenOverlay.AccountType.CRACKED || this.selectedAlt.storage.type != AddScreenOverlay.AccountType.ALTENING) { mail = this.selectedAlt.storage.email; String[] mailPart = mail.split("@"); String domain = mailPart[mailPart.length - 1]; @@ -671,7 +673,7 @@ public class AltManagerScreen extends ClientScreen implements FastTickable { protected void init() { RoundButton exit = new net.shadow.client.feature.gui.widget.RoundButton(net.shadow.client.feature.gui.widget.RoundButton.STANDARD, width - 20 - 5, 5, 20, 20, "X", () -> Objects.requireNonNull(client).setScreen(parent)); buttons.add(exit); - email = new RoundTextFieldWidget(width / 2d - (widgetWid - padding * 2) / 2d, height / 2d - widgetHei / 2d + padding, widgetWid - padding * 2, 20, "E-Mail or username"); + email = new RoundTextFieldWidget(width / 2d - (widgetWid - padding * 2) / 2d, height / 2d - widgetHei / 2d + padding, widgetWid - padding * 2, 20, "E-Mail or username or Token"); passwd = new RoundTextFieldWidget(width / 2d - (widgetWid - padding * 2) / 2d, height / 2d - widgetHei / 2d + padding * 2 + 20, widgetWid - padding * 2, 20, "Password"); type = new net.shadow.client.feature.gui.widget.RoundButton(net.shadow.client.feature.gui.widget.RoundButton.STANDARD, 0, 0, widgetWid / 2d - padding * 1.5, 20, "Type: " + AccountType.values()[accountTypeI].s, this::cycle); add = new net.shadow.client.feature.gui.widget.RoundButton(net.shadow.client.feature.gui.widget.RoundButton.STANDARD, 0, 0, widgetWid / 2d - padding * 1.5, 20, "Add", this::add); @@ -687,7 +689,7 @@ public class AltManagerScreen extends ClientScreen implements FastTickable { } boolean isAddApplicable() { - if (AccountType.values()[accountTypeI] == AccountType.CRACKED && !email.getText().isEmpty()) { + if (AccountType.values()[accountTypeI] == AccountType.CRACKED ||AccountType.values()[accountTypeI] == AccountType.ALTENING && !email.getText().isEmpty()) { return true; } else { return !email.getText().isEmpty() && !passwd.getText().isEmpty(); @@ -776,7 +778,7 @@ public class AltManagerScreen extends ClientScreen implements FastTickable { } enum AccountType { - MOJANG("Mojang"), MICROSOFT("Microsoft"), CRACKED("Cracked"); + MOJANG("Mojang"), MICROSOFT("Microsoft"), CRACKED("Cracked"), ALTENING("The Altening"); final String s; @@ -828,6 +830,7 @@ public class AltManagerScreen extends ClientScreen implements FastTickable { case MOJANG -> auth.login(storage.email, storage.password); case MICROSOFT -> auth.loginWithMicrosoft(storage.email, storage.password); case CRACKED -> null; + case ALTENING -> auth.loginWithAltening(storage.email); }; if (token == null && storage.password.equals("")) { storage.valid = true; @@ -840,9 +843,11 @@ public class AltManagerScreen extends ClientScreen implements FastTickable { throw new NullPointerException(); } storage.accessToken = token.getAccessToken(); + MinecraftProfile profile = auth.getGameProfile(token); storage.cachedName = profile.getUsername(); storage.cachedUuid = profile.getUuid(); + downloadTexture(); storage.valid = true; } catch (Exception ignored) { diff --git a/src/main/java/net/shadow/client/mixin/IMinecraftClientAccessor.java b/src/main/java/net/shadow/client/mixin/IMinecraftClientAccessor.java index fe1f74a..02638aa 100644 --- a/src/main/java/net/shadow/client/mixin/IMinecraftClientAccessor.java +++ b/src/main/java/net/shadow/client/mixin/IMinecraftClientAccessor.java @@ -11,6 +11,8 @@ import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mutable; import org.spongepowered.asm.mixin.gen.Accessor; +import java.net.Proxy; + @Mixin(MinecraftClient.class) public interface IMinecraftClientAccessor { @@ -20,4 +22,6 @@ public interface IMinecraftClientAccessor { @Accessor("renderTickCounter") RenderTickCounter getRenderTickCounter(); + @Accessor("networkProxy") + Proxy getProxy(); }