/// <summary> /// XSTS Authenticate /// </summary> /// <remarks>(Don't ask me what is XSTS, I DONT KNOW)</remarks> /// <param name="xblResponse"></param> /// <returns></returns> public XSTSAuthenticateResponse XSTSAuthenticate(XblAuthenticateResponse xblResponse) { var request = new ProxiedWebRequest(xsts); request.UserAgent = userAgent; request.Accept = "application/json"; request.Headers.Add("x-xbl-contract-version", "1"); string payload = "{" + "\"Properties\": {" + "\"SandboxId\": \"RETAIL\"," + "\"UserTokens\": [" + "\"" + xblResponse.Token + "\"" + "]" + "}," + "\"RelyingParty\": \"rp://api.minecraftservices.com/\"," + "\"TokenType\": \"JWT\"" + "}"; var response = request.Post("application/json", payload); if (Settings.DebugMessages) { ConsoleIO.WriteLine(response.ToString()); } if (response.StatusCode == 200) { string jsonString = response.Body; Json.JSONData json = Json.ParseJson(jsonString); string token = json.Properties["Token"].StringValue; string userHash = json.Properties["DisplayClaims"].Properties["xui"].DataArray[0].Properties["uhs"].StringValue; return(new XSTSAuthenticateResponse() { Token = token, UserHash = userHash }); } else { if (response.StatusCode == 401) { Json.JSONData json = Json.ParseJson(response.Body); if (json.Properties["XErr"].StringValue == "2148916233") { throw new Exception("The account doesn't have an Xbox account"); } else if (json.Properties["XErr"].StringValue == "2148916238") { throw new Exception("The account is a child (under 18) and cannot proceed unless the account is added to a Family by an adult"); } else { throw new Exception("Unknown XSTS error code: " + json.Properties["XErr"].StringValue); } } else { throw new Exception("XSTS Authentication failed"); } } }
/// <summary> /// Perform request to obtain access token by code or by refresh token /// </summary> /// <param name="postData">Complete POST data for the request</param> /// <returns></returns> private static LoginResponse RequestToken(string postData) { var request = new ProxiedWebRequest(tokenUrl); request.UserAgent = "MCC/" + Program.Version; var response = request.Post("application/x-www-form-urlencoded", postData); var jsonData = Json.ParseJson(response.Body); // Error handling if (jsonData.Properties.ContainsKey("error")) { throw new Exception(jsonData.Properties["error_description"].StringValue); } else { string accessToken = jsonData.Properties["access_token"].StringValue; string refreshToken = jsonData.Properties["refresh_token"].StringValue; int expiresIn = int.Parse(jsonData.Properties["expires_in"].StringValue); // Extract email from JWT string payload = JwtPayloadDecode.GetPayload(jsonData.Properties["id_token"].StringValue); var jsonPayload = Json.ParseJson(payload); string email = jsonPayload.Properties["email"].StringValue; return(new LoginResponse() { Email = email, AccessToken = accessToken, RefreshToken = refreshToken, ExpiresIn = expiresIn }); } }
/// <summary> /// Xbox Live Authenticate /// </summary> /// <param name="loginResponse"></param> /// <returns></returns> public static XblAuthenticateResponse XblAuthenticate(Microsoft.LoginResponse loginResponse) { var request = new ProxiedWebRequest(xbl); request.UserAgent = userAgent; request.Accept = "application/json"; request.Headers.Add("x-xbl-contract-version", "0"); var accessToken = loginResponse.AccessToken; if (Settings.LoginMethod == "browser") { // Our own client ID must have d= in front of the token or HTTP status 400 // "Stolen" client ID must not have d= in front of the token or HTTP status 400 accessToken = "d=" + accessToken; } string payload = "{" + "\"Properties\": {" + "\"AuthMethod\": \"RPS\"," + "\"SiteName\": \"user.auth.xboxlive.com\"," + "\"RpsTicket\": \"" + accessToken + "\"" + "}," + "\"RelyingParty\": \"http://auth.xboxlive.com\"," + "\"TokenType\": \"JWT\"" + "}"; var response = request.Post("application/json", payload); if (Settings.DebugMessages) { ConsoleIO.WriteLine(response.ToString()); } if (response.StatusCode == 200) { string jsonString = response.Body; //Console.WriteLine(jsonString); Json.JSONData json = Json.ParseJson(jsonString); string token = json.Properties["Token"].StringValue; string userHash = json.Properties["DisplayClaims"].Properties["xui"].DataArray[0].Properties["uhs"].StringValue; return(new XblAuthenticateResponse() { Token = token, UserHash = userHash }); } else { throw new Exception("XBL Authentication failed"); } }
/// <summary> /// Login to Minecraft using the XSTS token and user hash obtained before /// </summary> /// <param name="userHash"></param> /// <param name="xstsToken"></param> /// <returns></returns> public string LoginWithXbox(string userHash, string xstsToken) { var request = new ProxiedWebRequest(loginWithXbox); request.Accept = "application/json"; string payload = "{\"identityToken\": \"XBL3.0 x=" + userHash + ";" + xstsToken + "\"}"; var response = request.Post("application/json", payload); if (Settings.DebugMessages) { ConsoleIO.WriteLine(response.ToString()); } string jsonString = response.Body; Json.JSONData json = Json.ParseJson(jsonString); return(json.Properties["access_token"].StringValue); }
/// <summary> /// Perform login request /// </summary> /// <remarks>This step is to send the login request by using the PreAuth response</remarks> /// <param name="email">Microsoft account email</param> /// <param name="password">Account password</param> /// <param name="preAuth"></param> /// <returns></returns> public UserLoginResponse UserLogin(string email, string password, PreAuthResponse preAuth) { var request = new ProxiedWebRequest(preAuth.UrlPost, preAuth.Cookie); request.UserAgent = userAgent; string postData = "login="******"&loginfmt=" + Uri.EscapeDataString(email) + "&passwd=" + Uri.EscapeDataString(password) + "&PPFT=" + Uri.EscapeDataString(preAuth.PPFT); var response = request.Post("application/x-www-form-urlencoded", postData); if (Settings.DebugMessages) { ConsoleIO.WriteLine(response.ToString()); } if (response.StatusCode >= 300 && response.StatusCode <= 399) { string url = response.Headers.Get("Location"); string hash = url.Split('#')[1]; var request2 = new ProxiedWebRequest(url); var response2 = request2.Get(); if (response2.StatusCode != 200) { throw new Exception("Authentication failed"); } if (string.IsNullOrEmpty(hash)) { if (confirm.IsMatch(response2.Body)) { throw new Exception("Activity confirmation required"); } else { throw new Exception("Invalid credentials or 2FA enabled"); } } var dict = Request.ParseQueryString(hash); //foreach (var pair in dict) //{ // Console.WriteLine("{0}: {1}", pair.Key, pair.Value); //} return(new UserLoginResponse() { AccessToken = dict["access_token"], RefreshToken = dict["refresh_token"], ExpiresIn = int.Parse(dict["expires_in"]) }); } else { throw new Exception("Unexpected response. Check your credentials. Response code: " + response.StatusCode); } }
/// <summary> /// Perform login request /// </summary> /// <remarks>This step is to send the login request by using the PreAuth response</remarks> /// <param name="email">Microsoft account email</param> /// <param name="password">Account password</param> /// <param name="preAuth"></param> /// <returns></returns> public static Microsoft.LoginResponse UserLogin(string email, string password, PreAuthResponse preAuth) { var request = new ProxiedWebRequest(preAuth.UrlPost, preAuth.Cookie); request.UserAgent = userAgent; string postData = "login="******"&loginfmt=" + Uri.EscapeDataString(email) + "&passwd=" + Uri.EscapeDataString(password) + "&PPFT=" + Uri.EscapeDataString(preAuth.PPFT); var response = request.Post("application/x-www-form-urlencoded", postData); if (Settings.DebugMessages) { ConsoleIO.WriteLine(response.ToString()); } if (response.StatusCode >= 300 && response.StatusCode <= 399) { string url = response.Headers.Get("Location"); string hash = url.Split('#')[1]; var request2 = new ProxiedWebRequest(url); var response2 = request2.Get(); if (response2.StatusCode != 200) { throw new Exception("Authentication failed"); } if (string.IsNullOrEmpty(hash)) { throw new Exception("Cannot extract access token"); } var dict = Request.ParseQueryString(hash); //foreach (var pair in dict) //{ // Console.WriteLine("{0}: {1}", pair.Key, pair.Value); //} return(new Microsoft.LoginResponse() { Email = email, AccessToken = dict["access_token"], RefreshToken = dict["refresh_token"], ExpiresIn = int.Parse(dict["expires_in"]) }); } else { if (twoFA.IsMatch(response.Body)) { // TODO: Handle 2FA throw new Exception("2FA enabled but not supported yet. Use browser sign-in method or try to disable 2FA in Microsoft account settings"); } else if (invalidAccount.IsMatch(response.Body)) { throw new Exception("Invalid credentials. Check your credentials"); } else { throw new Exception("Unexpected response. Check your credentials. Response code: " + response.StatusCode); } } }