fix: Microsoft Account add family hint.

This commit is contained in:
huanghongxun 2021-09-11 23:50:13 +08:00
parent 0032242da6
commit 1b3e4e5bd6
7 changed files with 60 additions and 4 deletions

View File

@ -369,6 +369,8 @@ public final class Accounts {
} }
} else if (exception instanceof MicrosoftService.NoMinecraftJavaEditionProfileException) { } else if (exception instanceof MicrosoftService.NoMinecraftJavaEditionProfileException) {
return i18n("account.methods.microsoft.error.no_character"); return i18n("account.methods.microsoft.error.no_character");
} else if (exception instanceof MicrosoftService.NoXuiException) {
return i18n("account.methods.microsoft.error.add_family_probably");
} else if (exception.getClass() == AuthenticationException.class) { } else if (exception.getClass() == AuthenticationException.class) {
return exception.getLocalizedMessage(); return exception.getLocalizedMessage();
} else { } else {

View File

@ -120,6 +120,8 @@ public class CreateAccountPane extends JFXDialogLayout implements DialogAware {
{ {
lblErrorMessage = new Label(); lblErrorMessage = new Label();
lblErrorMessage.setWrapText(true);
lblErrorMessage.setMaxWidth(400);
btnAccept = new JFXButton(i18n("account.login")); btnAccept = new JFXButton(i18n("account.login"));
btnAccept.getStyleClass().add("dialog-accept"); btnAccept.getStyleClass().add("dialog-accept");
@ -134,7 +136,10 @@ public class CreateAccountPane extends JFXDialogLayout implements DialogAware {
btnCancel.setOnAction(e -> onCancel()); btnCancel.setOnAction(e -> onCancel());
onEscPressed(this, btnCancel::fire); onEscPressed(this, btnCancel::fire);
setActions(lblErrorMessage, new HBox(spinner, btnCancel)); HBox hbox = new HBox(spinner, btnCancel);
hbox.setAlignment(Pos.CENTER_RIGHT);
setActions(lblErrorMessage, hbox);
} }
if (showMethodSwitcher) { if (showMethodSwitcher) {
@ -254,6 +259,7 @@ public class CreateAccountPane extends JFXDialogLayout implements DialogAware {
lblErrorMessage.setText(""); lblErrorMessage.setText("");
} }
if (factory == Accounts.FACTORY_MICROSOFT) { if (factory == Accounts.FACTORY_MICROSOFT) {
VBox vbox = new VBox(8);
HintPane hintPane = new HintPane(MessageDialogPane.MessageType.INFORMATION); HintPane hintPane = new HintPane(MessageDialogPane.MessageType.INFORMATION);
hintPane.textProperty().bind(BindingMapping.of(logging).map(logging -> hintPane.textProperty().bind(BindingMapping.of(logging).map(logging ->
logging logging
@ -264,7 +270,18 @@ public class CreateAccountPane extends JFXDialogLayout implements DialogAware {
FXUtils.copyText(MicrosoftAuthenticationServer.lastlyOpenedURL); FXUtils.copyText(MicrosoftAuthenticationServer.lastlyOpenedURL);
} }
}); });
detailsPane = hintPane;
HBox box = new HBox(8);
Hyperlink birthLink = new Hyperlink(i18n("account.methods.microsoft.birth"));
birthLink.setOnAction(e -> FXUtils.openLink("https://support.microsoft.com/zh-cn/account-billing/如何更改-microsoft-帐户上的出生日期-837badbc-999e-54d2-2617-d19206b9540a"));
Hyperlink profileLink = new Hyperlink(i18n("account.methods.microsoft.profile"));
profileLink.setOnAction(e -> FXUtils.openLink("https://account.live.com/editprof.aspx"));
box.getChildren().setAll(profileLink, birthLink);
GridPane.setColumnSpan(box, 2);
vbox.getChildren().setAll(hintPane, box);
detailsPane = vbox;
btnAccept.setDisable(false); btnAccept.setDisable(false);
} else { } else {
detailsPane = new AccountDetailsInputPane(factory, btnAccept::fire); detailsPane = new AccountDetailsInputPane(factory, btnAccept::fire);
@ -608,4 +625,6 @@ public class CreateAccountPane extends JFXDialogLayout implements DialogAware {
} }
} }
} }
private static final String MICROSOFT_ACCOUNT_EDIT_PROFILE_URL = "https://support.microsoft.com/zh-cn/account-billing/%E5%A6%82%E4%BD%95%E6%9B%B4%E6%94%B9-microsoft-%E5%B8%90%E6%88%B7%E4%B8%8A%E7%9A%84%E5%87%BA%E7%94%9F%E6%97%A5%E6%9C%9F-837badbc-999e-54d2-2617-d19206b9540a";
} }

View File

@ -344,6 +344,7 @@
} }
.jfx-layout-actions { .jfx-layout-actions {
-fx-pref-width: 400;
-fx-padding: 10.0px 0.0 0.0 0.0; -fx-padding: 10.0px 0.0 0.0 0.0;
-fx-alignment: center-right; -fx-alignment: center-right;
} }

View File

@ -27,6 +27,12 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
<div> <div>
%close-page% %close-page%
</div> </div>
<script>
setTimeout(function() {
open("about:blank","_self").close();
}, 5000);
</script>
</body> </body>
</html> </html>

View File

@ -136,7 +136,7 @@ public class MicrosoftService {
if (response.displayClaims == null || response.displayClaims.xui == null || response.displayClaims.xui.size() == 0 || !response.displayClaims.xui.get(0).containsKey("uhs")) { if (response.displayClaims == null || response.displayClaims.xui == null || response.displayClaims.xui.size() == 0 || !response.displayClaims.xui.get(0).containsKey("uhs")) {
LOG.log(Level.WARNING, "Unrecognized xbox authorization response " + GSON.toJson(response)); LOG.log(Level.WARNING, "Unrecognized xbox authorization response " + GSON.toJson(response));
throw new ServerResponseMalformedException(); throw new NoXuiException();
} }
String uhs = (String) response.displayClaims.xui.get(0).get("uhs"); String uhs = (String) response.displayClaims.xui.get(0).get("uhs");
@ -169,6 +169,7 @@ public class MicrosoftService {
mapOf(pair("SandboxId", "RETAIL"), mapOf(pair("SandboxId", "RETAIL"),
pair("UserTokens", Collections.singletonList(xboxResponse.token)))), pair("UserTokens", Collections.singletonList(xboxResponse.token)))),
pair("RelyingParty", "rp://api.minecraftservices.com/"), pair("TokenType", "JWT"))) pair("RelyingParty", "rp://api.minecraftservices.com/"), pair("TokenType", "JWT")))
.ignoreHttpErrorCode(401)
.getJson(XBoxLiveAuthenticationResponse.class); .getJson(XBoxLiveAuthenticationResponse.class);
getUhs(minecraftXstsResponse, uhs); getUhs(minecraftXstsResponse, uhs);
@ -331,6 +332,9 @@ public class MicrosoftService {
public static class NoMinecraftJavaEditionProfileException extends AuthenticationException { public static class NoMinecraftJavaEditionProfileException extends AuthenticationException {
} }
public static class NoXuiException extends AuthenticationException {
}
/** /**
* Error response: {"error":"invalid_grant","error_description":"The provided * Error response: {"error":"invalid_grant","error_description":"The provided
* value for the 'redirect_uri' is not valid. The value must exactly match the * value for the 'redirect_uri' is not valid. The value must exactly match the

View File

@ -31,7 +31,9 @@ import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import static java.nio.charset.StandardCharsets.UTF_8; import static java.nio.charset.StandardCharsets.UTF_8;
@ -46,6 +48,7 @@ public abstract class HttpRequest {
protected final String method; protected final String method;
protected final Map<String, String> headers = new HashMap<>(); protected final Map<String, String> headers = new HashMap<>();
protected ExceptionalBiConsumer<URL, Integer, IOException> responseCodeTester; protected ExceptionalBiConsumer<URL, Integer, IOException> responseCodeTester;
protected final Set<Integer> toleratedHttpCodes = new HashSet<>();
private HttpRequest(String url, String method) { private HttpRequest(String url, String method) {
this.url = url; this.url = url;
@ -92,6 +95,11 @@ public abstract class HttpRequest {
return this; return this;
} }
public HttpRequest ignoreHttpErrorCode(int code) {
toleratedHttpCodes.add(code);
return this;
}
public HttpURLConnection createConnection() throws IOException { public HttpURLConnection createConnection() throws IOException {
HttpURLConnection con = createHttpConnection(new URL(url)); HttpURLConnection con = createHttpConnection(new URL(url));
con.setRequestMethod(method); con.setRequestMethod(method);
@ -157,7 +165,10 @@ public abstract class HttpRequest {
responseCodeTester.accept(new URL(url), con.getResponseCode()); responseCodeTester.accept(new URL(url), con.getResponseCode());
} else { } else {
if (con.getResponseCode() / 100 != 2) { if (con.getResponseCode() / 100 != 2) {
throw new ResponseCodeException(new URL(url), con.getResponseCode()); if (!toleratedHttpCodes.contains(con.getResponseCode())) {
String data = NetworkUtils.readData(con);
throw new ResponseCodeException(new URL(url), con.getResponseCode(), data);
}
} }
} }

View File

@ -24,11 +24,20 @@ public class ResponseCodeException extends IOException {
private final URL url; private final URL url;
private final int responseCode; private final int responseCode;
private final String data;
public ResponseCodeException(URL url, int responseCode) { public ResponseCodeException(URL url, int responseCode) {
super("Unable to request url " + url + ", response code: " + responseCode); super("Unable to request url " + url + ", response code: " + responseCode);
this.url = url; this.url = url;
this.responseCode = responseCode; this.responseCode = responseCode;
this.data = null;
}
public ResponseCodeException(URL url, int responseCode, String data) {
super("Unable to request url " + url + ", response code: " + responseCode + ", data: " + data);
this.url = url;
this.responseCode = responseCode;
this.data = data;
} }
public URL getUrl() { public URL getUrl() {
@ -38,4 +47,8 @@ public class ResponseCodeException extends IOException {
public int getResponseCode() { public int getResponseCode() {
return responseCode; return responseCode;
} }
public String getData() {
return data;
}
} }