mirror of
https://github.com/SteamRE/DepotDownloader.git
synced 2026-02-04 05:31:18 +01:00
Merge pull request #401 from SteamRE/auth/accesstoken
Add support for new Steam authentication
This commit is contained in:
@@ -17,8 +17,10 @@ namespace DepotDownloader
|
||||
[ProtoMember(2, IsRequired = false)]
|
||||
public ConcurrentDictionary<string, int> ContentServerPenalty { get; private set; }
|
||||
|
||||
[ProtoMember(3, IsRequired = false)]
|
||||
public Dictionary<string, string> LoginKeys { get; private set; }
|
||||
// Member 3 was a Dictionary<string, string> for LoginKeys.
|
||||
|
||||
[ProtoMember(4, IsRequired = false)]
|
||||
public Dictionary<string, string> LoginTokens { get; private set; }
|
||||
|
||||
string FileName;
|
||||
|
||||
@@ -26,7 +28,7 @@ namespace DepotDownloader
|
||||
{
|
||||
SentryData = new Dictionary<string, byte[]>();
|
||||
ContentServerPenalty = new ConcurrentDictionary<string, int>();
|
||||
LoginKeys = new Dictionary<string, string>();
|
||||
LoginTokens = new Dictionary<string, string>();
|
||||
}
|
||||
|
||||
static bool Loaded
|
||||
|
||||
@@ -312,20 +312,20 @@ namespace DepotDownloader
|
||||
|
||||
public static bool InitializeSteam3(string username, string password)
|
||||
{
|
||||
string loginKey = null;
|
||||
string loginToken = null;
|
||||
|
||||
if (username != null && Config.RememberPassword)
|
||||
{
|
||||
_ = AccountSettingsStore.Instance.LoginKeys.TryGetValue(username, out loginKey);
|
||||
_ = AccountSettingsStore.Instance.LoginTokens.TryGetValue(username, out loginToken);
|
||||
}
|
||||
|
||||
steam3 = new Steam3Session(
|
||||
new SteamUser.LogOnDetails
|
||||
{
|
||||
Username = username,
|
||||
Password = loginKey == null ? password : null,
|
||||
Password = loginToken == null ? password : null,
|
||||
ShouldRememberPassword = Config.RememberPassword,
|
||||
LoginKey = loginKey,
|
||||
AccessToken = loginToken,
|
||||
LoginID = Config.LoginID ?? 0x534B32, // "SK2"
|
||||
}
|
||||
);
|
||||
@@ -352,7 +352,6 @@ namespace DepotDownloader
|
||||
if (steam3 == null)
|
||||
return;
|
||||
|
||||
steam3.TryWaitForLoginKey();
|
||||
steam3.Disconnect();
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="protobuf-net" Version="3.2.16" />
|
||||
<PackageReference Include="SteamKit2" Version="2.4.1" />
|
||||
<PackageReference Include="QRCoder" Version="1.4.3" />
|
||||
<PackageReference Include="SteamKit2" Version="2.5.0-Beta.1" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -22,10 +22,11 @@ namespace DepotDownloader
|
||||
public int MaxServers { get; set; }
|
||||
public int MaxDownloads { get; set; }
|
||||
|
||||
public string SuppliedPassword { get; set; }
|
||||
public bool RememberPassword { get; set; }
|
||||
|
||||
// A Steam LoginID to allow multiple concurrent connections
|
||||
public uint? LoginID { get; set; }
|
||||
|
||||
public bool UseQrCode { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@ namespace DepotDownloader
|
||||
var username = GetParameter<string>(args, "-username") ?? GetParameter<string>(args, "-user");
|
||||
var password = GetParameter<string>(args, "-password") ?? GetParameter<string>(args, "-pass");
|
||||
ContentDownloader.Config.RememberPassword = HasParameter(args, "-remember-password");
|
||||
ContentDownloader.Config.UseQrCode = HasParameter(args, "-qr");
|
||||
|
||||
ContentDownloader.Config.DownloadManifestOnly = HasParameter(args, "-manifest-only");
|
||||
|
||||
@@ -268,31 +269,31 @@ namespace DepotDownloader
|
||||
|
||||
static bool InitializeSteam(string username, string password)
|
||||
{
|
||||
if (username != null && password == null && (!ContentDownloader.Config.RememberPassword || !AccountSettingsStore.Instance.LoginKeys.ContainsKey(username)))
|
||||
if (!ContentDownloader.Config.UseQrCode)
|
||||
{
|
||||
do
|
||||
if (username != null && password == null && (!ContentDownloader.Config.RememberPassword || !AccountSettingsStore.Instance.LoginTokens.ContainsKey(username)))
|
||||
{
|
||||
Console.Write("Enter account password for \"{0}\": ", username);
|
||||
if (Console.IsInputRedirected)
|
||||
do
|
||||
{
|
||||
password = Console.ReadLine();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Avoid console echoing of password
|
||||
password = Util.ReadPassword();
|
||||
}
|
||||
Console.Write("Enter account password for \"{0}\": ", username);
|
||||
if (Console.IsInputRedirected)
|
||||
{
|
||||
password = Console.ReadLine();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Avoid console echoing of password
|
||||
password = Util.ReadPassword();
|
||||
}
|
||||
|
||||
Console.WriteLine();
|
||||
} while (string.Empty == password);
|
||||
Console.WriteLine();
|
||||
} while (string.Empty == password);
|
||||
}
|
||||
else if (username == null)
|
||||
{
|
||||
Console.WriteLine("No username given. Using anonymous account with dedicated server subscription.");
|
||||
}
|
||||
}
|
||||
else if (username == null)
|
||||
{
|
||||
Console.WriteLine("No username given. Using anonymous account with dedicated server subscription.");
|
||||
}
|
||||
|
||||
// capture the supplied password in case we need to re-use it after checking the login key
|
||||
ContentDownloader.Config.SuppliedPassword = password;
|
||||
|
||||
return ContentDownloader.InitializeSteam3(username, password);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,9 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using QRCoder;
|
||||
using SteamKit2;
|
||||
using SteamKit2.Authentication;
|
||||
using SteamKit2.Internal;
|
||||
|
||||
namespace DepotDownloader
|
||||
@@ -53,11 +55,11 @@ namespace DepotDownloader
|
||||
bool bAborted;
|
||||
bool bExpectingDisconnectRemote;
|
||||
bool bDidDisconnect;
|
||||
bool bDidReceiveLoginKey;
|
||||
bool bIsConnectionRecovery;
|
||||
int connectionBackoff;
|
||||
int seq; // more hack fixes
|
||||
DateTime connectTime;
|
||||
AuthSession authSession;
|
||||
|
||||
// input
|
||||
readonly SteamUser.LogOnDetails logonDetails;
|
||||
@@ -72,14 +74,13 @@ namespace DepotDownloader
|
||||
{
|
||||
this.logonDetails = details;
|
||||
|
||||
this.authenticatedUser = details.Username != null;
|
||||
this.authenticatedUser = details.Username != null || ContentDownloader.Config.UseQrCode;
|
||||
this.credentials = new Credentials();
|
||||
this.bConnected = false;
|
||||
this.bConnecting = false;
|
||||
this.bAborted = false;
|
||||
this.bExpectingDisconnectRemote = false;
|
||||
this.bDidDisconnect = false;
|
||||
this.bDidReceiveLoginKey = false;
|
||||
this.seq = 0;
|
||||
|
||||
this.AppTokens = new Dictionary<uint, ulong>();
|
||||
@@ -112,11 +113,10 @@ namespace DepotDownloader
|
||||
this.callbacks.Subscribe<SteamUser.SessionTokenCallback>(SessionTokenCallback);
|
||||
this.callbacks.Subscribe<SteamApps.LicenseListCallback>(LicenseListCallback);
|
||||
this.callbacks.Subscribe<SteamUser.UpdateMachineAuthCallback>(UpdateMachineAuthCallback);
|
||||
this.callbacks.Subscribe<SteamUser.LoginKeyCallback>(LoginKeyCallback);
|
||||
|
||||
Console.Write("Connecting to Steam3...");
|
||||
|
||||
if (authenticatedUser)
|
||||
if (details.Username != null)
|
||||
{
|
||||
var fi = new FileInfo(String.Format("{0}.sentryFile", logonDetails.Username));
|
||||
if (AccountSettingsStore.Instance.SentryData != null && AccountSettingsStore.Instance.SentryData.ContainsKey(logonDetails.Username))
|
||||
@@ -419,7 +419,6 @@ namespace DepotDownloader
|
||||
bExpectingDisconnectRemote = false;
|
||||
bDidDisconnect = false;
|
||||
bIsConnectionRecovery = false;
|
||||
bDidReceiveLoginKey = false;
|
||||
}
|
||||
|
||||
void Connect()
|
||||
@@ -428,6 +427,7 @@ namespace DepotDownloader
|
||||
bConnected = false;
|
||||
bConnecting = true;
|
||||
connectionBackoff = 0;
|
||||
authSession = null;
|
||||
|
||||
ResetConnectionFlags();
|
||||
|
||||
@@ -466,23 +466,6 @@ namespace DepotDownloader
|
||||
steamClient.Disconnect();
|
||||
}
|
||||
|
||||
public void TryWaitForLoginKey()
|
||||
{
|
||||
if (logonDetails.Username == null || !credentials.LoggedOn || !ContentDownloader.Config.RememberPassword) return;
|
||||
|
||||
var totalWaitPeriod = DateTime.Now.AddSeconds(3);
|
||||
|
||||
while (true)
|
||||
{
|
||||
var now = DateTime.Now;
|
||||
if (now >= totalWaitPeriod) break;
|
||||
|
||||
if (bDidReceiveLoginKey) break;
|
||||
|
||||
callbacks.RunWaitAllCallbacks(TimeSpan.FromMilliseconds(100));
|
||||
}
|
||||
}
|
||||
|
||||
private void WaitForCallbacks()
|
||||
{
|
||||
callbacks.RunWaitCallbacks(TimeSpan.FromSeconds(1));
|
||||
@@ -496,11 +479,17 @@ namespace DepotDownloader
|
||||
}
|
||||
}
|
||||
|
||||
private void ConnectedCallback(SteamClient.ConnectedCallback connected)
|
||||
private async void ConnectedCallback(SteamClient.ConnectedCallback connected)
|
||||
{
|
||||
Console.WriteLine(" Done!");
|
||||
bConnecting = false;
|
||||
bConnected = true;
|
||||
|
||||
// Update our tracking so that we don't time out, even if we need to reconnect multiple times,
|
||||
// e.g. if the authentication phase takes a while and therefore multiple connections.
|
||||
connectTime = DateTime.Now;
|
||||
connectionBackoff = 0;
|
||||
|
||||
if (!authenticatedUser)
|
||||
{
|
||||
Console.Write("Logging anonymously into Steam3...");
|
||||
@@ -508,7 +497,102 @@ namespace DepotDownloader
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.Write("Logging '{0}' into Steam3...", logonDetails.Username);
|
||||
if (logonDetails.Username != null)
|
||||
{
|
||||
Console.WriteLine("Logging '{0}' into Steam3...", logonDetails.Username);
|
||||
}
|
||||
|
||||
if (authSession is null)
|
||||
{
|
||||
if (logonDetails.Username != null && logonDetails.Password != null && logonDetails.AccessToken is null)
|
||||
{
|
||||
try
|
||||
{
|
||||
authSession = await steamClient.Authentication.BeginAuthSessionViaCredentialsAsync(new SteamKit2.Authentication.AuthSessionDetails
|
||||
{
|
||||
Username = logonDetails.Username,
|
||||
Password = logonDetails.Password,
|
||||
IsPersistentSession = ContentDownloader.Config.RememberPassword,
|
||||
Authenticator = new UserConsoleAuthenticator(),
|
||||
});
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
return;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.Error.WriteLine("Failed to authenticate with Steam: " + ex.Message);
|
||||
Abort(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (logonDetails.AccessToken is null && ContentDownloader.Config.UseQrCode)
|
||||
{
|
||||
Console.WriteLine("Logging in with QR code...");
|
||||
|
||||
try
|
||||
{
|
||||
var session = await steamClient.Authentication.BeginAuthSessionViaQRAsync(new AuthSessionDetails
|
||||
{
|
||||
IsPersistentSession = ContentDownloader.Config.RememberPassword,
|
||||
Authenticator = new UserConsoleAuthenticator(),
|
||||
});
|
||||
|
||||
authSession = session;
|
||||
|
||||
// Steam will periodically refresh the challenge url, so we need a new QR code.
|
||||
session.ChallengeURLChanged = () =>
|
||||
{
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("The QR code has changed:");
|
||||
|
||||
DisplayQrCode(session.ChallengeURL);
|
||||
};
|
||||
|
||||
// Draw initial QR code immediately
|
||||
DisplayQrCode(session.ChallengeURL);
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
return;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.Error.WriteLine("Failed to authenticate with Steam: " + ex.Message);
|
||||
Abort(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (authSession != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await authSession.PollingWaitForResultAsync();
|
||||
|
||||
logonDetails.Username = result.AccountName;
|
||||
logonDetails.Password = null;
|
||||
logonDetails.AccessToken = result.RefreshToken;
|
||||
|
||||
AccountSettingsStore.Instance.LoginTokens[result.AccountName] = result.RefreshToken;
|
||||
AccountSettingsStore.Save();
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
return;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.Error.WriteLine("Failed to authenticate with Steam: " + ex.Message);
|
||||
Abort(false);
|
||||
return;
|
||||
}
|
||||
|
||||
authSession = null;
|
||||
}
|
||||
|
||||
steamUser.LogOn(logonDetails);
|
||||
}
|
||||
}
|
||||
@@ -517,6 +601,8 @@ namespace DepotDownloader
|
||||
{
|
||||
bDidDisconnect = true;
|
||||
|
||||
DebugLog.WriteLine(nameof(Steam3Session), $"Disconnected: bIsConnectionRecovery = {bIsConnectionRecovery}, UserInitiated = {disconnected.UserInitiated}, bExpectingDisconnectRemote = {bExpectingDisconnectRemote}");
|
||||
|
||||
// When recovering the connection, we want to reconnect even if the remote disconnects us
|
||||
if (!bIsConnectionRecovery && (disconnected.UserInitiated || bExpectingDisconnectRemote))
|
||||
{
|
||||
@@ -553,14 +639,14 @@ namespace DepotDownloader
|
||||
{
|
||||
var isSteamGuard = loggedOn.Result == EResult.AccountLogonDenied;
|
||||
var is2FA = loggedOn.Result == EResult.AccountLoginDeniedNeedTwoFactor;
|
||||
var isLoginKey = ContentDownloader.Config.RememberPassword && logonDetails.LoginKey != null && loggedOn.Result == EResult.InvalidPassword;
|
||||
var isAccessToken = ContentDownloader.Config.RememberPassword && logonDetails.AccessToken != null && loggedOn.Result == EResult.InvalidPassword; // TODO: Get EResult for bad access token
|
||||
|
||||
if (isSteamGuard || is2FA || isLoginKey)
|
||||
if (isSteamGuard || is2FA || isAccessToken)
|
||||
{
|
||||
bExpectingDisconnectRemote = true;
|
||||
Abort(false);
|
||||
|
||||
if (!isLoginKey)
|
||||
if (!isAccessToken)
|
||||
{
|
||||
Console.WriteLine("This account is protected by Steam Guard.");
|
||||
}
|
||||
@@ -573,23 +659,15 @@ namespace DepotDownloader
|
||||
logonDetails.TwoFactorCode = Console.ReadLine();
|
||||
} while (String.Empty == logonDetails.TwoFactorCode);
|
||||
}
|
||||
else if (isLoginKey)
|
||||
else if (isAccessToken)
|
||||
{
|
||||
AccountSettingsStore.Instance.LoginKeys.Remove(logonDetails.Username);
|
||||
AccountSettingsStore.Instance.LoginTokens.Remove(logonDetails.Username);
|
||||
AccountSettingsStore.Save();
|
||||
|
||||
logonDetails.LoginKey = null;
|
||||
|
||||
if (ContentDownloader.Config.SuppliedPassword != null)
|
||||
{
|
||||
Console.WriteLine("Login key was expired. Connecting with supplied password.");
|
||||
logonDetails.Password = ContentDownloader.Config.SuppliedPassword;
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.Write("Login key was expired. Please enter your password: ");
|
||||
logonDetails.Password = Util.ReadPassword();
|
||||
}
|
||||
// TODO: Handle gracefully by falling back to password prompt?
|
||||
Console.WriteLine("Access token was rejected.");
|
||||
Abort(false);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -700,16 +778,16 @@ namespace DepotDownloader
|
||||
steamUser.SendMachineAuthResponse(authResponse);
|
||||
}
|
||||
|
||||
private void LoginKeyCallback(SteamUser.LoginKeyCallback loginKey)
|
||||
private static void DisplayQrCode(string challengeUrl)
|
||||
{
|
||||
Console.WriteLine("Accepted new login key for account {0}", logonDetails.Username);
|
||||
// Encode the link as a QR code
|
||||
using var qrGenerator = new QRCodeGenerator();
|
||||
var qrCodeData = qrGenerator.CreateQrCode(challengeUrl, QRCodeGenerator.ECCLevel.L);
|
||||
using var qrCode = new AsciiQRCode(qrCodeData);
|
||||
var qrCodeAsAsciiArt = qrCode.GetGraphic(1, drawQuietZones: false);
|
||||
|
||||
AccountSettingsStore.Instance.LoginKeys[logonDetails.Username] = loginKey.LoginKey;
|
||||
AccountSettingsStore.Save();
|
||||
|
||||
steamUser.AcceptNewLoginKey(loginKey);
|
||||
|
||||
bDidReceiveLoginKey = true;
|
||||
Console.WriteLine("Use the Steam Mobile App to sign in with this QR code:");
|
||||
Console.WriteLine(qrCodeAsAsciiArt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user