예제 #1
0
		/// <summary>
		/// Enroll the authenticator with the server
		/// </summary>
		public bool Enroll(EnrollState state)
		{
			// clear error
			state.Error = null;

			try
			{
				var data = new NameValueCollection();
				var cookies = state.Cookies = state.Cookies ?? new CookieContainer();
				string response;

				if (string.IsNullOrEmpty(state.OAuthToken) == true)
				{
					// get session
					if (cookies.Count == 0)
					{
						cookies.Add(new Cookie("mobileClientVersion", "3067969+%282.1.3%29", "/", ".steamcommunity.com"));
						cookies.Add(new Cookie("mobileClient", "android", "/", ".steamcommunity.com"));
						cookies.Add(new Cookie("steamid", "", "/", ".steamcommunity.com"));
						cookies.Add(new Cookie("steamLogin", "", "/", ".steamcommunity.com"));
						cookies.Add(new Cookie("Steam_Language", "english", "/", ".steamcommunity.com"));
						cookies.Add(new Cookie("dob", "", "/", ".steamcommunity.com"));

						NameValueCollection headers = new NameValueCollection();
						headers.Add("X-Requested-With", "com.valvesoftware.android.steam.community");

						response = Request("https://steamcommunity.com/login?oauth_client_id=DE45CD61&oauth_scope=read_profile%20write_profile%20read_client%20write_client", "GET", null, cookies, headers);
					}

					// get the user's RSA key
					data.Add("username", state.Username);
					response = Request(COMMUNITY_BASE + "/login/getrsakey", "POST", data, cookies);
					var rsaresponse = JObject.Parse(response);
					if (rsaresponse.SelectToken("success").Value<bool>() != true)
					{
						throw new InvalidEnrollResponseException("Cannot get steam information for user: "******"steamid").Value<string>();

					// encrypt password with RSA key
					RNGCryptoServiceProvider random = new RNGCryptoServiceProvider();
					byte[] encryptedPassword;
					using (var rsa = new RSACryptoServiceProvider())
					{
						var passwordBytes = Encoding.ASCII.GetBytes(state.Password);
						var p = rsa.ExportParameters(false);
						p.Exponent = Authenticator.StringToByteArray(rsaresponse.SelectToken("publickey_exp").Value<string>());
						p.Modulus = Authenticator.StringToByteArray(rsaresponse.SelectToken("publickey_mod").Value<string>());
						rsa.ImportParameters(p);
						encryptedPassword = rsa.Encrypt(passwordBytes, false);
					}

					// login request
					data = new NameValueCollection();
					data.Add("password", Convert.ToBase64String(encryptedPassword));
					data.Add("username", state.Username);
					data.Add("twofactorcode", "");
					data.Add("emailauth", (state.EmailAuthText != null ? state.EmailAuthText : string.Empty));
					data.Add("loginfriendlyname", "#login_emailauth_friendlyname_mobile");
					data.Add("captchagid", (state.CaptchaId != null ? state.CaptchaId : "-1"));
					data.Add("captcha_text", (state.CaptchaText != null ? state.CaptchaText : "enter above characters"));
					data.Add("emailsteamid", (state.EmailAuthText != null ? state.SteamId ?? string.Empty : string.Empty));
					data.Add("rsatimestamp", rsaresponse.SelectToken("timestamp").Value<string>());
					data.Add("remember_login", "false");
					data.Add("oauth_client_id", "DE45CD61");
					data.Add("oauth_scope", "read_profile write_profile read_client write_client");
					data.Add("donotache", new DateTime().ToUniversalTime().Subtract(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds.ToString());
					response = Request(COMMUNITY_BASE + "/login/dologin/", "POST", data, cookies);
					Dictionary<string, object> loginresponse = JsonConvert.DeserializeObject<Dictionary<string, object>>(response);

					if (loginresponse.ContainsKey("emailsteamid") == true)
					{
						state.SteamId = loginresponse["emailsteamid"] as string;
					}

					// require captcha
					if (loginresponse.ContainsKey("captcha_needed") == true && (bool)loginresponse["captcha_needed"] == true)
					{
						state.RequiresCaptcha = true;
						state.CaptchaId = (string)loginresponse["captcha_gid"];
						state.CaptchaUrl = COMMUNITY_BASE + "/public/captcha.php?gid=" + state.CaptchaId;
					}
					else
					{
						state.RequiresCaptcha = false;
						state.CaptchaId = null;
						state.CaptchaUrl = null;
						state.CaptchaText = null;
					}

					// require email auth
					if (loginresponse.ContainsKey("emailauth_needed") == true && (bool)loginresponse["emailauth_needed"] == true)
					{
						if (loginresponse.ContainsKey("emaildomain") == true)
						{
							var emaildomain = (string)loginresponse["emaildomain"];
							if (string.IsNullOrEmpty(emaildomain) == false)
							{
								state.EmailDomain = emaildomain;
							}
						}
						state.RequiresEmailAuth = true;
					}
					else
					{
						state.EmailDomain = null;
						state.RequiresEmailAuth = false;
					}

					// require email auth
					if (loginresponse.ContainsKey("requires_twofactor") == true && (bool)loginresponse["requires_twofactor"] == true)
					{
						state.Requires2FA = true;
					}
					else
					{
						state.Requires2FA = false;
					}

					// if we didn't login, return the result
					if (loginresponse.ContainsKey("login_complete") == false || (bool)loginresponse["login_complete"] == false || loginresponse.ContainsKey("oauth") == false)
					{
						if (loginresponse.ContainsKey("oauth") == false)
						{
							state.Error = "No OAuth token in response";
						}
						if (loginresponse.ContainsKey("message") == true)
						{
							state.Error = (string)loginresponse["message"];
						}
						return false;
					}

					// get the OAuth token - is stringified json
					string oauth = (string)loginresponse["oauth"];
					var oauthjson = JObject.Parse(oauth);
					state.OAuthToken = oauthjson.SelectToken("oauth_token").Value<string>();
					if (oauthjson.SelectToken("steamid") != null)
					{
						state.SteamId = oauthjson.SelectToken("steamid").Value<string>();
					}
				}

				// login to webapi
				data.Clear();
				data.Add("access_token", state.OAuthToken);
				response = Request(WEBAPI_BASE + "/ISteamWebUserPresenceOAuth/Logon/v0001", "POST", data);

				var sessionid = cookies.GetCookies(new Uri(COMMUNITY_BASE + "/"))["sessionid"].Value;

				if (state.RequiresActivation == false)
				{
					data.Clear();
					data.Add("op", "has_phone");
					data.Add("arg", "null");
					data.Add("sessionid", sessionid);

					response = Request(COMMUNITY_BASE + "/steamguard/phoneajax", "POST", data, cookies);
					var jsonresponse = JObject.Parse(response);
					bool hasPhone = jsonresponse.SelectToken("has_phone").Value<Boolean>();
					if (hasPhone == false)
					{
						state.OAuthToken = null; // force new login
						state.RequiresLogin = true;
						state.Cookies = null;
						state.Error = "Your Steam account must have a SMS-capable phone number attached. Go into Account Details of the Steam client or Steam website and click Add a Phone Number.";
						return false;
					}

					//response = Request(COMMUNITY_BASE + "/steamguard/phone_checksms?bForTwoFactor=1&bRevoke2fOnCancel=", "GET", null, cookies);

					// add a new authenticator
					data.Clear();
					string deviceId = BuildRandomId();
					data.Add("access_token", state.OAuthToken);
					data.Add("steamid", state.SteamId);
					data.Add("authenticator_type", "1");
					data.Add("device_identifier", deviceId);
					data.Add("sms_phone_id", "1");
					response = Request(WEBAPI_BASE + "/ITwoFactorService/AddAuthenticator/v0001", "POST", data);
					var tfaresponse = JObject.Parse(response);
					if (response.IndexOf("status") == -1 && tfaresponse.SelectToken("response.status").Value<int>() == 84)
					{
						// invalid response
						state.OAuthToken = null; // force new login
						state.RequiresLogin = true;
						state.Cookies = null;
						state.Error = "Unable to send SMS. Check your phone is registered on your Steam account.";
						return false;
					}
					if (response.IndexOf("shared_secret") == -1)
					{
						// invalid response
						state.OAuthToken = null; // force new login
						state.RequiresLogin = true;
						state.Cookies = null;
						state.Error = "Invalid response from Steam: " + response;
						return false;
					}

					// save data into this authenticator
					var secret = tfaresponse.SelectToken("response.shared_secret").Value<string>();
					this.SecretKey = Convert.FromBase64String(secret);
					this.Serial = tfaresponse.SelectToken("response.serial_number").Value<string>();
					this.DeviceId = deviceId;
					state.RevocationCode = tfaresponse.SelectToken("response.revocation_code").Value<string>();

					// add the steamid into the data
					var steamdata = JObject.Parse(tfaresponse.SelectToken("response").ToString());
					if (steamdata.SelectToken("steamid") == null)
					{
						steamdata.Add("steamid", state.SteamId);
					}
					if (steamdata.SelectToken("steamguard_scheme") == null)
					{
						steamdata.Add("steamguard_scheme", "2");
					}
					this.SteamData = steamdata.ToString(Newtonsoft.Json.Formatting.None);

					// calculate server drift
					long servertime = tfaresponse.SelectToken("response.server_time").Value<long>() * 1000;
					ServerTimeDiff = servertime - CurrentTime;
					LastServerTime = DateTime.Now.Ticks;

					state.RequiresActivation = true;

					return false;
				}

				// finalize adding the authenticator
				data.Clear();
				data.Add("access_token", state.OAuthToken);
				data.Add("steamid", state.SteamId);
				data.Add("activation_code", state.ActivationCode);

				// try and authorise
				var retries = 0;
				while (state.RequiresActivation == true && retries < ENROLL_ACTIVATE_RETRIES)
				{
					data.Add("authenticator_code", this.CalculateCode(false));
					data.Add("authenticator_time", this.ServerTime.ToString());
					response = Request(WEBAPI_BASE + "/ITwoFactorService/FinalizeAddAuthenticator/v0001", "POST", data);
					var finalizeresponse = JObject.Parse(response);
					if (response.IndexOf("status") != -1 && finalizeresponse.SelectToken("response.status").Value<int>() == INVALID_ACTIVATION_CODE)
					{
						state.Error = "Invalid activation code";
						return false;
					}

					// reset our time
					if (response.IndexOf("server_time") != -1)
					{
						long servertime = finalizeresponse.SelectToken("response.server_time").Value<long>() * 1000;
						ServerTimeDiff = servertime - CurrentTime;
						LastServerTime = DateTime.Now.Ticks;
					}

					// check success
					if (finalizeresponse.SelectToken("response.success").Value<bool>() == true)
					{
						if (response.IndexOf("want_more") != -1 && finalizeresponse.SelectToken("response.want_more").Value<bool>() == true)
						{
							ServerTimeDiff += 30000L;
							retries++;
							continue;
						}
						state.RequiresActivation = false;
						break;
					}

					ServerTimeDiff += 30000L;
					retries++;
				}
				if (state.RequiresActivation == true)
				{
					state.Error = "There was a problem activating. There might be an issue with the Steam servers. Please try again later.";
					return false;
				}

				// mark and successful and return key
				state.Success = true;
				state.SecretKey = Authenticator.ByteArrayToString(this.SecretKey);

				// send confirmation email
				data.Clear();
				data.Add("access_token", state.OAuthToken);
				data.Add("steamid", state.SteamId);
				data.Add("email_type", "2");
				response = Request(WEBAPI_BASE + "/ITwoFactorService/SendEmail/v0001", "POST", data);

				return true;
			}
			catch (UnauthorisedRequestException ex)
			{
				throw new InvalidEnrollResponseException("You are not allowed to add an authenticator. Have you enabled 'community-generated content' in Family View?", ex);
			}
			catch (InvalidRequestException ex)
			{
				throw new InvalidEnrollResponseException("Error enrolling new authenticator", ex);
			}
		}
예제 #2
0
		/// <summary>
		/// Enroll the authenticator with the server
		/// </summary>
		public bool Enroll(EnrollState state)
		{
			// clear error
			state.Error = null;

			try
			{
				var data = new NameValueCollection();
				var cookies = state.Cookies = state.Cookies ?? new CookieContainer();
				string response;

				if (string.IsNullOrEmpty(state.OAuthToken) == true)
				{
					// get session
					response = Request(COMMUNITY_BASE + "/login/home?goto=0", "GET", null, cookies);

					// get the user's RSA key
					data.Add("username", state.Username);
					response = Request(COMMUNITY_BASE + "/login/getrsakey", "POST", data, cookies);
					var rsaresponse = JObject.Parse(response);
					if (rsaresponse.SelectToken("success").Value<bool>() != true)
					{
						throw new InvalidEnrollResponseException("Cannot get steam information for user: "******"steamid").Value<string>();

					// encrypt password with RSA key
					RNGCryptoServiceProvider random = new RNGCryptoServiceProvider();
					byte[] encryptedPassword;
					using (var rsa = new RSACryptoServiceProvider())
					{
						var passwordBytes = Encoding.ASCII.GetBytes(state.Password);
						var p = rsa.ExportParameters(false);
						p.Exponent = Authenticator.StringToByteArray(rsaresponse.SelectToken("publickey_exp").Value<string>());
						p.Modulus = Authenticator.StringToByteArray(rsaresponse.SelectToken("publickey_mod").Value<string>());
						rsa.ImportParameters(p);
						encryptedPassword = rsa.Encrypt(passwordBytes, false);
					}

					// login request
					data = new NameValueCollection();
					data.Add("password", Convert.ToBase64String(encryptedPassword));
					data.Add("username", state.Username);
					data.Add("twofactorcode", "");
					data.Add("emailauth", (state.EmailAuthText != null ? state.EmailAuthText : string.Empty));
					data.Add("loginfriendlyname", "#login_emailauth_friendlyname_mobile");
					data.Add("captchagid", (state.CaptchaId != null ? state.CaptchaId : "-1"));
					data.Add("captcha_text", (state.CaptchaText != null ? state.CaptchaText : "enter above characters"));
					data.Add("emailsteamid", (state.EmailAuthText != null ? state.SteamId : string.Empty));
					data.Add("rsatimestamp", rsaresponse.SelectToken("timestamp").Value<string>());
					data.Add("remember_login", "false");
					data.Add("oauth_client_id", "DE45CD61");
					data.Add("oauth_scope", "read_profile write_profile read_client write_client");
					data.Add("donotache", new DateTime().ToUniversalTime().Subtract(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds.ToString());
					response = Request(COMMUNITY_BASE + "/mobilelogin/dologin/", "POST", data, cookies);
					Dictionary<string, object> loginresponse = JsonConvert.DeserializeObject<Dictionary<string, object>>(response);

					// require captcha
					if (loginresponse.ContainsKey("captcha_needed") == true && (bool)loginresponse["captcha_needed"] == true)
					{
						state.RequiresCaptcha = true;
						state.CaptchaId = (string)loginresponse["captcha_gid"];
						state.CaptchaUrl = COMMUNITY_BASE + "/public/captcha.php?gid=" + state.CaptchaId;
					}
					else
					{
						state.RequiresCaptcha = false;
						state.CaptchaId = null;
						state.CaptchaUrl = null;
						state.CaptchaText = null;
					}

					// require email auth
					if (loginresponse.ContainsKey("emailauth_needed") == true && (bool)loginresponse["emailauth_needed"] == true)
					{
						if (loginresponse.ContainsKey("emaildomain") == true)
						{
							var emaildomain = (string)loginresponse["emaildomain"];
							if (string.IsNullOrEmpty(emaildomain) == false)
							{
								state.EmailDomain = emaildomain;
							}
						}
						state.RequiresEmailAuth = true;
					}
					else
					{
						state.EmailDomain = null;
						state.RequiresEmailAuth = false;
					}

					// require email auth
					if (loginresponse.ContainsKey("requires_twofactor") == true && (bool)loginresponse["requires_twofactor"] == true)
					{
						state.Requires2FA = true;
					}
					else
					{
						state.Requires2FA = false;
					}

					// if we didn't login, return the result
					if (loginresponse.ContainsKey("login_complete") == false || (bool)loginresponse["login_complete"] == false || loginresponse.ContainsKey("oauth") == false)
					{
						if (loginresponse.ContainsKey("oauth") == false)
						{
							state.Error = "No OAuth token in response";
						}
						if (loginresponse.ContainsKey("message") == true)
						{
							state.Error = (string)loginresponse["message"];
						}
						return false;
					}

					// get the OAuth token - is stringified json
					string oauth = (string)loginresponse["oauth"];
					var oauthjson = JObject.Parse(oauth);
					state.OAuthToken = oauthjson.SelectToken("oauth_token").Value<string>();
				}

				// login to webapi
				data.Clear();
				data.Add("access_token", state.OAuthToken);
				response = Request(WEBAPI_BASE + "/ISteamWebUserPresenceOAuth/Logon/v0001", "POST", data);

				if (state.RequiresActivation == false)
				{
					// add a new authenticator
					data.Clear();
					string deviceId = BuildRandomId();
					data.Add("access_token", state.OAuthToken);
					data.Add("steamid", state.SteamId);
					data.Add("authenticator_type", "1");
					data.Add("device_identifier", deviceId);
					response = Request(WEBAPI_BASE + "/ITwoFactorService/AddAuthenticator/v0001", "POST", data);
					var tfaresponse = JObject.Parse(response);
					if (response.IndexOf("revocation_code") == -1)
					{
						// invalid response
						state.OAuthToken = null; // force new login
						state.RequiresLogin = true;
						state.Cookies = null;
						state.Error = "Invalid response from Steam: " + response;
						return false;
					}
					
					// save data into this authenticator
					var secret = tfaresponse.SelectToken("response.shared_secret").Value<string>();
					this.SecretKey = Convert.FromBase64String(secret);
					this.Serial = tfaresponse.SelectToken("response.serial_number").Value<string>();
					this.DeviceId = deviceId;
					this.RevocationCode = state.RevocationCode = tfaresponse.SelectToken("response.revocation_code").Value<string>();

					// calculate server drift
					long servertime = tfaresponse.SelectToken("response.server_time").Value<long>() * 1000;
					ServerTimeDiff = servertime - CurrentTime;
					LastServerTime = DateTime.Now.Ticks;

					// send authorisation email
					data.Clear();
					data.Add("access_token", state.OAuthToken);
					data.Add("steamid", state.SteamId);
					data.Add("email_type", "1");
					data.Add("include_activation", "1");
					response = Request(WEBAPI_BASE + "/ITwoFactorService/SendEmail/v0001", "POST", data);

					state.RequiresActivation = true;

					return false;
				}

				// finalize adding the authenticator
				data.Clear();
				data.Add("access_token", state.OAuthToken);
				data.Add("steamid", state.SteamId);
				data.Add("activation_code", state.ActivationCode);

				// try and authorise
				ServerTimeDiff -= 40000; // start at previous interval
				var retries = 0;
				while (state.RequiresActivation == true && retries < ENROLL_ACTIVATE_RETRIES)
				{
					data.Add("authenticator_code", this.CalculateCode(false));
					data.Add("authenticator_time", this.ServerTime.ToString());
					response = Request(WEBAPI_BASE + "/ITwoFactorService/FinalizeAddAuthenticator/v0001", "POST", data);
					var finalizeresponse = JObject.Parse(response);
					if (response.IndexOf("status") != -1 && finalizeresponse.SelectToken("response.status").Value<int>() == INVALID_ACTIVATION_CODE)
					{
						state.Error = "Invalid activation code";
						return false;
					}

					// reset our time
					if (response.IndexOf("server_time") != -1)
					{
						long servertime = finalizeresponse.SelectToken("response.server_time").Value<long>() * 1000;
						ServerTimeDiff = servertime - CurrentTime;
						LastServerTime = DateTime.Now.Ticks;
					}

					// check success
					if (finalizeresponse.SelectToken("response.success").Value<bool>() == true)
					{
						if (response.IndexOf("want_more") != -1 && finalizeresponse.SelectToken("response.want_more").Value<bool>() == true)
						{
							ServerTimeDiff += 30000L;
							retries++;
							continue;
						}
						state.RequiresActivation = false;
						break;
					}

					ServerTimeDiff += 30000L;
					retries++;
				}
				if (state.RequiresActivation == true)
				{
					state.Error = "There was a problem activating. There might be an issue with the Steam servers. Please try again later.";
					return false;
				}

				// mark and successful and return key
				state.Success = true;
				state.SecretKey = Authenticator.ByteArrayToString(this.SecretKey);

				// send confirmation email
				data.Clear();
				data.Add("access_token", state.OAuthToken);
				data.Add("steamid", state.SteamId);
				data.Add("email_type", "2");
				response = Request(WEBAPI_BASE + "/ITwoFactorService/SendEmail/v0001", "POST", data);

				return true;
			}
			catch (InvalidRequestException ex)
			{
				throw new InvalidEnrollResponseException("Error enrolling new authenticator", ex);
			}
		}
예제 #3
0
        /// <summary>
        /// Enroll the authenticator with the server
        /// </summary>
        public bool Enroll(EnrollState state)
        {
            // clear error
            state.Error = null;

            try
            {
                var    data    = new NameValueCollection();
                var    cookies = state.Cookies = state.Cookies ?? new CookieContainer();
                string response;

                if (string.IsNullOrEmpty(state.OAuthToken) == true)
                {
                    // get session
                    if (cookies.Count == 0)
                    {
                        cookies.Add(new Uri(COMMUNITY_BASE + "/"), new Cookie("mobileClientVersion", "3067969+%282.1.3%29"));
                        cookies.Add(new Uri(COMMUNITY_BASE + "/"), new Cookie("mobileClient", "android"));
                        cookies.Add(new Uri(COMMUNITY_BASE + "/"), new Cookie("steamid", ""));
                        cookies.Add(new Uri(COMMUNITY_BASE + "/"), new Cookie("steamLogin", ""));
                        cookies.Add(new Uri(COMMUNITY_BASE + "/"), new Cookie("Steam_Language", "english"));
                        cookies.Add(new Uri(COMMUNITY_BASE + "/"), new Cookie("dob", ""));

                        NameValueCollection headers = new NameValueCollection();
                        headers.Add("X-Requested-With", "com.valvesoftware.android.steam.community");

                        response = Request("https://steamcommunity.com/mobilelogin?oauth_client_id=DE45CD61&oauth_scope=read_profile%20write_profile%20read_client%20write_client", "GET", null, cookies, headers);
                    }

                    // get the user's RSA key
                    data.Add("username", state.Username);
                    response = Request(COMMUNITY_BASE + "/mobilelogin/getrsakey", "POST", data, cookies);
                    var rsaresponse = JObject.Parse(response);
                    if (rsaresponse.SelectToken("success").Value <bool>() != true)
                    {
                        throw new InvalidEnrollResponseException("Cannot get steam information for user: "******"publickey_exp").Value <string>());
                        p.Modulus  = Authenticator.StringToByteArray(rsaresponse.SelectToken("publickey_mod").Value <string>());
                        rsa.ImportParameters(p);
                        encryptedPassword = rsa.Encrypt(passwordBytes, false);
                    }

                    // login request
                    data = new NameValueCollection();
                    data.Add("password", Convert.ToBase64String(encryptedPassword));
                    data.Add("username", state.Username);
                    data.Add("twofactorcode", "");
                    data.Add("emailauth", (state.EmailAuthText != null ? state.EmailAuthText : string.Empty));
                    data.Add("loginfriendlyname", "#login_emailauth_friendlyname_mobile");
                    data.Add("captchagid", (state.CaptchaId != null ? state.CaptchaId : "-1"));
                    data.Add("captcha_text", (state.CaptchaText != null ? state.CaptchaText : "enter above characters"));
                    data.Add("emailsteamid", (state.EmailAuthText != null ? state.SteamId ?? string.Empty : string.Empty));
                    data.Add("rsatimestamp", rsaresponse.SelectToken("timestamp").Value <string>());
                    data.Add("remember_login", "false");
                    data.Add("oauth_client_id", "DE45CD61");
                    data.Add("oauth_scope", "read_profile write_profile read_client write_client");
                    data.Add("donotache", new DateTime().ToUniversalTime().Subtract(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds.ToString());
                    response = Request(COMMUNITY_BASE + "/mobilelogin/dologin/", "POST", data, cookies);
                    Dictionary <string, object> loginresponse = JsonConvert.DeserializeObject <Dictionary <string, object> >(response);

                    if (loginresponse.ContainsKey("emailsteamid") == true)
                    {
                        state.SteamId = loginresponse["emailsteamid"] as string;
                    }

                    // require captcha
                    if (loginresponse.ContainsKey("captcha_needed") == true && (bool)loginresponse["captcha_needed"] == true)
                    {
                        state.RequiresCaptcha = true;
                        state.CaptchaId       = (string)loginresponse["captcha_gid"];
                        state.CaptchaUrl      = COMMUNITY_BASE + "/public/captcha.php?gid=" + state.CaptchaId;
                    }
                    else
                    {
                        state.RequiresCaptcha = false;
                        state.CaptchaId       = null;
                        state.CaptchaUrl      = null;
                        state.CaptchaText     = null;
                    }

                    // require email auth
                    if (loginresponse.ContainsKey("emailauth_needed") == true && (bool)loginresponse["emailauth_needed"] == true)
                    {
                        if (loginresponse.ContainsKey("emaildomain") == true)
                        {
                            var emaildomain = (string)loginresponse["emaildomain"];
                            if (string.IsNullOrEmpty(emaildomain) == false)
                            {
                                state.EmailDomain = emaildomain;
                            }
                        }
                        state.RequiresEmailAuth = true;
                    }
                    else
                    {
                        state.EmailDomain       = null;
                        state.RequiresEmailAuth = false;
                    }

                    // require email auth
                    if (loginresponse.ContainsKey("requires_twofactor") == true && (bool)loginresponse["requires_twofactor"] == true)
                    {
                        state.Requires2FA = true;
                    }
                    else
                    {
                        state.Requires2FA = false;
                    }

                    // if we didn't login, return the result
                    if (loginresponse.ContainsKey("login_complete") == false || (bool)loginresponse["login_complete"] == false || loginresponse.ContainsKey("oauth") == false)
                    {
                        if (loginresponse.ContainsKey("oauth") == false)
                        {
                            state.Error = "Invalid response from Steam (No OAuth token)";
                        }
                        if (loginresponse.ContainsKey("message") == true)
                        {
                            state.Error = (string)loginresponse["message"];
                        }
                        return(false);
                    }

                    // get the OAuth token - is stringified json
                    string oauth     = (string)loginresponse["oauth"];
                    var    oauthjson = JObject.Parse(oauth);
                    state.OAuthToken = oauthjson.SelectToken("oauth_token").Value <string>();
                    if (oauthjson.SelectToken("steamid") != null)
                    {
                        state.SteamId = oauthjson.SelectToken("steamid").Value <string>();
                    }
                }

                // login to webapi
                data.Clear();
                data.Add("access_token", state.OAuthToken);
                response = Request(WEBAPI_BASE + "/ISteamWebUserPresenceOAuth/Logon/v0001", "POST", data);

                var sessionid = cookies.GetCookies(new Uri(COMMUNITY_BASE + "/"))["sessionid"].Value;

                if (state.RequiresActivation == false)
                {
                    data.Clear();
                    data.Add("op", "has_phone");
                    data.Add("arg", "null");
                    data.Add("sessionid", sessionid);

                    response = Request(COMMUNITY_BASE + "/steamguard/phoneajax", "POST", data, cookies);
                    var  jsonresponse = JObject.Parse(response);
                    bool hasPhone     = jsonresponse.SelectToken("has_phone").Value <Boolean>();
                    if (hasPhone == false)
                    {
                        state.OAuthToken    = null;                      // force new login
                        state.RequiresLogin = true;
                        state.Cookies       = null;
                        state.Error         = "Your Steam account must have a SMS-capable phone number attached. Go into Account Details of the Steam client or Steam website and click Add a Phone Number.";
                        return(false);
                    }

                    //response = Request(COMMUNITY_BASE + "/steamguard/phone_checksms?bForTwoFactor=1&bRevoke2fOnCancel=", "GET", null, cookies);

                    // add a new authenticator
                    data.Clear();
                    string deviceId = BuildRandomId();
                    data.Add("access_token", state.OAuthToken);
                    data.Add("steamid", state.SteamId);
                    data.Add("authenticator_type", "1");
                    data.Add("device_identifier", deviceId);
                    data.Add("sms_phone_id", "1");
                    response = Request(WEBAPI_BASE + "/ITwoFactorService/AddAuthenticator/v0001", "POST", data);
                    var tfaresponse = JObject.Parse(response);
                    if (response.IndexOf("status") == -1 && tfaresponse.SelectToken("response.status").Value <int>() == 84)
                    {
                        // invalid response
                        state.OAuthToken    = null;                      // force new login
                        state.RequiresLogin = true;
                        state.Cookies       = null;
                        state.Error         = "Unable to send SMS. Check your phone is registered on your Steam account.";
                        return(false);
                    }
                    if (response.IndexOf("shared_secret") == -1)
                    {
                        // invalid response
                        state.OAuthToken    = null;                      // force new login
                        state.RequiresLogin = true;
                        state.Cookies       = null;
                        state.Error         = "Invalid response from Steam: " + response;
                        return(false);
                    }

                    // save data into this authenticator
                    var secret = tfaresponse.SelectToken("response.shared_secret").Value <string>();
                    this.SecretKey       = Convert.FromBase64String(secret);
                    this.Serial          = tfaresponse.SelectToken("response.serial_number").Value <string>();
                    this.DeviceId        = deviceId;
                    state.RevocationCode = tfaresponse.SelectToken("response.revocation_code").Value <string>();

                    // add the steamid into the data
                    var steamdata = JObject.Parse(tfaresponse.SelectToken("response").ToString());
                    if (steamdata.SelectToken("steamid") == null)
                    {
                        steamdata.Add("steamid", state.SteamId);
                    }
                    if (steamdata.SelectToken("steamguard_scheme") == null)
                    {
                        steamdata.Add("steamguard_scheme", "2");
                    }
                    this.SteamData = steamdata.ToString(Newtonsoft.Json.Formatting.None);

                    // calculate server drift
                    long servertime = tfaresponse.SelectToken("response.server_time").Value <long>() * 1000;
                    ServerTimeDiff = servertime - CurrentTime;
                    LastServerTime = DateTime.Now.Ticks;

                    state.RequiresActivation = true;

                    return(false);
                }

                // finalize adding the authenticator
                data.Clear();
                data.Add("access_token", state.OAuthToken);
                data.Add("steamid", state.SteamId);
                data.Add("activation_code", state.ActivationCode);

                // try and authorise
                var retries = 0;
                while (state.RequiresActivation == true && retries < ENROLL_ACTIVATE_RETRIES)
                {
                    data.Add("authenticator_code", this.CalculateCode(false));
                    data.Add("authenticator_time", this.ServerTime.ToString());
                    response = Request(WEBAPI_BASE + "/ITwoFactorService/FinalizeAddAuthenticator/v0001", "POST", data);
                    var finalizeresponse = JObject.Parse(response);
                    if (response.IndexOf("status") != -1 && finalizeresponse.SelectToken("response.status").Value <int>() == INVALID_ACTIVATION_CODE)
                    {
                        state.Error = "Invalid activation code";
                        return(false);
                    }

                    // reset our time
                    if (response.IndexOf("server_time") != -1)
                    {
                        long servertime = finalizeresponse.SelectToken("response.server_time").Value <long>() * 1000;
                        ServerTimeDiff = servertime - CurrentTime;
                        LastServerTime = DateTime.Now.Ticks;
                    }

                    // check success
                    if (finalizeresponse.SelectToken("response.success").Value <bool>() == true)
                    {
                        if (response.IndexOf("want_more") != -1 && finalizeresponse.SelectToken("response.want_more").Value <bool>() == true)
                        {
                            ServerTimeDiff += 30000L;
                            retries++;
                            continue;
                        }
                        state.RequiresActivation = false;
                        break;
                    }

                    ServerTimeDiff += 30000L;
                    retries++;
                }
                if (state.RequiresActivation == true)
                {
                    state.Error = "There was a problem activating. There might be an issue with the Steam servers. Please try again later.";
                    return(false);
                }

                // mark and successful and return key
                state.Success   = true;
                state.SecretKey = Authenticator.ByteArrayToString(this.SecretKey);

                // send confirmation email
                data.Clear();
                data.Add("access_token", state.OAuthToken);
                data.Add("steamid", state.SteamId);
                data.Add("email_type", "2");
                response = Request(WEBAPI_BASE + "/ITwoFactorService/SendEmail/v0001", "POST", data);

                return(true);
            }
            catch (UnauthorisedRequestException ex)
            {
                throw new InvalidEnrollResponseException("You are not allowed to add an authenticator. Have you enabled 'community-generated content' in Family View?", ex);
            }
            catch (InvalidRequestException ex)
            {
                throw new InvalidEnrollResponseException("Error enrolling new authenticator", ex);
            }
        }
예제 #4
0
        /// <summary>
        /// Enroll the authenticator with the server
        /// </summary>
        public bool Enroll(EnrollState state)
        {
            // clear error
            state.Error = null;

            try
            {
                var    data    = new NameValueCollection();
                var    cookies = state.Cookies = state.Cookies ?? new CookieContainer();
                string response;

                if (string.IsNullOrEmpty(state.OAuthToken) == true)
                {
                    // get session
                    response = Request(COMMUNITY_BASE + "/login/home?goto=0", "GET", null, cookies);

                    // get the user's RSA key
                    data.Add("username", state.Username);
                    response = Request(COMMUNITY_BASE + "/login/getrsakey", "POST", data, cookies);
                    var rsaresponse = JObject.Parse(response);
                    if (rsaresponse.SelectToken("success").Value <bool>() != true)
                    {
                        throw new InvalidEnrollResponseException("Cannot get steam information for user: "******"steamid").Value <string>();

                    // encrypt password with RSA key
                    RNGCryptoServiceProvider random = new RNGCryptoServiceProvider();
                    byte[] encryptedPassword;
                    using (var rsa = new RSACryptoServiceProvider())
                    {
                        var passwordBytes = Encoding.ASCII.GetBytes(state.Password);
                        var p             = rsa.ExportParameters(false);
                        p.Exponent = Authenticator.StringToByteArray(rsaresponse.SelectToken("publickey_exp").Value <string>());
                        p.Modulus  = Authenticator.StringToByteArray(rsaresponse.SelectToken("publickey_mod").Value <string>());
                        rsa.ImportParameters(p);
                        encryptedPassword = rsa.Encrypt(passwordBytes, false);
                    }

                    // login request
                    data = new NameValueCollection();
                    data.Add("password", Convert.ToBase64String(encryptedPassword));
                    data.Add("username", state.Username);
                    data.Add("twofactorcode", "");
                    data.Add("emailauth", (state.EmailAuthText != null ? state.EmailAuthText : string.Empty));
                    data.Add("loginfriendlyname", "#login_emailauth_friendlyname_mobile");
                    data.Add("captchagid", (state.CaptchaId != null ? state.CaptchaId : "-1"));
                    data.Add("captcha_text", (state.CaptchaText != null ? state.CaptchaText : "enter above characters"));
                    data.Add("emailsteamid", (state.EmailAuthText != null ? state.SteamId : string.Empty));
                    data.Add("rsatimestamp", rsaresponse.SelectToken("timestamp").Value <string>());
                    data.Add("remember_login", "false");
                    data.Add("oauth_client_id", "DE45CD61");
                    data.Add("oauth_scope", "read_profile write_profile read_client write_client");
                    data.Add("donotache", new DateTime().ToUniversalTime().Subtract(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds.ToString());
                    response = Request(COMMUNITY_BASE + "/mobilelogin/dologin/", "POST", data, cookies);
                    Dictionary <string, object> loginresponse = JsonConvert.DeserializeObject <Dictionary <string, object> >(response);

                    // require captcha
                    if (loginresponse.ContainsKey("captcha_needed") == true && (bool)loginresponse["captcha_needed"] == true)
                    {
                        state.RequiresCaptcha = true;
                        state.CaptchaId       = (string)loginresponse["captcha_gid"];
                        state.CaptchaUrl      = COMMUNITY_BASE + "/public/captcha.php?gid=" + state.CaptchaId;
                    }
                    else
                    {
                        state.RequiresCaptcha = false;
                        state.CaptchaId       = null;
                        state.CaptchaUrl      = null;
                        state.CaptchaText     = null;
                    }

                    // require email auth
                    if (loginresponse.ContainsKey("emailauth_needed") == true && (bool)loginresponse["emailauth_needed"] == true)
                    {
                        if (loginresponse.ContainsKey("emaildomain") == true)
                        {
                            var emaildomain = (string)loginresponse["emaildomain"];
                            if (string.IsNullOrEmpty(emaildomain) == false)
                            {
                                state.EmailDomain = emaildomain;
                            }
                        }
                        state.RequiresEmailAuth = true;
                    }
                    else
                    {
                        state.EmailDomain       = null;
                        state.RequiresEmailAuth = false;
                    }

                    // require email auth
                    if (loginresponse.ContainsKey("requires_twofactor") == true && (bool)loginresponse["requires_twofactor"] == true)
                    {
                        state.Requires2FA = true;
                    }
                    else
                    {
                        state.Requires2FA = false;
                    }

                    // if we didn't login, return the result
                    if (loginresponse.ContainsKey("login_complete") == false || (bool)loginresponse["login_complete"] == false || loginresponse.ContainsKey("oauth") == false)
                    {
                        if (loginresponse.ContainsKey("oauth") == false)
                        {
                            state.Error = "No OAuth token in response";
                        }
                        if (loginresponse.ContainsKey("message") == true)
                        {
                            state.Error = (string)loginresponse["message"];
                        }
                        return(false);
                    }

                    // get the OAuth token - is stringified json
                    string oauth     = (string)loginresponse["oauth"];
                    var    oauthjson = JObject.Parse(oauth);
                    state.OAuthToken = oauthjson.SelectToken("oauth_token").Value <string>();
                }

                // login to webapi
                data.Clear();
                data.Add("access_token", state.OAuthToken);
                response = Request(WEBAPI_BASE + "/ISteamWebUserPresenceOAuth/Logon/v0001", "POST", data);

                if (state.RequiresActivation == false)
                {
                    // add a new authenticator
                    data.Clear();
                    string deviceId = BuildRandomId();
                    data.Add("access_token", state.OAuthToken);
                    data.Add("steamid", state.SteamId);
                    data.Add("authenticator_type", "1");
                    data.Add("device_identifier", deviceId);
                    response = Request(WEBAPI_BASE + "/ITwoFactorService/AddAuthenticator/v0001", "POST", data);
                    var tfaresponse = JObject.Parse(response);
                    if (response.IndexOf("revocation_code") == -1)
                    {
                        // invalid response
                        state.OAuthToken    = null;                      // force new login
                        state.RequiresLogin = true;
                        state.Cookies       = null;
                        state.Error         = "Invalid response from Steam: " + response;
                        return(false);
                    }

                    // save data into this authenticator
                    var secret = tfaresponse.SelectToken("response.shared_secret").Value <string>();
                    this.SecretKey      = Convert.FromBase64String(secret);
                    this.Serial         = tfaresponse.SelectToken("response.serial_number").Value <string>();
                    this.DeviceId       = deviceId;
                    this.RevocationCode = state.RevocationCode = tfaresponse.SelectToken("response.revocation_code").Value <string>();

                    // calculate server drift
                    long servertime = tfaresponse.SelectToken("response.server_time").Value <long>() * 1000;
                    ServerTimeDiff = servertime - CurrentTime;
                    LastServerTime = DateTime.Now.Ticks;

                    // send authorisation email
                    data.Clear();
                    data.Add("access_token", state.OAuthToken);
                    data.Add("steamid", state.SteamId);
                    data.Add("email_type", "1");
                    data.Add("include_activation", "1");
                    response = Request(WEBAPI_BASE + "/ITwoFactorService/SendEmail/v0001", "POST", data);

                    state.RequiresActivation = true;

                    return(false);
                }

                // finalize adding the authenticator
                data.Clear();
                data.Add("access_token", state.OAuthToken);
                data.Add("steamid", state.SteamId);
                data.Add("activation_code", state.ActivationCode);

                // try and authorise
                ServerTimeDiff -= 40000;                 // start at previous interval
                var retries = 0;
                while (state.RequiresActivation == true && retries < ENROLL_ACTIVATE_RETRIES)
                {
                    data.Add("authenticator_code", this.CalculateCode(false));
                    data.Add("authenticator_time", this.ServerTime.ToString());
                    response = Request(WEBAPI_BASE + "/ITwoFactorService/FinalizeAddAuthenticator/v0001", "POST", data);
                    var finalizeresponse = JObject.Parse(response);
                    if (response.IndexOf("status") != -1 && finalizeresponse.SelectToken("response.status").Value <int>() == INVALID_ACTIVATION_CODE)
                    {
                        state.Error = "Invalid activation code";
                        return(false);
                    }

                    // reset our time
                    if (response.IndexOf("server_time") != -1)
                    {
                        long servertime = finalizeresponse.SelectToken("response.server_time").Value <long>() * 1000;
                        ServerTimeDiff = servertime - CurrentTime;
                        LastServerTime = DateTime.Now.Ticks;
                    }

                    // check success
                    if (finalizeresponse.SelectToken("response.success").Value <bool>() == true)
                    {
                        if (response.IndexOf("want_more") != -1 && finalizeresponse.SelectToken("response.want_more").Value <bool>() == true)
                        {
                            ServerTimeDiff += 30000L;
                            retries++;
                            continue;
                        }
                        state.RequiresActivation = false;
                        break;
                    }

                    ServerTimeDiff += 30000L;
                    retries++;
                }
                if (state.RequiresActivation == true)
                {
                    state.Error = "There was a problem activating. There might be an issue with the Steam servers. Please try again later.";
                    return(false);
                }

                // mark and successful and return key
                state.Success   = true;
                state.SecretKey = Authenticator.ByteArrayToString(this.SecretKey);

                // send confirmation email
                data.Clear();
                data.Add("access_token", state.OAuthToken);
                data.Add("steamid", state.SteamId);
                data.Add("email_type", "2");
                response = Request(WEBAPI_BASE + "/ITwoFactorService/SendEmail/v0001", "POST", data);

                return(true);
            }
            catch (InvalidRequestException ex)
            {
                throw new InvalidEnrollResponseException("Error enrolling new authenticator", ex);
            }
        }