@ -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 is LoginKey = ContentDownloader . Config . RememberPassword & & logonDetails . LoginKey ! = null & & loggedOn . Result = = EResult . InvalidPassword ;
var is AccessToken = ContentDownloader . Config . RememberPassword & & logonDetails . AccessToken ! = null & & loggedOn . Result = = EResult . InvalidPassword ; // TODO: Get EResult for bad access token
if ( isSteamGuard | | is2FA | | is LoginKey )
if ( isSteamGuard | | is2FA | | is AccessToken )
{
bExpectingDisconnectRemote = true ;
Abort ( false ) ;
if ( ! is LoginKey )
if ( ! is AccessToken )
{
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 ( is LoginKey )
else if ( is AccessToken )
{
AccountSettingsStore . Instance . Login Key s. Remove ( logonDetails . Username ) ;
AccountSettingsStore . Instance . Login Token s. 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 ) ;
AccountSettingsStore . Instance . LoginKeys [ logonDetails . Username ] = loginKey . LoginKey ;
AccountSettingsStore . Save ( ) ;
steamUser . AcceptNewLoginKey ( loginKey ) ;
bDidReceiveLoginKey = true ;
// 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 ) ;
Console . WriteLine ( "Use the Steam Mobile App to sign in with this QR code:" ) ;
Console. WriteLine ( qrCodeAsAsciiArt ) ;
}
}
}