def main(): parser = argparse.ArgumentParser(description="Authenticate with xbox live") parser.add_argument('--tokens', '-t', default=TOKENS_FILE, help="Token filepath, file gets created if nonexistent and auth is successful." " Default: {}".format(TOKENS_FILE)) parser.add_argument('--email', '-e', help="Microsoft Account Email address") parser.add_argument('--password', '-p', help="Microsoft Account password") args = parser.parse_args() tokens_loaded = False two_factor_auth_required = False server_data = None auth_mgr = AuthenticationManager() if args.tokens: try: auth_mgr.load(args.tokens) tokens_loaded = True except FileNotFoundError as e: print('Failed to load tokens from \'{}\'. Error: {}'.format(e.filename, e.strerror)) auth_mgr.email_address = args.email auth_mgr.password = args.password if (not args.email or not args.password) and not tokens_loaded: print("Please input authentication credentials") if not args.email and not tokens_loaded: auth_mgr.email_address = input("Microsoft Account Email: ") if not args.password and not tokens_loaded: auth_mgr.password = getpass.getpass('Microsoft Account Password: '******'2FA is required, message: %s' % e) two_factor_auth_required = True server_data = e.server_data except AuthenticationException as e: print('Email/Password authentication failed! Err: %s' % e) sys.exit(-1) if two_factor_auth_required: try: two_factor_auth(auth_mgr, server_data) except AuthenticationException as e: print('2FA Authentication failed! Err: %s' % e) sys.exit(-1) if args.tokens: auth_mgr.dump(args.tokens) print('Refresh Token: %s' % auth_mgr.refresh_token) print('XSTS Token: %s' % auth_mgr.xsts_token) print('Userinfo: %s' % auth_mgr.userinfo)
def test_auth_invalid_credentials(): auth_manager = AuthenticationManager() auth_manager.email_address = "*****@*****.**" auth_manager.password = "******" with Betamax(auth_manager.session).use_cassette('invalid_auth'): with pytest.raises(AuthenticationException): auth_manager.authenticate() assert auth_manager.authenticated is False
def test_auth_valid_credentials(): auth_manager = AuthenticationManager() auth_manager.email_address = "*****@*****.**" auth_manager.password = "******" with Betamax(auth_manager.session).use_cassette('full_auth'): auth_manager.authenticate(do_refresh=False) assert auth_manager.authenticated is True assert auth_manager.xsts_token.is_valid is True assert auth_manager.access_token.is_valid is True assert auth_manager.refresh_token.is_valid is True assert auth_manager.user_token.is_valid is True assert auth_manager.userinfo.userhash == '1674471606081042789' assert auth_manager.userinfo.xuid == '2535428504476914' assert auth_manager.userinfo.gamertag == 'xboxWebapiGamertag'
def _do_2fa(cassette_name, strategy_index, proof=None, otc=None): auth_manager = AuthenticationManager() auth_manager.email_address = '*****@*****.**' auth_manager.password = '******' with Betamax(auth_manager.session).use_cassette(cassette_name): with pytest.raises(TwoFactorAuthRequired) as excinfo: auth_manager.authenticate() two_fa_auth = TwoFactorAuthentication( auth_manager.session, auth_manager.email_address, excinfo.value.server_data ) two_fa_auth.check_otc(strategy_index, proof) access_token, refresh_token = two_fa_auth.authenticate(strategy_index, proof, otc) auth_manager.access_token = access_token auth_manager.refresh_token = refresh_token auth_manager.authenticate(do_refresh=False) return auth_manager
def test_auth_refresh_token(): auth_manager = AuthenticationManager() auth_manager.refresh_token = RefreshToken( "CuZ*4TX7!SAF33cW*kzdFmgCLPRcz0DtUHFqjQgF726!FG3ScC5yMiDLsJYJ03m4fURrzf3J7X8l6A8mJGhHoRf42aHeJLrtp6wS" "Jh*PudaQdPNGJZHD1CpU4diJJxz0zhrijFsaAYXMqf3mSU7EerR5RtdHOwbcrlRlj7TBQ9RdLqWpy9KWsNhyPrwOMDJnBfAf3xsZ" "3g3QkmMeKGil85*q*MV*YqMPZTa8UVLPfM!jJeHwOjVhWPaYVq4hf6zIAwSLJl1Reo6GbkkPktrK3laFBGeqSkq651YgdjwtepwC" "Ef7oMwzz8c8msv8l95RU*QmtIjdRFd!fYtQctiGLDGs$") with Betamax(auth_manager.session).use_cassette('token_refresh'): auth_manager.authenticate(do_refresh=True) assert auth_manager.authenticated is True assert auth_manager.xsts_token.is_valid is True assert auth_manager.access_token.is_valid is True assert auth_manager.refresh_token.is_valid is True assert auth_manager.user_token.is_valid is True assert auth_manager.userinfo.userhash == '1674471606081042789' assert auth_manager.userinfo.xuid == '2535428504476914' assert auth_manager.userinfo.gamertag == 'xboxWebapiGamertag'
class XBot(MicrosoftBot): def __init__(self, email, password, x_auth_key): self.x_auth_key = x_auth_key self.url = 'http://xapi.us/v2' self.client = Client(api_key=x_auth_key) # use the microsoft api to get the xuid info without using requests self.auth_mgr = AuthenticationManager() # set data for auth manager self.auth_mgr.email_address = email self.auth_mgr.password = password # authentication self.auth_mgr.authenticate(do_refresh=True) # set the new info to a xbl client self.xbl_client = XboxLiveClient(self.auth_mgr.userinfo.userhash, self.auth_mgr.xsts_token.jwt, self.auth_mgr.userinfo.xuid) self.profile_provider = ProfileProvider(self.xbl_client) self.people_provider = PeopleProvider(self.xbl_client) # must have a method here to get the recent activity of a user def get_games(self, gamertag): # this takes more time but saves api requests xuid = self.get_xuid(gamertag) gamer = self.client.gamer(gamertag=gamertag, xuid=xuid) try: titles = gamer.get('xboxonegames')['titles'] except ReadTimeout: time.sleep(30) return [] except JSONDecodeError: time.sleep(30) print(f"{gamertag} isn't a real gamer.") return [] games = [title['name'] for title in titles] return games def send_messages_url(self, gamertags, message): endpoint = f"{self.url}/messages" headers = { 'X-Auth': self.x_auth_key, 'Content-Type': 'application/json' } # convert the gamertags to xuids xuids = [self.get_xuid(gamertag) for gamertag in gamertags] body = {"to": xuids, "message": message} response = requests.post(url=endpoint, data=body, headers=headers) return response def send_message(self, gamertag, message): # set a boolean to whether or not the message was sent sent = False # create a game object try: xuid = self.get_xuid(gamertag) gamer = self.client.gamer(gamertag, xuid) # send the message gamer.send_message(message) sent = True except JSONDecodeError: pass return sent # create a method that extrapolates on a set of gamers and adds to it # games --> particular games the gamer has to play to be added to the set def extrapolate(self, gamertags, games, max_messages): candidates = set() list_manager = ListManager(games) for gamer in gamertags: # find the frients and games --> build with a dict builder friends = self.get_friends(gamer) # use a set operator to add the friends candidates = candidates | friends time.sleep(2) # randomize the candidates to increase spread candidates = list(candidates) print(candidates) shuffle(candidates) # find overlapping games --> add to gamertags if it the gamer matches one of the games for gamer in candidates: gamertag = self.get_gamertag(gamer) if gamertag: print(f"found {gamertag}") # this protexts against getting nonetype errrors if not gamertag: continue elif not gamer: continue print(f"Checking what games {gamertag} plays") games = list(set(self.get_games(gamertag))) # check if any of the games are in the the list of games --> the list manager return if list_manager.find_any_overlap(games): gamertags.add(gamertag) print(f"Added {gamertag} from friends") # check if the gamertags has reached its max if len(gamertags) >= max_messages: break # sleep to avoid throttling time.sleep(10) # return the gamertags return gamertags
class MicrosoftBot: def __init__(self, email, password): self.auth_mgr = AuthenticationManager() # set data for auth manager self.auth_mgr.email_address = email self.auth_mgr.password = password # authentication self.auth_mgr.authenticate(do_refresh=True) # set the new info to a xbl client self.xbl_client = XboxLiveClient(self.auth_mgr.userinfo.userhash, self.auth_mgr.xsts_token.jwt, self.auth_mgr.userinfo.xuid) # not currently used but could be useful later self.profile_provider = ProfileProvider(self.xbl_client) self.people_provider = PeopleProvider(self.xbl_client) # sends message to list of multiple users def send_message(self, message, users): response = self.xbl_client.message.send_message(message, gamertags=users) return response # create a function that gets xuids def get_xuid(self, gamertag): profile = self.profile_provider.get_profile_by_gamertag( gamertag).json() try: xuid = profile['profileUsers'][0]['id'] except KeyError: # this means the gamertag wasn't found return return xuid # create a function to convert xuids back to gamertags def get_gamertag(self, xuid): profile = self.profile_provider.get_profile_by_xuid(xuid).json() try: settings = profile['profileUsers'][0]['settings'] except KeyError: time.sleep(61) return None info_dict = {pair['id']: pair['value'] for pair in settings} gamertag = info_dict['Gamertag'] return gamertag # create a method to get the user's friends (use xuids to save time and maximize integration) def get_friends(self, gamertag): xuid = self.get_xuid(gamertag) response_json = self.people_provider.get_friends_by_xuid(xuid).json() try: friends_raw = response_json['people'] except KeyError: print(f"{gamertag} is a certified loner") friends_raw = [] # this often means that the api is blocking us time.sleep(180) xuids = {info['xuid'] for info in friends_raw} return xuids
class WebAPIDisplay(object): focus_map = { None: 'selected' } palette = [ ('bg', 'white', 'dark gray'), ('header', 'yellow', 'dark blue', 'standout'), # footer ('foot', 'dark cyan', 'dark blue', 'bold'), ('key', 'light cyan', 'dark blue', 'underline') ] header_text = ('header', [ "Xbox WebAPI" ]) footer_main_text = ('foot', [ ('key', 'L:'), "view log ", ('key', 'Q:'), "quit " ]) footer_log_text = ('foot', [ ('key', 'Q:'), "quit " ]) log_fmt = logging.Formatter(logging.BASIC_FORMAT) log_level = logging.DEBUG def __init__(self, tokenfile_path): self.tokenfile_path = tokenfile_path self.auth_mgr = AuthenticationManager() self.two_factor_auth = None # 2FA cache self.index = None self.proof = None self.otc = None self.loop = None self.log = LogListBox(self) self.view_stack = [] try: self.auth_mgr.load(self.tokenfile_path) except Exception as e: logging.debug('Tokens failed to load from file, Error: {}'.format(e)) ''' self.need_refresh = self.auth_mgr.refresh_token and \ self.auth_mgr.refresh_token.is_valid and \ self.auth_mgr.refresh_token.date_valid < (datetime.now(tzutc()) + timedelta(days=7)) ''' self.need_full_auth = not self.auth_mgr.refresh_token or not self.auth_mgr.refresh_token.is_valid def push_view(self, sender, view): self.view_stack.append(view) self.loop.widget = view self.loop.draw_screen() def pop_view(self, sender): if len(self.view_stack) > 1: top_widget = self.view_stack.pop() if hasattr(top_widget, 'close_view'): top_widget.close_view(sender) self.loop.widget = self.view_stack[-1] self.loop.draw_screen() else: self.do_quit() def _input_prompt(self, prompt, callback, entries=None): if entries: list_entries = [ urwid.AttrWrap(urwid.SelectableIcon(e, cursor_position=0), None) for e in entries ] walker = urwid.SimpleFocusListWalker([urwid.AttrMap(e, None, self.focus_map) for e in list_entries]) listbox = SelectableListBox(walker, callback) view = urwid.BoxAdapter(listbox, height=len(entries)) else: edit_text = QuestionBox(callback, align='left') view = urwid.AttrMap(edit_text, None, self.focus_map) box = urwid.LineBox(view, title=prompt) self._view_menu([box]) def view_main(self): if self.need_full_auth: self.view_authentication_menu() # elif self.need_refresh: # self._authenticate(status_text='Refreshing tokens...\n') else: self._authenticate() def _two_factor_finish_auth(self, otc): self.otc = otc self.view_msgbox('Waiting for 2FA to complete', 'Please wait') access_token, refresh_token = None, None try: access_token, refresh_token = self.two_factor_auth.authenticate( self.index, self.proof, self.otc ) except AuthenticationException as e: logging.debug('2FA Authentication failed, Error: {}'.format(e)) self.view_msgbox('2FA Authentication failed!\n{}\n'.format(e), 'Error', show_quit_button=True) self.auth_mgr.access_token = access_token self.auth_mgr.refresh_token = refresh_token self._authenticate() def _two_factor_auth_ask_otc(self, proof): self.proof = proof need_otc = self.two_factor_auth.check_otc(self.index, proof) if need_otc: self._input_prompt('Enter One-Time-Code (OTC)', self._two_factor_finish_auth) else: self._two_factor_finish_auth(None) def _two_factor_auth_verify_proof(self, index): self.index = index verification_prompt = self.two_factor_auth.get_method_verification_prompt(index) if verification_prompt: self._input_prompt(verification_prompt, self._two_factor_auth_ask_otc) else: self._two_factor_auth_ask_otc(None) def view_two_factor_auth(self, server_data): self.two_factor_auth = TwoFactorAuthentication( self.auth_mgr.session, self.auth_mgr.email_address, server_data ) entries = ['{!s}, Name: {}'.format( TwoFactorAuthMethods(strategy.get('type', 0)), strategy.get('display')) for strategy in self.two_factor_auth.auth_strategies ] self._input_prompt('Choose desired auth method', self._two_factor_auth_verify_proof, entries) def _authenticate(self, email=None, password=None, status_text='Authenticating...\n'): self.auth_mgr.email_address = email self.auth_mgr.password = password try: self.view_msgbox(status_text, 'Please wait') self.auth_mgr.authenticate(do_refresh=True) # do_refresh=self.need_refresh self.auth_mgr.dump(self.tokenfile_path) do_show_quit_button = True if not self.need_full_auth: # If authentication was done from tokens, auto close on success do_show_quit_button = False self.loop.set_alarm_in(2.0, lambda *args: self.do_quit()) self.view_msgbox('Authentication was successful!\n', 'Success', show_quit_button=do_show_quit_button) except TwoFactorAuthRequired as e: self.view_two_factor_auth(e.server_data) except AuthenticationException as e: logging.debug('Authentication failed, Error: {}'.format(e)) self.view_msgbox('Authentication failed!\n{}\n'.format(e), 'Error', show_quit_button=True) def _on_button_press(self, button, user_arg=None): label = button.get_label() if 'Authenticate' == label: email, pwd = (t.get_edit_text() for t in user_arg) self._authenticate(email, pwd) elif 'Quit' == label or 'Cancel' == label: self.do_quit() else: raise ValueError('tui: Unexpected button pressed: {}'.format(label)) def _view_menu(self, elements): header = urwid.AttrMap(urwid.Text(self.header_text), 'header') footer = urwid.AttrMap(urwid.Text(self.footer_main_text), 'foot') assert isinstance(elements, list) pile = urwid.Pile(elements) p = urwid.AttrWrap(pile, 'bg') padded = urwid.Padding(p, 'center', ('relative', 80)) filler = urwid.Filler(padded) frame = urwid.Frame(filler, header=header, footer=footer) self.push_view(self, frame) def view_authentication_menu(self): info_label = urwid.Text( 'Please authenticate with your Microsoft Account\n', align='center' ) div = urwid.Divider() email_text = urwid.AttrMap(urwid.Edit('Email Address: '), None, self.focus_map) password_text = urwid.AttrMap(urwid.Edit('Account Password: '******'*'), None, self.focus_map) authenticate_button = urwid.Button('Authenticate') authenticate_button._label.align = 'center' authenticate_button = urwid.AttrMap(authenticate_button, None, self.focus_map) cancel_button = urwid.Button('Cancel') cancel_button._label.align = 'center' cancel_button = urwid.AttrMap(cancel_button, None, self.focus_map) authenticate_button = urwid.Padding(authenticate_button, align='center', width=('relative', 30)) cancel_button = urwid.Padding(cancel_button, align='center', width=('relative', 23)) pile = TabSwitchingPile( [info_label, div, email_text, div, password_text, div, authenticate_button, cancel_button] ) box = urwid.LineBox(pile, title='Authentication required') urwid.connect_signal(authenticate_button.base_widget, 'click', self._on_button_press, user_arg=[email_text.base_widget, password_text.base_widget]) urwid.connect_signal(cancel_button.base_widget, 'click', self._on_button_press) self._view_menu([box]) def view_msgbox(self, msg, title, show_quit_button=False): text = urwid.Text(msg, align='center') if show_quit_button: button = urwid.Button('Quit') button._label.align = 'center' button = urwid.AttrMap(button, None, self.focus_map) pad_button = urwid.Padding(button, 'center', ('relative', 10)) pile = urwid.Pile([text, pad_button]) box = urwid.LineBox(pile, title) # Clicking OK exits UI urwid.connect_signal(button.base_widget, 'click', self._on_button_press) else: box = urwid.LineBox(text, title) self._view_menu([box]) def view_log(self): header = urwid.AttrMap(urwid.Text(self.header_text), 'header') footer = urwid.AttrMap(urwid.Text(self.footer_log_text), 'foot') frame = urwid.Frame(self.log, header=header, footer=footer) self.push_view(self, frame) def return_to_main_menu(self): while len(self.view_stack) > 1: self.pop_view(self) def do_quit(self): raise urwid.ExitMainLoop() def run(self): self.loop = urwid.MainLoop( urwid.SolidFill('x'), handle_mouse=False, palette=self.palette, unhandled_input=self.unhandled_input ) self.loop.set_alarm_in(0.0001, lambda *args: self.view_main()) self.loop.run() return self.auth_mgr.is_authenticated def unhandled_input(self, input): if input in ('q', 'Q'): self.do_quit() elif input in ('l', 'L'): self.view_log()