Hello, dear reader. If you at least once worked with the Vkontakte API and at the same time write everything in python
, probably the authorization of the application made you do several squats, after which you either don’t feel legs and faint, or you add quadriceps and still punch the API like Van Damm
For some reason, this seemingly unremarkable stage at first takes a huge amount of time and effort. My task: to help Habr's readers avoid leg injuries.
Next, I propose to consider a small library, which allows in one line to authorize your application for a specific user and get access_token
. At the end of the article there is a link to the github repository of this library with quickstart in the README
file.
We want a small module that allows you to authorize beautifully, universally and as safely as possible, and which is very simple to use.
It is worth saying that this solution is an improvement and generalization of the variant proposed in this article.
So, we use python3.5
, a library for html requests and getpass
requests for hidden password input.
Our task: several times contact the correct address, each time parsit <form>
, send the answer and finally get the desired access_token
.
Let's start by creating a class. During initialization, we will require a list of "permissions" to which the application wants to access, the id of this application and the version of the VK API. Plus, add a few optional parameters, the value of each of which is clarified further.
class VKAuth(object): def __init__(self, permissions, app_id, api_v, email=None, pswd=None, two_factor_auth=False, security_code=None, auto_access=True): """ Args: permissions: list of Strings with permissions to get from API app_id: (String) vk app id that one can get from vk.com api_v: (String) vk API version """ self.session = requests.Session() self.form_parser = FormParser() self.user_id = None self.access_token = None self.response = None self.permissions = permissions self.api_v = api_v self.app_id = app_id self.two_factor_auth= two_factor_auth self.security_code = security_code self.email = email self.pswd = pswd self.auto_access = auto_access if security_code != None and two_factor_auth == False: raise RuntimeError('Security code provided for non-two-factor authorization')
As mentioned in the article already mentioned, we need to skillfully roll cookies and redirectes. All this is done for us by the requests
library with an object of the Session class. We’ll get this one in the field self.session
. To parse the html document, the standard HTMLParser
class from the html.parser
module is html.parser
. For the parser, a class ( FormParser
) is also written, which doesn't make much sense, since it almost completely repeats the one from the mentioned article. The only significant difference is that the one used here allows you to gracefully reject the authorization of the application at the last step, if you suddenly change your mind.
The user_id
and access_token
will be filled in after successful authorization, response
stores the result of the last html request.
The library user will be provided with a single method - authorize
, which performs 3 steps:
permissions
Lets go through each step.
Carefully compose the url of the request (you can read about the parameters here ), send the request and parse the resulting html.
def authorize(self): api_auth_url = 'https://oauth.vk.com/authorize' app_id = self.app_id permissions = self.permissions redirect_uri = 'https://oauth.vk.com/blank.html' display = 'wap' api_version = self.api_v auth_url_template = '{0}?client_id={1}&scope={2}&redirect_uri={3}&display={4}&v={5}&response_type=token' auth_url = auth_url_template.format(api_auth_url, app_id, ','.join(permissions), redirect_uri, display, api_version) self.response = self.session.get(auth_url) # look for <form> element in response html and parse it if not self._parse_form(): raise RuntimeError('No <form> element found. Please, check url address')
The _log_in()
and _two_fact_auth()
methods are implemented for [not] successful authorization of a user in VK if he is not authorized (and he is not authorized). Both methods use the previously defined email
, pswd
, two_factor_auth
and security_code
fields. If some of the fields were not supplied with an argument when initializing an object of the VKAuth
class, they will be asked to enter into the console, and in case of failure they will be asked to enter again. Two-factor authentication is optional and disabled by default, and our module notifies the user of its presence with an error.
#look for <form> element in response html and parse it if not self._parse_form(): raise RuntimeError('No <form> element found. Please, check url address') else: # try to log in with email and password (stored or expected to be entered) while not self._log_in(): pass; # handling two-factor authentication # expecting a security code to enter here if self.two_factor_auth: self._two_fact_auth()
def _log_in(self): if self.email == None: self.email = '' while self.email.strip() == '': self.email = input('Enter an email to log in: ') if self.pswd == None: self.pswd = '' while self.pswd.strip() == '': self.pswd = getpass.getpass('Enter the password: ') self._submit_form({'email': self.email, 'pass': self.pswd}) if not self._parse_form(): raise RuntimeError('No <form> element found. Please, check url address') # if wrong email or password if 'pass' in self.form_parser.params: print('Wrong email or password') self.email = None self.pswd = None return False elif 'code' in self.form_parser.params and not self.two_factor_auth: raise RuntimeError('Two-factor authentication expected from VK.\nChange `two_factor_auth` to `True` and provide a security code.') else: return True
def _two_fact_auth(self): prefix = 'https://m.vk.com' if prefix not in self.form_parser.url: self.form_parser.url = prefix + self.form_parser.url if self.security_code == None: self.security_code = input('Enter security code for two-factor authentication: ') self._submit_form({'code': self.security_code}) if not self._parse_form(): raise RuntimeError('No <form> element found. Please, check url address')
permissions
and getting access_token
The hardest thing behind. Now it's up to you. We use our improvement of the form parser to find in the html document that has just arrived to us a button with the inscription "Allow" and pull out the authorization confirmation url from it. Nearby is the reject button - we will save its url. The auto_access
field auto_access
to True
, so this confirmation should not make life difficult for us.
Finally, we save the received access_token
and user_id
from the url, which was transmitted after confirmation of authorization.
Now you can have fun using the VK API.
http: // REDIRECT_URI # access_token = 533bacf01e11f55b536a565b57531ad114461ae8736d6506a3 & expires_in = 86400 & user_id = 8492
# allow vk to use this app and access self.permissions self._allow_access() # now get access_token and user_id self._get_params()
def _allow_access(self): parser = self.form_parser if 'submit_allow_access' in parser.params and 'grant_access' in parser.url: if not self.auto_access: answer = '' msg = 'Application needs access to the following details in your profile:\n' + \ str(self.permissions) + '\n' + \ 'Allow it to use them? (yes or no)' attempts = 5 while answer not in ['yes', 'no'] and attempts > 0: answer = input(msg).lower().strip() attempts-=1 if answer == 'no' or attempts == 0: self.form_parser.url = self.form_parser.denial_url print('Access denied') self._submit_form({})
def _get_params(self): try: params = self.response.url.split('#')[1].split('&') self.access_token = params[0].split('=')[1] self.user_id = params[2].split('=')[1] except IndexError(e): print(e) print('Coudln\'t fetch token')
github : VKAuth
Leave comments and reviews here and on github. Good luck on the battlefield, and take care of your feet.
Source: https://habr.com/ru/post/306022/
All Articles