OAuth авторизация через mail.ru (Мой Мир)

OAuth авторизация через mail.ru (Мой Мир)

iSergium

Mail.ru - самый популярный почтовик рунета, большинство российских пользователей имеет ящик именно на этом портале. Mail.ru является третим по популярности сайтом в России и его ежемесячная аудитория составляет более 32 млн. человек. И тут дело не только в самой почте, у mail.ru много других проектов - Мой Мир (свыше 40 млн. профайлов), Mail.Ru Агент, Hi-Tech.Mail.RuПоиск@Mail.Ru, Ответы@Mail.Ru и множество всяких Видео@, Гороскопов@, Товаров@ и тому подобных Всячин@Mail.Ru. Аудитория значительная, можно и упростить им авторизацию на нашем сайте.

мой мир mail.ru

Подготовка@Mail.Ru

При отсутствии аккаунта на mail.ru - ступайте на регистрацию. Список подключенных сайтов находится по этой ссылке. Чтобы добавлять сайты обязательно добавиться в Мой Мир, так как авторизация идет именно через него. Если Вас там нет, то при попытке пройти по ссылке выше Вас перекинут на создание этого аккаунта, так что самому можно никуда не идти. Все шаги в создании аккаунта можно пропустить.

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

my.mail.ru coddism

Нам предлагают скачать этот файлик и кинуть в корень сайта. Если не собираетесь использовать авторизацию через JavaScript, то этот файл Вам не нужен. Этот шаг можно пропустить, нажав на "Продолжить", увидев предупреждение кивнуть и нажать на появившуся ссылку "Пропустить":

my.mail.ru coddism

Независимо от того, как Вы закончили третий шаг, Вам покажут это:

my.mail.ru coddism

Список подключенных сайтов находится здесь.

Реализация@Mail.Ru

Основные определения были описаны в начале этой статьи, почитайте вступление, если еще не читали. Про redirect() было сказано здесь. Конечный результат можно скачать ниже из раздела "дополнительно"

Сначала Константы@Mail.Ru:

class OAuthMailRu {
    const APP_ID = 123456; //ID приложения
    const APP_PRIVATE = 'sometestappprivate'; //Приватный ключ
    const APP_SECRET = 'sometestappsecret'; //Защищенный ключ
    const URL_CALLBACK = 'http://example.com/oauth/mailru.php'; //URL, на который произойдет перенаправление после авторизации
}

Перенаправление пользователя на авторизацию и подтверждение доступа приложению:

class OAuthMailRu {
...
    const URL_AUTHORIZE = 'https://connect.mail.ru/oauth/authorize';
...
    public static function goToAuth()
    {
        Utils::redirect(self::URL_AUTHORIZE .
            '?client_id=' . self::APP_ID .
            '&response_type=code' .
            '&redirect_uri=' . urlencode(self::URL_CALLBACK));
    }
...
}

При какой-либо ошибке пользователя перебросит на MAILRU_URL_CALLBACK?error=xxx. Например, при отмене доступа массив $_GET выглядит так:

Array
(
    [error] => access_denied
)

При удачной авторизации и подтверждении пользователь направится на MAILRU_URL_CALLBACK?code=xxx. Вырисовывается вот такая логика:

if (!empty($_GET['error'])) {
    // Пришёл ответ с ошибкой.
} elseif (empty($_GET['code'])) {
    // Самый первый запрос
    OAuthMailRu::goToAuth();
} else {
    // После запроса авторизации ответ пришёл без ошибок
    // Запрос токена и данных пользователя
}

Запрос токена по code:

class OAuthMailRu {
...
    const URL_GET_TOKEN = 'https://connect.mail.ru/oauth/token';
    private static $token;
    public static $userId;
...
    public static function getToken($code) {
        $data = array(
            'client_id' => self::APP_ID,
            'client_secret' => self::APP_SECRET,
            'grant_type' => 'authorization_code',
            'code' => trim($code),
            'redirect_uri' => self::URL_CALLBACK
        );
 
        $opts = array('http' =>
            array(
                'method' => 'POST',
                'header' =>"Content-Type: application/x-www-form-urlencodedrn".
                    "Accept: */*rn",
                'content' => http_build_query($data)
            )
        );
        if (!($response = @file_get_contents(self::URL_GET_TOKEN, false, stream_context_create($opts)))) {
            return false;
        }
 
        $result = @json_decode($response);
        if (empty($result)) {
            return false;
        }
 
        self::$token = $result->access_token;
        self::$userId = $result->x_mailru_vid;
        return true;
    }
...
}

Если всё отлично, то $result будет таким:

stdClass Object
(
    [refresh_token] => 1815db7d1c9af7a5428665c5f17e8586
    [expires_in] => 86400
    [access_token] => 3c2561f27809734f37e8e0c007599077
    [token_type] => bearer
    [x_mailru_vid] => /* ID авторизовавшегося пользователя */
)

Далее проверка того, зарегистрирован ли у Вас на сайте такой юзер. Если такой уже отмечался, то авторизуем его и прерываем дальнейшие действия перенаправлением на какую-либо страницу. В ином случае узнаем данные этого пользователя с помощью метода getUser().

При запросе на URL_API все поля (и ключи массива $request_params в примере ниже) должны быть отсортированы в алфавитном порядке. Если лень сортировать вручную, то можете воспользоваться функцией ksort.

class OAuthMailRu {
...
    const URL_API = 'http://www.appsmail.ru/platform/api';
    public static $userData;
...
    public static function getUser() {
        $request_params = array(
            'app_id' => self::APP_ID,
            'method' => 'users.getInfo',
            'secure' => 1,
            'session_key' => self::$token,
            'uids' => self::$userId
        );
 
        $params = '';
        foreach ($request_params as $key => $value) {
            $params .= "$key=$value";
        }
 
        $url = self::URL_API .
            '?' . http_build_query($request_params).
            '&sig=' . md5($params . self::APP_SECRET);
 
        if (!($user = @file_get_contents($url))) {
            return false;
        }
 
        $user = json_decode($user);
        return self::$userData = $user;
    }
...
}

Sig - это подпись запроса. Если Вы пишете авторизацию для клиентского приложения, то Sig придется вычислять иначе:

'sig='.md5($result->x_mailru_vid . $params . MAILRU_APP_PRIVATE);

Также для клиентского приложения secure будет равен 0.

Персональные данные пользователя в массиве $userData выглядят примерно так:

Array
(
    [0] => stdClass Object
        (
            [first_name] => /*Имя*/
            [last_name] => /*Фамилия*/
            [nick] => /*Имя Фамилия*/
            [pic_22] => http://avt.appsmail.ru/mail/*nickname*/_avatar22
            [pic_32] => http://avt.appsmail.ru/mail/*nickname*/_avatar32
            [pic_40] => http://avt.appsmail.ru/mail/*nickname*/_avatar40
            [pic_50] => http://avt.appsmail.ru/mail/*nickname*/_avatar50
            [pic_128] => http://avt.appsmail.ru/mail/*nickname*/_avatar128
            [pic_180] => http://avt.appsmail.ru/mail/*nickname*/_avatar180
            [pic_190] => http://avt.appsmail.ru/mail/*nickname*/_avatar190
            [pic_big] => http://avt.appsmail.ru/mail/*nickname*/_avatarbig
            [age] => 24
            [is_friend] => 0
            [is_verified] => 1
            [is_online] => 1
            [has_pic] => 0
            [email] => *nickname*@mail.ru
            [referer_id] =>
            [vip] => 0
            [birthday] => /* дата рождения в формате dd.mm.yyyy*/
            [referer_type] =>
            [link] => http://my.mail.ru/mail/*nickname*/
            [last_visit] => 1374263383
            [uid] => /* id */
            [app_installed] => 1
            [status_text] =>
            [sex] => 0
            [pic] => http://avt.appsmail.ru/mail/*nickname*/_avatar
            [pic_small] => http://avt.appsmail.ru/mail/*nickname*/_avatarsmall
            [common_friends_count] => 0
            [friends_count] => 0
        )
 
)

В зависимости от заполненности профиля количество полей может отличаться. Названия полей говорящие, но немного пояснений последует.

Поля is_friend, is_verified, is_online, has_pic, vip, app_installed - булевые, принимают значения 0 (false, нет) или 1 (true, да). Поле sex (пол), можно сказать, тоже булевое - принимает значения 0 (мужской) или 1 (женский).

Поля referer_id и referer_type говорят о том, откуда пришел пользователь. Ничего важного, но если интересно - читайте по этой ссылке.

my.mail.ru coddism

Дополнительно: