Como atualizar o token com o cliente API do Google?


91

Tenho brincado com a API do Google Analytics (V3) e encontrei alguns erros. Em primeiro lugar, tudo está configurado corretamente e funcionou com minha conta de teste. Mas quando eu quero obter dados de outro ID de perfil (mesma conta do Google / GA), recebo um erro 403. O estranho é que os dados de algumas contas do GA retornarão dados enquanto outras geram esse erro.

Revoguei o token e autentiquei mais uma vez e agora parece que posso obter dados de todas as minhas contas. Problema resolvido? Não. Como a chave de acesso vai expirar, vou ter o mesmo problema novamente.

Se eu entendi as coisas direito, pode-se usar o resfreshToken para obter um novo authenticationTooken.

O problema é, quando eu executo:

$client->refreshToken(refresh_token_key) 

o seguinte erro é retornado:

Error refreshing the OAuth2 token, message: '{ "error" : "invalid_grant" }'

Verifiquei o código por trás do método refreshToken e rastreei a solicitação de volta ao arquivo “apiOAuth2.php”. Todos os parâmetros são enviados corretamente. O grant_type é codificado para 'refresh_token' dentro do método, então é difícil para mim entender o que está errado. A matriz de parâmetros se parece com isto:

Array ( [client_id] => *******-uqgau8uo1l96bd09eurdub26c9ftr2io.apps.googleusercontent.com [client_secret] => ******** [refresh_token] => 1\/lov250YQTMCC9LRQbE6yMv-FiX_Offo79UXimV8kvwY [grant_type] => refresh_token )

O procedimento é o seguinte.

$client = new apiClient();
$client->setClientId($config['oauth2_client_id']);
$client->setClientSecret($config['oauth2_client_secret']);
$client->setRedirectUri($config['oauth2_redirect_uri']);
$client->setScopes('https://www.googleapis.com/auth/analytics.readonly');
$client->setState('offline');

$client->setAccessToken($config['token']); // The access JSON object.

$client->refreshToken($config['refreshToken']); // Will return error here

Isso é um bug ou eu entendi algo completamente errado?


Não sei se é um bug ou algo assim, mas estou atualizando o token de acesso usando uma solicitação http CURL bruta e está funcionando bem.
gremo

Seorch ... você já descobriu isso? Mesmo problema aqui.
Brian Vanderbusch de

@gremo, você poderia compartilhar a solicitação http CURL bruta que usou aqui? Seria muito útil. Obrigado!
Silver Ringvee

Respostas:


76

Então, finalmente descobri como fazer isso. A ideia básica é que você tenha o token que receberá na primeira vez que solicitar autenticação. Este primeiro token possui um token de atualização. O primeiro token original expira após uma hora. Depois de uma hora, você deve usar o token de atualização do primeiro token para obter um novo token utilizável. Você usa $client->refreshToken($refreshToken)para recuperar um novo token. Vou chamar isso de "token temporário". Você também precisa armazenar este token temporário porque depois de uma hora ele também expira e observe que não há um token de atualização associado a ele. Para obter um novo token temporário, você precisa usar o método usado antes e usar o refreshtoken do primeiro token. Anexei o código abaixo, que é feio, mas sou novo nisso ...

//pull token from database
$tokenquery="SELECT * FROM token WHERE type='original'";
$tokenresult = mysqli_query($cxn,$tokenquery);
if($tokenresult!=0)
{
    $tokenrow=mysqli_fetch_array($tokenresult);
    extract($tokenrow);
}
$time_created = json_decode($token)->created;
$t=time();
$timediff=$t-$time_created;
echo $timediff."<br>";
$refreshToken= json_decode($token)->refresh_token;


//start google client note:
$client = new Google_Client();
$client->setApplicationName('');
$client->setScopes(array());
$client->setClientId('');
$client->setClientSecret('');
$client->setRedirectUri('');
$client->setAccessType('offline');
$client->setDeveloperKey('');

//resets token if expired
if(($timediff>3600)&&($token!=''))
{
    echo $refreshToken."</br>";
    $refreshquery="SELECT * FROM token WHERE type='refresh'";
    $refreshresult = mysqli_query($cxn,$refreshquery);
    //if a refresh token is in there...
    if($refreshresult!=0)
    {
        $refreshrow=mysqli_fetch_array($refreshresult);
        extract($refreshrow);
        $refresh_created = json_decode($token)->created;
        $refreshtimediff=$t-$refresh_created;
        echo "Refresh Time Diff: ".$refreshtimediff."</br>";
        //if refresh token is expired
        if($refreshtimediff>3600)
        {
            $client->refreshToken($refreshToken);
        $newtoken=$client->getAccessToken();
        echo $newtoken."</br>";
        $tokenupdate="UPDATE token SET token='$newtoken' WHERE type='refresh'";
        mysqli_query($cxn,$tokenupdate);
        $token=$newtoken;
        echo "refreshed again";
        }
        //if the refresh token hasn't expired, set token as the refresh token
        else
        {
        $client->setAccessToken($token);
           echo "use refreshed token but not time yet";
        }
    }
    //if a refresh token isn't in there...
    else
    {
        $client->refreshToken($refreshToken);
        $newtoken=$client->getAccessToken();
        echo $newtoken."</br>";
        $tokenupdate="INSERT INTO token (type,token) VALUES ('refresh','$newtoken')";
        mysqli_query($cxn,$tokenupdate);
        $token=$newtoken;
        echo "refreshed for first time";
    }      
}

//if token is still good.
if(($timediff<3600)&&($token!=''))
{
    $client->setAccessToken($token);
}

$service = new Google_DfareportingService($client);

52
Em vez de verificar 3600 segundos, você deve usar $ client-> isAccessTokenExpired ()
Gaurav Gupta

2
Pequena atualização. Na versão mais recente, quando você solicita um token de atualização, o novo token de acesso que é retornado agora vem com um novo token de atualização. Então, essencialmente, você pode usar o token json atualizado para substituir o token json anterior e não precisa mais reter o token de acesso inicial. .
Skidadon de

1
Observe que $client->isAccessTokenExpired()ainda só verificará os tempos mantidos localmente para ver se acha que o token expirou. O token ainda pode ter expirado e o aplicativo local só saberá realmente quando tentar usá-lo. Nesse caso, o cliente API retornará uma exceção e não atualizará automaticamente o token.
Jason

44

O problema está no token de atualização:

[refresh_token] => 1\/lov250YQTMCC9LRQbE6yMv-FiX_Offo79UXimV8kvwY

Quando uma string com um '/'é obtido json encoded, ele é escapado com um '\', portanto, você precisa removê-lo.

O token de atualização no seu caso deve ser:

1/lov250YQTMCC9LRQbE6yMv-FiX_Offo79UXimV8kvwY

O que estou supondo que você fez é que imprimiu a string json que o Google enviou de volta e copiou e colou o token em seu código porque, se você fizer json_decodeisso, ele removerá corretamente o '\'para você!


1
menção incrível, fez meu dia! horas economizadas!
Mircea Sandu

Você salvou meu dia!
Truong Dang

Eu gostaria de poder votar 100 vezes. Eu estava prestes a fazer um buraco na parede com meu teclado depois de olhar para a mensagem de "concessão ruim" por várias horas, depois de tentar de tudo para fazer o token funcionar. Cara do google, por que usar barras, só por quê?
Askerman

18

aqui está o snippet para definir o token, antes disso, certifique-se de que o tipo de acesso deve ser definido como offline

if (isset($_GET['code'])) {
  $client->authenticate();
  $_SESSION['access_token'] = $client->getAccessToken();
}

Para atualizar o token

$google_token= json_decode($_SESSION['access_token']);
$client->refreshToken($google_token->refresh_token);

isso irá atualizar seu token, você deve atualizá-lo na sessão para que você possa fazer

 $_SESSION['access_token']= $client->getAccessToken()

1
você fez meu dia com isso :) muito obrigado, muito mais simples do que eu pensei que seria, já que tenho passado muito tempo chegando a lugar nenhum: D
TB Ygg

16

O tipo de acesso deve ser definido como offline. stateé uma variável que você define para seu próprio uso, não para uso da API.

Certifique-se de ter a versão mais recente da biblioteca cliente e adicione:

$client->setAccessType('offline');

Consulte Formando a URL para obter uma explicação dos parâmetros.


Obrigado jk. Baixei a versão mais recente e revoguei o acesso ao aplicativo para minha conta. Então, concedi acesso mais uma vez e armazenei accessToken e refreshToken. A questão é que sempre recebo um refreshToken, mesmo que setAccessType tenha sido omitido. De qualquer forma, quando executo $ client-> refreshToken (refresh-token-key), ainda recebo o erro "invalid_grant". Eu verifiquei o auth-url e o padrão é "force". Se eu alterar para "automático" e executar o método de autenticação, não sou redirecionado, pois já concedi acesso. Mas o respons é um accessToken sem um refresh. Alguma ideia?
seorch.me

@ seorch.me Parece loucura, mas é possível que você tenha que configurar um novo $client( $client = new apiClient();) para usar o token de atualização?
jk.

1
@ seorch.me você deve definir $client->setApprovalPrompt('force'), bem como $client->setAccessType('offline')obter um novo token de atualização durante a autorização. Sem forçar o usuário a aprovar o escopo de acesso, o Google presume que você continuará usando o token de atualização antigo.
Jason

14

A resposta postada por @uri-weg funcionou para mim, mas como não achei as explicações dele muito claras, deixe-me reformulá-las um pouco.

Durante a primeira sequência de permissão de acesso, no retorno de chamada, quando você chegar ao ponto em que recebe um código de autenticação, deve salvar o token de acesso e o token de atualização também.

O motivo é que a API do Google envia a você um token de acesso com um token de atualização apenas ao solicitar permissão de acesso. Os próximos tokens de acesso serão enviados sem nenhum token de atualização (a menos que você use a approval_prompt=forceopção).

O token de atualização que você recebeu na primeira vez permanece válido até que o usuário revogue a permissão de acesso.

Em php simplista, um exemplo de sequência de retorno de chamada seria:

// init client
// ...

$authCode = $_GET['code'];
$accessToken = $client->authenticate($authCode);
// $accessToken needs to be serialized as json
$this->saveAccessToken(json_encode($accessToken));
$this->saveRefreshToken($accessToken['refresh_token']);

E mais tarde, em php simplista, a sequência de conexão seria:

// init client
// ...

$accessToken = $this->loadAccessToken();
// setAccessToken() expects json
$client->setAccessToken($accessToken);

if ($client->isAccessTokenExpired()) {
    // reuse the same refresh token
    $client->refreshToken($this->loadRefreshToken());
    // save the new access token (which comes without any refresh token)
    $this->saveAccessToken($client->getAccessToken());
}

perfeito, funcionou muito. a única coisa que eu diria é que você deve explicar que precisa passar o objeto json não apenas o token como uma string.
Oliver Bayes-Shelton

@ OliverBayes-Shelton Olá. Obrigado. Achei que // setAccessToken() expects jsonfosse o suficiente. Ou é para outra parte do código?
Daishi

Isso funciona muito bem para mim, mas você sabe se este código lida com situações em que um token expira por exceder o limite de 50 atualizações de token? Detalhes sobre 'Expiração de token' podem ser encontrados aqui: developers.google.com/identity/protocols/OAuth2#expiration
Bjorn

Parece que a versão 2.0 mais recente agora está retornando o token de atualização na matriz de tokens de acesso. Isso significa que salvar o token de acesso também salva o token de atualização, pois o token de atualização está incluído. Em resposta à expiração do token de atualização, suponho que isso teria que ser testado e tratado explicitamente - lembre-se de que o limite de 50 é "por usuário por cliente", ou seja, é de 50 por cliente, então é improvável que você o alcance, especialmente se você usa escopos incluídos para combinar tokens.
Brian C

8

Aqui está o código que estou usando em meu projeto e está funcionando bem:

public function getClient(){
    $client = new Google_Client();
    $client->setApplicationName(APPNAME);       // app name
    $client->setClientId(CLIENTID);             // client id
    $client->setClientSecret(CLIENTSECRET);     // client secret 
    $client->setRedirectUri(REDIRECT_URI);      // redirect uri
    $client->setApprovalPrompt('auto');

    $client->setAccessType('offline');         // generates refresh token

    $token = $_COOKIE['ACCESSTOKEN'];          // fetch from cookie

    // if token is present in cookie
    if($token){
        // use the same token
        $client->setAccessToken($token);
    }

    // this line gets the new token if the cookie token was not present
    // otherwise, the same cookie token
    $token = $client->getAccessToken();

    if($client->isAccessTokenExpired()){  // if token expired
        $refreshToken = json_decode($token)->refresh_token;

        // refresh the token
        $client->refreshToken($refreshToken);
    }

    return $client;
}

6

Tive o mesmo problema; meu script que funcionou ontem, por alguma razão estranha não funcionou hoje. Sem alterações.

Aparentemente, isso ocorreu porque o relógio do meu sistema estava atrasado em 2,5 (!!) segundos, a sincronização com o NTP corrigiu isso.

Veja também: https://code.google.com/p/google-api-php-client/wiki/OAuth2#Solving_invalid_grant_errors


Essa resposta me ajudou muito, cara. Você provavelmente me economizou muito tempo. Muito! Obrigado! Acabei de executar sudo apt-get install ntpna minha máquina Debian para instalar o NTP. Ele sincronizou o relógio e o problema foi resolvido.
Szymon Sadło

4

Para sua informação: a 3.0 Google Analytics API atualizará automaticamente o token de acesso se você tiver um token de atualização quando ele expirar, para que seu script nunca precise refreshToken.

(Veja o Sign função em auth/apiOAuth2.php)


"Atualizar automaticamente" significa que só preciso solicitar getAccessToken () e receberei um atualizado de volta? Mas primeiro preciso definir o token de atualização do banco de dados, certo? Caso contrário, a atualização funcionaria sem um token de atualização e eu não acho que isso funcionaria
ninsky

4

Às vezes, atualizar o token não foi gerado usando $client->setAccessType ("offline");.

Experimente isto:

$client->setAccessType ("offline");
$client->setApprovalPrompt ("force"); 

Para ser mais específico, parece que o token de atualização está incluído em sua primeira autorização. Se você salvar e depois usá-lo, acredito (de acordo com outros, não verificado) que o token de atualização continua a ser retornado. O doco agora também diz que eles irão atualizar automaticamente o token de acesso se tiverem um token de atualização, o que significa que é simplesmente uma questão de gerenciar o token de atualização com segurança. setApprovalPrompt ('force') força um token de atualização a ser emitido posteriormente; sem ele você não terá outro.
Brian C

2

Usei o exemplo por smartcodes com a versão atual da API do Google, mas aquele não funcionou. Acho que a API dele está muito desatualizada.

Então, acabei de escrever minha própria versão, com base em um dos exemplos de API ... Ele produz token de acesso, token de solicitação, tipo de token, token de ID, tempo de expiração e tempo de criação como strings

Se suas credenciais de cliente e chave de desenvolvedor estiverem corretas, este código deve funcionar imediatamente.

<?php
// Call set_include_path() as needed to point to your client library.
require_once 'google-api-php-client/src/Google_Client.php';
require_once 'google-api-php-client/src/contrib/Google_Oauth2Service.php';
session_start();

$client = new Google_Client();
$client->setApplicationName("Get Token");
// Visit https://code.google.com/apis/console?api=plus to generate your
// oauth2_client_id, oauth2_client_secret, and to register your oauth2_redirect_uri.
$oauth2 = new Google_Oauth2Service($client);

if (isset($_GET['code'])) {
    $client->authenticate($_GET['code']);
    $_SESSION['token'] = $client->getAccessToken();
    $redirect = 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'];
    header('Location: ' . filter_var($redirect, FILTER_SANITIZE_URL));
    return;
}

if (isset($_SESSION['token'])) {
    $client->setAccessToken($_SESSION['token']);
}

if (isset($_REQUEST['logout'])) {
    unset($_SESSION['token']);
    $client->revokeToken();
}
?>
<!doctype html>
<html>
    <head><meta charset="utf-8"></head>
    <body>
        <header><h1>Get Token</h1></header>
        <?php
        if ($client->getAccessToken()) {
            $_SESSION['token'] = $client->getAccessToken();
            $token = json_decode($_SESSION['token']);
            echo "Access Token = " . $token->access_token . '<br/>';
            echo "Refresh Token = " . $token->refresh_token . '<br/>';
            echo "Token type = " . $token->token_type . '<br/>';
            echo "Expires in = " . $token->expires_in . '<br/>';
            echo "ID Token = " . $token->id_token . '<br/>';
            echo "Created = " . $token->created . '<br/>';
            echo "<a class='logout' href='?logout'>Logout</a>";
        } else {
            $authUrl = $client->createAuthUrl();
            print "<a class='login' href='$authUrl'>Connect Me!</a>";
        }
        ?>
    </body>
</html>

1
Por favor, você poderia me explicar por que esta linha: $redirect = 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'];. Por que você redireciona para a mesma página? isso é necessário?
Tropicalista

@Tropicalista: Não é necessário recarregar a página em si, mas é assim que os fluxos de autenticação são normalmente implementados.
John Slegers

mas você não está usando o token de atualização para obter um novo token de acesso se o token de acesso tiver expirado.
apadana

1

Eu tenho o mesmo problema com google / google-api-php-client v2.0.0-RC7 e depois de pesquisar por 1 hora, resolvi esse problema usando json_encode assim:

    if ($client->isAccessTokenExpired()) {
        $newToken = json_decode(json_encode($client->getAccessToken()));
        $client->refreshToken($newToken->refresh_token);
        file_put_contents(storage_path('app/client_id.txt'), json_encode($client->getAccessToken()));
    }

1

Isso aqui funciona muito bem, talvez possa ajudar alguém:

index.php

session_start();

require_once __DIR__.'/client.php';

if(!isset($obj->error) && isset($_SESSION['access_token']) && $_SESSION['access_token'] && isset($obj->expires_in)) {
?>
<!DOCTYPE html>
<html>
<head>
<title>Google API Token Test</title>
<meta charset='utf-8' />
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script>
search('Music Mix 2010');
function search(q) {
    $.ajax({
        type: 'GET',
        url: 'action.php?q='+q,
        success: function(data) {
            if(data == 'refresh') location.reload();
            else $('#response').html(JSON.stringify(JSON.parse(data)));
        }
    });
}
</script>
</head>
<body>
<div id="response"></div>
</body>
</html>
<?php
}
else header('Location: '.filter_var('https://'.$_SERVER['HTTP_HOST'].dirname($_SERVER['PHP_SELF']).'/oauth2callback.php', FILTER_SANITIZE_URL));
?>

oauth2callback.php

require_once __DIR__.'/vendor/autoload.php';

session_start();

$client = new Google_Client();
$client->setAuthConfigFile('auth.json');
$client->setAccessType('offline');
$client->setApprovalPrompt('force');
$client->setRedirectUri('https://'.filter_var($_SERVER['HTTP_HOST'].$_SERVER['PHP_SELF'], FILTER_SANITIZE_URL));
$client->addScope(Google_Service_YouTube::YOUTUBE_FORCE_SSL);

if(isset($_GET['code']) && $_GET['code']) {
    $client->authenticate(filter_var($_GET['code'], FILTER_SANITIZE_STRING));
    $_SESSION['access_token'] = $client->getAccessToken();
    $_SESSION['refresh_token'] = $_SESSION['access_token']['refresh_token'];
    setcookie('refresh_token', $_SESSION['refresh_token'], time()+60*60*24*180, '/', filter_var($_SERVER['HTTP_HOST'], FILTER_SANITIZE_URL), true, true);
    header('Location: '.filter_var('https://'.$_SERVER['HTTP_HOST'].dirname($_SERVER['PHP_SELF']), FILTER_SANITIZE_URL));
    exit();
}
else header('Location: '.filter_var($client->createAuthUrl(), FILTER_SANITIZE_URL));
exit();

?>

client.php

// https://developers.google.com/api-client-library/php/start/installation
require_once __DIR__.'/vendor/autoload.php';

$client = new Google_Client();
$client->setAuthConfig('auth.json');
$client->setAccessType('offline');
$client->setApprovalPrompt('force');
$client->addScope(Google_Service_YouTube::YOUTUBE_FORCE_SSL);

// Delete Cookie Token
#setcookie('refresh_token', @$_SESSION['refresh_token'], time()-1, '/', filter_var($_SERVER['HTTP_HOST'], FILTER_SANITIZE_URL), true, true);

// Delete Session Token
#unset($_SESSION['refresh_token']);

if(isset($_SESSION['refresh_token']) && $_SESSION['refresh_token']) {
    $client->refreshToken($_SESSION['refresh_token']);
    $_SESSION['access_token'] = $client->getAccessToken();
}
elseif(isset($_COOKIE['refresh_token']) && $_COOKIE['refresh_token']) {
    $client->refreshToken($_COOKIE['refresh_token']);
    $_SESSION['access_token'] = $client->getAccessToken();
}

$url = 'https://www.googleapis.com/oauth2/v1/tokeninfo?access_token='.urlencode(@$_SESSION['access_token']['access_token']);
$curl_handle = curl_init();
curl_setopt($curl_handle, CURLOPT_URL, $url);
curl_setopt($curl_handle, CURLOPT_CONNECTTIMEOUT, 2);
curl_setopt($curl_handle, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl_handle, CURLOPT_USERAGENT, 'Google API Token Test');
$json = curl_exec($curl_handle);
curl_close($curl_handle);

$obj = json_decode($json);

?>

action.php

session_start();

require_once __DIR__.'/client.php';

if(isset($obj->error)) {
    echo 'refresh';
    exit();
}
elseif(isset($_SESSION['access_token']) && $_SESSION['access_token'] && isset($obj->expires_in) && isset($_GET['q']) && !empty($_GET['q'])) {
    $client->setAccessToken($_SESSION['access_token']);
    $service = new Google_Service_YouTube($client);
    $response = $service->search->listSearch('snippet', array('q' => filter_input(INPUT_GET, 'q', FILTER_SANITIZE_SPECIAL_CHARS), 'maxResults' => '1', 'type' => 'video'));
    echo json_encode($response['modelData']);
    exit();
}
?>

1

O Google fez algumas alterações desde que esta pergunta foi postada originalmente.

Aqui está meu exemplo de trabalho atualmente.

    public function update_token($token){

    try {

        $client = new Google_Client();
        $client->setAccessType("offline"); 
        $client->setAuthConfig(APPPATH . 'vendor' . DIRECTORY_SEPARATOR . 'google' . DIRECTORY_SEPARATOR . 'client_secrets.json');  
        $client->setIncludeGrantedScopes(true); 
        $client->addScope(Google_Service_Calendar::CALENDAR); 
        $client->setAccessToken($token);

        if ($client->isAccessTokenExpired()) {
            $refresh_token = $client->getRefreshToken();
            if(!empty($refresh_token)){
                $client->fetchAccessTokenWithRefreshToken($refresh_token);      
                $token = $client->getAccessToken();
                $token['refresh_token'] = json_decode($refresh_token);
                $token = json_encode($token);
            }
        }

        return $token;

    } catch (Exception $e) { 
        $error = json_decode($e->getMessage());
        if(isset($error->error->message)){
            log_message('error', $error->error->message);
        }
    }


}

1

Eu uso google-api-php-client v2.2.2 Recebo um novo token com a fetchAccessTokenWithRefreshToken();chamada de função if sem parâmetros, ele retorna um token de acesso atualizado e o token atualizado não é perdido.

if ($client->getAccessToken() && $client->isAccessTokenExpired()) {
    $new_token=$client->fetchAccessTokenWithRefreshToken();
    $token_data = $client->verifyIdToken();
}    

1

Você precisa salvar o token de acesso ao arquivo ou banco de dados como uma string json durante a solicitação de autorização inicial e definir o tipo de acesso como offline $client->setAccessType("offline")

Em seguida, durante as solicitações de API subsequentes, pegue o token de acesso de seu arquivo ou banco de dados e passe-o ao cliente:

$accessToken = json_decode($row['token'], true);
$client->setAccessToken($accessToken);

Agora você precisa verificar se o token expirou:

if ($client->isAccessTokenExpired()) {
    // access token has expired, use the refresh token to obtain a new one
    $client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
    // save the new token to file or db
    // ...json_encode($client->getAccessToken())

A fetchAccessTokenWithRefreshToken()função fará o trabalho para você e fornecerá um novo token de acesso, salvando-o de volta em seu arquivo ou banco de dados.



-1

use o seguinte snippet de código para obter seu token de atualização

    <?php

    require_once 'src/apiClient.php';
    require_once 'src/contrib/apiTasksService.php';

    $client = new apiClient();
    $client->setAccessType('offline');
    $tasksService = new apiTasksService($client);

    $auth = $client->authenticate();
    $token = $client->getAccessToken();
    // the refresh token
    $refresh_token = $token['refresh_token'];
    ?>
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.