package co.forsaken.projectindigo.session; import java.io.IOException; import java.net.URL; import java.util.List; import lombok.Data; import lombok.Getter; import lombok.NonNull; import lombok.Setter; import lombok.ToString; import org.codehaus.jackson.annotate.JsonIgnoreProperties; import co.forsaken.projectindigo.exceptions.AuthenticationException; import co.forsaken.projectindigo.utils.HttpRequest; @ToString(exclude = "password") public class YggdrasilSession implements Session { private static final URL AUTH_URL = HttpRequest.url("https://authserver.mojang.com/authenticate"); private static final URL REFRESH_URL = HttpRequest.url("https://authserver.mojang.com/refresh"); private final String id; @Getter @Setter private List<Identity> identities; @Getter @Setter private String password; @Getter @Setter private String clientToken; @Getter @Setter private String accessToken; public YggdrasilSession(@NonNull String id) { this.id = id; } public boolean isValid() { return accessToken != null; } public YggdrasilSession verify() throws AuthenticationException, InterruptedException, IOException { if (password != null && !password.isEmpty()) { authenticate(); } else if (accessToken != null && clientToken != null) { refresh(); } else { throw new AuthenticationException("Missing password/details", "Incomplete Credentials"); } return this; } private void authenticate() throws IOException, InterruptedException, AuthenticationException { Object payload = new AuthenticatePayload(id, password); HttpRequest request = HttpRequest.post(AUTH_URL).bodyJson(payload).execute(); if (request.getResponseCode() != 200) { ErrorResponse error = request.returnContent().asJson(ErrorResponse.class); throw new AuthenticationException(error.getErrorMessage(), error.getErrorMessage()); } else { AuthenticateResponse response = request.returnContent().asJson(AuthenticateResponse.class); accessToken = response.getAccessToken(); clientToken = response.getClientToken(); identities = response.getAvailableProfiles(); for (Identity identity : identities) { identity.setAccessToken(accessToken); identity.setClientToken(clientToken); } } } private void refresh() throws IOException, InterruptedException, AuthenticationException { Object payload = new RefreshPayload(accessToken, clientToken); HttpRequest request = HttpRequest.post(REFRESH_URL).bodyJson(payload).execute(); if (request.getResponseCode() != 200) { ErrorResponse error = request.returnContent().asJson(ErrorResponse.class); throw new AuthenticationException(error.getErrorMessage(), error.getErrorMessage()); } else { AuthenticateResponse response = request.returnContent().asJson(AuthenticateResponse.class); accessToken = response.getAccessToken(); clientToken = response.getClientToken(); identities = response.getAvailableProfiles(); for (Identity identity : identities) { identity.setAccessToken(accessToken); identity.setClientToken(clientToken); } } } @Data private static class Agent { private final String name = "Minecraft"; private final int version = 1; } @Data private static class AuthenticatePayload { private final Agent agent = new Agent(); private final String username; private final String password; } @Data @JsonIgnoreProperties(ignoreUnknown = true) private static class AuthenticateResponse { private String accessToken; private String clientToken; private List<Identity> availableProfiles; private Identity selectedProfile; } @Data private static class RefreshPayload { private final String accessToken; private final String clientToken; private Identity selectedProfile; } @Data private static class ErrorResponse { private String error; private String errorMessage; private String cause; } }