async def authorize(self) -> None: """Getting a new token from server""" html = await self._get_auth_page() url = URL('/authorize?email') for step in range(self.num_of_attempts): if url.path == '/authorize' and 'email' in url.query: # Invalid login or password and 'email' in q.query url, html = await self._process_auth_form(html) if url.path == '/login' and url.query.get('act', '') == 'authcheck': # Entering 2auth code url, html = await self._process_2auth_form(html) if url.path == '/login' and url.query.get('act', '') == 'authcheck_code': # Need captcha url, html = await self._process_auth_form(html) if url.path == '/authorize' and '__q_hash' in url.query: # Give rights for app url, html = await self._process_access_form(html) if url.path == '/blank.html': # Success self.access_token = url.query['access_token'] return raise VkAuthError('Something went wrong', 'Exceeded the number of attempts to log in')
async def _get_auth_page(self) -> str: """ Get authorization mobile page without js :return: html page """ # Prepare request params = { 'client_id': self.app_id, 'redirect_uri': 'https://oauth.vk.com/blank.html', 'display': 'mobile', 'response_type': 'token', 'v': self.API_VERSION } if self.scope: params['scope'] = self.scope # Send request status, response = await self.driver.get_text(self.AUTH_URL, params) # Process response if status != 200: error_dict = json.loads(response) raise VkAuthError(error_dict['error'], error_dict['error_description'], self.AUTH_URL, params) return response
async def _process_auth_form(self, html: str) -> (str, str): """ Parsing data from authorization page and filling the form and submitting the form :param html: html page :return: url and html from redirected page """ # Parse page p = AuthPageParser() p.feed(html) p.close() # Get data from hidden inputs form_data = dict(p.inputs) form_url = p.url form_data['email'] = self.login form_data['pass'] = self.password if p.message: # Show form errors raise VkAuthError('invalid_data', p.message, form_url, form_data) elif p.captcha_url: form_data['captcha_key'] = await self.enter_captcha( "https://m.vk.com{}".format(p.captcha_url), form_data['captcha_sid']) form_url = "https://m.vk.com{}".format(form_url) # Send request url, html = await self.driver.post_text(form_url, form_data) return url, html
def phone_number_is_needed(self, content, session): """ Default behavior on PHONE NUMBER is to raise exception Reload this in child """ logger.error('Authorization error (phone number is needed)') raise VkAuthError('Authorization error (phone number is needed)')
async def authorize(self, code=None): code = await self.get_code(code) params = { 'client_id': self.app_id, 'client_secret': self.app_secret, 'redirect_uri': self.redirect_uri, 'code': code } response = await self.driver.json(self.CODE_URL, params, self.timeout) if 'error' in response: raise VkAuthError(response['error'], response['error_description'], self.CODE_URL, params) self.access_token = response['access_token']
async def process_2auth_form(self, html): p = TwoFactorCodePageParser() p.feed(html) p.close() form_url = p.url form_data = dict(p.inputs) form_data['remember'] = 0 if p.message: raise VkAuthError('invalid_data', p.message, form_url, form_data) form_data['code'] = await self.enter_confirmation_сode() url, html = await self.driver.post_text(form_url, form_data) return url, html
async def process_auth_form(self, html): p = AuthPageParser() p.feed(html) p.close() form_data = dict(p.inputs) form_url = p.url form_data['email'] = self.login form_data['pass'] = self.password if p.message: raise VkAuthError('invalid_data', p.message, form_url, form_data) elif p.captcha_url: form_data['captcha_key'] = await self.enter_captcha(p.captcha_url, form_data['captcha_sid']) url, html = await self.driver.post_text(form_url, form_data) return url, html
def login(self): """ Login """ response = yield from self.auth_session.get(self.LOGIN_URL) response = RequestsLikeResponse(response) login_form_action = get_form_action((yield from response.text())) if not login_form_action: raise VkAuthError('VK changed login flow') login_form_data = { 'email': self.user_login, 'pass': self.user_password, } response = yield from self.auth_session.post(login_form_action, data=login_form_data) response = RequestsLikeResponse(response) yield from response.text() # logger.info('Cookies: %s', self.auth_session.cookies) response_url_query = get_url_query(response.url) if 'remixsid' in self.auth_session.cookies or 'remixsid6' in self.auth_session.cookies: # if 'remixsid' in response_url_query or 'remixsid6' in response_url_query: return if 'sid' in response_url_query: yield from self.auth_captcha_is_needed(response, login_form_data) elif response_url_query.get('act') == 'authcheck': self.auth_check_is_needed((yield from response.text())) elif 'security_check' in response_url_query: self.phone_number_is_needed((yield from response.text())) else: message = 'Authorization error (incorrect password)' logger.error(message) raise VkAuthError(message)
async def get_auth_page(self): params = {'client_id': self.app_id, 'redirect_uri': 'https://oauth.vk.com/blank.html', 'display': 'mobile', 'response_type': 'token', 'v': self.API_VERSION } if self.scope: params['scope'] = self.scope status, response = await self.driver.get_text(self.AUTH_URL, params) if status != 200: error_dict = json.loads(response) raise VkAuthError(error_dict['error'], error_dict['error_description'], self.AUTH_URL, params) return response
async def authorize(self, code: str = None) -> None: """Getting a new token from server""" code = await self.get_code(code) params = { 'client_id': self.app_id, 'client_secret': self.app_secret, 'redirect_uri': self.redirect_uri, 'code': code } _, response = await self.driver.post_json(self.CODE_URL, params, self.timeout) if 'error' in response: raise VkAuthError(response['error'], response['error_description'], self.CODE_URL, params) self.access_token = response['access_token']
def get_access_token(self): """ Get access token using app id and user login and password. """ logger.info('AuthMixin.get_access_token()') auth_session = aiohttp.ClientSession() with auth_session as self.auth_session: self.auth_session = auth_session yield from self.login() auth_response_url_query = yield from self.oauth2_authorization() if 'access_token' in auth_response_url_query: return auth_response_url_query[ 'access_token'], auth_response_url_query['expires_in'] else: raise VkAuthError('OAuth2 authorization error')
async def authorize(self): html = await self.get_auth_page() q = urllib.parse.urlparse('/authorize?email') for step in range(self.num_of_attempts): if q.path == '/authorize'and 'email' in q.query: # invalid login or password and 'email' in q.query url, html = await self.process_auth_form(html) q = urllib.parse.urlparse(url) if q.path == '/login': # entering 2auth code url, html = await self.process_2auth_form(html) q = urllib.parse.urlparse(url) if q.path == '/authorize' and '__q_hash' in q.query: # give rights for app url, html = await self.process_access_form(html) q = urllib.parse.urlparse(url) if q.path == '/blank.html': qs = dict(urllib.parse.parse_qsl(q.query)) self.access_token = qs['access_token'] return raise VkAuthError('Something went wrong', 'Exceeded the number of attempts to log in')
def auth_captcha_is_needed(self, response, login_form_data): logger.info('Captcha is needed') response_url_dict = get_url_query(response.url) # form_url = re.findall(r'<form method="post" action="(.+)" novalidate>', response.text) captcha_form_action = get_form_action((yield from response.text())) logger.info('form_url %s', captcha_form_action) if not captcha_form_action: raise VkAuthError('Cannot find form url') captcha_url = '%s?s=%s&sid=%s' % ( self.CAPTCHA_URI, response_url_dict['s'], response_url_dict['sid']) # logger.info('Captcha url %s', captcha_url) login_form_data['captcha_sid'] = response_url_dict['sid'] login_form_data['captcha_key'] = self.on_captcha_is_needed(captcha_url) response = yield from self.auth_session.post(captcha_form_action, data=login_form_data)
def oauth2_authorization(self): """ OAuth2 """ auth_data = { 'client_id': self.app_id, 'display': 'mobile', 'response_type': 'token', 'scope': self.scope, 'v': '5.37', # "redirect_uri": "https://oauth.vk.com/blank.html", } response = yield from self.auth_session.post(self.AUTHORIZE_URL, data=auth_data) response = RequestsLikeResponse(response) response_url_query = get_url_query(response.url) if 'access_token' in response_url_query: return response_url_query # Permissions is needed logger.info('Getting permissions') # form_action = re.findall(r'<form method="post" action="(.+?)">', auth_response.text)[0] form_action = get_form_action((yield from response.text())) logger.info('Response form action: %s', form_action) if form_action: response = yield from self.auth_session.get(form_action) response = RequestsLikeResponse(response) response_url_query = get_url_query(response.url) return response_url_query try: response_json = yield from response.json() except ValueError: # not JSON in response error_message = 'OAuth2 grant access error' else: error_message = 'VK error: [{}] {}'.format( response_json['error'], response_json['error_description']) logger.error('Permissions obtained') raise VkAuthError(error_message)
async def _process_2auth_form(self, html: str) -> (str, str): """ Parsing two-factor authorization page and filling the code :param html: html page :return: url and html from redirected page """ # Parse page p = TwoFactorCodePageParser() p.feed(html) p.close() # Prepare request data form_url = p.url form_data = dict(p.inputs) form_data['remember'] = 0 if p.message: raise VkAuthError('invalid_data', p.message, form_url, form_data) form_data['code'] = await self.enter_confirmation_code() # Send request url, html = await self.driver.post_text(form_url, form_data) return url, html
def get_auth_check_code(self): raise VkAuthError('Auth check code is needed')
def auth_captcha_is_needed(self, content, session): """ Default behavior on CAPTCHA is to raise exception Reload this in child """ raise VkAuthError('Authorization error (captcha)')
def auth_code_is_needed(self, content, session): """ Default behavior on 2-AUTH CODE is to raise exception Reload this in child """ raise VkAuthError('Authorization error (2-factor code is needed)')
async def authorize(self): raise VkAuthError('invalid_token', 'User authorization failed')
async def authorize(self) -> None: """Getting a new token from server""" # For `TokenSession` we have not credentials for getting new token raise VkAuthError('invalid_token', 'User authorization failed')
def phone_number_is_needed(self, text): raise VkAuthError('Phone number is needed')