OAuth авторизация через ВКонтакте

OAuth авторизация через ВКонтакте

iSergium

За несколько лет своего существования протокол OAuth смог зарекомендовать себя и обрел широкую популярность. Не удивительно, ведь это удобный и безопасный способ обмена данными между сервисами. Если до него для того, чтобы получить данные пользователя со стороннего сервиса, нужно было запрашивать пользовательские логин и пароль, что, разумеется, небезопасно, то с помощью OAuth всё происходит иначе.

Для начала немного терминов. В OAuth авторизации выделяются три роли – клиент, сервер и владелец ресурса (пользователь).
Клиент – это сервис, которому предоставляется доступ к данным, сервер – это сервис, который хранит данные, а владелец ресурса – это пользователь, который предоставляет свои данные.
Или на примере: Вы сделали на своем сайте авторизацию через ВКонтакте. На этот сайт зашел пользователь и хочет авторизоваться. В данном случае клиентом является Ваш сайт, сервером – ВКонтакте, а владельцем ресурса – этот самый пользователь.

Еще один термин, с которым придется часто сталкиваться – это токен. Токен в нашем случае – это средство авторизации в OAuth запросах, текстовый ключ, который предоставляет ограниченный временный доступ к данным владельца ресурса на сервере. Нередко используется связка из нескольких токенов.

Способы OAuth авторизации на разных серверах отличаются, но схема у них одна:

  1. Клиент отправляет на сервер запрос на получение токена авторизации. В запросе используются идентификационные данные клиента.
  2. После распознавания клиента сервер отдает токен авторизации.
  3. Клиент, используя этот токен, перенаправляет пользователя на сервер
  4. На сервере пользователь авторизовывается и подтверждает доступ клиенту к своим данным.
  5. После авторизации и подтверждения пользователь перенаправляется к клиенту, передавая при этом дополнительные токены (обычно один или два) доступа (обычно в GET-параметрах)
  6. Используя токен доступа клиент обращается к серверу за данными и получает их уже без подтверждения пользователем. Обычно доступ к данным по одному и тому же токену ограничивается лишь по времени, некоторые серверы дополнительно ограничивают количество запросов.

Шаги 1 и 2 – редкость, обычно идентификационные данные клиента передаются серверу вместе с перенаправлением пользователя на авторизацию вместо токена авторизации. Идентификационные данные клиенту выдаются при регистрации сайта на сервере, зачастую в виде приложения.

ВКонтакте – это социальная сеть с огромной аудиторией – более 40 млн. посетителей ежеджевно. Почему бы не упростить регистрацию этих людей на Вашем сайте?
Для вступления достаточно, пора приступить к делу.

Регистрация приложения

Скорее всего Вы уже зарегистрированы в соц. сети ВКонтакте, если нет, то Вам придется это сделать. Указывать номер телефона при регистрации обязательно. Переживать не стоит — никакой смс-рассылки от этих сервисов не приходит.
Далее необходимо создать приложение. (Скриншоты были сделаны в апреле 2015, с той поры интерфейс мог измениться)

Создание приложения ВКонтакте

После смс-подтверждения создания приложения оно, как можно догадаться, создаётся и Вы попадаете на страницу её редактирования. Всю информацию и иконки желателно добавить, но больше всего нас там интересуют id приложения и защищённый ключ— это и есть идентификационные данные нашего сайта:

Редакитрование приложения ВКонтакте

Приложения ВКонтакте поддерживают работу с несколькими доменами, так что можно создать одно приложение сразу для всех Ваших сайтов.

Что и как

Для удобства создаём класс, в него пишем константы (скачать конечный результат можно ниже из раздела "дополнительно"):

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

Первым делом нужно авторизовать пользователя, поэтому нужно перенаправить его на сайт ВКонтакте. Добавляем метод в имеющийся класс:

class OAuthVK {
  ...
    const URL_AUTHORIZE = 'https://oauth.vk.com/authorize';
  ...
    public static function goToAuth()
    {
        Utils::redirect(self::URL_AUTHORIZE .
            '?client_id=' . self::APP_ID .
            '&scope=offline' .
            '&redirect_uri=' . urlencode(self::URL_CALLBACK) .
            '&response_type=code');
    }
  ...
}

В поле scope передаются запрашиваемые права доступа приложения. Для считывания основной информации достаточно offline. Но если нужно большее, то их можно перечислять через запятую:

scope=friends,video,offline

Небольшое пояснение: redirect() - это метод, реализующий перенаправление. Для удобства в этом примере он вынесен в класс Utils

class Utils {
    public static function
redirect($uri = '')
    {
        header("HTTP/1.1 301 Moved Permanently");
        header("Location: ".$uri, TRUE, 302);
        exit;
    }
}

После авторизации и подтверждения доступа пользователь переадресовывается на адрес URL_CALLBACK?code=xxx. Теперь нужно по переданному нам коду взять токен и запросить данные пользователя. Если же пользователь отклонил запрос или возникла ошибка, то он перенаправится на адрес URL_CALLBACK с кодом и описанием ошибки, например URL_CALLBACK?error=invalid_request&error_description=Invalid+display+parameter.

То есть, логику можно разместить в эти условия:

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

Запросить токен:

class OAuthVK {
...
    const URL_ACCESS_TOKEN = 'https://oauth.vk.com/access_token';
    private static $token;
    public static $userId;
...
    public static function getToken($code) {
        $url = self::URL_ACCESS_TOKEN .
            '?client_id=' . self::APP_ID .
            '&client_secret=' . self::APP_SECRET .
            '&code=' . $_GET['code'] .
            '&redirect_uri=' . urlencode(self::URL_CALLBACK);
 
        if (!($res = @file_get_contents($url))) {
            return false;
        }
 
        $res = json_decode($res);
        if (empty($res->access_token) || empty($res->user_id)) {
            return false;
        }
 
        self::$token = $res->access_token;
        self::$userId = $res->user_id;
 
        return true;
    }
...
}

Объект $res при удачном запросе будет содержать такие поля:

stdClass Object
(
    [access_token] => xxx
    [expires_in] => 43200
    [user_id] => xxx
)

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

Далее по токену и user_id остается запросить данные пользователя. Все запросы к API ВКонтакте выполняются по такому адресу:
https://api.vk.com/method/METHOD_NAME?PARAMETERS&access_token=ACCESS_TOKEN

METHOD_NAME – название метода из списка функций API
PARAMETERS – параметры соответствующего метода API
ACCESS_TOKEN – ключ доступа, полученный в результате успешной авторизации приложения

Ответ приходит в формате JSON. Если же вы более привыкли в XML, то либо привыкайте к JSON, либо отсылайте запросы на такой адрес:
https://api.vk.com/method/METHOD_NAME.xml?PARAMETERS&access_token=ACCESS_TOKEN

Запрос данных пользователей по их user_id осуществляется через метод getProfiles или его полный аналог users.get. При необходимости можно также отправлять параметр fields и получать дополнительные данные пользователя.

class OAuthVK {
...
    const URL_GET_PROFILES = 'https://api.vk.com/method/getProfiles';
    public static $userData;
...
    public static function getUser() {
 
        if (!self::$userId) {
            return false;
        }
 
        $url = self::URL_GET_PROFILES.
            '?uid=' . self::$userId .
            '&access_token=' . self::$token;
 
        if (!($res = @file_get_contents($url))) {
            return false;
        }
 
        $user = json_decode($res);
 
        if (!empty($user->error)) {
            self::printError($user->error);
            return false;
        }
 
        if (empty($user->response[0])) {
            return false;
        }
 
        $user = $user->response[0];
        if (empty($user->uid) || empty($user->first_name) || empty($user->last_name)) {
            return false;
        }
 
        return self::$userData = $user;
    }
...
}

В нашем примере ответ от сервера приходит в таком виде (в зависимости от параметров количество полей может быть больше):

stdClass Object
(
    [response] => Array
        (
            [0] => stdClass Object
                (
                    [uid] => 1
                    [first_name] => Павел
                    [last_name] => Дуров
                )
        )
)

Имя, фамилия и uid пользователя теперь известны нам, что делать с ними дальше — решайте сами. Я использую OAuth для полноценной регистрации и создаю с этими данными нового пользователя на сайте.

coddism авторизация ВКонтакте

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

Комментарии

Куат Амангалиев 27.07.2014 16:14:01
можно исходник:?
iSergium 08.08.2014 06:05:52
Ссылка на исходник есть в конце статьи: "Метод OAuth авторизации через ВКонтакте для codeIgniter"
Куат Амангалиев 09.11.2014 14:25:00
Где исходник? ссылки нету!
iSergium 12.12.2014 19:36:19
Валентин Сопов 25.12.2014 19:11:50
Здравствуйте почему то у меня вместо формы выдает просто белую страницу((( в чем может быть причина не подскажите?
iSergium 28.12.2014 03:21:20
Перепроверьте адрес сайта, который был указан в приложении в ВК.
Ярослав Любацкий 10.02.2015 00:10:39
не работает, взял исходник и изменил данные, где константы, в приложении вк поставил правильно - свой сайт и белый экран тоже ..
делал просто index.php страничку
iSergium 11.02.2015 18:16:05
Это странно, так как авторизация на этом сайте сделана так же, как и в этой статейке.
Вы не могли бы поделиться ссылкой на эту страницу с примером или самим кодом?
Кимран Качков 31.05.2015 01:38:24
добавь пару строк чтобы пол пользователя определять. очень надо
Павел Харламов 04.06.2016 20:01:57
как достать почту пользователя?