📜 ⬆️ ⬇️

Simple client and server authorization of an Ajax site user using the VKontakte API

For one project, it was necessary to authorize the user on the site using the VKontakte API both on the client side, using the javascript Open API, and on the server side, using PHP.

Allowing access to the VK API

At first glance, the task is not so interesting, since VKontakte has good documentation with a detailed description of what and how, and even an example of client authorization, plus, there are already a lot of websites with authorization via the VKontakte API.

Let's try to get two lists of the user's friends, one using the server API and the other using the client.
At the same time in practice, let us deal with both variants of the authorization process.
')
But first, let's define a task in more detail:
  1. We do not want to preload the page once again, and only conduct the entire exchange with the server via Ajax;
  2. For the user, the authorization process should cause as few “back thoughts” as possible, that is, everything should be done as comfortably as possible.

Suppose we have the following HTML code
<!doctype html> <html> <head> <meta charset="utf-8"> <title>  </title> <link rel="stylesheet" href="style.css" /> <script src="script.js"></script> </head> <body> <section id="serverApi"> API</section> <section id="clientApi"> API</section> </body> </html> 

Client Authorization

There is really nothing difficult with client authorization.
According to the documentation , it is sufficient to load openapi.js asynchronously and “hang” VK.init({appid: YOUR_APP_ID}) on the window.vkAsyncInit event, after which you can call the VK.Auth.login(authInfo, YOUR_APP_PERMISSIONS) function VK.Auth.login(authInfo, YOUR_APP_PERMISSIONS) , which will execute user authorization.

Authorization and request through the Open API
 var vk = { data: {}, api: "//vk.com/js/api/openapi.js", appID: YOUR_APP_ID, appPermissions: YOUR_APP_PERMISSIONS, init: function(){ $.js(vk.api); window.vkAsyncInit = function(){ VK.init({apiId: vk.appID}); load(); } function load(){ VK.Auth.login(authInfo, vk.appPermissions); function authInfo(response){ if(response.session){ //   vk.data.user = response.session.user; vk.getFriends(); }else alert("  !"); } } }, getFriends: function(){ VK.Api.call('friends.get', {fields: ['uid', 'first_name', 'last_name'], order: 'name'}, function(r){ if(r.response){ r = r.response; var ol = $('#clientApi').add('ol'); for(var i = 0; i < r.length; ++i){ var li = ol.add('li').html(r[i].first_name+' '+r[i].last_name+' ('+r[i].uid+')') } }else alert("     "); }) } } $.ready(vk.init); 

First, at once I will make a reservation that the functions $ used here are not jQuery . However, their purpose may not be intuitive, so I will tell you what this or that function does. Their independent implementation should not be difficult, just like putting all the code on jQuery .

So,
$.ready(function) - This is the handler for DOMContentLoaded ;
$.js(text) - asynchronously loads the javascript file;
$(element) - returns a wrapper over the DOM node element ;
$(element).add(node) - creates a new child node for an element node node and returns a wrapper over it;
$(element).html(string) - wrapper for element.innerHTML = string .

In essence, this code does the following:
When the document is ready, the VKontakte API is loaded, and, after its initialization, the VK.Auth.login() method is VK.Auth.login() , which shows a pop-up window that successfully blocks with any banner cutter , in which the user must confirm his consent to provide data to the client application.
The authInfo function authInfo called after the user's consent or disagreement, and, in case of successful authorization, requests a list of friends.

The client part can thus be considered complete, however, it is worthwhile to somehow warn users about the possibility of their browser blocking the confirmation window.

Server authorization

Server authorization is much more fun. The server authorization mechanism for access to the VK API from a third-party site is based on the OAuth 2.0 protocol.

The authorization process is as follows:
  1. It is necessary in the user's browser to show a page where he will allow the application to access his data;
  2. After successful authorization of the application, the user's browser will be redirected to the address REDIRECT_URI specified when opening the authorization dialog. In this case, the code for transmitting the access key will be transmitted in the GET parameter;
  3. It is necessary to execute the request with the transfer of the application code and secret data to a special address, in response we will receive the access_token access key that we need to make requests to the API.

Unfortunately, the second point, namely, browser redirection, does not always work, more precisely, does not work anywhere except for IE, on that very first time when a user first allows an application to access his data. And as soon as the developers are not perverted, just to get the coveted code.
An example of authorization in one well-known music service
Authorization with the help of VK API on one music service
True, for quite a long time, instead of the “Login Success” writing on the page being opened, there is a warning that you don’t have to do exactly as it is written - you saw this message in the picture at the beginning of the topic.

In addition, the opening of an additional window is again connected with the danger that it will be blocked.

Fortunately, if we know the link to open the window, there are many good ways to open it so that it will not be blocked. For example, the good old iframe tag is still in the standard HTML format, and it is perfectly suited for our task.

First, since we know the address, we can open the frame and show the user a link where you can do everything.

Secondly, if a redirect occurs, and a page opens from our domain in the frame, we can get access to the main window from the frame and automatically start the function of closing the frame. At the same time, the server will already have access_token , which we will save in the session and by performing requests to the server from the main window we will be able to receive replies. The reloading of the page will not happen at the same time, which completely solves our problem.

Thirdly, if the redirection did not occur, and the user manually opened the application's access confirmation page, then there is definitely a redirect to the page from our domain, which will be opened not in the frame, but instead of the main one. Unfortunately, this is a reboot, but it is a smaller sacrifice than it could be. From this page, you can send the user back to the main page; the second time, authorization will pass without rebooting.

The implementation of the class of access to the VK API by OAuth in PHP can be easily found on the github, which I did (PHP> = 5.4).
(Quite by chance it turned out that this class was most likely written by the hackle user vladkens , for which many thanks to him)

We now turn to the most interesting.
We implement a server script that will respond to the AJAX requests of our page.
 <?php require_once('vk.php'); session_start(); function getPostStr($data, $default = false){ //   -  $_POST[$data] if(!isset($_POST[$data])) return $default; $data = $_POST[$data]; $data = htmlspecialchars(strip_tags(trim($data))); return ($data != "" ? $data : $default); } function error($code, $text, $params = Array()){ $result = Array('error' => Array('code' => $code, 'message' => $text)); if(count($params) > 0) foreach($params as $key => $value) $result['error'][$key] = $value; die(json_encode($result)); } $vkConf = Array( 'appID' => YOUR_APP_ID, 'apiSecret' => YOUR_API_SECRET, 'callbackUrl' => YOUR_DOMAIN . '/auth.php', 'apiSettings' => YOUR_APP_PERMISSIONS ); $vk = (isset($_SESSION['accessToken'])) ? new VK($vkConf['appID'], $vkConf['apiSecret'], $_SESSION['accessToken']) : null; function userIn($vk, $vkConf){ //   unset($_SESSION['accessToken']); $vk = new VK($vkConf['appID'], $vkConf['apiSecret']); $authorizeUrl = $vk -> getAuthorizeURL($vkConf['apiSettings'], $vkConf['callbackUrl']); error(-1, "  !", Array('url' => $authorizeUrl)); } function getFriends($fields, $order, $vk){ //     $userFriends = $vk -> api('friends.get', array('fields' => $fields, 'order' => $order)); $result = Array(); foreach($userFriends['response'] as $key => $value){ $result[] = Array('firstName' => $value['first_name'], 'lastName' => $value['last_name'], 'uid' => $value['uid']); } echo json_encode($result); } $method = strtolower($api -> getStr("method")); switch($method){ case "user.in" : userIn($vk, $vkConf); break; case "friends.get" : getFriends(getPostStr("fields"), getPostStr("order"), $vk); break; default: Api::error(0, "   Api"); } ?> 

We will send requests to this page using the POST method, where the parameter method contains the name of the method we want to perform on the server.

For successful authorization, we will send a request to user.in when our main page is ready, to which the server will respond with an error code of -1 , which we will catch and process.
At the same time, along with the error, the URL of the page on which the user must confirm access to the application will also come, and that is what we will open in the frame.
Let's add our client script with AJAX processing
 function Api(method, params, callback){ if(!params) params = {}; $.ajax($.copy({method: method}, params), 'server.php', function(r){ if(r.error){ switch(r.error.code){ case -1: { // notAuth var div = $(document.body).add('div').id('authPopup'), iframe = div.add('iframe').attr({src: r.error.url}); div.add('p').html('                 ,   -          .      ,      ,   ').add('a').attr({href: r.error.url}).html(' '); $.splash.open({onclose:function(){ div.remove() }}); } break; default: alert(r.error.code+': '+r.error.message); } }else if(callback) callback(r); }); } $.ready(function(){ Api('user.in'); }); 

I will briefly describe the $ function:
$.copy(object1, object2) - copies all object2 parameters to object2 ;
$.ajax(text, addr, callback) - sends a POST request to addr , passing a text message. It accepts the answer in the form of JSON and transmits it to the callback function;
$(element).id(elemID) - wrapper for element.id = elemID ;
$(element).attr(object) - sets the attributes of the element element tag. object keys - attribute names, key values ​​will be set as attribute values;
$(element).remove() - removes element ;
$.splash.open(params) - $.splash.open(params) browser window with a translucent dark block. The params.onclose parameter is a function that will be called upon $.splash.close() .

So, now when loading the document we have a window of client authorization of the application and a frame with the server authorization confirmation page. Or, instead of the confirmation window in the frame, we see a warning, but for this case we have a link where the user can go and confirm the application permissions.

It remains only to make the page on which the redirect is made.
Confirmation page of server authorization using VK
 <?php require_once('vk.php'); session_start(); $vkConf = Array( 'appID' => YOUR_APP_ID, 'apiSecret' => YOUR_API_SECRET, 'callbackUrl' => YOUR_DOMAIN . '/auth.php', ); if(isset($_REQUEST['code'])){ $vk = new VK($vkConf['appID'], $vkConf['apiSecret']); $accessToken = $vk -> getAccessToken($_REQUEST['code'], $vkConf['callbackUrl']); $_SESSION['accessToken'] = $accessToken['access_token']; }else die('Auth failed'); ?> <!doctype html> <html> <head> <meta charset="utf-8" /> <title>     </title> <script> $.ready(function(){ if(window.top){ window.top.vk.getFriends(); window.top.$.splash.close(); } }) </script> </head> <body> <h4> !</h4> <p>   <a href="YOUR_DOMAIN">  </a>,   </p> </body> </html> 

Here we simply save access_token into session variables, and the script processing Ajax requests can use it. In addition, if this page is opened in a frame, we call the method of the main window, which requests the list of friends of VKontakte from the server, and the method that closes the frame.

This is the implementation of unobtrusive authorization of the site user through the VKontakte API.
Screenshot
Result

As a homework, try to redirect to the YOUR_DOMAIN page from the confirmation page, if it is not open in the frame, and improve the userIn() method so that if there is an access_token session, its validity is checked and, if the key is valid, it is used instead of the new authorization .

Source: https://habr.com/ru/post/188102/


All Articles