diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/DialogController.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/DialogController.java index b2d52423b..7ba3c5b54 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/DialogController.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/DialogController.java @@ -20,8 +20,9 @@ package org.jackhuang.hmcl.ui; import org.jackhuang.hmcl.auth.Account; import org.jackhuang.hmcl.auth.AuthInfo; import org.jackhuang.hmcl.auth.AuthenticationException; +import org.jackhuang.hmcl.auth.microsoft.MicrosoftAccount; import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount; -import org.jackhuang.hmcl.ui.account.AccountLoginPane; +import org.jackhuang.hmcl.ui.account.AccountLoginWithPasswordDialog; import java.util.Optional; import java.util.concurrent.CancellationException; @@ -39,7 +40,7 @@ public final class DialogController { CountDownLatch latch = new CountDownLatch(1); AtomicReference res = new AtomicReference<>(null); runInFX(() -> { - AccountLoginPane pane = new AccountLoginPane(account, it -> { + AccountLoginWithPasswordDialog pane = new AccountLoginWithPasswordDialog(account, it -> { res.set(it); latch.countDown(); }, latch::countDown); @@ -47,6 +48,8 @@ public final class DialogController { }); latch.await(); return Optional.ofNullable(res.get()).orElseThrow(CancellationException::new); + } else if (account instanceof MicrosoftAccount) { + } return account.logIn(); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountLoginPane.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountLoginWithPasswordDialog.java similarity index 97% rename from HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountLoginPane.java rename to HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountLoginWithPasswordDialog.java index 4d9279f19..1783eb536 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountLoginPane.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountLoginWithPasswordDialog.java @@ -48,7 +48,7 @@ import static org.jackhuang.hmcl.ui.FXUtils.onEscPressed; import static org.jackhuang.hmcl.util.Logging.LOG; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; -public class AccountLoginPane extends StackPane { +public class AccountLoginWithPasswordDialog extends StackPane { private final Account oldAccount; private final Consumer success; private final Runnable failed; @@ -57,7 +57,7 @@ public class AccountLoginPane extends StackPane { private final Label lblCreationWarning = new Label(); private final JFXProgressBar progressBar; - public AccountLoginPane(Account oldAccount, Consumer success, Runnable failed) { + public AccountLoginWithPasswordDialog(Account oldAccount, Consumer success, Runnable failed) { this.oldAccount = oldAccount; this.success = success; this.failed = failed; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/microsoft/MicrosoftAccount.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/microsoft/MicrosoftAccount.java index 227b032bc..c79c403ca 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/microsoft/MicrosoftAccount.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/microsoft/MicrosoftAccount.java @@ -100,7 +100,17 @@ public class MicrosoftAccount extends Account { @Override public AuthInfo logInWithPassword(String password) throws AuthenticationException { - throw new UnsupportedOperationException(); + MicrosoftSession acquiredSession = service.authenticate(); + + if (acquiredSession.getProfile() == null) { + session = service.refresh(acquiredSession); + } else { + session = acquiredSession; + } + + authenticated = true; + invalidate(); + return session.toAuthInfo(); } @Override diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/microsoft/MicrosoftService.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/microsoft/MicrosoftService.java index 42af0c334..f8d273aad 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/microsoft/MicrosoftService.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/microsoft/MicrosoftService.java @@ -119,7 +119,12 @@ public class MicrosoftService { pair("client_secret", callback.getClientSecret()), pair("refresh_token", oldSession.getRefreshToken()), pair("grant_type", "refresh_token")) - .accept("application/json").getJson(LiveRefreshResponse.class); + .accept("application/json") + .ignoreHttpErrorCode(400) + .ignoreHttpErrorCode(401) + .getJson(LiveRefreshResponse.class); + + handleLiveErrorMessage(response); return authenticateViaLiveAccessToken(response.accessToken, response.refreshToken); } catch (IOException e) { @@ -129,6 +134,22 @@ public class MicrosoftService { } } + private void handleLiveErrorMessage(LiveErrorResponse response) throws AuthenticationException { + if (response.error == null || response.errorDescription == null) { + return; + } + + switch (response.error) { + case "invalid_grant": + if (response.errorDescription.contains("The user must sign in again and if needed grant the client application access to the requested scope")) { + throw new CredentialExpiredException(); + } + break; + } + + throw new RemoteAuthenticationException(response.error, response.errorDescription, ""); + } + private String getUhs(XBoxLiveAuthenticationResponse response, String existingUhs) throws AuthenticationException { if (response.errorCode != 0) { throw new XboxAuthorizationException(response.errorCode, response.redirectUrl); @@ -336,13 +357,24 @@ public class MicrosoftService { public static class NoXuiException extends AuthenticationException { } + public static class LiveErrorResponse { + @SerializedName("error") + public String error; + + @SerializedName("error_description") + public String errorDescription; + + @SerializedName("correlation_id") + public String correlationId; + } + /** * Error response: {"error":"invalid_grant","error_description":"The provided * value for the 'redirect_uri' is not valid. The value must exactly match the * redirect URI used to obtain the authorization * code.","correlation_id":"??????"} */ - public static class LiveAuthorizationResponse { + public static class LiveAuthorizationResponse extends LiveErrorResponse { @SerializedName("token_type") public String tokenType; @@ -365,7 +397,7 @@ public class MicrosoftService { public String foci; } - private static class LiveRefreshResponse { + private static class LiveRefreshResponse extends LiveErrorResponse { @SerializedName("expires_in") int expiresIn;