Inceptum/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/api/account/MicrosoftAuthServer.java

150 lines
6.5 KiB
Java

package io.gitlab.jfronny.inceptum.launcher.api.account;
import io.gitlab.jfronny.commons.http.server.JLHTTPServer;
import io.gitlab.jfronny.commons.http.server.util.VirtualHost;
import io.gitlab.jfronny.inceptum.common.Utils;
import io.gitlab.jfronny.inceptum.launcher.LauncherEnv;
import io.gitlab.jfronny.inceptum.launcher.model.microsoft.Entitlements;
import io.gitlab.jfronny.inceptum.launcher.model.microsoft.Profile;
import io.gitlab.jfronny.inceptum.launcher.model.microsoft.response.*;
import org.jetbrains.annotations.Nullable;
import java.io.Closeable;
import java.io.IOException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.Objects;
public class MicrosoftAuthServer implements Closeable {
private static final JLHTTPServer server = new JLHTTPServer(MicrosoftAuthAPI.MICROSOFT_LOGIN_REDIRECT_PORT);
private static final VirtualHost host = server.getVirtualHost(null);
private final MicrosoftAccount previous;
public MicrosoftAuthServer(@Nullable MicrosoftAccount previous) {
this.previous = previous;
}
public void start() throws IOException {
host.addContext("/", (req, res) -> {
if (req.getParams().containsKey("error")) {
res.getHeaders().add("Content-Type", "text/plain");
res.send(500, "Error logging in. Check console for more information");
Utils.LOGGER.error("Error logging into Microsoft account: " + URLDecoder
.decode(req.getParams().get("error_description"), StandardCharsets.UTF_8));
close();
return 0;
}
if (!req.getParams().containsKey("code")) {
res.getHeaders().add("Content-Type", "text/plain");
res.send(400, "Code is missing");
close();
return 0;
}
try {
acquireAccessToken(req.getParams().get("code"));
} catch (Exception e) {
Utils.LOGGER.error("Error acquiring accessToken", e);
res.getHeaders().add("Content-Type", "text/html");
res.send(500, "Error logging in. Check console for more information");
close();
return 0;
}
res.getHeaders().add("Content-Type", "text/plain");
// #. {0} is the name of the launcher
res.send(200, "Login complete. You can now close this window and go back to the Launcher");
close();
return 0;
}, "GET");
server.start();
}
private void addAccount(OauthTokenResponse oauthTokenResponse, XboxLiveAuthResponse xstsAuthResponse, LoginResponse loginResponse, Profile profile) {
if (this.previous != null || AccountManager.isAccountByName(loginResponse.username())) {
MicrosoftAccount account = (MicrosoftAccount) AccountManager.getAccountByName(loginResponse.username());
if (account == null) {
return;
}
// if forced to relogin, then make sure they logged into correct account
if (this.previous != null && !Objects.equals(account.accountId, this.previous.accountId)) {
LauncherEnv.showError("Logged into incorrect account. Please login again on the Accounts tab", "Incorrect account");
return;
}
account.update(oauthTokenResponse, xstsAuthResponse, loginResponse, profile);
AccountManager.saveAccounts();
} else {
MicrosoftAccount account = new MicrosoftAccount(oauthTokenResponse, xstsAuthResponse, loginResponse, profile);
AccountManager.addAccount(account);
}
}
private void acquireAccessToken(String authcode) throws Exception {
OauthTokenResponse oauthTokenResponse = MicrosoftAuthAPI.tradeCodeForAccessToken(authcode);
acquireXBLToken(oauthTokenResponse);
}
private void acquireXBLToken(OauthTokenResponse oauthTokenResponse) throws Exception {
XboxLiveAuthResponse xblAuthResponse = MicrosoftAuthAPI.getXBLToken(oauthTokenResponse.accessToken());
acquireXsts(oauthTokenResponse, xblAuthResponse.token());
}
private void acquireXsts(OauthTokenResponse oauthTokenResponse, String xblToken) throws Exception {
XboxLiveAuthResponse xstsAuthResponse = MicrosoftAuthAPI.getXstsToken(xblToken);
if (xstsAuthResponse != null) {
acquireMinecraftToken(oauthTokenResponse, xstsAuthResponse);
}
}
private void acquireMinecraftToken(OauthTokenResponse oauthTokenResponse, XboxLiveAuthResponse xstsAuthResponse) throws Exception {
LoginResponse loginResponse = MicrosoftAuthAPI.loginToMinecraft("XBL3.0 x=" + xstsAuthResponse.displayClaims().xui().get(0).uhs() + ";" + xstsAuthResponse.token());
if (loginResponse == null) {
throw new Exception("Failed to login to Minecraft");
}
Entitlements entitlements = MicrosoftAuthAPI.getEntitlements(loginResponse.accessToken());
if (!(entitlements.items().stream().anyMatch(i -> i.name().equalsIgnoreCase("product_minecraft"))
&& entitlements.items().stream().anyMatch(i -> i.name().equalsIgnoreCase("game_minecraft")))) {
LauncherEnv.showError("This account doesn't have a valid purchase of Minecraft.\nPlease make sure you've bought the Java edition of Minecraft and then try again.", "Doesn't own Minecraft");
throw new Exception("Account does not own Minecraft");
}
Profile profile = null;
try {
profile = MicrosoftAuthAPI.getMcProfile(loginResponse.accessToken());
} catch (Exception e) {
LauncherEnv.showError("""
No Minecraft profiles were found for this account. Have you purchased Minecraft?
Please make sure you've bought the Java edition of Minecraft and then try again.
If you're an Xbox Game Pass subscriber, make sure to login and play through the Minecraft
Launcher once in order to create your Minecraft profile, then try logging in again.""",
"Minecraft Profile Not Found");
throw new Exception("Minecraft Profile not found", e);
}
if (profile == null) {
throw new Exception("Failed to get Minecraft profile");
}
// add the account
addAccount(oauthTokenResponse, xstsAuthResponse, loginResponse, profile);
}
@Override
public void close() {
server.stop();
}
}