diff --git a/.idea/libraries/Gradle__com_wu_man_android_bsf_api_3_1_3_jar.xml b/.idea/libraries/Gradle__com_wu_man_android_bsf_api_3_1_3_jar.xml deleted file mode 100644 index f6c3e3206..000000000 --- a/.idea/libraries/Gradle__com_wu_man_android_bsf_api_3_1_3_jar.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Gradle__javax_annotation_javax_annotation_api_1_3_2_jar.xml b/.idea/libraries/Gradle__javax_annotation_javax_annotation_api_1_3_2_jar.xml deleted file mode 100644 index 7c5147fcf..000000000 --- a/.idea/libraries/Gradle__javax_annotation_javax_annotation_api_1_3_2_jar.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/java/net/kdt/pojavlaunch/PojavLoginActivity.java b/app/src/main/java/net/kdt/pojavlaunch/PojavLoginActivity.java index 738ed65f4..598fbafbb 100644 --- a/app/src/main/java/net/kdt/pojavlaunch/PojavLoginActivity.java +++ b/app/src/main/java/net/kdt/pojavlaunch/PojavLoginActivity.java @@ -311,6 +311,7 @@ public class PojavLoginActivity extends BaseActivity super.onNewIntent(intent); Uri data = intent.getData(); + Log.i("MicroAuth",data.toString()); if (data != null && data.getScheme().equals("ms-xal-00000000402b5328") && data.getHost().equals("auth")) { String error = data.getQueryParameter("error"); String error_description = data.getQueryParameter("error_description"); diff --git a/app/src/main/java/net/kdt/pojavlaunch/authenticator/microsoft/MicrosoftAuthTask.java b/app/src/main/java/net/kdt/pojavlaunch/authenticator/microsoft/MicrosoftAuthTask.java index 45db79ce8..9e750827e 100644 --- a/app/src/main/java/net/kdt/pojavlaunch/authenticator/microsoft/MicrosoftAuthTask.java +++ b/app/src/main/java/net/kdt/pojavlaunch/authenticator/microsoft/MicrosoftAuthTask.java @@ -50,8 +50,9 @@ public class MicrosoftAuthTask extends AsyncTask { @Override public Object doInBackground(String... args) { try { + String authCode = args[0]; - + /* publishProgress(); String msaAccessToken = acquireAccessToken(authCode); @@ -65,16 +66,22 @@ public class MicrosoftAuthTask extends AsyncTask { String mcAccessToken = acquireMinecraftToken(xstsData[0], xstsData[1]); publishProgress(); + + */ + Msa msa = new Msa(authCode); + // TODO migrate account format to json - MinecraftAccount acc = checkMcProfile(mcAccessToken); + //MinecraftAccount acc = new MinecraftAccount(); MCProfile.Builder profilePath = new MCProfile.Builder(); - - profilePath.setClientID("0" /* FIXME */); - profilePath.setAccessToken(acc.accessToken); - profilePath.setUsername(acc.username); - profilePath.setProfileID(acc.profileId); - profilePath.setIsMojangAccount(false); + if(msa.doesOwnGame) { + profilePath.setClientID("0" /* FIXME */); + profilePath.setAccessToken(msa.mcToken); + profilePath.setUsername(msa.mcName); + profilePath.setProfileID(msa.mcUuid); + profilePath.setIsMojangAccount(false); + } + MCProfile.build(profilePath); @@ -126,6 +133,7 @@ public class MicrosoftAuthTask extends AsyncTask { OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + /* private final class XSTSXUI { private String uhs; } @@ -297,5 +305,7 @@ public class MicrosoftAuthTask extends AsyncTask { } return HttpRequest.BodyPublishers.ofString(builder.toString()); } + + */ } diff --git a/app/src/main/java/net/kdt/pojavlaunch/authenticator/microsoft/Msa.java b/app/src/main/java/net/kdt/pojavlaunch/authenticator/microsoft/Msa.java new file mode 100644 index 000000000..f00c02c59 --- /dev/null +++ b/app/src/main/java/net/kdt/pojavlaunch/authenticator/microsoft/Msa.java @@ -0,0 +1,340 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package net.kdt.pojavlaunch.authenticator.microsoft; + +import android.util.Log; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.net.HttpURLConnection; + + +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.ResourceBundle; +import java.util.UUID; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + + + +public class Msa { + + private static final String loginUrl = "https://login.live.com/oauth20_authorize.srf" + + "?client_id=00000000402b5328" + + "&response_type=code" + + "&scope=service%3A%3Auser.auth.xboxlive.com%3A%3AMBI_SSL" + + "&redirect_uri=https%3A%2F%2Flogin.live.com%2Foauth20_desktop.srf"; + + private static final String redirectUrlSuffix = "https://login.live.com/oauth20_desktop.srf?code="; + + private static final String authTokenUrl = "https://login.live.com/oauth20_token.srf"; + + private static final String xblAuthUrl = "https://user.auth.xboxlive.com/user/authenticate"; + + private static final String xstsAuthUrl = "https://xsts.auth.xboxlive.com/xsts/authorize"; + + private static final String mcLoginUrl = "https://api.minecraftservices.com/authentication/login_with_xbox"; + + private static final String mcStoreUrl = "https://api.minecraftservices.com/entitlements/mcstore"; + + private static final String mcProfileUrl = "https://api.minecraftservices.com/minecraft/profile"; + + public String mcName; + public String mcToken; + public String mcUuid; + public boolean doesOwnGame; + public Msa(String authCode) throws IOException, JSONException { + acquireAccessToken(authCode); + } + public void acquireAccessToken(String authcode) throws IOException, JSONException { + + URL url = new URL(authTokenUrl); + Log.i("MicroAuth","authCode= "+authcode); + Map data = new HashMap();/*Map.of( + "client_id", "00000000402b5328", + "code", authcode, + "grant_type", "authorization_code", + "redirect_uri", "https://login.live.com/oauth20_desktop.srf", + "scope", "service::user.auth.xboxlive.com::MBI_SSL" + );*/ + data.put("client_id", "00000000402b5328"); + data.put("code", authcode); + data.put("grant_type", "authorization_code"); + data.put("redirect_uri", "https://login.live.com/oauth20_desktop.srf"); + data.put("scope", "service::user.auth.xboxlive.com::MBI_SSL"); + + //да пошла yf[eq1 она ваша джава 11 + String req = ofFormData(data); + HttpURLConnection conn = (HttpURLConnection)url.openConnection(); + conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); + conn.setRequestProperty("charset", "utf-8"); + conn.setRequestProperty("Content-Length", Integer.toString(req.getBytes("UTF-8").length)); + conn.setRequestMethod("POST"); + conn.setUseCaches(false); + conn.setDoInput(true); + conn.setDoOutput(true); + conn.connect(); + try(OutputStream wr = conn.getOutputStream()) { + wr.write(req.getBytes("UTF-8")); + } + if(conn.getResponseCode() >= 200 && conn.getResponseCode() < 300) { + String s = ""; int len = 0; byte[] buf = new byte[256]; + InputStream is = conn.getInputStream(); + while((len = is.read(buf)) != -1) { //читаем строчку пока не получим всё + s += new String(buf,0,len); + } + JSONObject jo = new JSONObject(s); + Log.i("MicroAuth","Acess Token = "+jo.getString("access_token")); + acquireXBLToken(jo.getString("access_token")); + }else{ + Log.i("MicroAuth","Error code: " + conn.getResponseCode() + ": "+conn.getResponseMessage()); + } + + } + + private void acquireXBLToken(String accessToken) throws IOException, JSONException { + + URL url = new URL(xblAuthUrl); + + Map data = new HashMap(); + Map properties = new HashMap(); + properties.put("AuthMethod", "RPS"); + properties.put("SiteName", "user.auth.xboxlive.com"); + properties.put("RpsTicket", accessToken); + data.put("Properties",properties); + data.put("RelyingParty", "http://auth.xboxlive.com"); + data.put("TokenType", "JWT"); + /*Map.of( + + "Properties", Map.of( + "AuthMethod", "RPS", + "SiteName", "user.auth.xboxlive.com", + "RpsTicket", accessToken + ), + "RelyingParty", "http://auth.xboxlive.com", + "TokenType", "JWT" + );*/ + String req = ofJSONData(data); + HttpURLConnection conn = (HttpURLConnection)url.openConnection(); + conn.setRequestProperty("Content-Type", "application/json"); + conn.setRequestProperty("Accept", "application/json"); + conn.setRequestProperty("charset", "utf-8"); + conn.setRequestProperty("Content-Length", Integer.toString(req.getBytes("UTF-8").length)); + conn.setRequestMethod("POST"); + conn.setUseCaches(false); + conn.setDoInput(true); + conn.setDoOutput(true); + conn.connect(); + try(OutputStream wr = conn.getOutputStream()) { + wr.write(req.getBytes("UTF-8")); + } + if(conn.getResponseCode() >= 200 && conn.getResponseCode() < 300) { + String s = ""; int len = 0; byte[] buf = new byte[256]; + InputStream is = conn.getInputStream(); + while((len = is.read(buf)) != -1) { //читаем строчку пока не получим всё + s += new String(buf,0,len); + } + JSONObject jo = new JSONObject(s); + Log.i("MicroAuth","Xbl Token = "+jo.getString("Token")); + acquireXsts(jo.getString("Token")); + }else{ + Log.i("MicroAuth","Error code: " + conn.getResponseCode() + ": "+conn.getResponseMessage()); + } + } + + private void acquireXsts(String xblToken) throws IOException, JSONException { + URL url = new URL(xstsAuthUrl); + Map data = new HashMap(); + Map properties = new HashMap(); + properties.put("SandboxId", "RETAIL"); + properties.put("UserTokens",Collections.singleton(xblToken)); + data.put("Properties",properties); + data.put("RelyingParty", "rp://api.minecraftservices.com/"); + data.put("TokenType", "JWT"); + /*Map data = Map.of( + "Properties", Map.of( + "SandboxId", "RETAIL", + "UserTokens", List.of(xblToken) + ), + "RelyingParty", "rp://api.minecraftservices.com/", + "TokenType", "JWT" + ); + */ + String req = ofJSONData(data); + HttpURLConnection conn = (HttpURLConnection)url.openConnection(); + conn.setRequestProperty("Content-Type", "application/json"); + conn.setRequestProperty("Accept", "application/json"); + conn.setRequestProperty("charset", "utf-8"); + conn.setRequestProperty("Content-Length", Integer.toString(req.getBytes("UTF-8").length)); + conn.setRequestMethod("POST"); + conn.setUseCaches(false); + conn.setDoInput(true); + conn.setDoOutput(true); + conn.connect(); + try(OutputStream wr = conn.getOutputStream()) { + wr.write(req.getBytes("UTF-8")); + } + + if(conn.getResponseCode() >= 200 && conn.getResponseCode() < 300) { + String s = ""; int len = 0; byte[] buf = new byte[256]; + InputStream is = conn.getInputStream(); + while((len = is.read(buf)) != -1) { //читаем строчку пока не получим всё + s += new String(buf,0,len); + } + JSONObject jo = new JSONObject(s); + String uhs = jo.getJSONObject("DisplayClaims").getJSONArray("xui").getJSONObject(0).getString("uhs"); + Log.i("MicroAuth","Xbl Xsts = "+jo.getString("Token")+"; Uhs = " + uhs); + acquireMinecraftToken(uhs,jo.getString("Token")); + }else{ + Log.i("MicroAuth","Error code: " + conn.getResponseCode() + ": "+conn.getResponseMessage()); + } + } + + private void acquireMinecraftToken(String xblUhs, String xblXsts) throws IOException, JSONException { + URL url = new URL(mcLoginUrl); + + Map data = new HashMap(); + data.put( "identityToken", "XBL3.0 x=" + xblUhs + ";" + xblXsts); + + String req = ofJSONData(data); + HttpURLConnection conn = (HttpURLConnection)url.openConnection(); + conn.setRequestProperty("Content-Type", "application/json"); + conn.setRequestProperty("Accept", "application/json"); + conn.setRequestProperty("charset", "utf-8"); + conn.setRequestProperty("Content-Length", Integer.toString(req.getBytes("UTF-8").length)); + conn.setRequestMethod("POST"); + conn.setUseCaches(false); + conn.setDoInput(true); + conn.setDoOutput(true); + conn.connect(); + try(OutputStream wr = conn.getOutputStream()) { + wr.write(req.getBytes("UTF-8")); + } + + if(conn.getResponseCode() >= 200 && conn.getResponseCode() < 300) { + String s = ""; int len = 0; byte[] buf = new byte[256]; + InputStream is = conn.getInputStream(); + while((len = is.read(buf)) != -1) { //читаем строчку пока не получим всё + s += new String(buf,0,len); + } + JSONObject jo = new JSONObject(s); + Log.i("MicroAuth","MC token: "+jo.getString("access_token")); + mcToken = jo.getString("access_token"); + checkMcProfile(jo.getString("access_token")); + checkMcStore(jo.getString("access_token")); + + }else{ + Log.i("MicroAuth","Error code: " + conn.getResponseCode() + ": "+conn.getResponseMessage()); + } + } + private void checkMcStore(String mcAccessToken) throws IOException, JSONException { + URL url = new URL(mcStoreUrl); + HttpURLConnection conn = (HttpURLConnection)url.openConnection(); + conn.setRequestProperty("Authorization", "Bearer " + mcAccessToken); + conn.setRequestMethod("GET"); + conn.setUseCaches(false); + conn.connect(); + if(conn.getResponseCode() >= 200 && conn.getResponseCode() < 300) { + String s = ""; int len = 0; byte[] buf = new byte[256]; + InputStream is = conn.getInputStream(); + while((len = is.read(buf)) != -1) { //читаем строчку пока не получим всё + s += new String(buf,0,len); + } + JSONObject jo = new JSONObject(s); + JSONArray ja = jo.getJSONArray("items"); + Log.i("MicroAuth","Store Len = " + ja.length()); + for(int i = 0; i < ja.length(); i++) { + String prod = ja.getJSONObject(i).getString("name"); + Log.i("MicroAuth","Product " + i +": " +prod); + } + }else{ + Log.i("MicroAuth","Error code: " + conn.getResponseCode() + ": "+conn.getResponseMessage()); + } + /* + HttpRequest request = HttpRequest.newBuilder(uri) + .header("Authorization", "Bearer " + mcAccessToken) + .GET().build(); + + HttpClient.newBuilder().build().sendAsync(request, HttpResponse.BodyHandlers.ofString()).thenAccept(resp -> { + if (resp.statusCode() >= 200 && resp.statusCode() < 300) { + String body = resp.body(); + Log.i("MicroAuth","store: " + body); + } + }); + */ + } + + private void checkMcProfile(String mcAccessToken) throws IOException, JSONException { + URL url = new URL(mcProfileUrl); + + HttpURLConnection conn = (HttpURLConnection)url.openConnection(); + conn.setRequestProperty("Authorization", "Bearer " + mcAccessToken); + conn.setUseCaches(false); + conn.connect(); + + if(conn.getResponseCode() >= 200 && conn.getResponseCode() < 300) { + String s = ""; int len = 0; byte[] buf = new byte[256]; + InputStream is = conn.getInputStream(); + while((len = is.read(buf)) != -1) { //читаем строчку пока не получим всё + s += new String(buf,0,len); + } + Log.i("MicroAuth","profile:" + s); + JSONObject jsonObject = new JSONObject(s); + String name = (String) jsonObject.get("name"); + String uuid = (String) jsonObject.get("id"); + String uuidDashes = uuid .replaceFirst( + "(\\p{XDigit}{8})(\\p{XDigit}{4})(\\p{XDigit}{4})(\\p{XDigit}{4})(\\p{XDigit}+)", "$1-$2-$3-$4-$5" + ); + doesOwnGame = true; + Log.i("MicroAuth","UserName = " + name); + Log.i("MicroAuth","Uuid Minecraft = " + uuidDashes); + mcName=name; + mcUuid=uuidDashes; + }else{ + Log.i("MicroAuth","Error code: " + conn.getResponseCode() + ": "+conn.getResponseMessage()); + Log.i("MicroAuth","It seems that this Microshit Account does not own the game."); + doesOwnGame = false; + } + + + } + + public static String ofJSONData(Map data) { + return new JSONObject(data).toString(); + + } + + public static String ofFormData(Map data) { + StringBuilder builder = new StringBuilder(); + for (Map.Entry entry : data.entrySet()) { + if (builder.length() > 0) { + builder.append("&"); + } + try { + builder.append(URLEncoder.encode(entry.getKey().toString(), "UTF-8")); + builder.append("="); + builder.append(URLEncoder.encode(entry.getValue().toString(), "UTF-8")); + } catch (UnsupportedEncodingException e) { + //Should not happen + } + } + return builder.toString(); + } + +} +