Browse Source

Added mobile authenticator features & Mobile/WebAuth to account.py

pull/35/head
philippj 9 years ago
parent
commit
e4b7e263b1
  1. 208
      steam/account.py

208
steam/account.py

@ -23,19 +23,28 @@ import os
import sys import sys
import json import json
import base64 import base64
import re
from cryptography.fernet import Fernet from cryptography.fernet import Fernet
from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.backends import default_backend from cryptography.hazmat.backends import default_backend
from steam.guard import * from steam.guard import *
import steam.webauth
if sys.platform.startswith('win'): if sys.platform.startswith('win'):
BASE_LOCATION = '.' #Windows BASE_LOCATION = '.' #Windows
else: else:
BASE_LOCATION = '.' BASE_LOCATION = '.'
ACCOUNT_ATTRIBUTES = ['username', 'password', 'steamid', 'shared_secret', 'identity_secret'] DEFAULT_MOBILE_HEADERS = {
'X-Requested-With': 'com.valvesoftware.android.steam.community',
'User-agent': 'Mozilla/5.0 (Linux; U; Android 4.1.1; en-us; Google Nexus 4 - 4.1.1 - API 16 - 768x1280 Build/JRO03S) \
AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30'
}
ACCOUNT_ATTRIBUTES = ['username', 'password', 'steamid', 'shared_secret', 'identity_secret', 'revocation_code',\
'secret_1', 'serial_number', 'deviceid', 'oauth_token']
class SteamAccount(object): class SteamAccount(object):
username = None username = None
@ -44,6 +53,8 @@ class SteamAccount(object):
_file = None _file = None
_fernet_key = None _fernet_key = None
_fernet_suite = None _fernet_suite = None
_web_auth = None
_session = None
def __init__(self, username, password): def __init__(self, username, password):
self.username = username self.username = username
@ -79,6 +90,141 @@ class SteamAccount(object):
except AttributeError: except AttributeError:
raise IdentitySecretNotSet('Add identity_secret to this instance to generate confirmation keys') raise IdentitySecretNotSet('Add identity_secret to this instance to generate confirmation keys')
def fetch_mobile_confirmations(self, retries=1):
self._verify_mobile_session()
if not self._verify_mobile_authenticator():
raise MobileAuthenticatorException('The steam mobile authenticator is required to access the mobile confirmations.')
timestamp = get_time_offset()
confirmation_key = self.get_confirmation_key('conf', timestamp)
confirmation_uri = 'https://steamcommunity.com/mobileconf/conf?p=%s&a=%s&k=%s&t=%s&m=android&tag=conf' %\
( self.deviceid, self.steamid, confirmation_key, timestamp)
response = self.session.get(confirmation_uri, headers=DEFAULT_MOBILE_HEADERS)
raw_confirmations = [ ]
if response.status_code == 200:
if 'Invalid authenticator' in response.text:
retries += 1
return self.fetch_mobile_confirmations(retries)
confirmation_ids = re.findall(r'data-confid="(\d+)"', response.text)
confirmation_keys = re.findall(r'data-key="(\d+)"', response.text)
confirmation_descriptions = re.findall(r'<div>((Confirm|Trade with|Sell -) .+)<\/div>', response.text)
if confirmation_ids and confirmation_keys:
for index, confirmation_id in enumerate(confirmation_ids):
raw_confirmations.append({
'id': confirmation_id,
'key': confirmation_keys[index],
'description': confirmation_descriptions[index]
})
return raw_confirmations
return [ ]
def add_mobile_authenticator(self):
if self._verify_mobile_authenticator():
raise MobileAuthenticatorException('The steam mobile authenticator is already enabled.')
self._verify_mobile_session()
deviceid = getattr(self, 'deviceid') or generate_device_id(self.steamid)
data = {
'steamid': self.steamid,
'sms_phone_id': 1,
'access_token': self.oauth_token,
'authenticator_time': get_time_offset(),
'authenticator_type': 1,
'device_identifier': deviceid
}
response = self.session.post('https://api.steampowered.com/ITwoFactorService/AddAuthenticator/v1/',
data, headers=DEFAULT_MOBILE_HEADERS)
if response.status_code == 200:
response_json = json.loads(response.text)
if response_json.get('response').get('status') == 1:
self.set_account_property('shared_secret', response_json.get('response').get('shared_secret'))
self.set_account_property('identity_secret', response_json.get('response').get('identity_secret'))
self.set_account_property('revocation_code', response_json.get('response').get('revocation_code'))
self.set_account_property('secret_1', response_json.get('response').get('secret_1'))
self.set_account_property('serial_number', response_json.get('response').get('serial_number'))
self.set_account_property('deviceid', deviceid)
return True
return False
def finalize_mobile_authenticator(self, sms_code, retries=1):
if self._verify_mobile_authenticator():
raise MobileAuthenticatorException('The steam mobile authenticator is already enabled.')
self._verify_mobile_session()
if not sms_code:
raise SMSCodeNotProvided('The sms code is required for finalizing the process of adding the mobile\
authenticator')
timestamp = get_time_offset()
data = {
'steamid': self.steamid,
'access_token': self.oauth_token,
'authenticator_time': timestamp,
'authenticator_code': generate_twofactor_code_for_time(self.shared_secret, timestamp),
'activation_code': sms_code
}
response = self.session.post('https://api.steampowered.com/ITwoFactorService/FinalizeAddAuthenticator/v1/',
data, headers=DEFAULT_MOBILE_HEADERS)
if response.status_code == 200:
response_json = json.loads(response.text)
if response_json.get('response').get('success'):
self.set_account_property('has_mobile_authenticator', True)
return True
else:
if response_json.get('response').get('success') and retries < 30:
retries += 1
return self._finalize_mobile_authenticator(sms_code, retries)
return False
def remove_mobile_authenticator(self):
if not self._verify_mobile_authenticator():
raise MobileAuthenticatorException('The steam mobile authenticator is not enabled.')
self._verify_mobile_session()
data = {
'steamid': self.steamid,
'steamguard_scheme': 2,
'revocation_code': self.revocation_code,
'access_token': self.oauth_token
}
response = self.session.post('https://api.steampowered.com/ITwoFactorService/RemoveAuthenticator/v1/',
data, headers=DEFAULT_MOBILE_HEADERS)
if response.status_code == 200:
response_json = json.loads(response.text)
if response_json.get('response').get('success'):
self.set_account_property('has_mobile_authenticator', False)
return True
return False
def _verify_mobile_session(self):
if not isinstance(self._web_auth, steam.webauth.MobileWebAuth):
raise MobileAuthenticatorException('A mobile session is required.')
if self._web_auth.complete:
raise MobileAuthenticatorException('The mobile session has to be logged in to steam.')
def _verify_mobile_authenticator(self):
if getattr(self, 'has_mobile_authenticator') and self.has_mobile_authenticator:
return True
return False
def _setup(self): def _setup(self):
self._generate_fernet_key() self._generate_fernet_key()
self._spawn_fernet_suite() self._spawn_fernet_suite()
@ -129,6 +275,45 @@ class SteamAccount(object):
def _spawn_fernet_suite(self): def _spawn_fernet_suite(self):
self._fernet_suite = Fernet(self._fernet_key) self._fernet_suite = Fernet(self._fernet_key)
def _spawn_web_session(self):
self._web_auth = steam.webauth.WebAuth(self.username, self.password)
def _spawn_mobile_session(self):
self._web_auth = steam.webauth.MobileWebAuth(self.username, self.password)
def _login_web_session(self, captcha='', email_code='', twofactor_code=''):
try:
self._web_auth.login()
except steam.webauth.CaptchaRequired:
if not captcha:
raise CaptchaNotProvided('The steam login captcha is required for logging in, but was not provided.')
self._web_auth.login(captcha=captcha)
except steam.webauth.EmailCodeRequired:
if not email_code:
raise EMailCodeNotProvided('The email code is required for logging in, but was not provided.')
self._web_auth.login(email_code=email_code)
except steam.webauth.TwoFactorCodeRequired:
if not twofactor_code:
try:
twofactor_code = self.login_code
except SharedSecretNotSet:
raise TwoFACodeNotProvided('The twofactor code is required for logging in, but was not provided.')
self._web_auth.login(twofactor_code=twofactor_code)
if self._web_auth.complete:
if not getattr(self, 'steamid'):
self.set_account_property('steamid', self._web_auth.steamid)
if isinstance(self._web_auth, steam.webauth.MobileWebAuth) and not getattr(self, 'oauth_token'):
self.set_account_property('oauth_token', self._web_auth.oauth_token)
self._session = self._web_auth.session
else:
raise WebAuthNotComplete('The web authentication could not be completed.')
class SteamAccountException(Exception): class SteamAccountException(Exception):
pass pass
@ -136,4 +321,25 @@ class SharedSecretNotSet(SteamAccountException):
pass pass
class IdentitySecretNotSet(SteamAccountException): class IdentitySecretNotSet(SteamAccountException):
pass
class WebAuthNotComplete(SteamAccountException):
pass
class MobileAuthenticatorException(SteamAccountException):
pass
class ParameterNotProvidedException(SteamAccountException):
pass
class CaptchaNotProvided(ParameterNotProvidedException):
pass
class EMailCodeNotProvided(ParameterNotProvidedException):
pass
class TwoFACodeNotProvided(ParameterNotProvidedException):
pass
class SMSCodeNotProvided(ParameterNotProvidedException):
pass pass
Loading…
Cancel
Save