mirror of
https://github.com/MCCTeam/Minecraft-Console-Client
synced 2025-10-14 21:22:49 +00:00
Implement browser sign-in method (#1447)
* Implement browser sign-in method * Handle empty link * Improve * Handle user cancel login
This commit is contained in:
parent
424f514be2
commit
71eb1dca17
6 changed files with 124 additions and 8 deletions
|
|
@ -136,16 +136,21 @@ namespace MinecraftClient
|
||||||
}
|
}
|
||||||
|
|
||||||
//Asking the user to type in missing data such as Username and Password
|
//Asking the user to type in missing data such as Username and Password
|
||||||
|
bool useBrowser = Settings.AccountType == ProtocolHandler.AccountType.Microsoft && Settings.LoginMethod == "browser";
|
||||||
if (Settings.Login == "")
|
if (Settings.Login == "")
|
||||||
{
|
{
|
||||||
|
if (useBrowser)
|
||||||
|
ConsoleIO.WriteLine("Press Enter to skip session cache checking and continue sign-in with browser");
|
||||||
Console.Write(ConsoleIO.BasicIO ? Translations.Get("mcc.login_basic_io") + "\n" : Translations.Get("mcc.login"));
|
Console.Write(ConsoleIO.BasicIO ? Translations.Get("mcc.login_basic_io") + "\n" : Translations.Get("mcc.login"));
|
||||||
Settings.Login = Console.ReadLine();
|
Settings.Login = Console.ReadLine();
|
||||||
}
|
}
|
||||||
if (Settings.Password == "" && (Settings.SessionCaching == CacheType.None || !SessionCache.Contains(Settings.Login.ToLower())))
|
if (Settings.Password == ""
|
||||||
|
&& (Settings.SessionCaching == CacheType.None || !SessionCache.Contains(Settings.Login.ToLower()))
|
||||||
|
&& !useBrowser)
|
||||||
{
|
{
|
||||||
RequestPassword();
|
RequestPassword();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
startupargs = args;
|
startupargs = args;
|
||||||
InitializeClient();
|
InitializeClient();
|
||||||
|
|
@ -209,7 +214,6 @@ namespace MinecraftClient
|
||||||
SessionCache.Store(Settings.Login.ToLower(), session);
|
SessionCache.Store(Settings.Login.ToLower(), session);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result == ProtocolHandler.LoginResult.Success)
|
if (result == ProtocolHandler.LoginResult.Success)
|
||||||
|
|
@ -315,6 +319,7 @@ namespace MinecraftClient
|
||||||
case ProtocolHandler.LoginResult.NotPremium: failureReason = "error.login.premium"; break;
|
case ProtocolHandler.LoginResult.NotPremium: failureReason = "error.login.premium"; break;
|
||||||
case ProtocolHandler.LoginResult.OtherError: failureReason = "error.login.network"; break;
|
case ProtocolHandler.LoginResult.OtherError: failureReason = "error.login.network"; break;
|
||||||
case ProtocolHandler.LoginResult.SSLError: failureReason = "error.login.ssl"; break;
|
case ProtocolHandler.LoginResult.SSLError: failureReason = "error.login.ssl"; break;
|
||||||
|
case ProtocolHandler.LoginResult.UserCancel: failureReason = "error.login.cancel"; break;
|
||||||
default: failureReason = "error.login.unknown"; break;
|
default: failureReason = "error.login.unknown"; break;
|
||||||
}
|
}
|
||||||
failureMessage += Translations.Get(failureReason);
|
failureMessage += Translations.Get(failureReason);
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,8 @@ namespace MinecraftClient.Protocol
|
||||||
private Regex invalidAccount = new Regex("Sign in to", RegexOptions.IgnoreCase);
|
private Regex invalidAccount = new Regex("Sign in to", RegexOptions.IgnoreCase);
|
||||||
private Regex twoFA = new Regex("Help us protect your account", RegexOptions.IgnoreCase);
|
private Regex twoFA = new Regex("Help us protect your account", RegexOptions.IgnoreCase);
|
||||||
|
|
||||||
|
public string SignInUrl { get { return authorize; } }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Pre-authentication
|
/// Pre-authentication
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -114,7 +116,7 @@ namespace MinecraftClient.Protocol
|
||||||
if (twoFA.IsMatch(response.Body))
|
if (twoFA.IsMatch(response.Body))
|
||||||
{
|
{
|
||||||
// TODO: Handle 2FA
|
// TODO: Handle 2FA
|
||||||
throw new Exception("2FA enabled but not supported yet. Try to disable it in Microsoft account settings");
|
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))
|
else if (invalidAccount.IsMatch(response.Body))
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -330,7 +330,7 @@ namespace MinecraftClient.Protocol
|
||||||
return Protocol18Forge.ServerForceForge(protocol);
|
return Protocol18Forge.ServerForceForge(protocol);
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum LoginResult { OtherError, ServiceUnavailable, SSLError, Success, WrongPassword, AccountMigrated, NotPremium, LoginRequired, InvalidToken, InvalidResponse, NullError };
|
public enum LoginResult { OtherError, ServiceUnavailable, SSLError, Success, WrongPassword, AccountMigrated, NotPremium, LoginRequired, InvalidToken, InvalidResponse, NullError, UserCancel };
|
||||||
public enum AccountType { Mojang, Microsoft };
|
public enum AccountType { Mojang, Microsoft };
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -344,7 +344,10 @@ namespace MinecraftClient.Protocol
|
||||||
{
|
{
|
||||||
if (type == AccountType.Microsoft)
|
if (type == AccountType.Microsoft)
|
||||||
{
|
{
|
||||||
return MicrosoftLogin(user, pass, out session);
|
if (Settings.LoginMethod == "mcc")
|
||||||
|
return MicrosoftMCCLogin(user, pass, out session);
|
||||||
|
else
|
||||||
|
return MicrosoftBrowserLogin(out session);
|
||||||
}
|
}
|
||||||
else if (type == AccountType.Mojang)
|
else if (type == AccountType.Mojang)
|
||||||
{
|
{
|
||||||
|
|
@ -353,6 +356,13 @@ namespace MinecraftClient.Protocol
|
||||||
else throw new InvalidOperationException("Account type must be Mojang or Microsoft");
|
else throw new InvalidOperationException("Account type must be Mojang or Microsoft");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Login using Mojang account. Will be outdated after account migration
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="user"></param>
|
||||||
|
/// <param name="pass"></param>
|
||||||
|
/// <param name="session"></param>
|
||||||
|
/// <returns></returns>
|
||||||
private static LoginResult MojangLogin(string user, string pass, out SessionToken session)
|
private static LoginResult MojangLogin(string user, string pass, out SessionToken session)
|
||||||
{
|
{
|
||||||
session = new SessionToken() { ClientID = Guid.NewGuid().ToString().Replace("-", "") };
|
session = new SessionToken() { ClientID = Guid.NewGuid().ToString().Replace("-", "") };
|
||||||
|
|
@ -432,7 +442,101 @@ namespace MinecraftClient.Protocol
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static LoginResult MicrosoftLogin(string email, string password, out SessionToken session)
|
/// <summary>
|
||||||
|
/// Sign-in to Microsoft Account without using browser. Only works if 2FA is disabled.
|
||||||
|
/// Might not work well in some rare cases.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="email"></param>
|
||||||
|
/// <param name="password"></param>
|
||||||
|
/// <param name="session"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private static LoginResult MicrosoftMCCLogin(string email, string password, out SessionToken session)
|
||||||
|
{
|
||||||
|
var ms = new XboxLive();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var msaResponse = ms.UserLogin(email, password, ms.PreAuth());
|
||||||
|
return MicrosoftLogin(msaResponse, out session);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
session = new SessionToken() { ClientID = Guid.NewGuid().ToString().Replace("-", "") };
|
||||||
|
ConsoleIO.WriteLineFormatted("§cMicrosoft authenticate failed: " + e.Message);
|
||||||
|
if (Settings.DebugMessages)
|
||||||
|
{
|
||||||
|
ConsoleIO.WriteLineFormatted("§c" + e.StackTrace);
|
||||||
|
}
|
||||||
|
return LoginResult.WrongPassword; // Might not always be wrong password
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sign-in to Microsoft Account by asking user to open sign-in page using browser.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// The downside is this require user to copy and paste lengthy content from and to console.
|
||||||
|
/// Sign-in page: 218 chars
|
||||||
|
/// Response URL: around 1500 chars
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="session"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static LoginResult MicrosoftBrowserLogin(out SessionToken session)
|
||||||
|
{
|
||||||
|
var ms = new XboxLive();
|
||||||
|
string[] askOpenLink =
|
||||||
|
{
|
||||||
|
"Copy the following link to your browser and login to your Microsoft Account",
|
||||||
|
">>>>>>>>>>>>>>>>>>>>>>",
|
||||||
|
"",
|
||||||
|
ms.SignInUrl,
|
||||||
|
"",
|
||||||
|
"<<<<<<<<<<<<<<<<<<<<<<",
|
||||||
|
"NOTICE: Once successfully logged in, you will see a blank page in your web browser.",
|
||||||
|
"Copy the contents of your browser's address bar and paste it below to complete the login process.",
|
||||||
|
};
|
||||||
|
ConsoleIO.WriteLine(string.Join("\n", askOpenLink));
|
||||||
|
string[] parts = { };
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
string link = ConsoleIO.ReadLine();
|
||||||
|
if (string.IsNullOrEmpty(link))
|
||||||
|
{
|
||||||
|
session = new SessionToken();
|
||||||
|
return LoginResult.UserCancel;
|
||||||
|
}
|
||||||
|
parts = link.Split('#');
|
||||||
|
if (parts.Length < 2)
|
||||||
|
{
|
||||||
|
ConsoleIO.WriteLine("Invalid link. Please try again.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else break;
|
||||||
|
}
|
||||||
|
string hash = parts[1];
|
||||||
|
var dict = Request.ParseQueryString(hash);
|
||||||
|
var msaResponse = new XboxLive.UserLoginResponse()
|
||||||
|
{
|
||||||
|
AccessToken = dict["access_token"],
|
||||||
|
RefreshToken = dict["refresh_token"],
|
||||||
|
ExpiresIn = int.Parse(dict["expires_in"])
|
||||||
|
};
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return MicrosoftLogin(msaResponse, out session);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
session = new SessionToken() { ClientID = Guid.NewGuid().ToString().Replace("-", "") };
|
||||||
|
ConsoleIO.WriteLineFormatted("§cMicrosoft authenticate failed: " + e.Message);
|
||||||
|
if (Settings.DebugMessages)
|
||||||
|
{
|
||||||
|
ConsoleIO.WriteLineFormatted("§c" + e.StackTrace);
|
||||||
|
}
|
||||||
|
return LoginResult.WrongPassword; // Might not always be wrong password
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static LoginResult MicrosoftLogin(XboxLive.UserLoginResponse msaResponse, out SessionToken session)
|
||||||
{
|
{
|
||||||
session = new SessionToken() { ClientID = Guid.NewGuid().ToString().Replace("-", "") };
|
session = new SessionToken() { ClientID = Guid.NewGuid().ToString().Replace("-", "") };
|
||||||
var ms = new XboxLive();
|
var ms = new XboxLive();
|
||||||
|
|
@ -440,7 +544,6 @@ namespace MinecraftClient.Protocol
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var msaResponse = ms.UserLogin(email, password, ms.PreAuth());
|
|
||||||
var xblResponse = ms.XblAuthenticate(msaResponse);
|
var xblResponse = ms.XblAuthenticate(msaResponse);
|
||||||
var xsts = ms.XSTSAuthenticate(xblResponse); // Might throw even password correct
|
var xsts = ms.XSTSAuthenticate(xblResponse); // Might throw even password correct
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ login=
|
||||||
password=
|
password=
|
||||||
serverip=
|
serverip=
|
||||||
type=mojang # Account type. mojang or microsoft
|
type=mojang # Account type. mojang or microsoft
|
||||||
|
method=mcc # Microsoft Account sign-in method. mcc OR browser
|
||||||
|
|
||||||
# Advanced settings
|
# Advanced settings
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,7 @@ error.login.network=Network error.
|
||||||
error.login.ssl=SSL Error.
|
error.login.ssl=SSL Error.
|
||||||
error.login.unknown=Unknown Error.
|
error.login.unknown=Unknown Error.
|
||||||
error.login.ssl_help=§8It appears that you are using Mono to run this program.\nThe first time, you have to import HTTPS certificates using:\nmozroots --import --ask-remove
|
error.login.ssl_help=§8It appears that you are using Mono to run this program.\nThe first time, you have to import HTTPS certificates using:\nmozroots --import --ask-remove
|
||||||
|
error.login.cancel=User cancelled.
|
||||||
error.login_failed=Failed to login to this server.
|
error.login_failed=Failed to login to this server.
|
||||||
error.join=Failed to join this server.
|
error.join=Failed to join this server.
|
||||||
error.connect=Failed to connect to this IP.
|
error.connect=Failed to connect to this IP.
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ namespace MinecraftClient
|
||||||
public static string Username = "";
|
public static string Username = "";
|
||||||
public static string Password = "";
|
public static string Password = "";
|
||||||
public static ProtocolHandler.AccountType AccountType = ProtocolHandler.AccountType.Mojang;
|
public static ProtocolHandler.AccountType AccountType = ProtocolHandler.AccountType.Mojang;
|
||||||
|
public static string LoginMethod = "mcc";
|
||||||
public static string ServerIP = "";
|
public static string ServerIP = "";
|
||||||
public static ushort ServerPort = 25565;
|
public static ushort ServerPort = 25565;
|
||||||
public static string ServerVersion = "";
|
public static string ServerVersion = "";
|
||||||
|
|
@ -277,6 +278,9 @@ namespace MinecraftClient
|
||||||
case "type": AccountType = argValue == "mojang"
|
case "type": AccountType = argValue == "mojang"
|
||||||
? ProtocolHandler.AccountType.Mojang
|
? ProtocolHandler.AccountType.Mojang
|
||||||
: ProtocolHandler.AccountType.Microsoft; break;
|
: ProtocolHandler.AccountType.Microsoft; break;
|
||||||
|
case "method": LoginMethod = argValue.ToLower() == "browser"
|
||||||
|
? "browser"
|
||||||
|
: "mcc"; break;
|
||||||
case "serverip": if (!SetServerIP(argValue)) serverAlias = argValue; ; break;
|
case "serverip": if (!SetServerIP(argValue)) serverAlias = argValue; ; break;
|
||||||
case "singlecommand": SingleCommand = argValue; break;
|
case "singlecommand": SingleCommand = argValue; break;
|
||||||
case "language": Language = argValue; break;
|
case "language": Language = argValue; break;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue