/*
* SK's Minecraft Launcher
* Copyright (C) 2010-2014 Albert Pham <http://www.sk89q.com> and contributors
* Please see LICENSE.txt for license information.
*/
package com.skcraft.launcher.auth;
import com.fasterxml.jackson.annotation.*;
import com.skcraft.launcher.util.HttpRequest;
import lombok.Data;
import lombok.NonNull;
import lombok.ToString;
import java.io.IOException;
import java.net.URL;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
* Creates authenticated sessions using the Mojang Yggdrasil login protocol.
*/
public class YggdrasilLoginService implements LoginService {
private final URL authUrl;
/**
* Create a new login service with the given authentication URL.
*
* @param authUrl the authentication URL
*/
public YggdrasilLoginService(@NonNull URL authUrl) {
this.authUrl = authUrl;
}
@Override
public List<? extends Session> login(String agent, String id, String password)
throws IOException, InterruptedException, AuthenticationException {
Object payload = new AuthenticatePayload(new Agent(agent), id, password);
HttpRequest request = HttpRequest
.post(authUrl)
.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);
return response.getAvailableProfiles();
}
}
@Data
private static class Agent {
private final String name;
private final int version = 1;
}
@Data
private static class AuthenticatePayload {
private final Agent agent;
private final String username;
private final String password;
}
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
private static class AuthenticateResponse {
private String accessToken;
private String clientToken;
@JsonManagedReference private List<Profile> availableProfiles;
private Profile selectedProfile;
}
@Data
private static class ErrorResponse {
private String error;
private String errorMessage;
private String cause;
}
/**
* Return in the list of available profiles.
*/
@Data
@ToString(exclude = "response")
@JsonIgnoreProperties(ignoreUnknown = true)
private static class Profile implements Session {
@JsonProperty("id") private String uuid;
private String name;
private boolean legacy;
@JsonIgnore private final Map<String, String> userProperties = Collections.emptyMap();
@JsonBackReference private AuthenticateResponse response;
@Override
@JsonIgnore
public String getSessionToken() {
return String.format("token:%s:%s", getAccessToken(), getUuid());
}
@Override
@JsonIgnore
public String getClientToken() {
return response.getClientToken();
}
@Override
@JsonIgnore
public String getAccessToken() {
return response.getAccessToken();
}
@Override
@JsonIgnore
public UserType getUserType() {
return legacy ? UserType.LEGACY : UserType.MOJANG;
}
@Override
public boolean isOnline() {
return true;
}
}
}