[MSA] Migrate from the old auth to my rewritten auth

This commit is contained in:
artdeell 2020-12-15 20:16:45 +03:00
parent d2e87bd4e3
commit 3aa5621c97
5 changed files with 359 additions and 34 deletions

View File

@ -1,13 +0,0 @@
<component name="libraryTable">
<library name="Gradle: com.wu-man:android-bsf-api:3.1.3@jar">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.wu-man/android-bsf-api/3.1.3/f89b7955c5b01302e2227d75ee9ee24e5dc611fb/android-bsf-api-3.1.3.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.wu-man/android-bsf-api/3.1.3/627f8295fa1ae98e7d5b89dc1784fe4cb83a9093/android-bsf-api-3.1.3-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.wu-man/android-bsf-api/3.1.3/7b4fe6354c0df85bd90d0e902a92a32b8a4f5ceb/android-bsf-api-3.1.3-sources.jar!/" />
</SOURCES>
</library>
</component>

View File

@ -1,13 +0,0 @@
<component name="libraryTable">
<library name="Gradle: javax.annotation:javax.annotation-api:1.3.2@jar">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/javax.annotation/javax.annotation-api/1.3.2/934c04d3cfef185a8008e7bf34331b79730a9d43/javax.annotation-api-1.3.2.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/javax.annotation/javax.annotation-api/1.3.2/cbbe0a4445e038f75c4c8aea3179bd17108ec57e/javax.annotation-api-1.3.2-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/javax.annotation/javax.annotation-api/1.3.2/65dfd2c47380bf72ec62a5b8c4ceb78a4eda1a53/javax.annotation-api-1.3.2-sources.jar!/" />
</SOURCES>
</library>
</component>

View File

@ -311,6 +311,7 @@ public class PojavLoginActivity extends BaseActivity
super.onNewIntent(intent); super.onNewIntent(intent);
Uri data = intent.getData(); Uri data = intent.getData();
Log.i("MicroAuth",data.toString());
if (data != null && data.getScheme().equals("ms-xal-00000000402b5328") && data.getHost().equals("auth")) { if (data != null && data.getScheme().equals("ms-xal-00000000402b5328") && data.getHost().equals("auth")) {
String error = data.getQueryParameter("error"); String error = data.getQueryParameter("error");
String error_description = data.getQueryParameter("error_description"); String error_description = data.getQueryParameter("error_description");

View File

@ -50,8 +50,9 @@ public class MicrosoftAuthTask extends AsyncTask<String, Void, Object> {
@Override @Override
public Object doInBackground(String... args) { public Object doInBackground(String... args) {
try { try {
String authCode = args[0];
String authCode = args[0];
/*
publishProgress(); publishProgress();
String msaAccessToken = acquireAccessToken(authCode); String msaAccessToken = acquireAccessToken(authCode);
@ -65,16 +66,22 @@ public class MicrosoftAuthTask extends AsyncTask<String, Void, Object> {
String mcAccessToken = acquireMinecraftToken(xstsData[0], xstsData[1]); String mcAccessToken = acquireMinecraftToken(xstsData[0], xstsData[1]);
publishProgress(); publishProgress();
*/
Msa msa = new Msa(authCode);
// TODO migrate account format to json // TODO migrate account format to json
MinecraftAccount acc = checkMcProfile(mcAccessToken); //MinecraftAccount acc = new MinecraftAccount();
MCProfile.Builder profilePath = new MCProfile.Builder(); MCProfile.Builder profilePath = new MCProfile.Builder();
if(msa.doesOwnGame) {
profilePath.setClientID("0" /* FIXME */); profilePath.setClientID("0" /* FIXME */);
profilePath.setAccessToken(acc.accessToken); profilePath.setAccessToken(msa.mcToken);
profilePath.setUsername(acc.username); profilePath.setUsername(msa.mcName);
profilePath.setProfileID(acc.profileId); profilePath.setProfileID(msa.mcUuid);
profilePath.setIsMojangAccount(false); profilePath.setIsMojangAccount(false);
}
MCProfile.build(profilePath); MCProfile.build(profilePath);
@ -126,6 +133,7 @@ public class MicrosoftAuthTask extends AsyncTask<String, Void, Object> {
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
*/ */
/*
private final class XSTSXUI { private final class XSTSXUI {
private String uhs; private String uhs;
} }
@ -297,5 +305,7 @@ public class MicrosoftAuthTask extends AsyncTask<String, Void, Object> {
} }
return HttpRequest.BodyPublishers.ofString(builder.toString()); return HttpRequest.BodyPublishers.ofString(builder.toString());
} }
*/
} }

View File

@ -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<Object, Object> 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<Object, Object> data = new HashMap();
Map<Object, Object> 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<Object, Object> data = new HashMap();
Map<Object, Object> 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<Object, Object> 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<Object, Object> 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<Object, Object> data) {
return new JSONObject(data).toString();
}
public static String ofFormData(Map<Object, Object> data) {
StringBuilder builder = new StringBuilder();
for (Map.Entry<Object, Object> 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();
}
}