Exemple #1
0
    def _windows_live_token_refresh(self, refresh_token):
        """
        Internal method to refresh Windows Live Token, called by `self.authenticate`

        Raises:
            AuthenticationException: When provided Refresh-Token is invalid.

        Args:
            refresh_token (object): Instance of :class:`RefreshToken`

        Returns:
            tuple: If authentication succeeds, `tuple` of (AccessToken, RefreshToken) is returned
        """
        if refresh_token and refresh_token.is_valid:
            resp = self.__window_live_token_refresh_request(refresh_token)
            response = json.loads(resp.content.decode('utf-8'))

            if 'access_token' not in response:
                raise AuthenticationException(
                    "Could not refresh token via RefreshToken")

            access_token = AccessToken(response['access_token'],
                                       response['expires_in'])
            refresh_token = RefreshToken(response['refresh_token'])
            return access_token, refresh_token
        else:
            raise AuthenticationException("No valid RefreshToken")
    def finish_auth(self,
                    email,
                    server_data,
                    auth_type,
                    auth_data=None,
                    otc=None,
                    slk=None,
                    proof_confirmation=None):
        """
        Finish the Two-Factor-Authentication. If it succeeds we are provided with Access and Refresh-Token.

        Args:
            email (str): Email Address of the Windows Live Account
            server_data (dict): Parsed javascript-object `serverData`, obtained from Windows Live Auth Request
            auth_type (TwoFactorAuthMethods): Member of :class:`TwoFactorAuthMethods`
            auth_data (str): Authentication data for this provided, specific authorization method
            otc (str): One-Time-Code, required for every method except MS Authenticator v2
            slk (str): Session-Lookup-Key, only needed for auth-method MS Authenticator v2
            proof_confirmation (str): Confirmation of Email or mobile phone number, if that method was chosen

        Returns:
            requests.Response: Instance of :class:`requests.Response`
        """
        if TwoFactorAuthMethods.SMS  == auth_type or \
            TwoFactorAuthMethods.Voice == auth_type or \
            TwoFactorAuthMethods.Email == auth_type:
            post_type = '18'
            general_verify = False
        elif TwoFactorAuthMethods.TOTPAuthenticator == auth_type:
            post_type = '19'
            general_verify = False
        elif TwoFactorAuthMethods.TOTPAuthenticatorV2 == auth_type:
            post_type = '22'
            general_verify = None
        else:
            raise AuthenticationException('Unhandled case for submitting OTC')

        post_data = {
            'login': email,
            'PPFT': server_data.get('sFT'),
            'SentProofIDE': auth_data,
            'sacxt': '1',
            'saav': '0',
            'GeneralVerify': general_verify,
            'type': post_type,
            'purpose': 'eOTT_OneTimePassword',
            'i18':
            '__DefaultSAStrings|1,__DefaultSA_Core|1,__DefaultSA_Wizard|1'
        }

        if otc:
            post_data.update({'otc': otc})
        if slk:
            post_data.update({'slk': slk})
        if proof_confirmation:
            post_data.update({'ProofConfirmation': proof_confirmation})

        return self.session.post(server_data.get('urlPost'),
                                 data=post_data,
                                 allow_redirects=False)
Exemple #3
0
    def _windows_live_authenticate(self, email_address, password):
        """
        Internal method to authenticate with Windows Live, called by `self.authenticate`

        In case of required two-factor-authentication the respective routine is initialized and user gets asked for
        input of verification details.

        Args:
            email_address (str): Microsoft Account Email address
            password (str):  Microsoft Account password

        Raises:
            AuthenticationException: When two-factor-authentication fails or returned headers do not contain
            Access-/Refresh-Tokens.

        Returns:
            tuple: If authentication succeeds, `tuple` of (AccessToken, RefreshToken) is returned
        """
        response = self.__window_live_authenticate_request(
            email_address, password)

        proof_type = self._extract_js_object(response.content.decode("utf-8"),
                                             "PROOF.Type")
        if proof_type:
            log.info("Two Factor Authentication required!")
            twofactor = TwoFactorAuthentication(self.session)
            server_data = self._extract_js_object(
                response.content.decode("utf-8"), "ServerData")
            response = twofactor.authenticate(email_address, server_data)
            if not response:
                raise AuthenticationException(
                    "Two Factor Authentication failed!")

        if 'Location' not in response.headers:
            # we can only assume the login failed
            raise AuthenticationException(
                "Could not log in with supplied credentials")

        # the access token is included in fragment of the location header
        location = urlparse(response.headers['Location'])
        fragment = parse_qs(location.fragment)

        access_token = AccessToken(fragment['access_token'][0],
                                   fragment['expires_in'][0])
        refresh_token = RefreshToken(fragment['refresh_token'][0])
        return access_token, refresh_token
    def request_otc(self, email, server_data, auth_type, proof, auth_data):
        """
        Request OTC (One-Time-Code) if 2FA via Email, Mobile phone or MS Authenticator v2 is desired.

        Args:
            email (str): Email Address of the Windows Live Account
            server_data (dict): Parsed javascript-object `serverData`, obtained from Windows Live Auth Request
            auth_type (TwoFactorAuthMethods): Member of :class:`TwoFactorAuthMethods
            proof (str): Proof Verification, used by mobile phone and email-method, for MS Authenticator provide `None`
            auth_data (str): Authentication data for this provided, specific authorization method

        Raises:
            AuthenticationException: If requested 2FA Authentication Type is unsupported

        Returns:
            requests.Response: Instance of :class:`requests.Response
        """
        post_url = 'https://login.live.com/pp1600/GetOneTimeCode.srf'

        if TwoFactorAuthMethods.Email == auth_type:
            channel = 'Email'
            post_field = 'AltEmailE'
        elif TwoFactorAuthMethods.SMS == auth_type:
            channel = 'SMS'
            post_field = 'MobileNumE'
        elif TwoFactorAuthMethods.Voice == auth_type:
            channel = 'Voice'
            post_field = 'MobileNumE'
        elif TwoFactorAuthMethods.TOTPAuthenticatorV2 == auth_type:
            channel = 'PushNotifications'
            post_field = 'SAPId'
        elif TwoFactorAuthMethods.TOTPAuthenticator == auth_type:
            log.warning('Requesting OTC not necessary for Authenticator v2!')
        else:
            raise AuthenticationException(
                'Unsupported TwoFactor Auth-Type: %d' % auth_type)

        post_data = {
            'login': email,
            'flowtoken': server_data.get('sFT'),
            'purpose': 'eOTT_OneTimePassword',
            'UIMode': '11',
            'channel': channel,
            post_field: auth_data,
        }

        if proof:
            post_data.update({'ProofConfirmation': proof})

        return self.session.post(post_url,
                                 data=post_data,
                                 allow_redirects=False)
Exemple #5
0
    def _xbox_live_authenticate(self, access_token):
        """
        Internal method to authenticate with Xbox Live, called by `self.authenticate`

        Args:
            access_token (object): Instance of :class:`AccessToken`

        Raises:
            AuthenticationException: When provided Access-Token is invalid

        Returns:
            object: If authentication succeeds, returns :class:`UserToken`
        """
        if access_token and access_token.is_valid:
            json_data = self.__xbox_live_authenticate_request(
                access_token).json()
            return UserToken(json_data['Token'], json_data['IssueInstant'],
                             json_data['NotAfter'])
        else:
            raise AuthenticationException("No valid AccessToken")
Exemple #6
0
    def authenticate(self,
                     email_address=None,
                     password=None,
                     ts=None,
                     do_refresh=True):
        """
        Authenticate with Xbox Live using either tokens or user credentials.

        After being called, its property `authenticated` should be checked for success.

        Raises:
            AuthenticationException: When neither token and credential authentication is successful

        Args:
            email_address (str): Microsoft Account Email address
            password (str): Microsoft Account password
            ts (object): Instance of :class:`Tokenstore`
            do_refresh (bool): Refresh Access- and Refresh Token even if still valid, default: True

        Returns:
            object: On success return instance of :class:`Tokenstore`
        """

        full_authentication_required = False

        if not ts:
            log.debug('Creating new tokenstore')
            ts = Tokenstore()

        if self.token_filepath:
            ts = self.load_token_files(ts)

        try:
            # Refresh and Access Token
            if not do_refresh and ts.access_token and ts.refresh_token and \
                    ts.access_token.is_valid and ts.refresh_token.is_valid:
                pass
            else:
                ts.access_token, ts.refresh_token = self._windows_live_token_refresh(
                    ts.refresh_token)

            # User Token
            if ts.user_token and ts.user_token.is_valid:
                pass
            else:
                ts.user_token = self._xbox_live_authenticate(ts.access_token)
            '''
            TODO: Fix
            # Device Token
            if ts.device_token and ts.device_token.is_valid:
                pass
            else:
                ts.device_token = self._xbox_live_device_auth(ts.access_token)

            # Title Token
            if ts.title_token and ts.title_token.is_valid:
                pass
            else:
                ts.title_token = self._xbox_live_title_auth(ts.device_token, ts.access_token)
            '''

            # XSTS Token
            if ts.xsts_token and ts.xsts_token.is_valid and ts.userinfo:
                self.authenticated = True
            else:
                ts.xsts_token, ts.userinfo = self._xbox_live_authorize(
                    ts.user_token)
                self.authenticated = True
        except AuthenticationException:
            full_authentication_required = True

        # Authentication via credentials
        if full_authentication_required and email_address and password:
            ts.access_token, ts.refresh_token = self._windows_live_authenticate(
                email_address, password)
            ts.user_token = self._xbox_live_authenticate(ts.access_token)
            '''
            TODO: Fix
            ts.device_token = self._xbox_live_device_auth(ts.access_token)
            ts.title_token = self._xbox_live_title_auth(ts.device_token, ts.access_token)
            '''
            ts.xsts_token, ts.userinfo = self._xbox_live_authorize(
                ts.user_token)
            self.authenticated = True

        if not self.authenticated:
            raise AuthenticationException(
                "AuthenticationManager was not able to authenticate "
                "with provided tokens or user credentials!")

        self.save_token_files(ts)
        return ts