feat(microsoft): WIP: handle credentials expiration.
This commit is contained in:
@@ -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<AuthInfo> 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();
|
||||
}
|
||||
|
||||
@@ -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<AuthInfo> 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<AuthInfo> success, Runnable failed) {
|
||||
public AccountLoginWithPasswordDialog(Account oldAccount, Consumer<AuthInfo> success, Runnable failed) {
|
||||
this.oldAccount = oldAccount;
|
||||
this.success = success;
|
||||
this.failed = failed;
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user