diff --git a/launcher/src/main/java/com/skcraft/launcher/auth/MicrosoftLoginService.java b/launcher/src/main/java/com/skcraft/launcher/auth/MicrosoftLoginService.java index 6c2f44e..fb5beff 100644 --- a/launcher/src/main/java/com/skcraft/launcher/auth/MicrosoftLoginService.java +++ b/launcher/src/main/java/com/skcraft/launcher/auth/MicrosoftLoginService.java @@ -64,17 +64,16 @@ form.add("client_id", clientId); formConsumer.accept(form); - HttpRequest request = HttpRequest.post(MS_TOKEN_URL) + return HttpRequest.post(MS_TOKEN_URL) .bodyForm(form) - .execute(); + .execute() + .expectResponseCodeOr(200, (req) -> { + TokenError error = req.returnContent().asJson(TokenError.class); - if (request.getResponseCode() == 200) { - return request.returnContent().asJson(TokenResponse.class); - } else { - TokenError error = request.returnContent().asJson(TokenError.class); - - throw new AuthenticationException(error.errorDescription); - } + return new AuthenticationException(error.errorDescription); + }) + .returnContent() + .asJson(TokenResponse.class); } private Profile performLogin(String microsoftToken, SavedSession previous) diff --git a/launcher/src/main/java/com/skcraft/launcher/auth/microsoft/XboxTokenAuthorizer.java b/launcher/src/main/java/com/skcraft/launcher/auth/microsoft/XboxTokenAuthorizer.java index 6230391..092ead5 100644 --- a/launcher/src/main/java/com/skcraft/launcher/auth/microsoft/XboxTokenAuthorizer.java +++ b/launcher/src/main/java/com/skcraft/launcher/auth/microsoft/XboxTokenAuthorizer.java @@ -1,7 +1,9 @@ package com.skcraft.launcher.auth.microsoft; +import com.skcraft.launcher.auth.AuthenticationException; import com.skcraft.launcher.auth.microsoft.model.*; import com.skcraft.launcher.util.HttpRequest; +import com.skcraft.launcher.util.SharedLocale; import java.io.IOException; import java.net.URL; @@ -12,7 +14,8 @@ private static final URL XBL_AUTHENTICATE_URL = url("https://user.auth.xboxlive.com/user/authenticate"); private static final URL XSTS_AUTHENTICATE_URL = url("https://xsts.auth.xboxlive.com/xsts/authorize"); - public static XboxAuthorization authorizeWithXbox(String accessToken) throws IOException, InterruptedException { + public static XboxAuthorization authorizeWithXbox(String accessToken) + throws IOException, InterruptedException, AuthenticationException { XboxAuthRequest xblPayload = new XboxAuthRequest<>(new XblAuthProperties("d=" + accessToken)); @@ -20,7 +23,9 @@ .bodyJson(xblPayload) .header("Accept", "application/json") .execute() - .expectResponseCode(200) + .expectResponseCodeOr(200, (req) -> + new AuthenticationException("Error authenticating with Xbox Live", + SharedLocale.tr("login.xbox.generic"))) .returnContent() .asJson(XboxAuthResponse.class); @@ -32,10 +37,25 @@ .bodyJson(xstsPayload) .header("Accept", "application/json") .execute() - .expectResponseCode(200) + .expectResponseCodeOr(200, (req) -> { + XstsError xstsError = req.returnContent().asJson(XstsError.class); + + return new AuthenticationException(xstsError.getMessage(), getErrorMessage(xstsError.getXErr())); + }) .returnContent() .asJson(XboxAuthResponse.class); return new XboxAuthorization(xstsResponse.getToken(), xstsResponse.getUhs()); } + + private static String getErrorMessage(long xboxErrorCode) { + if (xboxErrorCode == 2148916233L) { + return SharedLocale.tr("login.xbox.noXboxAccount"); + } + if (xboxErrorCode == 2148916238L) { + return SharedLocale.tr("login.xbox.isChild"); + } + + return SharedLocale.tr("login.xbox.unknown", xboxErrorCode); + } } diff --git a/launcher/src/main/java/com/skcraft/launcher/auth/microsoft/model/XstsError.java b/launcher/src/main/java/com/skcraft/launcher/auth/microsoft/model/XstsError.java new file mode 100644 index 0000000..e008803 --- /dev/null +++ b/launcher/src/main/java/com/skcraft/launcher/auth/microsoft/model/XstsError.java @@ -0,0 +1,15 @@ +package com.skcraft.launcher.auth.microsoft.model; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.databind.PropertyNamingStrategy; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import lombok.Data; + +@Data +@JsonNaming(PropertyNamingStrategy.PascalCaseStrategy.class) +@JsonIgnoreProperties(ignoreUnknown = true) +public class XstsError { + private long xErr; + private String message; + private String redirect; +} diff --git a/launcher/src/main/java/com/skcraft/launcher/util/HttpFunction.java b/launcher/src/main/java/com/skcraft/launcher/util/HttpFunction.java new file mode 100644 index 0000000..67788e8 --- /dev/null +++ b/launcher/src/main/java/com/skcraft/launcher/util/HttpFunction.java @@ -0,0 +1,8 @@ +package com.skcraft.launcher.util; + +import java.io.IOException; + +@FunctionalInterface +public interface HttpFunction { + V call(T arg) throws IOException, InterruptedException; +} diff --git a/launcher/src/main/java/com/skcraft/launcher/util/HttpRequest.java b/launcher/src/main/java/com/skcraft/launcher/util/HttpRequest.java index 37ebda4..e699258 100644 --- a/launcher/src/main/java/com/skcraft/launcher/util/HttpRequest.java +++ b/launcher/src/main/java/com/skcraft/launcher/util/HttpRequest.java @@ -203,6 +203,27 @@ } /** + * Continue if the response code matches, otherwise call the provided function + * to generate an exception. + * + * @param code HTTP status code to continue on. + * @param onError Function invoked when the code does not match, should return an error that will be thrown. + * @return this object if successful + * @throws Exception either an {@link IOException} on I/O error or a user-defined {@link Exception} subclass + * if the code does not match. + */ + public HttpRequest expectResponseCodeOr(int code, HttpFunction onError) + throws E, IOException, InterruptedException { + int responseCode = getResponseCode(); + + if (code == responseCode) return this; + + E exc = onError.call(this); + close(); + throw exc; + } + + /** * Get the response code. * * @return the response code diff --git a/launcher/src/main/resources/com/skcraft/launcher/lang/Launcher.properties b/launcher/src/main/resources/com/skcraft/launcher/lang/Launcher.properties index 888e2a7..ec83350 100644 --- a/launcher/src/main/resources/com/skcraft/launcher/lang/Launcher.properties +++ b/launcher/src/main/resources/com/skcraft/launcher/lang/Launcher.properties @@ -110,6 +110,11 @@ login.noLoginTitle=Missing Account login.minecraftNotOwnedError=Sorry, Minecraft is not owned on that account. +login.xbox.generic=Failed to authenticate with Xbox Live. +login.xbox.noXboxAccount=That account does not have an Xbox account associated! +login.xbox.isChild=The account is a child (under 18) and cannot proceed unless it is part of a Family. +login.xbox.unknown=An unknown error occurred while logging in with Xbox (XErr %d) + console.title=Messages and Errors console.launcherConsoleTitle=Launcher Messages console.uploadLog=Upload Log