Return the Mojang login (testing required)

This commit is contained in:
artdeell 2022-04-03 11:22:34 +03:00
parent 81169bccaa
commit dca7ced92f
12 changed files with 313 additions and 14 deletions

View File

@ -44,6 +44,8 @@ import androidx.core.content.ContextCompat;
import net.kdt.pojavlaunch.authenticator.microsoft.MicrosoftAuthTask; import net.kdt.pojavlaunch.authenticator.microsoft.MicrosoftAuthTask;
import net.kdt.pojavlaunch.authenticator.microsoft.ui.MicrosoftLoginGUIActivity; import net.kdt.pojavlaunch.authenticator.microsoft.ui.MicrosoftLoginGUIActivity;
import net.kdt.pojavlaunch.authenticator.mojang.InvalidateTokenTask; import net.kdt.pojavlaunch.authenticator.mojang.InvalidateTokenTask;
import net.kdt.pojavlaunch.authenticator.mojang.LoginListener;
import net.kdt.pojavlaunch.authenticator.mojang.LoginTask;
import net.kdt.pojavlaunch.authenticator.mojang.RefreshListener; import net.kdt.pojavlaunch.authenticator.mojang.RefreshListener;
import net.kdt.pojavlaunch.customcontrols.CustomControls; import net.kdt.pojavlaunch.customcontrols.CustomControls;
import net.kdt.pojavlaunch.multirt.MultiRTConfigDialog; import net.kdt.pojavlaunch.multirt.MultiRTConfigDialog;
@ -70,9 +72,9 @@ public class PojavLoginActivity extends BaseActivity {
private final Object mLockStoragePerm = new Object(); private final Object mLockStoragePerm = new Object();
private final Object mLockSelectJRE = new Object(); private final Object mLockSelectJRE = new Object();
private EditText edit2; private EditText edit2, edit3;
private final int REQUEST_STORAGE_REQUEST_CODE = 1; private final int REQUEST_STORAGE_REQUEST_CODE = 1;
private CheckBox sRemember; private CheckBox sRemember, sLocal;
private TextView startupTextView; private TextView startupTextView;
private SharedPreferences firstLaunchPrefs; private SharedPreferences firstLaunchPrefs;
private MinecraftAccount mProfile = null; private MinecraftAccount mProfile = null;
@ -237,9 +239,14 @@ public class PojavLoginActivity extends BaseActivity {
public void onNothingSelected(AdapterView<?> adapter) {} public void onNothingSelected(AdapterView<?> adapter) {}
}); });
edit2 = (EditText) findViewById(R.id.login_edit_email); edit2 = findViewById(R.id.login_edit_email);
edit3 = findViewById(R.id.login_edit_password);
sRemember = findViewById(R.id.login_switch_remember); sRemember = findViewById(R.id.login_switch_remember);
sLocal = findViewById(R.id.login_switch_local);
sLocal.setOnCheckedChangeListener((p1, p2) -> {
// May delete later
edit3.setEnabled(!p2);
});
isSkipInit = true; isSkipInit = true;
} }
@ -551,7 +558,7 @@ public class PojavLoginActivity extends BaseActivity {
}; };
MinecraftAccount acc = MinecraftAccount.load(selectedAccName); MinecraftAccount acc = MinecraftAccount.load(selectedAccName);
if (acc.accessToken.length() >= 5){ if (acc.isMicrosoft){
new MicrosoftAuthTask(PojavLoginActivity.this, authListener) new MicrosoftAuthTask(PojavLoginActivity.this, authListener)
.execute("true", acc.msaRefreshToken); .execute("true", acc.msaRefreshToken);
} else { } else {
@ -617,8 +624,42 @@ public class PojavLoginActivity extends BaseActivity {
public void loginMC(final View v) public void loginMC(final View v)
{ {
mProfile = loginLocal(); if (sLocal.isChecked()) {
playProfile(false); mProfile = loginLocal();
playProfile(false);
} else {
ProgressBar prb = findViewById(R.id.launcherAccProgress);
new LoginTask().setLoginListener(new LoginListener(){
@Override
public void onBeforeLogin() {
v.setEnabled(false);
prb.setVisibility(View.VISIBLE);
}
@Override
public void onLoginDone(String[] result) {
if(result[0].equals("ERROR")){
Tools.dialogOnUiThread(PojavLoginActivity.this,
getResources().getString(R.string.global_error), strArrToString(result));
} else{
MinecraftAccount builder = new MinecraftAccount();
builder.accessToken = result[1];
builder.clientToken = result[2];
builder.profileId = result[3];
builder.username = result[4];
builder.updateSkinFace();
mProfile = builder;
}
runOnUiThread(() -> {
v.setEnabled(true);
prb.setVisibility(View.GONE);
playProfile(false);
});
}
}).execute(edit2.getText().toString(), edit3.getText().toString());
}
} }
private void playProfile(boolean notOnLogin) { private void playProfile(boolean notOnLogin) {

View File

@ -8,6 +8,9 @@ import android.util.Log;
import com.google.gson.JsonSyntaxException; import com.google.gson.JsonSyntaxException;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import net.kdt.pojavlaunch.authenticator.mojang.RefreshListener;
import net.kdt.pojavlaunch.authenticator.mojang.RefreshTokenTask;
import net.kdt.pojavlaunch.value.MinecraftAccount; import net.kdt.pojavlaunch.value.MinecraftAccount;
public class PojavProfile { public class PojavProfile {
@ -101,4 +104,7 @@ public class PojavProfile {
Intent intent = new Intent(ctx, PojavLauncherActivity.class); //MCLauncherActivity.class); Intent intent = new Intent(ctx, PojavLauncherActivity.class); //MCLauncherActivity.class);
ctx.startActivity(intent); ctx.startActivity(intent);
} }
public static void updateTokens(final Activity ctx, final String name, RefreshListener listen) throws Exception {
new RefreshTokenTask(ctx, listen).execute(name);
}
} }

View File

@ -202,7 +202,7 @@ public final class Tools {
versionName = versionInfo.inheritsFrom; versionName = versionInfo.inheritsFrom;
} }
String userType = "msa"; String userType = "mojang";
File gameDir = new File(strGameDir); File gameDir = new File(strGameDir);
gameDir.mkdirs(); gameDir.mkdirs();

View File

@ -0,0 +1,24 @@
package net.kdt.pojavlaunch.authenticator.mojang.yggdrasil;
import java.util.UUID;
public class AuthenticateRequest {
public AgentInfo agent = new AgentInfo();
public UUID clientToken;
public String password;
public String username;
public static class AgentInfo {
public String name;
public int version;
}
public AuthenticateRequest(String username, String password, UUID clientToken, String clientName, int clientVersion) {
this.username = username;
this.password = password;
this.clientToken = clientToken;
this.agent.name = clientName;
this.agent.version = clientVersion;
}
}

View File

@ -0,0 +1,7 @@
package net.kdt.pojavlaunch.authenticator.mojang;
public interface LoginListener
{
public void onBeforeLogin();
public void onLoginDone(String[] result);
}

View File

@ -0,0 +1,71 @@
package net.kdt.pojavlaunch.authenticator.mojang;
import android.os.*;
import net.kdt.pojavlaunch.authenticator.mojang.yggdrasil.*;
import java.io.*;
import java.util.*;
import net.kdt.pojavlaunch.*;
public class LoginTask extends AsyncTask<String, Void, Void>
{
private YggdrasilAuthenticator authenticator = new YggdrasilAuthenticator();
//private String TAG = "MojangAuth-login";
private LoginListener listener;
public LoginTask setLoginListener(LoginListener listener) {
this.listener = listener;
return this;
}
private UUID getRandomUUID() {
return UUID.randomUUID();
}
@Override
protected void onPreExecute() {
listener.onBeforeLogin();
super.onPreExecute();
}
@Override
protected Void doInBackground(String[] args) {
ArrayList<String> str = new ArrayList<String>();
str.add("ERROR");
try{
try{
AuthenticateResponse response = authenticator.authenticate(args[0], args[1], getRandomUUID());
if (response.selectedProfile == null) {
str.add("Can't login a demo account!\n");
} else {
if (new File(Tools.DIR_ACCOUNT_NEW + "/" + response.selectedProfile.name + ".json").exists()) {
str.add("This account already exist!\n");
} else {
str.add(response.accessToken); // Access token
str.add(response.clientToken.toString()); // Client token
str.add(response.selectedProfile.id); // Profile ID
str.add(response.selectedProfile.name); // Username
str.set(0, "NORMAL");
}
}
}
//MainActivity.updateStatus(804);
catch(Throwable e){
str.add(e.getMessage());
}
}
catch(Exception e){
str.add(e.getMessage());
}
listener.onLoginDone(str.toArray(new String[0]));
return null;
}
@Override
protected void onPostExecute(Void result) {
// listener.onLoginDone(result);
super.onPostExecute(result);
}
}

View File

@ -0,0 +1,78 @@
package net.kdt.pojavlaunch.authenticator.mojang;
import android.content.*;
import android.os.*;
import com.google.gson.*;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.*;
import net.kdt.pojavlaunch.*;
import net.kdt.pojavlaunch.authenticator.mojang.yggdrasil.*;
import android.app.*;
import net.kdt.pojavlaunch.value.*;
public class RefreshTokenTask extends AsyncTask<String, Void, Throwable> {
private YggdrasilAuthenticator authenticator = new YggdrasilAuthenticator();
//private Gson gson = new Gson();
private RefreshListener listener;
private MinecraftAccount profilePath;
private final WeakReference<Context> ctx;
private ProgressDialog build;
public RefreshTokenTask(Context ctx, RefreshListener listener) {
this.ctx = new WeakReference<>(ctx);
this.listener = listener;
}
@Override
public void onPreExecute() {
build = new ProgressDialog(ctx.get());
build.setMessage(ctx.get().getString(R.string.global_waiting));
build.setProgressStyle(ProgressDialog.STYLE_SPINNER);
build.setCancelable(false);
build.show();
}
@Override
public Throwable doInBackground(String... args) {
try {
this.profilePath = MinecraftAccount.load(args[0]);
int responseCode = 400;
try {
responseCode = this.authenticator.validate(profilePath.accessToken).statusCode;
}catch(RuntimeException e) {}
if (responseCode == 403) {
RefreshResponse response = this.authenticator.refresh(profilePath.accessToken, UUID.fromString(profilePath.clientToken));
if (response == null) {
// Refresh when offline?
return null;
} else if (response.selectedProfile == null) {
throw new IllegalArgumentException("Can't refresh a demo account!");
}
profilePath.clientToken = response.clientToken.toString();
profilePath.accessToken = response.accessToken;
profilePath.username = response.selectedProfile.name;
profilePath.profileId = response.selectedProfile.id;
}
profilePath.updateSkinFace();
profilePath.save();
return null;
} catch (Throwable e) {
return e;
}
}
@Override
public void onPostExecute(Throwable result) {
build.dismiss();
if (result == null) {
listener.onSuccess(profilePath);
} else {
listener.onFailed(result);
}
}
}

View File

@ -0,0 +1,10 @@
package net.kdt.pojavlaunch.authenticator.mojang.yggdrasil;
import java.util.UUID;
public class AuthenticateResponse {
public String accessToken;
public Profile[] availableProfiles;
public UUID clientToken;
public Profile selectedProfile;
}

View File

@ -0,0 +1,9 @@
package net.kdt.pojavlaunch.authenticator.mojang.yggdrasil;
import java.util.UUID;
public class RefreshResponse {
public String accessToken;
public UUID clientToken;
public Profile selectedProfile;
}

View File

@ -89,6 +89,31 @@ public class YggdrasilAuthenticator {
throw th; throw th;
} }
} }
public AuthenticateResponse authenticate(String username, String password, UUID clientId) throws IOException, Throwable {
NetworkResponse obj = makeRequest("authenticate", new net.kdt.pojavlaunch.authenticator.mojang.yggdrasil.AuthenticateRequest(username, password, clientId, this.clientName, this.clientVersion), AuthenticateResponse.class);
/*
if (obj.statusCode != 200) {
throw new RuntimeException("Invalid username or password, status code: " + obj.statusCode);
}
*/
obj.throwExceptionIfNeed();
return (AuthenticateResponse) obj.response;
}
public RefreshResponse refresh(String authToken, UUID clientId) throws IOException, Throwable {
NetworkResponse obj = makeRequest("refresh", new RefreshRequest(authToken, clientId), RefreshResponse.class);
if (obj == null) {
return null;
} else {
obj.throwExceptionIfNeed(); // "Invalid username or password, status code: " + obj.statusCode);
return (RefreshResponse) obj.response;
}
}
public NetworkResponse validate(String authToken) throws Throwable {
return makeRequest("validate", new RefreshRequest(authToken, null), null);
}
public NetworkResponse invalidate(String authToken, UUID clientId) throws Throwable { public NetworkResponse invalidate(String authToken, UUID clientId) throws Throwable {
return makeRequest("invalidate", new RefreshRequest(authToken, clientId), null); return makeRequest("invalidate", new RefreshRequest(authToken, clientId), null);

View File

@ -16,12 +16,12 @@
<ImageView <ImageView
android:id="@+id/imageView2"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:elevation="-10dp" android:elevation="-10dp"
android:src="@drawable/ic_setting_sign_in_background"
android:scaleType="centerCrop" android:scaleType="centerCrop"
/> android:src="@drawable/ic_setting_sign_in_background" />
<Spinner <Spinner
android:id="@+id/login_spinner_language" android:id="@+id/login_spinner_language"
@ -88,8 +88,8 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="start" android:layout_gravity="start"
android:text="@string/login_online_check_keeplogin" android:text="@string/login_online_check_keeplogin"
app:layout_constraintStart_toStartOf="@+id/login_edit_email" app:layout_constraintStart_toStartOf="@+id/login_edit_password"
app:layout_constraintTop_toBottomOf="@+id/login_edit_email" /> app:layout_constraintTop_toBottomOf="@+id/login_switch_local" />
<com.kdt.mcgui.MineButton <com.kdt.mcgui.MineButton
android:id="@+id/mineButton" android:id="@+id/mineButton"
@ -147,4 +147,31 @@
app:layout_constraintStart_toStartOf="@+id/mineButton" app:layout_constraintStart_toStartOf="@+id/mineButton"
app:layout_constraintTop_toBottomOf="@+id/mineButton" /> app:layout_constraintTop_toBottomOf="@+id/mineButton" />
<TextView
android:id="@+id/textView5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/login_online_password_hint"
app:layout_constraintStart_toStartOf="@+id/login_edit_email"
app:layout_constraintTop_toBottomOf="@+id/login_edit_email" />
<com.kdt.mcgui.MineEditText
android:id="@+id/login_edit_password"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="0dp"
android:imeOptions="flagNoExtractUi"
android:inputType="textPassword"
app:layout_constraintEnd_toEndOf="@+id/login_edit_email"
app:layout_constraintStart_toStartOf="@+id/login_edit_email"
app:layout_constraintTop_toBottomOf="@+id/textView5" />
<CheckBox
android:id="@+id/login_switch_local"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/mcl_account_local"
app:layout_constraintStart_toStartOf="@+id/login_edit_password"
app:layout_constraintTop_toBottomOf="@+id/login_edit_password" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -10,7 +10,8 @@
<!-- Languages list part --> <!-- Languages list part -->
<!-- Login strings --> <!-- Login strings -->
<string name="login_online_username_hint">Local username</string> <string name="login_online_username_hint">Email or username</string>
<string name="login_online_password_hint">Password</string>
<string name="login_online_check_keeplogin">Keep me logged in</string> <string name="login_online_check_keeplogin">Keep me logged in</string>
<string name="login_online_login_label">Login</string> <string name="login_online_login_label">Login</string>