Авторизация на сайте через Mail.ru (oAuth 2.0)

в 8:51, , рубрики: mail.ru, oauth 2.0, php, авторизация, Веб-разработка, Песочница, метки: , , ,

Всем привет!
На хабре уже была статья от Nodge про авторизацию на различных сервисах, в том числе и Mail.ru. Но она была для Yii и в ней детально не описывался сам процесс. Так же была статья от propovednik, но в ней описывался процесс авторизации через javascript + php. В этой статье я бы хотел детально разобрать серверный метод авторизации. Для авторизации будет использован PHP и модель сервер-сервер.

Шаг 1. Регистрация сайта

Переходим по ссылке http://api.mail.ru/sites/my/add и регистрируем наш сайт. Далее скачиваем и заливаем на ftp файл receiver.html, который нужен для верификации нашего сайта.
После регистрации нам выдадут 3 параметра:

  • ID
  • Приватный ключ
  • Секретный ключ

Нас будет интересовать только ID и Секретный ключ (Приватный ключ нужен для модели клиент-сервер). Сохраним их в конфигурационном файле нашего скрипта под следующими именами.

$APP_ID;
$APP_SECRET;

Шаг 2. Получение Code

Для получения Code нужно обратиться по адресу connect.mail.ru/oauth/authorize, передав ему $_GET параметры:

  • client_id — ID сайта
  • response_type — 3 варианта на выбор. token — доступ к API будет предоставлен только через javascript, code_and_token — доступ к API через сервер и javascript, code — доступ к API через сервер. В нашем случае это будет code.
  • redirect_uri — Адрес страницы получения response_type
  • scopeПривилегии приложения. Параметр необязателен для заполнения и в нашем случае избыточен. Если вы решили запросить привилегии, то учтите, что далеко не все пользователи захотят передать вам их приватные данные, поэтому просто для авторизации параметр scope использовать не надо.

Для удобства разделим логику генерации ссылки на получение code и его обработки:
example.com/login.php — генерируем ссылку для получения code (по-сути ссылка для авторизации).
example.com/auth.php — обработка code.

Итоговый запрос будет выглядеть так:

$redirect_uri = urlencode("http://example.com/auth.php");
$login_url = "https://connect.mail.ru/oauth/authorize?client_id={$APP_ID}&response_type=code&redirect_uri={$redirect_uri}";

Перейдя по этому адресу неавторизованный на Mail.ru пользователь увидит:
image
Если пользователь авторизован на Mail.ru, то он увидит такое же окно, но без ввода логина и пароля.
Теперь у пользователя 2 варианта действий: разрешить и запретить. Запретив, пользователь будет перенаправлен на страницу указанную в redirect_uri с указанием ошибки.

Если всё прошло как надо и пользователь разрешил сайту доступ к своим данным, то пользователь будет перенаправлен на страницу redirect_uri (http://example.com/auth.php), с $_GET параметром code.
Сохраним его под именем

$APP_CODE;

Шаг 3. Получение token и uid

Далее нам надо обменять полученный code на идентификатор сессии (token) и id пользователя Mail.ru от лица которого идёт доступ к API.

Для этого надо обратиться по адресу connect.mail.ru/oauth/token, передав ему параметры:

  • client_id ($APP_ID)
  • client_secret ($APP_SECRET)
  • grant_type (authorization_code)
  • code ($APP_CODE)
  • redirect_uri ($redirect_uri из шага 2)

Все параметры обязательтны. redirect_uri должен точно совпадать с тем, который мы использовали на 2-ом шаге.

Запрос на получения token может быть выполнен только через POST запрос, поэтому cURL в помощь:

$ch = curl_init();
$url = "https://connect.mail.ru/oauth/token";
$fields = Array(
	'client_id'		=>	$APP_ID,
	'client_secret'		=>	$APP_SECRET,
	'grant_type'		=>	"authorization_code",
	'code'			=>	$APP_CODE,
	'redirect_uri'		=>	urlencode(redirect_uri)
);
foreach($fields as $key => $value){
	$fields_string .= $key . '=' . $value . '&';
}
rtrim($fields_string, '&');

curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string);

$result = curl_exec($ch);
curl_close($ch);
		
$arr = json_decode($result, true);
//или $arr = (array) json_decode($result). Кстати кто-нибудь знает, есть ли разница?	

$token	= $arr['access_token'];
$uid	= $arr['x_mailru_vid'];

Шаг 4. Получаем пользовательские данные

После получения token и uid сайт получает долгожданный доступ к API Mail.ru по адресу www.appsmail.ru/platform/api.
Но не всё так просто, каждый запрос к API должен быть подписан. Подпись (в нашем случае) представляет собой хэш, рассчитанный по алгоритму md5 из отсортированной в алфавитном порядке строки из передаваемых API параметров без разделителя & и Секретного ключа.
Например:

md5('app_id=423004method=friends.getsession_key=be6ef89965d58e56decdfacb9b62bdaa' . $APP_SECRET);

Mail.ru предусматривает 2 варианта подписи: клиент-сервер и сервер-сервер. Отличие заключается в большей безопасности 2-го подхода. Его мы и будем использовать. Для этого в запросе к API надо указывать secure=1.

Выполним запрос на получение анкетной информации пользователя Mail.ru, воспользовавшись методом users.getInfo.

$request_params = Array(
	'app_id'	=>	$APP_ID,
	'uids'		=>	$uid,
	'method'	=>	'users.getInfo',
	'secure'	=>	1,
	'session_key'	=>	$token
);
//Параметры обязательно должны быть отсортированы в алфавитном порядке
ksort($request_params);
$params = '';
foreach ($request_params as $key => $value) {
	$params .= "$key=$value";
}
//В модели сервер-сервер подпись выглядит так
$sig = md5($params . $APP_SECRET);

//Формируем запрос
$url = "http://www.appsmail.ru/platform/api?method=users.getInfo&app_id={$APP_ID}&session_key={$token}&sig={$sig}&uids={$uid}&secure=1";

$response = file_get_contents($url);

$info = (array) json_decode($response);
$info = $info[0];

//весь результат
print_r($info);

Вот и всё. Таким образом мы получили необходимые нам данные для занесения пользователя в базу данных. Весь дальнейший процесс уже неоднократно был описан, например здесь. Так что не вижу смысла его описывать.

PROFIT!!1 Всем добра. Если статья окажется интересной, то могу так же разобрать процесс серверной oAuth 2.0 авторизации для Vkontakte и Facebook.

Ссылки

Автор: yeee737

  1. Евгений:

    Статья действительно актуальна. Это первый подробный и полезный обзор, который я нашел в сети.

    Конечно же для ВК и Фейсбука будем ждать статей. (сохраняю вас в закладки)

  2. Анастасия:

    По поводу вопроса у вас на сайте в 3м шаге:
    $arr = json_decode($result, true);
    //или $arr = (array) json_decode($result). Кстати кто-нибудь знает, есть ли разница?
    —-
    Обе строки кода выполняют преобразование JSON-строки в ассоциативный массив в PHP, но есть разница в способе, которым это делается:

    $arr = json_decode($result, true);
    В этой строке json_decode() вызывается с двумя аргументами. Первый аргумент $result – это JSON-строка, которую вы хотите декодировать. Второй аргумент, установленный в true, говорит функции json_decode(), что нужно преобразовать JSON-объекты в ассоциативные массивы. В итоге, переменная $arr будет содержать ассоциативный массив.

    $arr = (array) json_decode($result);
    В этой строке json_decode() вызывается с одним аргументом – JSON-строкой. Затем результат этого вызова оборачивается в (array) для явного преобразования в массив. Этот способ приводит к получению числовых индексов в массиве, если в JSON-строке были массивы, и строковых ключей для объектов. Важно отметить, что такой способ может не сохранить точную структуру JSON, и он преобразует JSON-массивы в обычные числовые индексы.

    В общем, если вам нужен ассоциативный массив, то первый способ, с использованием второго аргумента json_decode() с true, является более надежным и предсказуемым способом получения ожидаемого результата.

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js