diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/setting/Accounts.java b/HMCL/src/main/java/org/jackhuang/hmcl/setting/Accounts.java index 8688afbe3..2f56baa0e 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/setting/Accounts.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/setting/Accounts.java @@ -360,6 +360,8 @@ public final class Accounts { long errorCode = ((MicrosoftService.XboxAuthorizationException) exception).getErrorCode(); if (errorCode == MicrosoftService.XboxAuthorizationException.ADD_FAMILY) { return i18n("account.methods.microsoft.error.add_family"); + } else if (errorCode == MicrosoftService.XboxAuthorizationException.COUNTRY_UNAVAILABLE) { + return i18n("account.methods.microsoft.error.country_unavailable"); } else if (errorCode == MicrosoftService.XboxAuthorizationException.MISSING_XBOX_ACCOUNT) { return i18n("account.methods.microsoft.error.missing_xbox_account"); } else { diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index abb9ccedc..b94fab399 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -76,6 +76,7 @@ account.methods.authlib_injector=authlib-injector account.methods.microsoft=Microsoft Account account.methods.microsoft.close_page=Microsoft account authorization has been finished. There are some remaining logging-in steps to be finished later. You can close this page right now. account.methods.microsoft.error.add_family=Since you are not yet 18 years old, an adult must add you to a family in order for you to play Minecraft. +account.methods.microsoft.error.country_unavailable=XBox Live is not available in your country/region. account.methods.microsoft.error.missing_xbox_account=Your Microsoft account is not connected to an Xbox account. Please create one before continuing. account.methods.microsoft.error.no_character=Account is missing a Minecraft Java profile. While the Microsoft account is valid, it does not own the game. account.methods.microsoft.error.unknown=Failed to log in. Microsoft respond with error code %d. diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh.properties b/HMCL/src/main/resources/assets/lang/I18N_zh.properties index e3d7e1fe0..b272abd99 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh.properties @@ -69,6 +69,7 @@ account.methods.authlib_injector=authlib-injector 登入 account.methods.microsoft=微軟帳戶 account.methods.microsoft.close_page=已完成微軟帳號授權,接下來啟動器還需要完成剩餘登錄步驟。你已經可以關閉本頁面了。 account.methods.microsoft.error.add_family=由於你未滿 18 歲,你的帳號必須被加入到家庭中才能登錄遊戲。 +account.methods.microsoft.error.country_unavailable=你所在的國家或地區不受 XBox Live 的支持。 account.methods.microsoft.error.missing_xbox_account=你的微軟帳號尚未關聯 XBox 帳號,你必須先創建 XBox 帳號,才能登錄遊戲。 account.methods.microsoft.error.no_character=該帳號沒有包含 Minecraft Java 版購買記錄 account.methods.microsoft.error.unknown=登錄失敗,錯誤碼:%d diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties index 9bac3f17e..24db6899d 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties @@ -74,7 +74,8 @@ account.methods=登录方式 account.methods.authlib_injector=外置登录 (authlib-injector) account.methods.microsoft=微软账户 account.methods.microsoft.close_page=已完成微软账号授权,接下来启动器还需要完成剩余登录步骤。你已经可以关闭本页面了。 -account.methods.microsoft.error.add_family=由于你未满 18 岁,你的账号必须被加入到家庭中才能登录游戏。 +account.methods.microsoft.error.add_family=由於你的帳號登記年齡未滿 18 歲,你的帳號必須被加入到家庭中才能登錄遊戲。你可以更改你的帳號的出生日期設置,使年齡滿 18 歲以上以繼續登錄。 +account.methods.microsoft.error.country_unavailable=你所在的国家或地区不受 XBox Live 的支持。 account.methods.microsoft.error.missing_xbox_account=你的微软账号尚未关联 XBox 账号,你必须先创建 XBox 账号,才能登录游戏。 account.methods.microsoft.error.no_character=该账号没有包含 Minecraft Java 版购买记录 account.methods.microsoft.error.unknown=登录失败,错误码:%d 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 5db3aedd2..a97557564 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 @@ -129,6 +129,25 @@ public class MicrosoftService { } } + private String getUhs(XBoxLiveAuthenticationResponse response, String existingUhs) throws AuthenticationException { + if (response.errorCode != 0) { + throw new XboxAuthorizationException(response.errorCode, response.redirectUrl); + } + + 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)); + throw new ServerResponseMalformedException(); + } + + String uhs = (String) response.displayClaims.xui.get(0).get("uhs"); + if (existingUhs != null) { + if (!Objects.equals(uhs, existingUhs)) { + throw new ServerResponseMalformedException("uhs mismatched"); + } + } + return uhs; + } + private MicrosoftSession authenticateViaLiveAccessToken(String liveAccessToken, String liveRefreshToken) throws IOException, JsonParseException, AuthenticationException { // Authenticate with XBox Live XBoxLiveAuthenticationResponse xboxResponse = HttpRequest @@ -140,11 +159,7 @@ public class MicrosoftService { pair("RelyingParty", "http://auth.xboxlive.com"), pair("TokenType", "JWT"))) .accept("application/json").getJson(XBoxLiveAuthenticationResponse.class); - if (xboxResponse.errorCode != 0) { - throw new XboxAuthorizationException(xboxResponse.errorCode); - } - - String uhs = (String) xboxResponse.displayClaims.xui.get(0).get("uhs"); + String uhs = getUhs(xboxResponse, null); // Authenticate Minecraft with XSTS XBoxLiveAuthenticationResponse minecraftXstsResponse = HttpRequest @@ -156,14 +171,7 @@ public class MicrosoftService { pair("RelyingParty", "rp://api.minecraftservices.com/"), pair("TokenType", "JWT"))) .getJson(XBoxLiveAuthenticationResponse.class); - if (xboxResponse.errorCode != 0) { - throw new XboxAuthorizationException(xboxResponse.errorCode); - } - - String minecraftXstsUhs = (String) minecraftXstsResponse.displayClaims.xui.get(0).get("uhs"); - if (!Objects.equals(uhs, minecraftXstsUhs)) { - throw new ServerResponseMalformedException("uhs mismatched"); - } + getUhs(minecraftXstsResponse, uhs); // Authenticate XBox with XSTS XBoxLiveAuthenticationResponse xboxXstsResponse = HttpRequest @@ -175,14 +183,7 @@ public class MicrosoftService { pair("RelyingParty", "http://xboxlive.com"), pair("TokenType", "JWT"))) .getJson(XBoxLiveAuthenticationResponse.class); - if (xboxXstsResponse.errorCode != 0) { - throw new XboxAuthorizationException(xboxXstsResponse.errorCode); - } - - String xboxXstsUhs = (String) xboxXstsResponse.displayClaims.xui.get(0).get("uhs"); - if (!Objects.equals(uhs, xboxXstsUhs)) { - throw new ServerResponseMalformedException("uhs mismatched"); - } + getUhs(xboxXstsResponse, uhs); getXBoxProfile(uhs, xboxXstsResponse.token); @@ -307,16 +308,23 @@ public class MicrosoftService { public static class XboxAuthorizationException extends AuthenticationException { private final long errorCode; + private final String redirect; - public XboxAuthorizationException(long errorCode) { + public XboxAuthorizationException(long errorCode, String redirect) { this.errorCode = errorCode; + this.redirect = redirect; } public long getErrorCode() { return errorCode; } + public String getRedirect() { + return redirect; + } + public static final long MISSING_XBOX_ACCOUNT = 2148916233L; + public static final long COUNTRY_UNAVAILABLE = 2148916235L; public static final long ADD_FAMILY = 2148916238L; } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/HttpRequest.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/HttpRequest.java index be958fa4c..eee824501 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/HttpRequest.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/HttpRequest.java @@ -149,6 +149,10 @@ public abstract class HttpRequest { HttpURLConnection con = createConnection(); con.setDoOutput(true); + try (OutputStream os = con.getOutputStream()) { + os.write(bytes); + } + if (responseCodeTester != null) { responseCodeTester.accept(new URL(url), con.getResponseCode()); } else { @@ -157,9 +161,6 @@ public abstract class HttpRequest { } } - try (OutputStream os = con.getOutputStream()) { - os.write(bytes); - } return NetworkUtils.readData(con); } } diff --git a/README.md b/README.md index 846603fc8..31245d8d5 100644 --- a/README.md +++ b/README.md @@ -56,3 +56,5 @@ Make sure you have Java installed with JavaFX 8 at least. Liberica full JDK 8 or |`-Dhmcl.update_source.override=`|Override the update source.| |`-Dhmcl.authlibinjector.location=`|Use specified authlib-injector (instead of downloading one).| |`-Dhmcl.openjfx.repo=`|Add custom maven repository for download OpenJFX.| +|`-Dhmcl.microsoft.auth.id=`|Override Microsoft OAuth App ID.| +|`-Dhmcl.microsoft.auth.secret=`|Override Microsoft OAuth App secret.|