/// <summary> /// Tries to authenticate a user with the provided user credentials and returns session data corresponding to a /// successful login; fails if information provided is not enough or service is unavailable. /// </summary> /// <param name="credentials">The credentials to be used for login process.</param> /// <returns>Logged in session to be used with other classes</returns> /// <exception cref="ArgumentException"> /// Username and/or password is missing. - credentials /// or /// Two factor authentication code is required for login process to continue. - credentials /// or /// Email verification code is required for login process to continue. - credentials /// or /// Captcha is required for login process to continue. - credentials /// </exception> /// <exception cref="UserLoginException"> /// Raises when there is a problem with login process or there is a need for more information. Capture and decide if /// you should repeat the process. /// </exception> public virtual async Task <WebSession> DoLogin(LoginCredentials credentials) { if (string.IsNullOrWhiteSpace(credentials.UserName) || string.IsNullOrWhiteSpace(credentials.Password)) { throw new ArgumentException("Username and/or password is missing.", nameof(credentials)); } if (RequiresTwoFactorAuthenticationCode && string.IsNullOrWhiteSpace(credentials.TwoFactorAuthenticationCode)) { throw new ArgumentException("Two factor authentication code is required for login process to continue.", nameof(credentials)); } if (RequiresEmailVerification && string.IsNullOrWhiteSpace(credentials.EmailVerificationCode)) { throw new ArgumentException("Email verification code is required for login process to continue.", nameof(credentials)); } if (RequiresCaptchaCode && string.IsNullOrWhiteSpace(credentials.CaptchaCode)) { throw new ArgumentException("Captcha is required for login process to continue.", nameof(credentials)); } // Lock this instance await LockObject.WaitAsync().ConfigureAwait(false); try { // Retrieve guest cookies for login process if missing if (string.IsNullOrEmpty(SteamWebAccess?.Session?.SessionId)) { await GetGuestSession().ConfigureAwait(false); } var loginRequest = await ConstructLoginRequest(credentials).ConfigureAwait(false); var loginResponse = loginRequest != null ? await OperationRetryHelper.Default .RetryOperationAsync(() => SteamWebAccess.FetchString(loginRequest)).ConfigureAwait(false) : null; if (loginResponse == null) { throw new UserLoginException(UserLoginErrorCode.GeneralFailure, this); } if (!await ProcessLoginResponse(loginResponse).ConfigureAwait(false)) { throw new UserLoginException(UserLoginErrorCode.BadCredentials, this); } var sessionData = SteamWebAccess.Session; ResetStates(); return(sessionData); } finally { // Unlock this instance LockObject.Release(); } }
protected virtual async Task <SteamWebAccessRequest> ConstructLoginRequest(LoginCredentials credentials) { // Get a RSA public key for password encryption var serverResponse = await SteamWebAccess.FetchString( new SteamWebAccessRequest( WebLoginRSAUrl, SteamWebAccessRequestMethod.Post, new QueryStringBuilder { { "donotcache", (DateTime.UtcNow - Epoch).TotalMilliseconds }, { "username", credentials.UserName } } ) ).ConfigureAwait(false); if (string.IsNullOrWhiteSpace(serverResponse) || serverResponse.Contains("<BODY>\nAn error occurred while processing your request.")) { throw new UserLoginException(UserLoginErrorCode.GeneralFailure, this); } var rsaResponse = JsonConvert.DeserializeObject <RSAResponse>(serverResponse); if (rsaResponse?.Success != true) { throw new UserLoginException(UserLoginErrorCode.BadRSAResponse, this); } // Sleep for a bit to give Steam a chance to catch up?? await Task.Delay(350).ConfigureAwait(false); string encryptedPassword; using (var rsaEncrypt = new RSACryptoServiceProvider()) { rsaEncrypt.ImportParameters(new RSAParameters { Exponent = HexStringToByteArray(rsaResponse.Exponent), Modulus = HexStringToByteArray(rsaResponse.Modulus) }); encryptedPassword = Convert.ToBase64String( rsaEncrypt.Encrypt( Encoding.UTF8.GetBytes(credentials.Password) , false) ); } return(new SteamWebAccessRequest( WebLoginUrl, SteamWebAccessRequestMethod.Post, new QueryStringBuilder { { "donotcache", (DateTime.UtcNow - Epoch).TotalMilliseconds }, { "rsatimestamp", rsaResponse.Timestamp }, { "password", encryptedPassword }, { "username", credentials.UserName }, { "twofactorcode", credentials.TwoFactorAuthenticationCode ?? "" }, { "emailauth", RequiresEmailVerification ? (credentials.EmailVerificationCode ?? "") : "" }, { "loginfriendlyname", "" }, { "captchagid", RequiresCaptchaCode ? (CaptchaGID ?? "-1") : "-1" }, { "captcha_text", RequiresCaptchaCode ? (credentials.CaptchaCode ?? "") : "" }, { "emailsteamid", RequiresTwoFactorAuthenticationCode || RequiresEmailVerification ? (SteamId?.ToString() ?? "") : "" }, { "rsatimestamp", rsaResponse.Timestamp }, { "remember_login", "true" } } )); }
public static async Task Login() { var username = "******"; var password = "******"; var loginCredentials = new LoginCredentials(username, password); var webLogin = new WebLogin(); while (true) { try { var session = await webLogin.DoLogin(loginCredentials); if (session?.HasEnoughInfo() == true) { // Login complete, serialize session or create an instance of SteamWebAccess return; } } catch (UserLoginException e) { switch (e.ErrorCode) { case UserLoginErrorCode.GeneralFailure: throw; case UserLoginErrorCode.BadRSAResponse: throw; case UserLoginErrorCode.BadCredentials: throw; case UserLoginErrorCode.TooManyFailedLoginAttempts: throw; case UserLoginErrorCode.NeedsCaptchaCode: // Should download the captcha image and fill the CaptchaCode property of LoginCredentials and try again var captchaImage = await e.UserLogin.DownloadCaptchaImage(); loginCredentials.CaptchaCode = "CAPTCHA CODE"; break; case UserLoginErrorCode.NeedsTwoFactorAuthenticationCode: // This account has a mobile authenticator associated with it // Should fill the TwoFactorAuthenticationCode property of LoginCredentials and try again loginCredentials.TwoFactorAuthenticationCode = "AUTHENTICATOR 2FA CODE"; break; case UserLoginErrorCode.NeedsEmailVerificationCode: // This account uses Steam Guard for added security // Should fill the EmailVerificationCode property of LoginCredentials and try again loginCredentials.EmailVerificationCode = "EMAIL VERIFICATION CODE"; break; } } } }