Pull to refresh

Авторизация через ВКонтакте, Mail.ru и другие для самых начинающих — 2

Reading time 5 min
Views 10K
В прошлой части мы рассматривали авторизацию через вконтакте, сегодня остановимся на mail.ru и использовании аватарок из обоих социальных сетей.
Для начала как всегда нам необходимо зарегистрировать в социальной сети свое приложение (у майла четкое разделение между приложениями и сайтами, но я по привычке буду говорить «приложение»). Кстати в процессе регистрации вы увидите от трех до пяти js ошибок, когда-нибудь mail.ru их исправит. После регистрации вам доступны ID приложения, приватный ключ (ничего приватного, используется для js-api вызовов, виден всем) и секретный ключ (используется для сервер-сервер взаимодействия, не говорите никому и не забывайте в комментариях).
На странице настройки приложения мы видим еще один интересный параметр «Адрес страницы receiver.html». Если не ставит целью узнать для чего же она нужна, то единственное что мы узнаем из справки:
Для корректной работы API сайтов необходимо разместить файл receiver.html на вашем домене.

Если не планируете использовать JS API, то этот файл вам не нужен. На страничке авторизации/регистрации мы разместим кнопку. PR-отдел майла очень строго запрещает что-либо менять в логотипе, поэтому мы используем его в строго таком виде, каким он нам предоставлен.

Ссылка на картинке будет либо такая:
connect.mail.ru/oauth/authorize?client_id=APP_ID&response_type=token&redirect_uri=_REDIRECT_URI&host=http://HOST.com

в которой вам необходимо указать номер вашего приложения, ваш домен в параметре host и URI для перенаправления в параметре redirect_uri.
Второй вариант — сделать промежуточную страницу, использующую JS API, ниже родной пример с майла с убивающими отступами в один пробел.
<script type="text/javascript" src="http://cdn.connect.mail.ru/js/loader.js"></script>
<script type="text/javascript">

  mailru.loader.require('api', function() {
   mailru.connect.init('_APP_ID_', '__PRIVATE_KEY__' );
   mailru.events.listen(mailru.connect.events.login, function(session){
   window.location.reload();
   });
   mailru.events.listen(mailru.connect.events.logout, function(){
   window.location.reload();
   });
   mailru.connect.getLoginStatus(function(result) {
   if (result.is_app_user != 1) {
    mailru.connect.initButton();
   } else {
    mailru.common.users.getInfo(function(result){console.log(result[0].uid)});
    location.href='/mail.login.php';
   }
   });
  });

</script>
<a class="mrc__connectButton">вход@mail.ru</a>


* This source code was highlighted with Source Code Highlighter.

Зачем может понадобиться второй вариант? Как показала жизнь если пользователь щелкнет по ссылке из первого варианта и попадет на страничку с вводом имени пользователя и пароля (если он не авторизован в майле в данный момент), то хоть и в строчке адреса https, и домен mail.ru, но пользователь может запаниковать и подумать, что у него пытаются украсть логин и пароль. Непонятно почему, но если показывать эти же поля во всплывающем окне (видимо более привычном и чаще используемом), то паникующих пользователей меньше.
Итак, вне зависимости от того какой вариант мы выбрали, в любом случае в результате пользователя перебросит на страницу mail.login.php или другую, указанную вами. В случае успешной авторизации пользователя на стороне mail.ru на вашем домене он создает cookie с названием mrc, которую можно разобрать следующим образом:
parse_str(urldecode($_COOKIE['mrc']),$array);
И тут выясняются не сильно приятные моменты: для подписи/проверки подписи API использует сравнение md5(список всех параметров запроса, отсортированных в алфавитном порядке без знаков &) и параметр sig из mail.ru-шной cookie. Поэтому нам нужны две небольшие функции, предложенные нам командой майла:
function sign_client_server(array $request_params, $uid, $private_key) {
 ksort($request_params);
 $params = '';
 foreach ($request_params as $key => $value) {
   $params .= "$key=$value";
 }
 return md5($uid . $params . $private_key);
}

function sign_server_server(array $request_params, $secret_key) {
 ksort($request_params);
 $params = '';
 foreach ($request_params as $key => $value) {
   if ($key!='sig') {
   $params .= "$key=$value";
   }
 }
 return md5($params . $secret_key);
}


* This source code was highlighted with Source Code Highlighter.

Функцию sign_server_server я немного подправил, добавив проверку того, чтобы ключ элемента массива не был равен 'sig'. Это сделано для того, чтобы перед каждой проверкой не удалять элемент из массива или не копировать массив в другой для проверки.
Для начала проверим действительно ли ответ пришел от социальной сети (в массиве $array у нас информация из майлрушной куки):
if (sign_server_server($array,$secretkey)==$array['sig']) {

Что удивительно если бы мы использовали «родную» функцию для проверки подписи не удалив элемент sig из массива, то сравнение возвращало бы ложь. Так как изначальная информация от майла достаточно скудна (только идентификаторы и время истечения сессии пользователя на майле, которое нам собственно незачем), нам необходимо получить имя, фамилию и номер кредитки через метод users.getInfo. Это делается вот таким образом:
  //получим анкетную инфу
  $params = array(
    "method"=>"users.getInfo",
    "app_id"=>"640345",
    "session_key"=>$array['session_key'],
    "uids"=>$array['vid'],
    "secure"=>"1"
  );
  
  $url = "http://www.appsmail.ru/platform/api?".http_build_query($params)."&sig=".sign_server_server($params,$secretkey);
  $response = json_decode(file_get_contents($url));


* This source code was highlighted with Source Code Highlighter.

Для формирования запроса велосипед, как делают многие, изобретать не нужно, есть встроенная функция http_build_query. $response возвращает нам массив объектов пользователей (в нашем случае в массиве 1 элемент), из которого мы получим имя, фамилию, ник, идентификатор и аватарку. Мы делаем это перед запросами к базе, потому что в любом случае нам эти данные понадобятся (мы обновим имя, фамилию и аватарку).
После того как мы получили данные от социальной сети, пора поработать на нашей стороне. Приняв критику и немного абстрагировавшись от типа используемой базы данных:
$stmt = $dbh->prepare("SELECT id FROM tracker_users WHERE username = :username");
$stmt->bindParam(":username", "mm-{$array['vid']}", PDO::PARAM_STR, 23);
$stmt->execute();


Для пользователей из социальной сети МойМир я решил выделить логины вида «mm-20цифр». Да, именно 20-тизначное число в качестве айдишника, по запарке не сделайте приведение к обычному целому. После этого если пользователь уже есть в нашей базе, мы просто обновим его данные, создадим ему наши куки и перебросим на главную страницу, если же его нет, то мы создадим запись в таблице и выполним действия из прошлого пункта.
if ($stmt ->fetchColumn() > 0) {
//если пользователь есть
} else {
//если пользователя нет
}


Интереса в полном изложении всех запросов и мелочей, как выяснилось из прошлой статьи, ни у кого нет, поэтому остановимся на следующем этапе интеграции — аватарки из социальных сетей. Маленькая картинка (идеально для отображения возле комментариев) находится в $response[0]->pic_small, а вконтактовскую аватарку из прошлой статьи можно получить из GET параметров $_GET['photo_rec']. Надо отметить, что майловские аватарки имеют размер 45 на 45, в то время как из ВКонтакта — 50 на 50. Радует, что оба варианта квадратные и их можно привести к одному размеру. Если хочется привести к 50 на 50, то из майла лучше взять увеличенную аватарку 90 на 90.
Tags:
Hubs:
+48
Comments 33
Comments Comments 33

Articles