Closed SeanWalk closed 1 year 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!
Mojang has APIs for getting Auth Tokens (Way to login to server) wiki.vg/Mojang_API
I know @TwoB00m, but I have no way to test if it works, because I don't own a Minecraft account
You can try to implement TheAltening Alt token, the token are free and valid for 15min @Defective4 TheAltening has also got a API
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.
Mojang authentication is now supported in release v1.7.0, however it needs to be tested.
But many have Microsoft Minecraft Accounts...... It Mojang Login doesn't help
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
Here's an tutorial for Microsoft authentication
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;
}
}
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 :)
The auth server in the code are legit, and the code looks OK
The Microsoft authentication is finally added in Release v1.11.0!
Is this offline server or cracked server only? Can't find any authentication place.