How to authorize using VKontakte in a desktop application

Over the several years of its existence, the OAuth protocol has been able to establish itself and gain wide popularity. Not surprising, because this is a convenient and secure way to exchange data between services. If before, in order to obtain user data from a third-party service, it was necessary to request a user login and password, which, of course, is unsafe, then with the help of OAuth everything happens differently.

First, a few terms. In OAuth authorization, there are three roles – client, server and resource owner (user). The client is the service that is given access to the data, the server is the service that stores the data, and the resource owner is the user who provides their data. Or using an example: You logged into your website via VKontakte. A user has visited this site and wants to log in. In this case, the client is your website, the server is VKontakte, and the owner of the resource is this very user.

Another term that you will often come across is token. A token in our case is a means of authorization in OAuth requests, a text key that provides limited temporary access to the resource owner’s data on the server. Often a bundle of several tokens is used.

OAuth authorization methods differ on different servers, but they have the same scheme:

  1. The client sends a request to the server to obtain an authorization token. The request uses the client's identification data.
  2. After recognizing the client, the server issues an authorization token.
  3. The client, using this token, redirects the user to the server
  4. On the server, the user logs in and confirms the client’s access to his data.
  5. After authorization and confirmation, the user is redirected to the client, passing additional access tokens (usually one or two) (usually in GET parameters)
  6. Using an access token, the client contacts the server for data and receives it without user confirmation. Typically, access to data using the same token is limited only by time; some servers additionally limit the number of requests.

Steps 1 and 2 are rare, usually the client's credentials are passed to the server along with redirecting the user to authorization instead of an authorization token. Identification data is issued to the client when registering the site on the server, often in the form of an application.

VKontakte is a social network with a huge audience - more than 40 million visitors every day. Why not make it easier for these people to register on your site? That's enough for the introduction, it's time to get down to business.

What's happened?

Hello dear reader.
If you have at least once had the opportunity to work with the VKontakte API and at the same time write everything in python, probably authorizing the application forced you to do several squats, after which you either don’t feel your legs and faint, or pump up your quadriceps and still break through the API, like Van Damm. For some reason, this seemingly most unremarkable stage initially takes a huge amount of effort and time. My task: to help Habr readers 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 an access_token. At the end of the article there is a link to the github repository of this library with a quickstart in the README file.

Application registration

Most likely you are already registered on social media. VKontakte network, if not, then you will have to do it. It is mandatory to provide a phone number when registering. There is no need to worry - no SMS messages are received from these services. Next you need to create an application. (Screenshots were taken in April 2015, the interface may have changed since then)

After SMS confirmation of the creation of the application, as you might guess, it is created and you are taken to the page for editing it. It is advisable to add all the information and icons, but what we are most interested in there is the application id and the protected key - this is the identification data of our site:

VKontakte applications support working with multiple domains, so you can create one application for all your sites at once.

Task

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

So, we use python3.5, a library for html requests requests and getpass for hidden password entry.

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

, send a response and finally receive the desired access_token.

Facebook

1. Log in to Facebook and follow the link.

Click on the “Add a New App” button, in the form that appears, indicate the name of the application and your email for contact:

Please note: the translation of this section into Russian at the time of writing this article, although readable, is not entirely correct. You may want to use Facebook in English; to do this, simply change the language in your account settings.

2. After the security check, the application settings page will open, find the “Login with Facebook” section on it and click “Configure”:

3. Specify “Web” as the platform:

4. In the window that opens, enter the address of your store, click the “Save” and “Continue” button:

5. Go to main app settings in the sidebar, add your site domain and privacy policy URL:

6. Enter the store URL:

7. The same section displays the application ID and access token (“application secret”):

8. Enter this information in the back office of your store:

9. In the “Login with Facebook” application settings, you must fill in “Valid redirect URIs for OAuth”:

Here you need to insert the address: https://domain/auth/facebook/callback , where the word “domain” must be replaced with the domain of your store, for example myshop.ru.

Important: if your domain includes “www”, then this address must also contain “www”.

10. In the top panel there is a switch and the text “Status: in development.” You need to activate it for the status to change to “Published”:

Everything is ready to go. You can also add a description and logo for your app.

Implementation

Let's start by creating a class.
During initialization, we will require a list of “permissions” that the application wants to access, the id of this application and the VK API version. Plus, we’ll add several optional parameters, the meaning of each of which will become clearer later. Method __init__
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 manage cookies and redirects. The requests library does all this for us with an object of the Session class. Let's create one for ourselves in the self.session field. To parse an html document, use the standard HTMLParser class from the html.parser module. A class (FormParser) was also written for the parser, which makes little sense to analyze, 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 application authorization at the last step if you suddenly change your mind.

The user_id and access_token fields will be filled in after successful authorization, response stores the result of the last html request.

We will provide the library user with one single method – authorize, which performs 3 steps:

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

Let's go through each step.

Step 1. Application authorization request

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

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

The _log_in() and _two_fact_auth() methods have been implemented for [un]successfully authorizing a user in VK if he is not authorized (and he is definitely not authorized). Both methods use the previously defined fields email, pswd, two_factor_auth and security_code. If any of the fields were not supplied as an argument when initializing the VKAuth class object, they will be asked to enter them in the console, and if unsuccessful, they will be asked to enter them again. Two-factor authentication is optional and disabled by default, and our module notifies the user of its presence with an error.

The authorize method for Step 2 (continued from 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

Method _two_fact_auth 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')

Simple authorization

At this stage, everything is quite rational. After you connect the class to the file, all that remains is to get the VKAuth instance by passing the settings to it. Below we describe a simple handler that catches the $_GET[code] variable and checks authentication.

require_once("VKAuth.php"); $vk = new VKAuth(array( "client_id" => "Application_ID", "client_secret" => "protected_key", "redirect_uri" => "site_address" )); if(isset($_GET["code"])){ if($vk->auth($_GET["code"])){ // Doing our own thing } }

Next, to authorize, you will need to check the presence of the user in your database and, if not, add it. Otherwise, update its data (before updating, it is advisable to check whether they have changed). Regarding the database, two fields are usually added: authorization type and user id on the social network. This is how authentication and authorization are carried out through the social network VKontakte.

In the archive you will find a ready-made example of working with the VKAuth class, where user data is displayed.

"Remember me" function

It is thanks to the cookie expiration date mechanism that the “remember me” function works. By default, the service creates a session for the user that expires when the browser is closed. But if the user checks the “remember me” box, the server installs a cookie with a fairly long shelf life: from a month to several years, depending on the service.

Unfortunately, the longer the cookie's shelf life, the higher the likelihood of so-called session theft: an attacker stealing the session key stored in the cookie. Stealing a session key allows an attacker to perform actions on behalf of the user. For this reason, services such as payment systems and personal accounts on bank websites do not provide a “remember me” mechanism. Moreover, they often introduce additional security, such as sending a special code to the user’s mobile phone to confirm his identity.

Session and cookies

A session consists of a key

- some unique set of characters - and
data
. In the simplest case, session data is limited to the user ID from the database. But often there is a lot of other data stored there that must be destroyed as soon as the user leaves the system: an unpaid order from the cart, the text of an unsent comment, an unpublished photo, etc.

When a session is created, its key is sent to the user (the session data itself never leaves the server). Subsequently, the user's browser sends it to the server on every request. This makes it easier to re-identify a user within the same session. Without this mechanism, the user would have to log in when moving from page to page.

The browser sends the session key using a mechanism called cookies.

(
Cookies
).
For each cookie, the server can set an expiration date
, after which the user's browser automatically destroys the cookie. If no expiration date is specified, it is destroyed as soon as the user closes the browser window.

We create the necessary pages

Before we post chunks with code fragments, we will create five pages.

  • Login page (1):
    page containing the login form
  • Password reset (2):
    page where users can request password recovery
  • Password reset handler (3):
    a hidden page that will actually reset the password. Users will not see it.
  • Users-only page (4):
    a page whose contents are visible only to authorized users of the site
  • Logout page (5):
    The page the user is redirected to after successfully logging out

This is what my resource tree looks like at the moment.
Keep in mind that your resource ID will be different. In this example, there is nothing except pages for the “Login” component. Next, we need to assign the correct rights to users and resources.

Create the necessary user groups and resource groups

MODX Revo has a very flexible permissions system when it comes to user permissions, but in this topic we will only do what we need without going too deep into the topic. So, let's get started. 1. Security → Resource Groups
Click on “Create a resource group” and name it “For users only”, for example.
Click “Save” and that’s it, we don’t change anything else on this page. 2. Security → Access Control
On the first “User Groups” tab, click on “New User Group”.
Let’s name the new group “Users” and click “Save”. The user group will have access to "Users Only" resources. We will find out why we need this a little later in this lesson. 3. On the same page ( Security → Access Control
), right-click on the created user group and select “Edit user group”.
Next, go to the “Access to resource groups” tab and click on “Add resource group”. For proper operation, there must be at least the following parameters: Resource group:
Users only (the one we just created)
Minimum role:
Member-9999
Access policy:
Load, List and View
Context:
web And save.
4. Security → User Management
We create a “new user” and thereby check how the differentiation of access rights for users will work.
In this case, use a simple login and password, because, as I wrote above, it is important for us to make sure that the new user is in the “Users” group. To do this, go to the “Access Rights” tab and click on “Add user to group”. User group:
Users
Role:
Member Then click “save” in the window, and then
again
in the right corner of the control panel. This should ensure that a new user can log in with “User” rights to view the page with “Users Only” rights. Now let's go back to the pages to add snippets and code snippets to the corresponding pages.

Rating
( 2 ratings, average 4.5 out of 5 )
Did you like the article? Share with friends:
Для любых предложений по сайту: [email protected]