Defective4 / Another-Minecraft-Chat-Client

[ARCHIVED - See README] Another Minecraft Chat Client written from scratch in Java. It has inventory handling, movement, automatic messages and many more little things. It supports Minecraft versions 1.8 to 1.19.2 (With Forge on 1.8 to 1.12.2)
Apache License 2.0
76 stars 8 forks source link

Support Server type #7

Closed SeanWalk closed 1 year ago

SeanWalk commented 2 years ago

Is this offline server or cracked server only? Can't find any authentication place.

Defective4 commented 2 years ago

Currently joining Online servers is not supported. I plan to implement it at some point in the future, but I don't own a Minecraft account, so I can't test if it actually works.

Sorry for the inconvenience!

TheTwoBoom commented 2 years ago

Mojang has APIs for getting Auth Tokens (Way to login to server) wiki.vg/Mojang_API

Defective4 commented 2 years ago

I know @TwoB00m, but I have no way to test if it works, because I don't own a Minecraft account

TheTwoBoom commented 2 years ago

You can try to implement TheAltening Alt token, the token are free and valid for 15min @Defective4 TheAltening has also got a API

Defective4 commented 2 years ago

Thank you @TwoB00m, your answer actually helped me a lot and hopefully I will be able to release a new update soon, this time with fully working authentication.

Defective4 commented 2 years ago

Mojang authentication is now supported in release v1.7.0, however it needs to be tested.

TheTwoBoom commented 2 years ago

But many have Microsoft Minecraft Accounts...... It Mojang Login doesn't help

Defective4 commented 2 years ago

Yeah, that's why I didn't close this issue yet. I have to do further testing to finally implement Microsoft authentication. Hopefully I will be able to add this soon

TheTwoBoom commented 2 years ago

https://mojang-api-docs.netlify.app/authentication/msa.html

TheTwoBoom commented 2 years ago

Here's an tutorial for Microsoft authentication

gabe4278 commented 2 years ago

I literally created a remote auth code in java, here's the code (and the refresh token is included to keep the account logged in)

import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Timer;
import java.util.TimerTask;
import java.util.UUID;

import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.internal.JavaVersion;

public class ClientLogin extends TimerTask {
    private final Timer authcodeTimer = new Timer();
    private String deviceCode;
    private String refreshToken;
    private String clientId = "389b1b32-b5d5-43b2-bddc-84ce938d6737"; // token from https://github.com/microsoft/Office365APIEditor

    private UUID getOfflineUUID(String name) {
        String value = "OfflinePlayer:" + name;
        return UUID.nameUUIDFromBytes(value.getBytes(StandardCharsets.UTF_8));
    }

    private String readFile(File file) throws IOException {
        BufferedReader reader = new BufferedReader(new FileReader(file));
        String ln = reader.readLine();
        String dt = "";
        while (ln != null) {
            dt += ln;
            ln = reader.readLine();
        }
        reader.close();
        return dt;
    }

    private void writeFile(File file, String data) throws IOException {
        BufferedWriter writer = new BufferedWriter(new FileWriter(file));
        if (!file.exists()) file.createNewFile();
        writer.write(data);
        writer.close();
    }

    public UserInfo getUserInfo() {
        JsonObject userInfo = null;
        try {
            userInfo = JsonParser.parseString(readFile(new File("userInfo.json"))).getAsJsonObject();

        }
        catch (IOException e) {
            e.printStackTrace();
        }
        return new UserInfo(userInfo.get("id").getAsString(), userInfo.get("name").getAsString(), userInfo.get("access_token").getAsString());
    }

    public void logOut() throws IOException {
        writeFile(new File("userInfo.json"), "{}");
    }

    public void setPlayerName(String name) throws IOException {
        JsonObject creds = new JsonObject();
        creds.addProperty("refresh_token", "");
        creds.addProperty("access_token", "");
        creds.addProperty("name", name);
        creds.addProperty("id", getOfflineUUID(name).toString());
        writeFile(new File("userInfo.json"), creds.toString());
    }

    public void refreshToken() throws IOException {
        JsonObject profile = JsonParser.parseString(readFile(new File("userInfo.json"))).getAsJsonObject();
        JsonElement rt = profile.get("refresh_token");
        if (rt != null) {
            URL url = new URL("https://login.microsoftonline.com/consumers/oauth2/v2.0/token");
            HttpURLConnection http = (HttpURLConnection)  url.openConnection();
            http.setRequestMethod("POST");
            String data = "client_id=" + clientId + "&grant_type=refresh_token&refresh_token=" + rt.getAsString();
            http.setDoOutput(true);
            http.setRequestProperty("User-Agent", "java/" + JavaVersion.getMajorJavaVersion());
            http.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
            http.setRequestProperty("Content-Length", String.valueOf(data.length()));
            http.getOutputStream().write(data.getBytes(StandardCharsets.UTF_8));
            BufferedReader reader = new BufferedReader(new InputStreamReader(http.getInputStream()));
            String ln = reader.readLine();
            String dt = "";
            while (ln != null) {
                dt += ln;
                ln = reader.readLine();
            }
            http.disconnect();
            JsonObject resData = JsonParser.parseString(dt).getAsJsonObject();
            refreshToken = resData.get("refresh_token").getAsString();
            authenticateWithXBL(resData.get("access_token").getAsString());
        }
        else {
            throw new IOException("The item 'refresh_token' is missing.");
        }
    }

    public void doRemoteAuth() {
        try {
            URL url = new URL("https://login.microsoftonline.com/consumers/oauth2/v2.0/devicecode");
            HttpURLConnection http = (HttpURLConnection)  url.openConnection();
            http.setRequestMethod("POST");
            String data = "client_id=" + clientId + "&scope=XboxLive.signin%20offline_access";
            http.setDoOutput(true);
            http.setRequestProperty("User-Agent", "java/" + JavaVersion.getMajorJavaVersion());
            http.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
            http.setRequestProperty("Content-Length", String.valueOf(data.length()));
            http.getOutputStream().write(data.getBytes(StandardCharsets.UTF_8));
            BufferedReader reader = new BufferedReader(new InputStreamReader(http.getInputStream()));
            String ln = reader.readLine();
            String dt = "";
            while (ln != null) {
                dt += ln;
                ln = reader.readLine();
            }
            http.disconnect();
            JsonObject deviceCodeAuth = JsonParser.parseString(dt).getAsJsonObject();
            System.out.println("[msa] First time signing in. Please authenticate now:");
            System.out.println("To sign in, use a web browser to open the page " + deviceCodeAuth.get("verification_uri").getAsString() + " and enter the code " + deviceCodeAuth.get("user_code").getAsString() + " to authenticate.");
            deviceCode = deviceCodeAuth.get("device_code").getAsString();
            authcodeTimer.schedule(this, deviceCodeAuth.get("interval").getAsInt() * 1000, deviceCodeAuth.get("interval").getAsInt() * 1000);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void getMinecraftJavaProfile(String tokenType, String accessToken) throws IOException {
        URL url = new URL("https://api.minecraftservices.com/minecraft/profile");
        HttpURLConnection http = (HttpURLConnection) url.openConnection();
        http.setDoOutput(true);
        http.setRequestProperty("User-Agent", "java/" + JavaVersion.getMajorJavaVersion());
        http.setRequestProperty("Authorization", tokenType + " " + accessToken);
        BufferedReader reader = new BufferedReader(new InputStreamReader(http.getInputStream()));
        String ln = reader.readLine();
        String dt = "";
        while (ln != null) {
            dt += ln;
            ln = reader.readLine();
        }
        JsonObject resObject = JsonParser.parseString(dt).getAsJsonObject();
        String name = resObject.get("name").getAsString();
        String id = resObject.get("id").getAsString();
        JsonObject creds = new JsonObject();
        creds.addProperty("refresh_token", refreshToken);
        creds.addProperty("access_token", accessToken);
        creds.addProperty("name", name);
        creds.addProperty("id", id);
        writeFile(new File("userInfo.json"), creds.toString());
    }

    private void checkGameOwnership(String tokenType, String accessToken) throws IOException {
        URL url = new URL("https://api.minecraftservices.com/entitlements/mcstore");
        HttpURLConnection http = (HttpURLConnection) url.openConnection();
        http.setDoOutput(true);
        http.setRequestProperty("User-Agent", "java/" + JavaVersion.getMajorJavaVersion());
        http.setRequestProperty("Authorization", tokenType + " " + accessToken);
        BufferedReader reader = new BufferedReader(new InputStreamReader(http.getInputStream()));
        String ln = reader.readLine();
        String dt = "";
        while (ln != null) {
            dt += ln;
            ln = reader.readLine();
        }
        boolean hasGameMinecraft = false;
        boolean hasProductMinecraft = false;
        JsonObject resObject = JsonParser.parseString(dt).getAsJsonObject();
        JsonArray items = resObject.get("items").getAsJsonArray();
        for (JsonElement e : items) {
            if (e.getAsJsonObject().get("name").getAsString().equals("product_minecraft")) hasProductMinecraft = true;
            if (e.getAsJsonObject().get("name").getAsString().equals("game_minecraft")) hasGameMinecraft = true;
        }
        if (hasProductMinecraft && hasGameMinecraft) getMinecraftJavaProfile(tokenType, accessToken);
        else System.err.println("This user doesn't have the game. You can buy it at Minecraft.net");
    }

    private void authenticateWithMinecraftJava(String userHash, String XSTSToken) throws IOException {
        URL url = new URL("https://api.minecraftservices.com/authentication/login_with_xbox");
        HttpURLConnection http = (HttpURLConnection) url.openConnection();
        http.setDoOutput(true);
        JsonObject requestData = new JsonObject();
        requestData.addProperty("identityToken", "XBL3.0 x=" + userHash + ";" + XSTSToken);
        http.setRequestMethod("POST");
        http.setRequestProperty("User-Agent", "java/" + JavaVersion.getMajorJavaVersion());
        http.setRequestProperty("Content-Type", "application/json");
        http.setRequestProperty("Content-Length", String.valueOf(requestData.toString().length()));
        http.getOutputStream().write(requestData.toString().getBytes(StandardCharsets.UTF_8));
        BufferedReader reader = new BufferedReader(new InputStreamReader(http.getInputStream()));
        String ln = reader.readLine();
        String dt = "";
        while (ln != null) {
            dt += ln;
            ln = reader.readLine();
        }
        JsonObject resObject = JsonParser.parseString(dt).getAsJsonObject();
        checkGameOwnership(resObject.get("token_type").getAsString(), resObject.get("access_token").getAsString());
    }

    private void authenticateWithXSTS(String XBLToken) throws IOException {
        URL url = new URL("https://xsts.auth.xboxlive.com/xsts/authorize");
        HttpURLConnection http = (HttpURLConnection) url.openConnection();
        http.setDoOutput(true);
        http.setRequestMethod("POST");
        JsonObject requestData = new JsonObject();
        JsonObject properties = new JsonObject();
        JsonArray userTokens = new JsonArray();
        properties.addProperty("SandboxId", "RETAIL");
        userTokens.add(XBLToken);
        properties.add("UserTokens", userTokens);
        requestData.add("Properties", properties);
        requestData.addProperty("RelyingParty", "rp://api.minecraftservices.com/");
        requestData.addProperty("TokenType", "JWT");
        http.setRequestProperty("User-Agent", "java/" + JavaVersion.getMajorJavaVersion());
        http.setRequestProperty("Content-Type", "application/json");
        http.setRequestProperty("Content-Length", String.valueOf(requestData.toString().length()));
        http.getOutputStream().write(requestData.toString().getBytes(StandardCharsets.UTF_8));
        BufferedReader reader = new BufferedReader(new InputStreamReader(http.getInputStream()));
        String ln = reader.readLine();
        String dt = "";
        while (ln != null) {
            dt += ln;
            ln = reader.readLine();
        }
        JsonObject resObject = JsonParser.parseString(dt).getAsJsonObject();
        authenticateWithMinecraftJava(resObject.get("DisplayClaims").getAsJsonObject().get("xui").getAsJsonArray().get(0).getAsJsonObject().get("uhs").getAsString(), resObject.get("Token").getAsString());
    }

    private void authenticateWithXBL(String accessToken) throws IOException {
        URL url = new URL("https://user.auth.xboxlive.com/user/authenticate");
        HttpURLConnection http = (HttpURLConnection) url.openConnection();
        http.setDoOutput(true);
        http.setRequestMethod("POST");
        JsonObject requestData = new JsonObject();
        JsonObject properties = new JsonObject();
        requestData.add("Properties", properties);
        properties.addProperty("AuthMethod", "RPS");
        properties.addProperty("SiteName", "user.auth.xboxlive.com");
        properties.addProperty("RpsTicket", "d=" + accessToken);
        requestData.addProperty("RelyingParty", "http://auth.xboxlive.com");
        requestData.addProperty("TokenType", "JWT");
        http.setRequestProperty("User-Agent", "java/" + JavaVersion.getMajorJavaVersion());
        http.setRequestProperty("Content-Type", "application/json");
        http.setRequestProperty("Content-Length", String.valueOf(requestData.toString().length()));
        http.getOutputStream().write(requestData.toString().getBytes(StandardCharsets.UTF_8));
        BufferedReader reader = new BufferedReader(new InputStreamReader(http.getInputStream()));
        String ln = reader.readLine();
        String dt = "";
        while (ln != null) {
            dt += ln;
            ln = reader.readLine();
        }
        JsonObject resObject = JsonParser.parseString(dt).getAsJsonObject();
        authenticateWithXSTS(resObject.get("Token").getAsString());
    }

    @Override
    public void run() {
        try {
            URL url = new URL("https://login.microsoftonline.com/consumers/oauth2/v2.0/token");
            HttpURLConnection http = (HttpURLConnection)  url.openConnection();
            http.setRequestMethod("POST");
            String data = "grant_type=urn:ietf:params:oauth:grant-type:device_code&client_id=" + clientId + "&device_code=" + deviceCode;
            http.setDoOutput(true);
            http.setRequestProperty("User-Agent", "java/" + JavaVersion.getMajorJavaVersion());
            http.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
            http.setRequestProperty("Content-Length", String.valueOf(data.length()));
            http.getOutputStream().write(data.getBytes(StandardCharsets.UTF_8));
            String dt = "";
            if (http.getResponseCode() >= 400) {
                BufferedReader errorStreamReader = new BufferedReader(new InputStreamReader(http.getErrorStream()));
                String ln = errorStreamReader.readLine();
                while (ln != null) {
                    dt += ln;
                    ln = errorStreamReader.readLine();
                }
                JsonObject errdata = JsonParser.parseString(dt).getAsJsonObject();
                String err = errdata.get("error").getAsString();
                if (err == "authorization_declined") authcodeTimer.cancel();
                else if (err == "expired_token") {
                    System.out.println("Code expired");
                    authcodeTimer.cancel();
                }
            }
            else {
                BufferedReader reader = new BufferedReader(new InputStreamReader(http.getInputStream()));
                String ln = reader.readLine();
                while (ln != null) {
                    dt += ln;
                    ln = reader.readLine();
                }
                authcodeTimer.cancel();
                System.out.println("[msa] Signed in with Microsoft");
                JsonObject resObject = JsonParser.parseString(dt).getAsJsonObject();
                refreshToken = resObject.get("refresh_token").getAsString();
                authenticateWithXBL(resObject.get("access_token").getAsString());
            }
            http.disconnect();
        }
        catch (IOException e) {
            e.printStackTrace();
        }
    }
}

And UserInfo.java:

public class UserInfo {
    public String id;
    public String name;
    public String accessToken;

    public UserInfo(String _id, String _name, String _accessToken) {
        id = _id;
        name = _name;
        accessToken = _accessToken;
    }
}
Defective4 commented 2 years ago

Thank you @gabe4278! I will try out your code as soon as I can and if possible I will add this into my client if you agree. Of course I will give you an appropriate credit :)

TheTwoBoom commented 2 years ago

The auth server in the code are legit, and the code looks OK

Defective4 commented 1 year ago

The Microsoft authentication is finally added in Release v1.11.0!