Exemple #1
0
    def do_direct_authorization(self, session):
        """ Direct Authorization, more info: https://vk.com/dev/auth_direct """
        logger.info('Doing direct authorization, app_id=%s', self.app_id)
        auth_data = {
            'client_id': self.app_id,
            'client_secret': self._client_secret,
            'username': self._login,
            'password': self._password,
            'grant_type': 'password',
            '2fa_supported': self._two_fa_supported,
            'scope': self.scope,
            'v': self.api_version
        }
        response = session.post(url=self.DIRECT_AUTHORIZE_URL,
                                data=stringify_values(auth_data))
        try:
            response_json = response.json()
        except ValueError:  # not JSON in response
            error_message = 'OAuth2 grant access error'
            logger.error(response.text)
            raise VkAuthError(error_message)
        else:
            if 'access_token' in response_json:
                return response_json

            if response_json['error'] == 'need_validation':
                return self.direct_auth_require_2fa(session, auth_data)
            elif response_json['error'] == 'need_captcha':
                return self.direct_auth_require_captcha(
                    session, response_json, auth_data)
            else:
                error_message = 'VK error: [{}] {}'.format(
                    response_json['error'], response_json['error_description'])
                raise VkAuthError(error_message)
Exemple #2
0
    def require_auth_captcha(self, query_params, form_text, login_form_data,
                             session):
        """Resolve auth captcha case

        :param query_params: dict: response query params, for example:
        {'s': '0', 'email': 'my@email', 'dif': '1', 'role': 'fast', 'sid': '1'}

        :param form_text: str: raw form html data
        :param login_form_data: dict
        :param session: requests.Session
        :return: :raise VkAuthError:
        """
        logger.info('Captcha is needed')

        action_url = parse_form_action_url(form_text)
        logger.debug('form_url %s', action_url)
        if not action_url:
            raise VkAuthError('Cannot find form action url')

        captcha_url = '%s?s=%s&sid=%s' % (self.CAPTCHA_URI, query_params['s'],
                                          query_params['sid'])
        logger.info('Captcha url %s', captcha_url)

        login_form_data['captcha_sid'] = query_params['sid']
        login_form_data['captcha_key'] = self.get_captcha_key(captcha_url)

        response = session.post(action_url, login_form_data)
        return response
Exemple #3
0
    def _get_access_token(self):
        """Get access token using app_id, login and password OR service token
        (service token docs: https://vk.com/dev/service_token
        """
        if self._service_token:
            logger.info('Use service token: %s',
                        5 * '*' + self._service_token[50:])
            return self._service_token

        if not all([self.app_id, self._login, self._password]):
            raise ValueError(
                'app_id=%s, login=%s password=%s (masked) must be given' %
                (self.app_id, self._login,
                 '*' * len(self._password) if self._password else 'None'))

        logger.info("Getting access token for user '%s'" % self._login)
        with self.http_session as s:
            self.do_login(http_session=s)
            url_query_params = self.do_oauth2_authorization(session=s)
            logger.debug('url_query_params: %s', url_query_params)

        if 'access_token' in url_query_params:
            logger.info('Access token has been gotten')
            return url_query_params['access_token']
        else:
            raise VkAuthError('OAuth2 authorization error. Url params: %s' %
                              url_query_params)
Exemple #4
0
    def require_auth_captcha(self, response, query_params, login_form_data,
                             http_session):
        """Resolve auth captcha case

        :param response: http response
        :param query_params: dict: response query params, for example:
        {'s': '0', 'email': 'my@email', 'dif': '1', 'role': 'fast', 'sid': '1'}

        :param login_form_data: dict
        :param http_session: requests.Session
        :return: :raise VkAuthError:
        """
        logger.info('Captcha is needed. Query params: %s', query_params)
        form_text = response.text

        action_url = parse_form_action_url(form_text)
        logger.debug('form action url: %s', action_url)
        if not action_url:
            raise VkAuthError('Cannot find form action url')

        captcha_sid, captcha_url = parse_captcha_html(
            html=response.text, response_url=response.url)
        logger.info('Captcha url %s', captcha_url)

        login_form_data['captcha_sid'] = captcha_sid
        login_form_data['captcha_key'] = self.get_captcha_key(captcha_url)

        response = http_session.post(action_url, login_form_data)
        return response
Exemple #5
0
    def get_access_token(self):
        """
        Get access token using app id and user login and password
        if no stored token provided
        else use stored token as access token
        """

        if not all([self.app_id, self._login, self._password
                    ]) and not self._stored_token:
            raise ValueError(
                'app_id=%s, login=%s password=%s (masked) must be given' %
                (self.app_id, self._login, bool(self._password)))

        logger.info("Getting access token for user '%s'" % self._login)
        with VerboseHTTPSession() as s:
            if self._stored_token:
                url_query_params = {'access_token': self._stored_token}
                self._stored_token = None
            else:
                self.do_login(session=s)
                url_query_params = self.do_oauth2_authorization(session=s)
                logger.debug('url_query_params: %s', url_query_params)

        if 'access_token' in url_query_params:
            logger.info('Done')
            return url_query_params['access_token']
        else:
            raise VkAuthError('OAuth2 authorization error')
Exemple #6
0
 def get_2fa_code(self):
     if self.interactive:
         auth_check_code = raw_input('Auth check code: ')
         return auth_check_code.strip()
     else:
         raise VkAuthError(
             'Auth check code is needed (SMS, Google Authenticator or '
             'one-time password). '
             'Use interactive mode to enter the code manually')
Exemple #7
0
    def do_login(self, session):
        """Do vk login

        :param session: vk_requests.utils.LoggingSession: http session
        """

        response = session.get(self.LOGIN_URL)
        login_form_action = parse_form_action_url(response.text)
        if not login_form_action:
            raise VkAuthError('VK changed login flow')

        login_form_data = {'email': self._login, 'pass': self._password}
        response = session.post(login_form_action, login_form_data)
        logger.debug('Cookies: %s', session.cookies)

        response_url_query = parse_url_query_params(response.url,
                                                    fragment=False)
        act = response_url_query.get('act')
        logger.debug('response_url_query: %s', response_url_query)

        # Check response url query params firstly
        if 'sid' in response_url_query:
            self.require_auth_captcha(query_params=response_url_query,
                                      form_text=response.text,
                                      login_form_data=login_form_data,
                                      session=session)

        elif act == 'authcheck':
            self.require_sms_code(html=response.text, session=session)

        elif act == 'security_check':
            self.require_phone_number(html=response.text, session=session)

        session_cookies = ('remixsid' in session.cookies, 'remixsid6'
                           in session.cookies)
        if any(session_cookies):
            # Session is already established
            logger.info('Session is already established')
            return None
        else:
            message = 'Authorization error (incorrect password)'
            logger.error(message)
            raise VkAuthError(message)
Exemple #8
0
    def get_captcha_key(self, captcha_image_url):
        """Read CAPTCHA key from user input"""

        if self.interactive:
            print(
                'Open CAPTCHA image url in your browser and enter it below: ',
                captcha_image_url)
            captcha_key = raw_input('Enter CAPTCHA key: ')
            return captcha_key
        else:
            raise VkAuthError(
                'Captcha is required. Use interactive mode to enter it '
                'manually')
Exemple #9
0
    def do_login(self, http_session):
        """Do vk login

        :param http_session: vk_requests.utils.VerboseHTTPSession: http session
        """

        response = http_session.get(self.LOGIN_URL)
        action_url = parse_form_action_url(response.text)

        # Stop login it action url is not found
        if not action_url:
            logger.debug(response.text)
            raise VkParseError("Can't parse form action url")

        login_form_data = {'email': self._login, 'pass': self._password}
        login_response = http_session.post(action_url, login_form_data)
        logger.debug('Cookies: %s', http_session.cookies)

        response_url_query = parse_url_query_params(login_response.url,
                                                    fragment=False)

        logger.debug('response_url_query: %s', response_url_query)
        act = response_url_query.get('act')

        # Check response url query params firstly
        if 'sid' in response_url_query:
            self.require_auth_captcha(query_params=response_url_query,
                                      form_text=login_response.text,
                                      login_form_data=login_form_data,
                                      http_session=http_session)

        elif act == 'authcheck':
            self.require_2fa(html=login_response.text,
                             http_session=http_session)

        elif act == 'security_check':
            self.require_phone_number(html=login_response.text,
                                      session=http_session)

        session_cookies = ('remixsid' in http_session.cookies, 'remixsid6'
                           in http_session.cookies)
        if any(session_cookies):
            logger.info('VK session is established')
            return True
        else:
            message = 'Authorization error: incorrect password or ' \
                      'authentication code'
            logger.error(message)
            raise VkAuthError(message)
Exemple #10
0
    def direct_auth_require_captcha(self, session, response, auth_data):
        logger.info('Captcha is needed. Response: %s', response)

        captcha_url = response['captcha_img']
        logger.info('Captcha url %s', captcha_url)

        auth_data['captcha_sid'] = response['captcha_sid']
        auth_data['captcha_key'] = self.get_captcha_key(captcha_url)

        response = session.post(url=self.DIRECT_AUTHORIZE_URL,
                                data=stringify_values(auth_data))
        try:
            response_json = response.json()
        except ValueError:  # not JSON in response
            error_message = 'OAuth2 grant access error'
            logger.error(response.text)
            raise VkAuthError(error_message)
        return response_json
Exemple #11
0
 def direct_auth_require_2fa(self, session, auth_data):
     if self._two_fa_force_sms:
         auth_data['force_sms'] = self._two_fa_force_sms
         session.post(url=self.DIRECT_AUTHORIZE_URL,
                      data=stringify_values(auth_data))
     logger.info(
         'User enabled 2 factors authentication. Auth check code is needed '
         '(SMS, Google Authenticator or one-time password generated by vk)')
     auth_data['code'] = self.get_2fa_code()
     response = session.post(url=self.DIRECT_AUTHORIZE_URL,
                             data=stringify_values(auth_data))
     try:
         response_json = response.json()
     except ValueError:  # not JSON in response
         error_message = 'OAuth2 grant access error'
         logger.error(response.text)
         raise VkAuthError(error_message)
     return response_json
Exemple #12
0
    def do_implicit_flow_authorization(self, session):
        """ Standard OAuth2 authorization method. It's used for getting access token
        More info: https://vk.com/dev/implicit_flow_user
        """
        logger.info('Doing implicit flow authorization, app_id=%s',
                    self.app_id)
        auth_data = {
            'client_id': self.app_id,
            'display': 'mobile',
            'response_type': 'token',
            'scope': self.scope,
            'redirect_uri': 'https://oauth.vk.com/blank.html',
            'v': self.api_version
        }
        response = session.post(url=self.AUTHORIZE_URL,
                                data=stringify_values(auth_data))
        url_query_params = parse_url_query_params(response.url)

        if 'expires_in' in url_query_params:
            logger.info('Token will be expired in %s sec.' %
                        url_query_params['expires_in'])
        if 'access_token' in url_query_params:
            return url_query_params

        # Permissions are needed
        logger.info('Getting permissions')
        action_url = parse_form_action_url(response.text)
        logger.debug('Response form action: %s', action_url)

        if action_url:
            response = session.get(action_url)
            url_query_params = parse_url_query_params(response.url)
            return url_query_params
        try:
            response_json = response.json()
        except ValueError:  # not JSON in response
            error_message = 'OAuth2 grant access error'
            logger.error(response.text)
        else:
            error_message = 'VK error: [{}] {}'.format(
                response_json['error'], response_json['error_description'])
        logger.error('Permissions obtained')
        raise VkAuthError(error_message)
Exemple #13
0
    def do_oauth2_authorization(self, session):
        """ OAuth2. More info: https://vk.com/dev/auth_mobile
        """
        logger.info('Doing oauth2')
        auth_data = {
            'client_id': self.app_id,
            'display': 'mobile',
            'response_type': 'token',
            'scope': self.scope,
            'v': self.api_version
        }
        response = session.post(url=self.AUTHORIZE_URL,
                                data=stringify_values(auth_data))
        url_query_params = parse_url_query_params(response.url)
        if 'expires_in' in url_query_params:
            logger.info('Token will be expired in %s sec.' %
                        url_query_params['expires_in'])
        if 'access_token' in url_query_params:
            return url_query_params

        # Permissions is needed
        logger.info('Getting permissions')
        form_action = parse_form_action_url(response.text)
        logger.debug('Response form action: %s', form_action)

        if form_action:
            response = session.get(form_action)
            url_query_params = parse_url_query_params(response.url)
            return url_query_params
        try:
            response_json = response.json()
        except ValueError:  # not JSON in response
            error_message = 'OAuth2 grant access error'
            logger.error(response.text)
        else:
            error_message = 'VK error: [{}] {}'.format(
                response_json['error'], response_json['error_description'])
        logger.error('Permissions obtained')
        raise VkAuthError(error_message)
Exemple #14
0
    def require_phone_number(self, html, session):
        logger.info(
            'Auth requires phone number. You do login from unusual place')

        # Raises VkPageWarningsError in case of warnings
        # NOTE: we check only 'security_check' case on warnings for now
        # in future it might be extended for other cases as well
        check_html_warnings(html=html)

        # Determine form action url
        action_url = parse_form_action_url(html)

        # Get masked phone from html to make things more clear
        phone_prefix, phone_suffix = parse_masked_phone_number(html)

        if self._phone_number:
            code = self._phone_number[len(phone_prefix):-len(phone_suffix)]
        else:
            if self.interactive:
                prompt = 'Enter missing digits of your phone number (%s****%s): '\
                            % (phone_prefix, phone_suffix)
                code = raw_input(prompt)
            else:
                raise VkAuthError(
                    'Phone number is required. Create an API instance using '
                    'phone_number parameter or use interactive mode')

        params = parse_url_query_params(action_url, fragment=False)
        auth_data = {
            'code': code,
            'act': 'security_check',
            'hash': params['hash']
        }
        response = session.post(url=self.LOGIN_URL + action_url,
                                data=auth_data)
        logger.debug('require_phone_number resp: %s', response.text)
Exemple #15
0
 def get_2fa_code(self):
     raise VkAuthError('Auth check code is needed '
                       '(SMS, Google Authenticator or one-time password)')
Exemple #16
0
 def get_sms_code(self):
     raise VkAuthError('Auth check code is needed')