Authorization in VK for people

What happened?


Hello dear reader. If you have ever worked with the Vkontakte API at least once and write everything on python, probably the authorization of the application forced you to do some squats, after which you either don’t feel your legs and pass out, or you pump in quadriceps and still punch the API like Van Damme .


For some reason, this, it would seem, the most unremarkable stage at first takes a huge amount of time and effort. My task: to help readers of Habr avoid leg injuries.


Next, I propose to consider a small library that allows you to authorize your application for a specific user in one line and get it access_token. At the end of the article, there is a link to the github repository of this library with quickstart in the READMEfile.


Task


We want a small module that allows you to authorize beautifully, universally and as reliably as possible, and which is very simple to use.
It is worth saying that this solution is an improvement and generalization of the option proposed in this article.


So, we use the python3.5library for html requests requests and getpassfor hidden password entry.


Our task: to apply to the correct address several times, to parse each time

send a response and finally get what you want access_token.


Implementation


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, we add a few optional parameters, the meaning of each of which will become clear later.


__Init__ method
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 already mentioned article, we need to skillfully turn cookies and redirects. The library requestswith an object of the Session class does all this for us . Let’s get one in the field self.session. For parsing an html document, a standard class HTMLParserfrom a module is used html.parser. A class ( FormParser) has also been written for the parser , which doesn’t make much sense, since it almost completely repeats that of the mentioned article. The only significant difference is that the one used here allows you to gracefully reject the authorization of the application in the last step if you suddenly change your mind.


Fields user_idand access_tokenwill be filled after successful authorization, responsestores the result of the last html request.


We will provide the library user with one single method - authorizewhich performs 3 steps:


  1. application authorization request
  2. user authorization
    2.1 introduction of a key code in the case of two-factor authorization
  3. confirmation of permission to use permissions

Let's go through each step.


Step 1. Request for authorization of the application


We carefully compose the request url (you can read about the parameters here ), send the request and the parsed html received.


Authorize method for Step 1
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  element in response html and parse it
        if not self._parse_form():
            raise RuntimeError('No  element found. Please, check url address')

Step 2. User authorization


Implemented methods _log_in()and _two_fact_auth()for [not] successful authorization of the user in the VC, if it is not authorized (and it is certainly not authorized). Both methods use the previously defined field email, pswd, two_factor_authand security_code. If any of the fields was not given as an argument during initialization of the class object VKAuth, they will be asked to enter in the console, and in case of failure they will be asked to re-enter. Two-factor authorization is optional and disabled by default, and our module notifies the user of its presence with an error.


Authorize method for Step 2 (continuation of Step 1)
#look for  element in response html and parse it
        if not self._parse_form():
            raise RuntimeError('No  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()

_Log_in method for Step 2
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  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

_Two_fact_auth method for Step 2
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  element found. Please, check url address')

Step 3. Confirmation permissionsand receiptaccess_token


The hardest part is behind. Now it's up to the small. We use our improvement of the form parser to find the button with the inscription "Allow" in the html document that just arrived to us and pull out the authorization confirmation url from it. Nearby is a button with a refusal - we will save its url too. The auto_accessdefault field is in state True, so this confirmation should not make our life any more difficult.


Finally, keep obtained access_tokenand user_idfrom url No, that was given 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

Authorize method for Step 3
    # allow vk to use this app and access self.permissions
    self._allow_access()
     # now get access_token and user_id
     self._get_params()

_Allow_access Method for Step 3
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({})

_Get_params method for Step 3
    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.


Also popular now: