def login(self): """ Login """ response = self.auth_session.get(self.LOGIN_URL) login_form_action = get_form_action(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 = self.auth_session.post(login_form_action, login_form_data) logger.debug('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: return if 'sid' in response_url_query: self.auth_captcha_is_needed(response, login_form_data) elif response_url_query.get('act') == 'authcheck': self.auth_check_is_needed(response.text) elif 'security_check' in response_url_query: self.phone_number_is_needed(response.text) else: message = 'Authorization error (incorrect password)' logger.error(message) raise VkAuthError(message)
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 _process_auth_form(self, html: str) -> typing.Tuple[URL, 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 """ parser = AuthPageParser() parser.feed(html) parser.close() form_data = dict(parser.inputs) form_url = parser.url form_data["email"] = self.login form_data["pass"] = self.password if parser.message: raise VkAuthError("invalid_data", parser.message, form_url, form_data) if parser.captcha_url: form_data["captcha_key"] = await self.enter_captcha( "https://m.vk.com{}".format(parser.captcha_url), form_data["captcha_sid"], ) form_url = "https://m.vk.com{}".format(form_url) url, html = await self._get_data_from_form(form_url, form_data) return url, html
async def _get_auth_page(self) -> str: """ Get authorization mobile page without js :return: html page """ 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 async with self.session.get(url=self.AUTH_URL, params=params) as resp: response = await resp.text() status = resp.status if status != 200: error_dict = JSON_LIBRARY.loads(response) raise VkAuthError( error_dict["error"], error_dict["error_description"], self.AUTH_URL, params, ) return response
async def authorize(self) -> None: """Getting a new token from server""" html = await self._get_auth_page() url = URL("/authorize?email") for _ in range(self.num_of_attempts): if url.path == "/authorize" and "email" in url.query: url, html = await self._process_auth_form(html) if ( url.path == "/login" and url.query.get("act", "") == "authcheck" ): url, html = await self._process_2auth_form(html) if ( url.path == "/login" and url.query.get("act", "") == "authcheck_code" ): url, html = await self._process_auth_form(html) if url.path == "/authorize" and "__q_hash" in url.query: url, html = await self._process_access_form(html) if url.path == "/blank.html": parsed_fragments = dict(parse_qsl(url.fragment)) self.access_token = parsed_fragments["access_token"] await self.session.close() return None raise VkAuthError( "Something went wrong", "Exceeded the number of attempts to log in" )
def oauth2_authorization(self): """ OAuth2 """ auth_data = { 'client_id': self.app_id, 'display': 'mobile', 'response_type': 'token', 'scope': self.scope, 'v': '5.28', } response = self.auth_session.post(self.AUTHORIZE_URL, auth_data) 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(response.text) logger.debug('Response form action: %s', form_action) if form_action: response = self.auth_session.get(form_action) response_url_query = get_url_query(response.url) return response_url_query try: response_json = 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)
def get_access_token(self): """ Get access token using app id and user login and password. """ logger.debug('AuthMixin.get_access_token()') auth_session = LoggingSession() with auth_session as self.auth_session: self.auth_session = auth_session self.login() auth_response_url_query = self.oauth2_authorization() if 'access_token' in auth_response_url_query: return auth_response_url_query['access_token'] else: raise VkAuthError('OAuth2 authorization error')
def auth_captcha_is_needed(self, response, login_form_data): logger.info('Captcha is needed') response_url_dict = get_url_query(response.url) captcha_form_action = get_form_action(response.text) logger.debug('form_url %s', captcha_form_action) if not captcha_form_action: raise VkAuthError('Cannot find form url') # todo Are we sure that `response_url_dict` doesn't contain CAPTCHA image url? captcha_url = '%s?s=%s&sid=%s' % ( self.CAPTCHA_URI, response_url_dict['s'], response_url_dict['sid']) login_form_data['captcha_sid'] = response_url_dict['sid'] login_form_data['captcha_key'] = self.get_captcha_key(captcha_url) self.auth_session.post(captcha_form_action, login_form_data)
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(response.text) logger.debug('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.debug('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 = self.auth_session.post(captcha_form_action, login_form_data)
async def _process_2auth_form(self, html: str) -> typing.Tuple[URL, str]: """ Parsing two-factor authorization page and filling the code :param html: html page :return: url and html from redirected page """ parser = TwoFactorCodePageParser() parser.feed(html) parser.close() form_url = parser.url form_data = dict(parser.inputs) form_data["remember"] = 0 if parser.message: raise VkAuthError("invalid_data", parser.message, form_url, form_data) form_data["code"] = await self.enter_confirmation_code() url, html = await self._get_data_from_form(form_url, form_data) return url, html
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)')
def get_auth_check_code(self): raise VkAuthError('Auth check code is needed')
def phone_number_is_needed(self, text): raise VkAuthError('Phone number is needed')