web-dev-qa-db-de.com

Wie aktualisiere ich ein Token mit dem Google API-Client?

Ich habe mit der Google Analytics-API (V3) herumgespielt und bin auf einige Fehler gestoßen. Erstens ist alles korrekt eingerichtet und funktioniert mit meinem Testkonto. Wenn ich jedoch Daten von einer anderen Profil-ID (demselben Google Accont/GA-Konto) abrufen möchte, wird ein 403-Fehler angezeigt. Das Seltsame ist, dass Daten von einigen GA Konten Daten zurückgeben, während andere diesen Fehler erzeugen.

Ich habe das Token wieder gesperrt und mich erneut authentifiziert, und jetzt kann ich anscheinend Daten von allen meinen Konten abrufen. Problem gelöst? Nicht. Da der Zugriffsschlüssel abläuft, werde ich erneut auf dasselbe Problem stoßen.

Wenn ich die Dinge richtig verstanden habe, könnte man den resfreshToken verwenden, um einen neuen authenticationTooken zu erhalten.

Das Problem ist, wenn ich laufe:

$client->refreshToken(refresh_token_key) 

der folgende Fehler wird zurückgegeben:

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

Ich habe den Code hinter der refreshToken-Methode überprüft und die Anforderung in die Datei "apiOAuth2.php" zurückverfolgt. Alle Parameter werden korrekt gesendet. Der grant_type ist in der Methode fest mit "refresh_token" codiert, daher ist es für mich schwer zu verstehen, was falsch ist. Das Parameter-Array sieht folgendermaßen aus:

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

Die Vorgehensweise ist wie folgt.

$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

Ist das ein Fehler oder habe ich etwas völlig falsch verstanden?

86
seorch.me

Also habe ich endlich herausgefunden, wie das geht. Die Grundidee ist, dass Sie das Token haben, das Sie erhalten, wenn Sie zum ersten Mal nach der Authentifizierung fragen. Dieses erste Token enthält ein Aktualisierungstoken. Das erste ursprüngliche Token läuft nach einer Stunde ab. Nach einer Stunde müssen Sie das Aktualisierungstoken des ersten Tokens verwenden, um ein neues verwendbares Token zu erhalten. Sie verwenden $client->refreshToken($refreshToken), um ein neues Token abzurufen. Ich werde dies "temporäres Token" nennen. Sie müssen dieses temporäre Token auch speichern, da es nach einer Stunde ebenfalls abläuft und kein Aktualisierungstoken zugeordnet ist. Um ein neues temporäres Token zu erhalten, müssen Sie die zuvor verwendete Methode verwenden und das Auffrischungs-Token des ersten Tokens verwenden. Ich habe unten Code angehängt, was hässlich ist, aber ich bin neu in diesem ...

//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);
74
Uri Weg

Das Problem liegt im Aktualisierungstoken:

[refresh_token] => 1\/lov250YQTMCC9LRQbE6yMv-FiX_Offo79UXimV8kvwY

Wenn ein String mit einem '/' einen json encoded erhält, wird er mit einem '\' maskiert, daher müssen Sie ihn entfernen.

Das Aktualisierungstoken in Ihrem Fall sollte sein:

1/lov250YQTMCC9LRQbE6yMv-FiX_Offo79UXimV8kvwY

Ich gehe davon aus, dass Sie die JSON-Zeichenfolge gedruckt haben, die Google zurückgesendet und das Token kopiert und in Ihren Code eingefügt hat. Wenn Sie es json_decode dann wird das '\' für dich!

41
Asim

hier ist das zu setzende Token-Snippet. Stellen Sie zuvor sicher, dass der Zugriffstyp auf offline eingestellt ist

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

Token aktualisieren

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

dadurch wird Ihr Token aktualisiert. Sie müssen es in der Sitzung aktualisieren, damit Sie dies tun können

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

Der Zugriffstyp sollte auf offline eingestellt sein. state ist eine Variable, die Sie für Ihre eigene Verwendung festlegen, nicht für die Verwendung der API.

Stellen Sie sicher, dass Sie die neueste Version der Client-Bibliothek haben, und fügen Sie Folgendes hinzu:

$client->setAccessType('offline');

Eine Erläuterung der Parameter finden Sie unter Bildung der URL .

17
jk.

Die Antwort von @ uri-weg hat bei mir funktioniert, aber da ich seine Erklärungen nicht sehr klar fand, lass es mich ein wenig umformulieren.

Wenn Sie während der ersten Zugriffsberechtigungssequenz im Rückruf den Punkt erreichen, an dem Sie einen Authentifizierungscode erhalten, müssen Sie das Zugriffstoken und das Aktualisierungstoken speichern ebenfalls.

Der Grund dafür ist, dass Google API Ihnen nur dann ein Zugriffstoken mit einem Aktualisierungstoken sendet, wenn Sie nach einer Zugriffsberechtigung gefragt werden. Die nächsten Zugriffstoken werden ohne Aktualisierungstoken gesendet (es sei denn, Sie verwenden approval_Prompt=force Möglichkeit).

Das Aktualisierungs-Token, das Sie zum ersten Mal erhalten haben, bleibt gültig, bis der Benutzer die Zugriffsberechtigung widerruft.

In vereinfachtem PHP wäre ein Beispiel für die Callback-Sequenz:

// 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']);

Und später, in vereinfachtem PHP, wäre die Verbindungssequenz:

// 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());
}
14
Daishi

Hier ist der Code, den ich in meinem Projekt verwende und der gut funktioniert:

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;
}
7
Mr_Green

Hatte das gleiche Problem; Mein Skript, das gestern funktionierte, hat aus irgendeinem Grund heute nicht funktioniert. Keine Änderungen.

Anscheinend lag dies daran, dass meine Systemuhr um 2,5 (!!) Sekunden abgestellt war, was durch die Synchronisierung mit NTP behoben wurde.

Siehe auch: https://code.google.com/p/google-api-php-client/wiki/OAuth2#Solving_invalid_grant_errors

6
strikernl

Zu Ihrer Information: Die 3.0 Google Analytics-API aktualisiert das Zugriffstoken automatisch, wenn Sie ein Aktualisierungstoken haben, wenn es abläuft, sodass Ihr Skript niemals refreshToken benötigt.

(Siehe die Funktion Sign in auth/apiOAuth2.php)

3
Mark Smith

Manchmal wird das Refresh-Token nicht mithilfe von $client->setAccessType ("offline"); generiert.

Versuche dies:

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

Ich habe das Beispiel von Smartcodes mit der aktuellen Version der Google-API verwendet, aber das hat nicht funktioniert. Ich denke, seine API ist zu veraltet.

Also habe ich gerade meine eigene Version geschrieben, basierend auf einem der API-Beispiele ... Sie gibt Zugriffstoken, Anforderungstoken, Tokentyp, ID-Token, Ablaufzeit und Erstellungszeit als Zeichenfolgen aus

Wenn Ihre Client-Anmeldeinformationen und der Entwicklerschlüssel korrekt sind, sollte dieser Code sofort funktionieren.

<?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>
2
John Slegers

Das hier funktioniert sehr gut, vielleicht könnte es jemandem helfen:

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
user1768700

Ich habe das gleiche Problem mit google/google-api-php-client v2.0.0-RC7 und nach 1-stündiger Suche habe ich dieses Problem mit gelöst json_encode wie folgt:

    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
Grandong

Ich verwende den Google-API-PHP-Client v2.2.2. Ich erhalte ein neues Token mit fetchAccessTokenWithRefreshToken();. Wenn der Funktionsaufruf ohne Parameter ausgeführt wird, wird ein aktualisiertes Zugriffstoken zurückgegeben und das aktualisierte Token geht nicht verloren.

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

Google hat einige Änderungen vorgenommen, seit diese Frage ursprünglich veröffentlicht wurde.

Hier ist mein aktuelles Beispiel.

    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);
        }
    }


}
0
Dave Spelts

Sie müssen das Zugriffstoken für Datei oder Datenbank während der anfänglichen Autorisierungsanforderung als JSON-Zeichenfolge speichern und den Zugriffstyp auf offline $client->setAccessType("offline") festlegen.

Nehmen Sie dann bei nachfolgenden API-Anforderungen das Zugriffstoken aus Ihrer Datei oder Datenbank und übergeben Sie es an den Client:

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

Jetzt müssen Sie überprüfen, ob das Token abgelaufen ist:

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())

Die Funktion fetchAccessTokenWithRefreshToken() erledigt die Arbeit für Sie und stellt ein neues Zugriffstoken bereit. Speichern Sie es in Ihrer Datei oder Datenbank.

0
Sam Thompson

Laut Authentifizierung bei Google: OAuth2 gibt weiterhin 'invalid_grant' zurück

"Sie sollten das Zugriffstoken, das Sie nach der ersten erfolgreichen Authentifizierung erhalten, wiederverwenden. Sie erhalten einen invalid_grant-Fehler, wenn Ihr vorheriges Token noch nicht abgelaufen ist. Zwischenspeichern Sie es irgendwo, damit Sie es wiederverwenden können."

ich hoffe es hilft

0
Jon