MrAbdrahimov
@MrAbdrahimov
Web-разработчик

Как лучше вытащить access_token в VK?

Всем привет!
Передо мной стоит задача получить access_token из Вконтакте для занесения его в базу.
Должен быть получен с такой ссылки:
// https://oauth.vk.com/authorize?client_id=111111&scope=wall,photos,offline&redirect_uri=http://api.vk.com/blank.html&display=wap&response_type=token

При переходе по ссылке пользователь проходит авторизацию, и после ему выдаёт ссылку типа
// http://api.vk.com/blank.html#access_token=11111111111111111111111111111111111111111111111111111111111111111111&expires_in=0&user_id=11111111

Как выдернуть access_token и закрыть окно что бы человек не заморачивался с ручным копированием access_token ?
Может быть есть другие способы его получения?
  • Вопрос задан
  • 35179 просмотров
Пригласить эксперта
Ответы на вопрос 5
thewind
@thewind
php программист, front / backend developer
Я делаю вот так и оно работает:
в JavaScript перехожу по ссылке
http://oauth.vk.com/authorize?client_id=XXXXXX&display=popup&redirect_uri=http://'+document.domain+'/vk_login.php&response_type=code

в скрипте vk_login.php пишу следующее:
$vk_app_id = '';  // id вашего приложения
$vk_app_secret = '';  // секретный ключ вашего приложения

$url = 'https://oauth.vk.com/access_token?client_id='.$vk_app_id.'&client_secret='.$vk_app_secret.'&code='.$_REQUEST['code'].'&redirect_uri=http://'.$_SERVER['SERVER_NAME'].'/vk_login.php';
$result = file_get_contents($url);
$result = json_decode($result, true);
$access_token = $result['access_token'];
// дальше то, что требуется

Обратите внимание, что redirect_url должен быть одинаковым в обеих ссылках.
Ответ написан
Sanasol
@Sanasol Куратор тега JavaScript
нельзя просто так взять и загуглить ошибку
Никто из ответивших ни разу не работал с ВК похоже. Так хотя бы не отвечали :)

Вопрос в том зачем вам такие широкие права для приложения? Вы собираетесь использовать методы которые требуют Standalone?

Никак не вытаить этот токен кроме копирования, если конечно не делать это через собственный бразуер или если в десктоп приложении.
Ответ написан
> Как выдернуть access_token и закрыть окно что бы человек не заморачивался с ручным копированием access_token ?
> Может быть есть другие способы его получения?
Я сделал возможность автоматического копирования токена.
Суть в том что вы собираете заголовки всех 3 запросов:
1. запрос где указан cliend_id, scope и redirect_uri, возвращает Location на второй запрос
2. второй запрос вернет Location на redirect_uri, вот в нем и будет наш access_token.
3. Заголовок к вашему redirect_uri.
Дальше обычной регуляркой вытаскиваете Location из третьего запроса. Примерно это выглядит вот так:

<?
class VKClass {
	protected $email = "TELEPHONE";
	protected $pass = "PASSWORD";
	protected $auth_url = "https://m.vk.com";
	protected $cookie = "cookie.txt";
	protected $client_id = "XXXXXXX";
	protected $redir_url = "http://goto.ru/token";

	public function __construct() {
		//вызываем метод для загрузки необходимых объектов
		if(empty($_SESSION["loggedIn"]))
			$this->logIn();

		if(empty($_SESSION["access_token"]))
			$this->getAccessToken();
	}

	public function prependCurl($options){
		$ch = curl_init();
		curl_setopt($ch, CURLOPT_URL, $options["url"]);
		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
		curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
		curl_setopt($ch, CURLOPT_FOLLOWLOCATION, $options["flocation"]);
		curl_setopt($ch, CURLOPT_POST, $options["post"]);
		curl_setopt($ch, CURLOPT_HEADER, $options["header"]);
		if($options["post"])
			curl_setopt($ch, CURLOPT_POSTFIELDS, $options["postdata"]);
		if($options["cookie_w"])
			curl_setopt($ch, CURLOPT_COOKIEJAR, $_SERVER["DOCUMENT_ROOT"].$this->cookie);
		if($options["cookie_r"])
			curl_setopt($ch, CURLOPT_COOKIEFILE, $_SERVER["DOCUMENT_ROOT"].$this->cookie);
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
		$data = curl_exec($ch);
		curl_close($ch);
        return $data;
	}

	public function logIn(){
		$options = ["url" => $this->auth_url, "flocation" => 0, "post" => 0, "header" => 0, "cookie_w" => 1, "cookie_r" => 0];
		$login_page = $this->prependCurl($options);
		unset($options);
                //функция str_get_html используется из бибилиотеки Simple HTML DOM Parser
		$html = str_get_html($login_page);
		$login_url = @$html->find("form",0)->action;

		$options = ["url" => $login_url, "flocation" => 1, "post" => 1, "header" => 0, "postdata" => ["email" => $this->email, "pass" => $this->pass], "cookie_w" => 1, "cookie_r" => 1];
		$this->prependCurl($options);

		$_SESSION["loggedIn"] = $this->loggedIn= true;
	}

	public function getLocations($url){
		$options = ["url" => $url, "flocation" => 1, "post" => 0, "header" => 1, "cookie_w" => 1, "cookie_r" => 1];
		$data = $this->prependCurl($options);
		return $data;
	}

	public function getAccessToken(){
		//по ссылке возвращается access_token. собираем все заголовки запросов
		$url = "https://oauth.vk.com/authorize?client_id=".$this->client_id."&redirect_uri=".$this->redir_url."?display=page&response_type=token";
		$headers = $this->getLocations($url);
		//разбиваем на кол-во запросов
		$hdrs = explode("\r\n\r\n", $headers);
		//т.к. у нас 2 редиректа (с oauth.vk.com на login.vk.com и с login.vk.com на домен), то нам нужен второй запрос ($hdrs[1]), где соджержится access_token
		preg_match("/Location: (.*)/m", $hdrs[1], $matches);
		//вырезаем наш access_token из ссылки что нам прислал вк сервер
		@preg_match("/#access_token=(.*?)&/", $matches[1], $token);
		//объявляем access_token в сессии и в классе
		@$_SESSION["access_token"] = $this->access_token = $token[1];
	}
}

Да, забыл что это на PHP версии.
В JS я получал его обычным windlow.location.hash, проблема была получить его именно в PHP т.к. хэш (#) в пхп не передается.
p.s. через некоторое время ВК стал отдавать ошибку Security Error, пришлось создать новое приложение и с ним всё работает без проблем.
Ответ написан
Комментировать
Menlod
@Menlod
Front-end developer
if (document.location.search.match(/[?|&]client_id=(\d+)&*$/)) { 
client_id = RegExp.$1
}
Ответ написан
Комментировать
In4in
@In4in
°•× JavaScript Developer ^_^ ו°
Пользователь Игорь попросил расписать решение подробнее. Да запросто:

Теория
Под рукой у меня сейчас только Paint и Arial, так что я заранее извиняюсь, если кому-то следующий рисунок сожжет глаза...
c8578d97aee44a12950e6870e8533d9d.png
_
Проблема.
Так как наше окошко, вызываемое с "Something Page" (далее SP), мы планируем натравить на домен https://oauth.vk.com/, а оттуда получить редиректом в ногу, то мы не сможем повесить на него какие-либо события, типа того же onload. Вследствие чего, нам никак не удастся отследить факт перехода на наш RedirectURI (или на страницу ошибки).

Решение.
Вывод напрашивается простой - страница RedirectURI должна нам сама о себе сообщить после загрузки. Для этого достаточно организовать общение между двумя нашими окнами посредством postMessage. То есть, с RedirectURI отправляем, а на "SP" подписываемся на событие и ловим.

И так, поехали:
Страница, вызывающая окно (SP)
var getToken = (function (Win, tokenWindow){

  /**
   * (Win, tokenWindow)
   *
   * @param {Window} Win
   * @param {Null} tokenWindow
   *
   * @return {Function} getToken
   */

   //ID приложения
   var OPTION_APP_ID = 1234567;

   //Ссылка для перенаправления (должна быть указана в настройках приложения)
   var OPTION_APP_REDIRECT = "http://mysite.ru/vkAuth.php?action=get_token";

   //Запрашиваемые права
   var OPTION_APP_SCOPE = ["wall", "photos", "offline"];




  function http_build_query(obj){

    /**
     * http_build_query(obj)
     *
     * @param {Object} obj
     *
     * @return {String}
     */
    
    var k, res = "";
    
    for(k in obj){
      res += ("&" + k + "=" + Win.encodeURIComponent(obj[k]));
    }
    
    return res.slice(1);
  
  }
  
  function openTokenWindow(app, redirect, scope){

    /**
     * openTokenWindow(app, redirect, scope)
     *
     * @param {Number|String} app
     * @param {String} redirect
     * @param {Array} scope
     *
     * @return {Window}
     */
    
  
   return Win.open("https://oauth.vk.com/authorize?" + http_build_query(
      {
        "client_id"     : app,
        "scope"         : scope.join(","),
        "redirect_uri"  : redirect,
        "display"       : "wap",
        "response_type" : "token"
      }),
      "Авторизация",    
      (
        "menubar=yes,"    + 
        "status=yes,"     + 
        "toolbar=yes,"    + 
        "location=no,"    + 
        "resizable=yes,"  + 
        "scrollbars=yes," + 
        "left=50,"        + 
        "top=50,"         + 
        "width=500,"      + 
        "height=500"
      ) 
    );

  }

  return function getToken(callbackSuccess, callbackError){

    /**
     * getToken(callbackSuccess, callbackError)
     *
     * @param {Function} callbackSuccess
     * @param {Function} callbackError
     *
     * @return {Undefined}
     */


    if(tokenWindow && !tokenWindow.closed){
      return alert("Уже открыто окно авторизации"), undefined;
    }

    tokenWindow = openTokenWindow(OPTION_APP_ID, OPTION_APP_REDIRECT, OPTION_APP_SCOPE);

    Win.addEventListener("message", function x(e){
        
      var message = e.data, source = e.source;
      
      if(source === tokenWindow){

        tokenWindow = tokenWindow.close(), null;
        Win.removeEventListener("message", x);

        message = JSON.parse(message);

        message.errno ? callbackSuccess(message.token) : callbackError();        
      
      }
        
    });


  }

})(window, null);



getToken(
  function(x){
    //Функция, которая будет вызвана при успешной авторизации
    alert("Токен:" + x);
  }, 
  function(){
    //Функция, которая будет вызвана при ошибке
    alert("Ошибка");
  }
);

Страница RedirectURI

Переадресация будет происходить на redirectURI#access_token={token}&...., где наше искомое значение - это {token}

!function(hash, win, reg){

  /**
   * (hash, win, reg)
   *
   * @param {String} hash
   * @param {*} win
   * @param {RegExp} reg
   *
   * @return {Undefined}
   */

  //На самом деле, достаточно просто уведомить opener о том, что авторизация успешна. 
  //А сам токен выдирать из tokenWindow.location.hash
  //Но зачем...
  
  var data = reg.exec(hash), token = data && data[1];

  win && win.postMessage(
    JSON.stringify(
      {
        errno : data,
        token : token
      }
    )
  ); 

  //need targetOrigin condition?


}(location.hash, opener, /access_token=([^&]+)/);



_
P.s: У меня сейчас нет возможности протестировать данное решение. Извиняюсь, если есть логические или синтаксические ошибки, писал на коленке.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы