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
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 propagated to 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: prompt = 'Enter missing digits of your phone number (%s****%s): '\ % (phone_prefix, phone_suffix) code = raw_input(prompt) 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)
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
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
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 propagated to 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: prompt = 'Enter missing digits of your phone number (%s****%s): '\ % (phone_prefix, phone_suffix) code = raw_input(prompt) 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)
def require_sms_code(self, html, session): logger.info('User enabled 2 factors authorization. ' 'Auth check code is needed') action_url = parse_form_action_url(html) auth_check_code = self.get_sms_code() auth_check_data = { 'code': auth_check_code, '_ajax': '1', 'remember': '1' } response = session.post(action_url, data=auth_check_data) return response
def require_sms_code(self, html, session): logger.info('User enabled 2 factors authorization. ' 'Auth check code is needed') auth_check_form_action = parse_form_action_url(html) auth_check_code = self.get_sms_code() auth_check_data = { 'code': auth_check_code, '_ajax': '1', 'remember': '1' } response = session.post(auth_check_form_action, data=auth_check_data) return response
def require_2fa(self, html, session): logger.info( 'User enabled 2 factors authentication. Auth check code is needed ' '(SMS, Google Authenticator or one-time password generated by vk)') action_url = parse_form_action_url(html) auth_check_code = self.get_2fa_code() auth_check_data = { 'code': auth_check_code, '_ajax': '1', 'remember': '1' } url = '/'.join([self.LOGIN_URL + action_url]) response = session.post(url=url, data=auth_check_data) return response
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)
def do_login(self, session): """Do vk login :param session: vk_requests.utils.VerboseHTTPSession: http session """ response = 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 = session.post(action_url, login_form_data) logger.debug('Cookies: %s', session.cookies) response_url_query = parse_url_query_params( login_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=login_response.text, login_form_data=login_form_data, session=session) elif act == 'authcheck': self.require_2fa(html=login_response.text, session=session) elif act == 'security_check': self.require_phone_number(html=login_response.text, session=session) session_cookies = ('remixsid' in session.cookies, 'remixsid6' in session.cookies) if any(session_cookies): logger.info('Session is already established') return None else: message = 'Authorization error: incorrect password or ' \ 'authentication code' logger.error(message) raise VkAuthError(message)
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)
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)
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)
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 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)
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)
def test_parse_form_action(self): html = get_fixture('require_phone_num_resp.html') action_url = utils.parse_form_action_url(html) self.assertEqual( action_url, '/login.php?act=security_check&to=&hash=4b07a4650e9f22038b')
def test_parse_form_action(self): html = get_fixture('require_phone_num_resp.html') form = utils.parse_form_action_url(html) self.assertEqual( form, '/login.php?act=security_check&to=&hash=4b07a4650e9f22038b')