Bawiłem się interfejsem API Google Analytics (V3) i napotkałem pewne błędy. Po pierwsze, wszystko jest poprawnie skonfigurowane i działa z moim kontem testowym. Ale kiedy chcę pobrać dane z innego identyfikatora profilu (to samo konto Google Accont / GA), pojawia się błąd 403. Dziwne jest to, że dane z niektórych kont GA zwracają dane, podczas gdy inne generują ten błąd.
Wycofałem token i uwierzytelniłem się jeszcze raz, a teraz wygląda na to, że mogę pobrać dane ze wszystkich moich kont. Problem rozwiązany? Nie. Ponieważ klucz dostępu wygaśnie, ponownie napotkam ten sam problem.
Jeśli dobrze zrozumiałem, można użyć resfreshToken, aby uzyskać nowy plik authenticationTooken.
Problem w tym, że kiedy biegam:
$client->refreshToken(refresh_token_key)
zwracany jest następujący błąd:
Error refreshing the OAuth2 token, message: '{ "error" : "invalid_grant" }'
Sprawdziłem kod za metodą refreshToken i prześledziłem żądanie z powrotem do pliku „apiOAuth2.php”. Wszystkie parametry są wysyłane poprawnie. Grant_type jest zakodowany na stałe jako „refresh_token” w metodzie, więc trudno mi zrozumieć, co jest nie tak. Tablica parametrów wygląda następująco:
Array ( [client_id] => *******-uqgau8uo1l96bd09eurdub26c9ftr2io.apps.googleusercontent.com [client_secret] => ******** [refresh_token] => 1\/lov250YQTMCC9LRQbE6yMv-FiX_Offo79UXimV8kvwY [grant_type] => refresh_token )
Procedura jest następująca.
$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
Czy to błąd, czy kompletnie coś źle zrozumiałem?
źródło
Odpowiedzi:
Więc w końcu wymyśliłem, jak to zrobić. Podstawową ideą jest to, że masz token, który otrzymujesz, gdy pierwszy raz poprosisz o uwierzytelnienie. Ten pierwszy token zawiera token odświeżania. Pierwszy oryginalny token traci ważność po godzinie. Po godzinie musisz użyć tokena odświeżania z pierwszego tokena, aby uzyskać nowy użyteczny token. Używasz
$client->refreshToken($refreshToken)
do pobierania nowego tokena. Nazwę to „tymczasowym tokenem”. Musisz również przechowywać ten tymczasowy token, ponieważ po godzinie również wygasa i zauważ, że nie ma skojarzonego z nim tokena odświeżania. Aby uzyskać nowy tymczasowy token, musisz użyć metody, z której korzystałeś wcześniej i użyć refreshtoken pierwszego tokena. Załączam kod poniżej, który jest brzydki, ale jestem nowy w tym ...//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);
źródło
$client->isAccessTokenExpired()
nadal będzie sprawdzać tylko czasy przechowywane lokalnie, aby sprawdzić, czy uważa, że token wygasł. Token mógł nadal wygasnąć, a lokalna aplikacja będzie naprawdę wiedzieć tylko wtedy, gdy spróbuje go użyć. W takim przypadku klient API zwróci wyjątek i nie odświeży automatycznie tokena.Problem dotyczy tokena odświeżania:
[refresh_token] => 1\/lov250YQTMCC9LRQbE6yMv-FiX_Offo79UXimV8kvwY
Kiedy ciąg z a
'/'
dostajejson encoded
, jest uciekany z a'\'
, dlatego musisz go usunąć.Token odświeżania w Twoim przypadku powinien wyglądać następująco:
1/lov250YQTMCC9LRQbE6yMv-FiX_Offo79UXimV8kvwY
Zakładam, że zrobiłeś to, że wydrukowałeś ciąg json, który Google odesłał, a następnie skopiował i wkleił token do twojego kodu, ponieważ jeśli
json_decode
to zrobisz , to poprawnie usunie'\'
za ciebie!źródło
oto fragment do ustawienia tokena, przedtem upewnij się, że typ dostępu powinien być ustawiony na offline
if (isset($_GET['code'])) { $client->authenticate(); $_SESSION['access_token'] = $client->getAccessToken(); }
Aby odświeżyć token
$google_token= json_decode($_SESSION['access_token']); $client->refreshToken($google_token->refresh_token);
to odświeży twój token, musisz go zaktualizować w sesji, aby to zrobić
$_SESSION['access_token']= $client->getAccessToken()
źródło
Należy ustawić typ dostępu na
offline
.state
jest zmienną ustawianą na własny użytek, a nie na użytek interfejsu API.Upewnij się, że masz najnowszą wersję biblioteki klienta i dodaj:
$client->setAccessType('offline');
Aby uzyskać wyjaśnienie parametrów, zobacz Tworzenie adresu URL .
źródło
$client
($client = new apiClient();
), aby używać tokena odświeżania?$client->setApprovalPrompt('force')
a także$client->setAccessType('offline')
uzyskać nowy token odświeżania podczas autoryzacji. Bez zmuszania użytkownika do zatwierdzenia zakresu dostępu Google zakłada, że będziesz nadal używać starego tokena odświeżania.Odpowiedź zamieszczona przez @ uri-weg zadziałała dla mnie, ale ponieważ jego wyjaśnienia nie były dla mnie zbyt jasne, pozwolę sobie trochę przeformułować.
Podczas pierwszej sekwencji uprawnień dostępu, w wywołaniu zwrotnym, gdy dojdziesz do punktu, w którym otrzymasz kod uwierzytelniający, musisz również zapisać token dostępu oraz token odświeżania .
Powodem jest to, że Google API wysyła token dostępu z tokenem odświeżania tylko wtedy, gdy prosi o pozwolenie na dostęp. Kolejne tokeny dostępu zostaną wysłane bez odświeżania tokena (chyba że skorzystasz z
approval_prompt=force
opcji).Token odświeżania, który otrzymałeś za pierwszym razem, zachowuje ważność, dopóki użytkownik nie cofnie uprawnień dostępu.
W uproszczonym php, przykładem sekwencji wywołania zwrotnego byłoby:
// 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']);
A później, w uproszczonym php, sekwencja połączeń wyglądałaby następująco:
// 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()); }
źródło
// setAccessToken() expects json
to wystarczy. A może dotyczy innej części kodu?Oto kod, którego używam w moim projekcie i działa dobrze:
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; }
źródło
Miał ten sam problem; mój skrypt, który działał wczoraj, z jakiegoś dziwnego powodu nie działał dzisiaj. Bez zmian.
Najwyraźniej było tak, ponieważ mój zegar systemowy był wyłączony o 2,5 (!!) sekundy, synchronizacja z NTP naprawiła to.
Zobacz też: https://code.google.com/p/google-api-php-client/wiki/OAuth2#Solving_invalid_grant_errors
źródło
sudo apt-get install ntp
na mojej maszynie Debiana, aby zainstalować NTP. Zsynchronizował zegar i problem został rozwiązany.Do Twojej wiadomości: 3.0 Google Analytics API automatycznie odświeży token dostępu, jeśli masz token odświeżania po jego wygaśnięciu, więc twój skrypt nigdy nie potrzebuje
refreshToken
.(Zobacz
Sign
funkcję wauth/apiOAuth2.php
)źródło
Czasami Refresh Token nie został wygenerowany przy użyciu
$client->setAccessType ("offline");
.Spróbuj tego:
$client->setAccessType ("offline"); $client->setApprovalPrompt ("force");
źródło
Skorzystałem z przykładu przez smartcodes z aktualną wersją API Google, ale ten nie zadziałał. Myślę, że jego API jest zbyt przestarzałe.
Właśnie napisałem własną wersję, opartą na jednym z przykładów API ... Wyprowadza token dostępu, token żądania, typ tokenu, token identyfikatora, czas wygaśnięcia i czas utworzenia jako ciągi
Jeśli poświadczenia klienta i klucz programisty są poprawne, ten kod powinien działać po wyjęciu z pudełka.
<?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>
źródło
$redirect = 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'];
. Dlaczego przekierowujesz na tę samą stronę? czy to konieczne?Musisz zapisać token dostępu do pliku lub bazy danych jako ciąg json podczas początkowego żądania autoryzacji i ustawić typ dostępu na offline
$client->setAccessType("offline")
Następnie, podczas kolejnych żądań API, pobierz token dostępu ze swojego pliku lub bazy danych i przekaż go klientowi:
$accessToken = json_decode($row['token'], true); $client->setAccessToken($accessToken);
Teraz musisz sprawdzić, czy token wygasł:
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())
fetchAccessTokenWithRefreshToken()
Funkcja wykona pracę za Ciebie i dostarczenie nowego tokenu dostępu, zapisać go z powrotem do pliku lub bazy danych.źródło
Mam ten sam problem z google / google-api-php-client v2.0.0-RC7 i po 1 godzinach wyszukiwania rozwiązałem ten problem używając json_encode w następujący sposób:
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())); }
źródło
To tutaj działa bardzo dobrze, może mogłoby komuś pomóc:
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(); } ?>
źródło
Firma Google wprowadziła pewne zmiany od czasu opublikowania tego pytania.
Oto mój obecnie działający przykład.
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); } } }
źródło
Używam google-api-php-client v2.2.2. Otrzymuję nowy token z
fetchAccessTokenWithRefreshToken();
wywołaniem funkcji if bez parametrów, zwraca zaktualizowany token dostępu, a odświeżony token nie jest utracony.if ($client->getAccessToken() && $client->isAccessTokenExpired()) { $new_token=$client->fetchAccessTokenWithRefreshToken(); $token_data = $client->verifyIdToken(); }
źródło
użyj poniższego fragmentu kodu, aby uzyskać token odświeżania
<?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']; ?>
źródło
Zgodnie z uwierzytelnianiem w Google: OAuth2 ciągle zwraca „invalid_grant”
„Powinieneś ponownie użyć tokena dostępu, który otrzymałeś po pierwszym udanym uwierzytelnieniu. Jeśli twój poprzedni token jeszcze nie wygasł, pojawi się błąd invalid_grant. Zapisz go w pamięci podręcznej, aby móc go ponownie użyć.”
mam nadzieję, że to pomoże
źródło