mirror of
https://github.com/SKCraft/Launcher.git
synced 2024-11-27 12:46:22 +01:00
Implement Microsoft OAuth login process
This commit is contained in:
parent
3ddaea55dc
commit
d2127e9b13
@ -12,10 +12,7 @@ import com.google.common.base.Strings;
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.util.concurrent.ListeningExecutorService;
|
||||
import com.google.common.util.concurrent.MoreExecutors;
|
||||
import com.skcraft.launcher.auth.AccountList;
|
||||
import com.skcraft.launcher.auth.LoginService;
|
||||
import com.skcraft.launcher.auth.UserType;
|
||||
import com.skcraft.launcher.auth.YggdrasilLoginService;
|
||||
import com.skcraft.launcher.auth.*;
|
||||
import com.skcraft.launcher.launch.LaunchSupervisor;
|
||||
import com.skcraft.launcher.model.minecraft.Library;
|
||||
import com.skcraft.launcher.model.minecraft.VersionManifest;
|
||||
@ -166,6 +163,10 @@ public final class Launcher {
|
||||
return new YggdrasilLoginService(HttpRequest.url(getProperties().getProperty("yggdrasilAuthUrl")), accounts.getClientId());
|
||||
}
|
||||
|
||||
public MicrosoftLoginService getMicrosoftLogin() {
|
||||
return new MicrosoftLoginService(getProperties().getProperty("microsoftClientId"));
|
||||
}
|
||||
|
||||
public LoginService getLoginService(UserType type) {
|
||||
if (type == UserType.MICROSOFT) {
|
||||
return null; // TODO: Microsoft login service
|
||||
|
@ -0,0 +1,152 @@
|
||||
package com.skcraft.launcher.auth;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonNaming;
|
||||
import com.skcraft.launcher.auth.microsoft.MicrosoftWebAuthorizer;
|
||||
import com.skcraft.launcher.auth.microsoft.MinecraftServicesAuthorizer;
|
||||
import com.skcraft.launcher.auth.microsoft.XboxTokenAuthorizer;
|
||||
import com.skcraft.launcher.auth.microsoft.model.McAuthResponse;
|
||||
import com.skcraft.launcher.auth.microsoft.model.McProfileResponse;
|
||||
import com.skcraft.launcher.auth.microsoft.model.TokenResponse;
|
||||
import com.skcraft.launcher.auth.microsoft.model.XboxAuthorization;
|
||||
import com.skcraft.launcher.util.HttpRequest;
|
||||
import lombok.Data;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static com.skcraft.launcher.util.HttpRequest.url;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class MicrosoftLoginService implements LoginService {
|
||||
private static final URL MS_TOKEN_URL = url("https://login.live.com/oauth20_token.srf");
|
||||
|
||||
private final String clientId;
|
||||
|
||||
public Session login() throws IOException, InterruptedException, AuthenticationException {
|
||||
MicrosoftWebAuthorizer authorizer = new MicrosoftWebAuthorizer(clientId);
|
||||
String code = authorizer.authorize();
|
||||
|
||||
TokenResponse response = exchangeToken(form -> {
|
||||
form.add("grant_type", "authorization_code");
|
||||
form.add("redirect_uri", authorizer.getRedirectUri());
|
||||
form.add("code", code);
|
||||
});
|
||||
|
||||
Profile session = performLogin(response.getAccessToken());
|
||||
session.setRefreshToken(response.getRefreshToken());
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Session restore(SavedSession savedSession)
|
||||
throws IOException, InterruptedException, AuthenticationException {
|
||||
TokenResponse response = exchangeToken(form -> {
|
||||
form.add("grant_type", "refresh_token");
|
||||
form.add("refresh_token", savedSession.getRefreshToken());
|
||||
});
|
||||
|
||||
Profile session = performLogin(response.getAccessToken());
|
||||
session.setRefreshToken(response.getRefreshToken());
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
private TokenResponse exchangeToken(Consumer<HttpRequest.Form> formConsumer)
|
||||
throws IOException, InterruptedException, AuthenticationException {
|
||||
HttpRequest.Form form = HttpRequest.Form.form();
|
||||
form.add("client_id", clientId);
|
||||
formConsumer.accept(form);
|
||||
|
||||
HttpRequest request = HttpRequest.post(MS_TOKEN_URL)
|
||||
.bodyForm(form)
|
||||
.execute();
|
||||
|
||||
if (request.getResponseCode() == 200) {
|
||||
return request.returnContent().asJson(TokenResponse.class);
|
||||
} else {
|
||||
TokenError error = request.returnContent().asJson(TokenError.class);
|
||||
|
||||
throw new AuthenticationException(error.errorDescription, error.errorDescription);
|
||||
}
|
||||
}
|
||||
|
||||
private Profile performLogin(String microsoftToken)
|
||||
throws IOException, InterruptedException, AuthenticationException {
|
||||
XboxAuthorization xboxAuthorization = XboxTokenAuthorizer.authorizeWithXbox(microsoftToken);
|
||||
McAuthResponse auth = MinecraftServicesAuthorizer.authorizeWithMinecraft(xboxAuthorization);
|
||||
McProfileResponse profile = MinecraftServicesAuthorizer.getUserProfile(auth);
|
||||
|
||||
Profile session = new Profile(auth, profile);
|
||||
session.setAvatarImage(VisageSkinService.fetchSkinHead(profile.getUuid()));
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class Profile implements Session {
|
||||
private final McAuthResponse auth;
|
||||
private final McProfileResponse profile;
|
||||
private final Map<String, String> userProperties = Collections.emptyMap();
|
||||
private String refreshToken;
|
||||
private String avatarImage;
|
||||
|
||||
@Override
|
||||
public String getUuid() {
|
||||
return profile.getUuid();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return profile.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAccessToken() {
|
||||
return auth.getAccessToken();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSessionToken() {
|
||||
return String.format("token:%s:%s", getAccessToken(), getUuid());
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserType getUserType() {
|
||||
return UserType.MICROSOFT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOnline() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SavedSession toSavedSession() {
|
||||
SavedSession savedSession = new SavedSession();
|
||||
|
||||
savedSession.setType(getUserType());
|
||||
savedSession.setUsername(getName());
|
||||
savedSession.setUuid(getUuid());
|
||||
savedSession.setAccessToken(getAccessToken());
|
||||
savedSession.setRefreshToken(getRefreshToken());
|
||||
savedSession.setAvatarImage(getAvatarImage());
|
||||
|
||||
return savedSession;
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
@JsonNaming(PropertyNamingStrategy.LowerCaseWithUnderscoresStrategy.class)
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
private static class TokenError {
|
||||
private String error;
|
||||
private String errorDescription;
|
||||
}
|
||||
}
|
@ -37,11 +37,6 @@ public class OfflineSession implements Session {
|
||||
return (new UUID(0, 0)).toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClientToken() {
|
||||
return "0";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAccessToken() {
|
||||
return "0";
|
||||
|
@ -27,13 +27,6 @@ public interface Session {
|
||||
*/
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* Get the client token.
|
||||
*
|
||||
* @return client token
|
||||
*/
|
||||
String getClientToken();
|
||||
|
||||
/**
|
||||
* Get the access token.
|
||||
*
|
||||
|
@ -124,12 +124,6 @@ public class YggdrasilLoginService implements LoginService {
|
||||
return String.format("token:%s:%s", getAccessToken(), getUuid());
|
||||
}
|
||||
|
||||
@Override
|
||||
@JsonIgnore
|
||||
public String getClientToken() {
|
||||
return response.getClientToken();
|
||||
}
|
||||
|
||||
@Override
|
||||
@JsonIgnore
|
||||
public String getAccessToken() {
|
||||
|
@ -0,0 +1,57 @@
|
||||
package com.skcraft.launcher.auth.microsoft;
|
||||
|
||||
import com.skcraft.launcher.auth.AuthenticationException;
|
||||
import com.skcraft.launcher.util.HttpRequest;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
/**
|
||||
* Handles the Microsoft leg of OAuth authorization.
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class MicrosoftWebAuthorizer {
|
||||
private final String clientId;
|
||||
@Getter private String redirectUri;
|
||||
|
||||
public String authorize() throws IOException, AuthenticationException, InterruptedException {
|
||||
if (Desktop.isDesktopSupported()) {
|
||||
// Interactive auth
|
||||
return authorizeInteractive();
|
||||
} else {
|
||||
// TODO Device code auth
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private String authorizeInteractive() throws IOException, AuthenticationException, InterruptedException {
|
||||
OauthHttpHandler httpHandler = new OauthHttpHandler();
|
||||
Desktop.getDesktop().browse(generateInteractiveUrl(httpHandler.getPort()));
|
||||
|
||||
return httpHandler.await();
|
||||
}
|
||||
|
||||
private URI generateInteractiveUrl(int port) throws AuthenticationException {
|
||||
redirectUri = "http://localhost:" + port;
|
||||
|
||||
URI interactive;
|
||||
try {
|
||||
HttpRequest.Form query = HttpRequest.Form.form();
|
||||
query.add("client_id", clientId);
|
||||
query.add("scope", "XboxLive.signin XboxLive.offline_access");
|
||||
query.add("response_type", "code");
|
||||
query.add("redirect_uri", redirectUri);
|
||||
|
||||
interactive = new URI("https://login.microsoftonline.com/consumers/oauth2/v2.0/authorize?"
|
||||
+ query.toString());
|
||||
} catch (URISyntaxException e) {
|
||||
throw new AuthenticationException(e, "Failed to generate OAuth URL");
|
||||
}
|
||||
|
||||
return interactive;
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
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 java.io.IOException;
|
||||
import java.net.URL;
|
||||
|
||||
import static com.skcraft.launcher.util.HttpRequest.url;
|
||||
|
||||
public class MinecraftServicesAuthorizer {
|
||||
private static final URL MC_SERVICES_LOGIN = url("https://api.minecraftservices.com/authentication/login_with_xbox");
|
||||
private static final URL MC_SERVICES_PROFILE = url("https://api.minecraftservices.com/minecraft/profile");
|
||||
|
||||
public static McAuthResponse authorizeWithMinecraft(XboxAuthorization auth) throws IOException, InterruptedException {
|
||||
McAuthRequest request = new McAuthRequest("XBL3.0 x=" + auth.getCombinedToken());
|
||||
|
||||
return HttpRequest.post(MC_SERVICES_LOGIN)
|
||||
.bodyJson(request)
|
||||
.header("Accept", "application/json")
|
||||
.execute()
|
||||
.expectResponseCode(200)
|
||||
.returnContent()
|
||||
.asJson(McAuthResponse.class);
|
||||
}
|
||||
|
||||
public static McProfileResponse getUserProfile(McAuthResponse auth)
|
||||
throws IOException, InterruptedException, AuthenticationException {
|
||||
HttpRequest request = HttpRequest.get(MC_SERVICES_PROFILE)
|
||||
.header("Authorization", auth.getAuthorization())
|
||||
.execute();
|
||||
|
||||
if (request.getResponseCode() == 200) {
|
||||
return request.returnContent().asJson(McProfileResponse.class);
|
||||
} else {
|
||||
McServicesError error = request.returnContent().asJson(McServicesError.class);
|
||||
|
||||
throw new AuthenticationException(error.getErrorMessage(), error.getErrorMessage());
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
package com.skcraft.launcher.auth.microsoft;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.base.Splitter;
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import com.sun.net.httpserver.HttpHandler;
|
||||
import com.sun.net.httpserver.HttpServer;
|
||||
import lombok.extern.java.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
@Log
|
||||
public class OauthHttpHandler {
|
||||
private Executor executor = Executors.newCachedThreadPool();
|
||||
private HttpServer server;
|
||||
private String result;
|
||||
|
||||
public OauthHttpHandler() throws IOException {
|
||||
server = HttpServer.create(new InetSocketAddress("localhost", 0), 0);
|
||||
|
||||
server.createContext("/", new Handler());
|
||||
server.setExecutor(executor);
|
||||
server.start();
|
||||
}
|
||||
|
||||
public int getPort() {
|
||||
return server.getAddress().getPort();
|
||||
}
|
||||
|
||||
public String await() throws InterruptedException {
|
||||
synchronized (this) {
|
||||
this.wait();
|
||||
}
|
||||
|
||||
server.stop(3);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private class Handler implements HttpHandler {
|
||||
@Override
|
||||
public void handle(HttpExchange httpExchange) throws IOException {
|
||||
String query = httpExchange.getRequestURI().getQuery();
|
||||
Map<String, String> qs = Splitter.on('&').withKeyValueSeparator('=').split(query);
|
||||
result = qs.get("code");
|
||||
|
||||
synchronized (OauthHttpHandler.this) {
|
||||
OauthHttpHandler.this.notifyAll();
|
||||
}
|
||||
|
||||
byte[] response = "OK: you can close the browser now".getBytes(Charsets.UTF_8);
|
||||
httpExchange.sendResponseHeaders(200, response.length);
|
||||
httpExchange.getResponseBody().write(response);
|
||||
httpExchange.getResponseBody().flush();
|
||||
httpExchange.getResponseBody().close();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
package com.skcraft.launcher.auth.microsoft;
|
||||
|
||||
import com.skcraft.launcher.auth.microsoft.model.*;
|
||||
import com.skcraft.launcher.util.HttpRequest;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
|
||||
import static com.skcraft.launcher.util.HttpRequest.url;
|
||||
|
||||
public class XboxTokenAuthorizer {
|
||||
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 {
|
||||
XboxAuthRequest<XblAuthProperties> xblPayload =
|
||||
new XboxAuthRequest<>(new XblAuthProperties("d=" + accessToken));
|
||||
|
||||
XboxAuthResponse xblResponse = HttpRequest.post(XBL_AUTHENTICATE_URL)
|
||||
.bodyJson(xblPayload)
|
||||
.header("Accept", "application/json")
|
||||
.execute()
|
||||
.expectResponseCode(200)
|
||||
.returnContent()
|
||||
.asJson(XboxAuthResponse.class);
|
||||
|
||||
XboxAuthRequest<XstsAuthProperties> xstsPayload =
|
||||
new XboxAuthRequest<>(new XstsAuthProperties(xblResponse.getToken()));
|
||||
xstsPayload.setRelyingParty("rp://api.minecraftservices.com/");
|
||||
|
||||
XboxAuthResponse xstsResponse = HttpRequest.post(XSTS_AUTHENTICATE_URL)
|
||||
.bodyJson(xstsPayload)
|
||||
.header("Accept", "application/json")
|
||||
.execute()
|
||||
.expectResponseCode(200)
|
||||
.returnContent()
|
||||
.asJson(XboxAuthResponse.class);
|
||||
|
||||
return new XboxAuthorization(xstsResponse.getToken(), xstsResponse.getUhs());
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package com.skcraft.launcher.auth.microsoft.model;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class McAuthRequest {
|
||||
private final String identityToken;
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package com.skcraft.launcher.auth.microsoft.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
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.LowerCaseWithUnderscoresStrategy.class)
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class McAuthResponse {
|
||||
private String accessToken;
|
||||
private String tokenType;
|
||||
private int expiresIn;
|
||||
|
||||
@JsonIgnore
|
||||
public String getAuthorization() {
|
||||
return String.format("%s %s", tokenType, accessToken);
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package com.skcraft.launcher.auth.microsoft.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class McProfileResponse {
|
||||
@JsonProperty("id") private String uuid;
|
||||
private String name;
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package com.skcraft.launcher.auth.microsoft.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class McServicesError {
|
||||
private String error;
|
||||
private String errorMessage;
|
||||
}
|
@ -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.LowerCaseWithUnderscoresStrategy.class)
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class TokenResponse {
|
||||
private String tokenType;
|
||||
private String accessToken;
|
||||
private String refreshToken;
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package com.skcraft.launcher.auth.microsoft.model;
|
||||
|
||||
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonNaming;
|
||||
import lombok.Data;
|
||||
import lombok.NonNull;
|
||||
|
||||
@Data
|
||||
@JsonNaming(PropertyNamingStrategy.PascalCaseStrategy.class)
|
||||
public class XblAuthProperties {
|
||||
private String authMethod = "RPS";
|
||||
private String siteName = "user.auth.xboxlive.com";
|
||||
@NonNull
|
||||
private String rpsTicket;
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package com.skcraft.launcher.auth.microsoft.model;
|
||||
|
||||
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonNaming;
|
||||
import lombok.Data;
|
||||
import lombok.NonNull;
|
||||
|
||||
@Data
|
||||
@JsonNaming(PropertyNamingStrategy.PascalCaseStrategy.class)
|
||||
public class XboxAuthRequest<T> {
|
||||
@NonNull private T properties;
|
||||
private String relyingParty = "http://auth.xboxlive.com";
|
||||
private String tokenType = "JWT";
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
package com.skcraft.launcher.auth.microsoft.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonNaming;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@JsonNaming(PropertyNamingStrategy.PascalCaseStrategy.class)
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class XboxAuthResponse {
|
||||
private String token;
|
||||
private DisplayClaims displayClaims;
|
||||
|
||||
@JsonIgnore
|
||||
public String getUhs() {
|
||||
return getDisplayClaims().getXui().get(0).getUhs();
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class DisplayClaims {
|
||||
private List<UhsContainer> xui;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class UhsContainer {
|
||||
private String uhs;
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package com.skcraft.launcher.auth.microsoft.model;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class XboxAuthorization {
|
||||
private final String token;
|
||||
private final String uhs;
|
||||
|
||||
public String getCombinedToken() {
|
||||
return String.format("%s;%s", uhs, token);
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package com.skcraft.launcher.auth.microsoft.model;
|
||||
|
||||
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonNaming;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@JsonNaming(PropertyNamingStrategy.PascalCaseStrategy.class)
|
||||
public class XstsAuthProperties {
|
||||
private String sandboxId = "RETAIL";
|
||||
private List<String> userTokens;
|
||||
|
||||
public XstsAuthProperties(String token) {
|
||||
this.userTokens = Collections.singletonList(token);
|
||||
}
|
||||
}
|
@ -2,6 +2,8 @@ package com.skcraft.launcher.dialog;
|
||||
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.skcraft.concurrency.DefaultProgress;
|
||||
import com.skcraft.concurrency.ObservableFuture;
|
||||
import com.skcraft.concurrency.ProgressObservable;
|
||||
import com.skcraft.launcher.Launcher;
|
||||
@ -84,7 +86,7 @@ public class AccountSelectDialog extends JDialog {
|
||||
add(listPane, BorderLayout.CENTER);
|
||||
add(buttonsPanel, BorderLayout.SOUTH);
|
||||
|
||||
loginButton.addActionListener(ev -> attemptLogin(accountList.getSelectedValue()));
|
||||
loginButton.addActionListener(ev -> attemptExistingLogin(accountList.getSelectedValue()));
|
||||
cancelButton.addActionListener(ev -> dispose());
|
||||
|
||||
addMojangButton.addActionListener(ev -> {
|
||||
@ -96,9 +98,7 @@ public class AccountSelectDialog extends JDialog {
|
||||
}
|
||||
});
|
||||
|
||||
addMicrosoftButton.addActionListener(ev -> {
|
||||
// TODO
|
||||
});
|
||||
addMicrosoftButton.addActionListener(ev -> attemptMicrosoftLogin());
|
||||
|
||||
removeSelected.addActionListener(ev -> {
|
||||
if (accountList.getSelectedValue() != null) {
|
||||
@ -133,7 +133,25 @@ public class AccountSelectDialog extends JDialog {
|
||||
dispose();
|
||||
}
|
||||
|
||||
private void attemptLogin(SavedSession session) {
|
||||
private void attemptMicrosoftLogin() {
|
||||
ListenableFuture<?> future = launcher.getExecutor().submit(() -> {
|
||||
Session newSession = launcher.getMicrosoftLogin().login();
|
||||
|
||||
if (newSession != null) {
|
||||
launcher.getAccounts().add(newSession.toSavedSession());
|
||||
setResult(newSession);
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
String status = SharedLocale.tr("login.loggingInStatus");
|
||||
ProgressDialog.showProgress(this, future, new DefaultProgress(-1, status),
|
||||
SharedLocale.tr("login.loggingInTitle"), status);
|
||||
SwingHelper.addErrorDialogCallback(this, future);
|
||||
}
|
||||
|
||||
private void attemptExistingLogin(SavedSession session) {
|
||||
if (session == null) return;
|
||||
|
||||
LoginService loginService = launcher.getLoginService(session.getType());
|
||||
|
@ -99,15 +99,10 @@ login.playOffline=Play offline
|
||||
login.title=Minecraft Login
|
||||
login.idEmail=ID/Email\:
|
||||
login.password=Password\:
|
||||
login.forgetUser=Forget selected user
|
||||
login.forgetPassword=Forget password
|
||||
login.forgetAllPasswords=Forget all passwords...
|
||||
login.confirmForgetAllPasswords=Are you sure that you want to forget all saved passwords?
|
||||
login.forgetAllPasswordsTitle=Forget passwords
|
||||
login.noPasswordError=Please enter a password.
|
||||
login.noPasswordTitle=Missing Password
|
||||
login.loggingInTitle=Logging in...
|
||||
login.loggingInStatus=Logging in to Mojang...
|
||||
login.loggingInStatus=Logging in to Minecraft...
|
||||
login.noLoginError=Please enter your account details.
|
||||
login.noLoginTitle=Missing Account
|
||||
login.minecraftNotOwnedError=Sorry, Minecraft is not owned on that account.
|
||||
|
@ -13,6 +13,7 @@ versionManifestUrl=https://launchermeta.mojang.com/mc/game/version_manifest.json
|
||||
librariesSource=https://libraries.minecraft.net/
|
||||
assetsSource=http://resources.download.minecraft.net/
|
||||
yggdrasilAuthUrl=https://authserver.mojang.com/authenticate
|
||||
microsoftClientId=d18bb4d8-a27f-4451-a87f-fe6de4436813
|
||||
resetPasswordUrl=https://minecraft.net/resetpassword
|
||||
|
||||
newsUrl=http://update.skcraft.com/template/news.html?version=%s
|
||||
|
Loading…
Reference in New Issue
Block a user