mirror of
https://e.coding.net/circlecloud/CTZLauncher.git
synced 2024-11-15 00:58:50 +00:00
351 lines
7.9 KiB
C#
351 lines
7.9 KiB
C#
|
namespace KMCCC.Modules.Yggdrasil
|
|||
|
{
|
|||
|
#region
|
|||
|
|
|||
|
using System;
|
|||
|
using System.Collections.Generic;
|
|||
|
using System.Net;
|
|||
|
using System.Text;
|
|||
|
using System.Threading;
|
|||
|
using System.Threading.Tasks;
|
|||
|
using LitJson;
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Yggdrasil(正版验证)客户端
|
|||
|
/// </summary>
|
|||
|
public class YggdrasilClient
|
|||
|
{
|
|||
|
public const string MojnagAuthServer = @"https://authserver.mojang.com";
|
|||
|
public const string Auth_Authentication = MojnagAuthServer + "/authenticate";
|
|||
|
public const string Auth_Refresh = MojnagAuthServer + "/refresh";
|
|||
|
private readonly object _locker = new object();
|
|||
|
|
|||
|
public YggdrasilClient() : this(Guid.NewGuid())
|
|||
|
{
|
|||
|
}
|
|||
|
|
|||
|
public YggdrasilClient(Guid clientToken)
|
|||
|
{
|
|||
|
ClientToken = clientToken;
|
|||
|
}
|
|||
|
|
|||
|
public Guid ClientToken { get; private set; }
|
|||
|
public Guid AccessToken { get; private set; }
|
|||
|
public Guid UUID { get; private set; }
|
|||
|
public string DisplayName { get; private set; }
|
|||
|
public string Properties { get; private set; }
|
|||
|
public string AccountType { get; private set; }
|
|||
|
|
|||
|
public void Clear()
|
|||
|
{
|
|||
|
lock (_locker)
|
|||
|
{
|
|||
|
AccessToken = Guid.Empty;
|
|||
|
UUID = Guid.Empty;
|
|||
|
DisplayName = string.Empty;
|
|||
|
Properties = string.Empty;
|
|||
|
AccountType = string.Empty;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private void UpdateFomrResponse(AuthenticationResponse response)
|
|||
|
{
|
|||
|
AccessToken = Guid.Parse(response.AccessToken);
|
|||
|
if (response.User != null)
|
|||
|
{
|
|||
|
AccountType = response.User.Legacy ? "Legacy" : "Mojang";
|
|||
|
Properties = response.User.Properties != null
|
|||
|
? response.User.Properties.ToJson()
|
|||
|
: "{}";
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
AccountType = "Mojang";
|
|||
|
Properties = "{}";
|
|||
|
}
|
|||
|
DisplayName = response.SelectedProfile.Name;
|
|||
|
UUID = Guid.Parse(response.SelectedProfile.Id);
|
|||
|
}
|
|||
|
|
|||
|
#region Refresh
|
|||
|
|
|||
|
public bool Refresh(bool twitchEnabled = true)
|
|||
|
{
|
|||
|
lock (_locker)
|
|||
|
{
|
|||
|
try
|
|||
|
{
|
|||
|
var wc = new WebClient();
|
|||
|
var requestBody = JsonMapper.ToJson(new RefreshRequest
|
|||
|
{
|
|||
|
Agent = Agent.Minecraft,
|
|||
|
AccessToken = AccessToken.ToString("N"),
|
|||
|
RequestUser = twitchEnabled,
|
|||
|
ClientToken = ClientToken.ToString("N")
|
|||
|
});
|
|||
|
var responseBody = wc.UploadString(new Uri(Auth_Refresh), requestBody);
|
|||
|
var response = JsonMapper.ToObject<AuthenticationResponse>(responseBody);
|
|||
|
if (response.AccessToken == null)
|
|||
|
{
|
|||
|
return false;
|
|||
|
}
|
|||
|
if (response.SelectedProfile == null)
|
|||
|
{
|
|||
|
return false;
|
|||
|
}
|
|||
|
UpdateFomrResponse(response);
|
|||
|
return true;
|
|||
|
}
|
|||
|
catch (Exception)
|
|||
|
{
|
|||
|
return false;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public bool Refresh(Guid accessToken, bool twitchEnabled = true)
|
|||
|
{
|
|||
|
lock (_locker)
|
|||
|
{
|
|||
|
Clear();
|
|||
|
try
|
|||
|
{
|
|||
|
var wc = new WebClient();
|
|||
|
var requestBody = JsonMapper.ToJson(new RefreshRequest
|
|||
|
{
|
|||
|
Agent = Agent.Minecraft,
|
|||
|
AccessToken = accessToken.ToString("N"),
|
|||
|
RequestUser = twitchEnabled,
|
|||
|
ClientToken = ClientToken.ToString("N")
|
|||
|
});
|
|||
|
var responseBody = wc.UploadString(new Uri(Auth_Refresh), requestBody);
|
|||
|
var response = JsonMapper.ToObject<AuthenticationResponse>(responseBody);
|
|||
|
if (response.AccessToken == null)
|
|||
|
{
|
|||
|
return false;
|
|||
|
}
|
|||
|
if (response.SelectedProfile == null)
|
|||
|
{
|
|||
|
return false;
|
|||
|
}
|
|||
|
UpdateFomrResponse(response);
|
|||
|
return true;
|
|||
|
}
|
|||
|
catch (Exception)
|
|||
|
{
|
|||
|
return false;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
#region Authenticate
|
|||
|
|
|||
|
public bool Authenticate(string email, string password, bool twitchEnabled = true)
|
|||
|
{
|
|||
|
lock (_locker)
|
|||
|
{
|
|||
|
Clear();
|
|||
|
try
|
|||
|
{
|
|||
|
var wc = new WebClient();
|
|||
|
var requestBody = JsonMapper.ToJson(new AuthenticationRequest
|
|||
|
{
|
|||
|
Agent = Agent.Minecraft,
|
|||
|
Email = email,
|
|||
|
Password = password,
|
|||
|
RequestUser = twitchEnabled,
|
|||
|
ClientToken = ClientToken.ToString("N")
|
|||
|
});
|
|||
|
var responseBody = wc.UploadString(new Uri(Auth_Authentication), requestBody);
|
|||
|
var response = JsonMapper.ToObject<AuthenticationResponse>(responseBody);
|
|||
|
if (response.AccessToken == null)
|
|||
|
{
|
|||
|
return false;
|
|||
|
}
|
|||
|
if (response.SelectedProfile == null)
|
|||
|
{
|
|||
|
return false;
|
|||
|
}
|
|||
|
UpdateFomrResponse(response);
|
|||
|
return true;
|
|||
|
}
|
|||
|
catch (Exception)
|
|||
|
{
|
|||
|
return false;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public Task<bool> AuthenticateAsync(string email, string password, bool twitchEnabled = true, CancellationToken token = default(CancellationToken))
|
|||
|
{
|
|||
|
Clear();
|
|||
|
var task = new TaskCompletionSource<bool>(token);
|
|||
|
try
|
|||
|
{
|
|||
|
var wc = new WebClient();
|
|||
|
var requestBody = JsonMapper.ToJson(new AuthenticationRequest
|
|||
|
{
|
|||
|
Agent = Agent.Minecraft,
|
|||
|
Email = email,
|
|||
|
Password = password,
|
|||
|
RequestUser = twitchEnabled,
|
|||
|
ClientToken = ClientToken.ToString("N")
|
|||
|
});
|
|||
|
wc.UploadStringCompleted += (sender, e) =>
|
|||
|
{
|
|||
|
try
|
|||
|
{
|
|||
|
if (e.Error != null)
|
|||
|
{
|
|||
|
task.SetException(e.Error);
|
|||
|
return;
|
|||
|
}
|
|||
|
var response = JsonMapper.ToObject<AuthenticationResponse>(e.Result);
|
|||
|
if ((response.AccessToken == null) || (response.SelectedProfile == null))
|
|||
|
{
|
|||
|
task.SetResult(false);
|
|||
|
return;
|
|||
|
}
|
|||
|
UpdateFomrResponse(response);
|
|||
|
task.SetResult(true);
|
|||
|
}
|
|||
|
catch (Exception exception)
|
|||
|
{
|
|||
|
task.SetException(exception);
|
|||
|
}
|
|||
|
};
|
|||
|
wc.UploadStringAsync(new Uri(Auth_Authentication), requestBody);
|
|||
|
return task.Task;
|
|||
|
}
|
|||
|
catch (Exception exception)
|
|||
|
{
|
|||
|
task.SetException(exception);
|
|||
|
return task.Task;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
#endregion
|
|||
|
}
|
|||
|
|
|||
|
#region Request & Response
|
|||
|
|
|||
|
public class RefreshRequest
|
|||
|
{
|
|||
|
[JsonPropertyName("agent")]
|
|||
|
public Agent Agent { get; set; }
|
|||
|
|
|||
|
[JsonPropertyName("accessToken")]
|
|||
|
public string AccessToken { get; set; }
|
|||
|
|
|||
|
[JsonPropertyName("requestUser")]
|
|||
|
public bool RequestUser { get; set; }
|
|||
|
|
|||
|
[JsonPropertyName("clientToken")]
|
|||
|
public string ClientToken { get; set; }
|
|||
|
}
|
|||
|
|
|||
|
public class AuthenticationRequest
|
|||
|
{
|
|||
|
[JsonPropertyName("agent")]
|
|||
|
public Agent Agent { get; set; }
|
|||
|
|
|||
|
[JsonPropertyName("username")]
|
|||
|
public string Email { get; set; }
|
|||
|
|
|||
|
[JsonPropertyName("password")]
|
|||
|
public string Password { get; set; }
|
|||
|
|
|||
|
[JsonPropertyName("requestUser")]
|
|||
|
public bool RequestUser { get; set; }
|
|||
|
|
|||
|
[JsonPropertyName("clientToken")]
|
|||
|
public string ClientToken { get; set; }
|
|||
|
}
|
|||
|
|
|||
|
public class AuthenticationResponse
|
|||
|
{
|
|||
|
[JsonPropertyName("clientToken")]
|
|||
|
public string ClientToken { get; set; }
|
|||
|
|
|||
|
[JsonPropertyName("accessToken")]
|
|||
|
public string AccessToken { get; set; }
|
|||
|
|
|||
|
[JsonPropertyName("availableProfiles")]
|
|||
|
public List<GameProfile> AvailableProfiles { get; set; }
|
|||
|
|
|||
|
[JsonPropertyName("selectedProfile")]
|
|||
|
public GameProfile SelectedProfile { get; set; }
|
|||
|
|
|||
|
[JsonPropertyName("user")]
|
|||
|
public User User { get; set; }
|
|||
|
|
|||
|
[JsonPropertyName("error")]
|
|||
|
public string Error { get; set; }
|
|||
|
|
|||
|
[JsonPropertyName("errorMessage")]
|
|||
|
public string ErrorMessage { get; set; }
|
|||
|
|
|||
|
[JsonPropertyName("cause")]
|
|||
|
public string Cause { get; set; }
|
|||
|
}
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
public class Agent
|
|||
|
{
|
|||
|
public static readonly Agent Minecraft = new Agent {Name = "Minecraft", Version = 1};
|
|||
|
|
|||
|
[JsonPropertyName("name")]
|
|||
|
public string Name { get; set; }
|
|||
|
|
|||
|
[JsonPropertyName("version")]
|
|||
|
public int Version { get; set; }
|
|||
|
}
|
|||
|
|
|||
|
public class User
|
|||
|
{
|
|||
|
[JsonPropertyName("id")]
|
|||
|
public string Id { get; set; }
|
|||
|
|
|||
|
[JsonPropertyName("properties")]
|
|||
|
public List<Property> Properties { get; set; }
|
|||
|
|
|||
|
[JsonPropertyName("legacy")]
|
|||
|
public bool Legacy { get; set; }
|
|||
|
}
|
|||
|
|
|||
|
public class Property
|
|||
|
{
|
|||
|
[JsonPropertyName("name")]
|
|||
|
public string Name { get; set; }
|
|||
|
|
|||
|
[JsonPropertyName("value")]
|
|||
|
public string Value { get; set; }
|
|||
|
}
|
|||
|
|
|||
|
public class GameProfile
|
|||
|
{
|
|||
|
[JsonPropertyName("id")]
|
|||
|
public string Id { get; set; }
|
|||
|
|
|||
|
[JsonPropertyName("name")]
|
|||
|
public string Name { get; set; }
|
|||
|
}
|
|||
|
|
|||
|
internal static class Extensions
|
|||
|
{
|
|||
|
internal static string ToJson(this List<Property> properties)
|
|||
|
{
|
|||
|
var sb = new StringBuilder().Append('{');
|
|||
|
foreach (var item in properties)
|
|||
|
{
|
|||
|
sb.Append('\"').Append(item.Name).Append("\":[\"").Append(item.Value).Append("\"]");
|
|||
|
}
|
|||
|
return sb.Append("}").ToString();
|
|||
|
}
|
|||
|
}
|
|||
|
}
|