복붙노트

Google API 클라이언트로 토큰을 새로 고치는 방법은 무엇입니까?

PHP

Google API 클라이언트로 토큰을 새로 고치는 방법은 무엇입니까?

저는 Google Analytics API (V3)를 가지고 놀았으며 som 오류가 발생했습니다. 첫째, 모든 것이 올바르게 설정되어 있고 테스트 계정으로 작업했습니다. 그러나 다른 프로필 ID (동일한 Google Accont / GA 계정)에서 데이터를 가져 오려면 403 오류가 발생합니다. 이상한 점은 일부 GA 계정의 데이터는 데이터를 반환하지만 다른 오류는이 오류를 생성한다는 것입니다.

나는 토큰을 취소하고 한 번 더 인증을 받았으며 이제는 모든 계정에서 데이터를 가져올 수있는 것처럼 보입니다. 문제 해결됨? 아니. 액세스 키가 만료 될 때마다 동일한 문제가 다시 발생합니다.

올바르게 이해했다면 새로 고침 토큰을 사용하여 새 인증 토큰을 얻을 수 있습니다.

문제는 다음과 같습니다.

$client->refreshToken(refresh_token_key) 

다음 오류가 리턴됩니다.

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

내가 refreshToken 메서드 뒤에 코드를 확인하고 요청을 다시 "apiOAuth2.php"파일을 추적했습니다. 모든 매개 변수가 올바르게 전송됩니다. grant_type은 메소드 내에서 'refresh_token'으로 하드 코딩되어 있으므로 잘못된 점을 이해하기 어렵습니다. 매개 변수 배열은 다음과 같습니다.

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

절차는 다음과 같습니다.

$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

이 버그입니까, 아니면 완전히 오해 한 것입니까?

해결법

  1. ==============================

    1.그래서 나는 마침내 어떻게하는지 알아 냈습니다. 기본 아이디어는 처음으로 인증을 요청할 때 얻을 수있는 토큰이 있다는 것입니다. 이 첫 번째 토큰에는 새로 고침 토큰이 있습니다. 첫 번째 원래 토큰은 한 시간 후에 만료됩니다. 1 시간 후 첫 번째 토큰에서 새로 고침 토큰을 사용하여 새로운 사용 가능한 토큰을 얻어야합니다. $ client-> refreshToken ($ refreshToken)을 사용하여 새 토큰을 검색합니다. 나는 이것을 "임시 토큰"이라고 부를 것이다. 한 시간 후에 만료되기 때문에이 임시 토큰도 저장해야합니다.이 토큰 토큰에는 새로 고침 토큰이 없습니다. 새로운 임시 토큰을 얻으려면 이전에 사용한 방법을 사용하고 첫 번째 토큰의 새로 고침을 사용해야합니다. 나는 추한, 아래 코드를 첨부했지만이 새로운 메신저 ...

    그래서 나는 마침내 어떻게하는지 알아 냈습니다. 기본 아이디어는 처음으로 인증을 요청할 때 얻을 수있는 토큰이 있다는 것입니다. 이 첫 번째 토큰에는 새로 고침 토큰이 있습니다. 첫 번째 원래 토큰은 한 시간 후에 만료됩니다. 1 시간 후 첫 번째 토큰에서 새로 고침 토큰을 사용하여 새로운 사용 가능한 토큰을 얻어야합니다. $ client-> refreshToken ($ refreshToken)을 사용하여 새 토큰을 검색합니다. 나는 이것을 "임시 토큰"이라고 부를 것이다. 한 시간 후에 만료되기 때문에이 임시 토큰도 저장해야합니다.이 토큰 토큰에는 새로 고침 토큰이 없습니다. 새로운 임시 토큰을 얻으려면 이전에 사용한 방법을 사용하고 첫 번째 토큰의 새로 고침을 사용해야합니다. 나는 추한, 아래 코드를 첨부했지만이 새로운 메신저 ...

    //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);
    
  2. ==============================

    2.새로 고침 토큰에 문제가 있습니다.

    새로 고침 토큰에 문제가 있습니다.

    [refresh_token] => 1\/lov250YQTMCC9LRQbE6yMv-FiX_Offo79UXimV8kvwY
    

    '/'가있는 문자열에 json 인코딩이되면 '\'로 이스케이프 처리되므로 제거해야합니다.

    사례의 새로 고침 토큰은 다음과 같아야합니다.

    1/lov250YQTMCC9LRQbE6yMv-FiX_Offo79UXimV8kvwY
    

    내가 뭘했다고 가정하고있는 건 당신이 json 문자열을 출력했다는 것인데, json 문자열을 출력하여 json_decode에 '\'문자를 제거 할 것이기 때문에 google이 보낸 코드를 복사하여 붙여 넣었다.

  3. ==============================

    3.액세스 유형을 오프라인으로 설정해야합니다. 상태는 API의 용도가 아닌 자신이 사용하도록 설정 한 변수입니다.

    액세스 유형을 오프라인으로 설정해야합니다. 상태는 API의 용도가 아닌 자신이 사용하도록 설정 한 변수입니다.

    최신 버전의 클라이언트 라이브러리가 있는지 확인하고 다음을 추가하십시오.

    $client->setAccessType('offline');
    

    매개 변수에 대한 설명은 URL 작성을 참조하십시오.

  4. ==============================

    4.여기에 토큰을 설정하는 스 니펫이 있으며 그 전에 액세스 유형을 오프라인으로 설정해야합니다.

    여기에 토큰을 설정하는 스 니펫이 있으며 그 전에 액세스 유형을 오프라인으로 설정해야합니다.

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

    토큰을 새로 고치려면

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

    이것은 토큰을 새로 고침합니다. 할 수있는 일에 세션에서 업데이트해야합니다.

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

    5.@ uri-weg에 의해 게시 된 대답은 나를 위해 일했지만 내가 그의 설명을 아주 명확하게 찾지 못했기 때문에 조금 더 말하게하겠습니다.

    @ uri-weg에 의해 게시 된 대답은 나를 위해 일했지만 내가 그의 설명을 아주 명확하게 찾지 못했기 때문에 조금 더 말하게하겠습니다.

    콜백에서 첫 번째 액세스 권한 순서 중에 인증 코드를받는 지점에 도달하면 액세스 토큰과 새로 고침 토큰도 저장해야합니다.

    이유는 액세스 권한을 요청할 때만 Google API가 사용자에게 새로 고침 토큰이있는 액세스 토큰을 보내는 것입니다. 다음 액세스 토큰은 refresh_ 토큰없이 전송됩니다 (approval_prompt = force 옵션을 사용하지 않는 한).

    첫 번째로받은 새로 고침 토큰은 사용자가 액세스 권한을 취소 할 때까지 유효합니다.

    단순한 PHP에서 콜백 시퀀스의 예는 다음과 같습니다.

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

    그리고 나중에 단순 PHP에서 연결 순서는 다음과 같습니다.

    // 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());
    }
    
  6. ==============================

    6.내 프로젝트에서 사용하고있는 코드는 다음과 같습니다.

    내 프로젝트에서 사용하고있는 코드는 다음과 같습니다.

    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. ==============================

    7.같은 문제가 있었다. 어제 일한 나의 대본은, 이상한 이유로 오늘은 아니었다. 변경 사항 없음.

    같은 문제가 있었다. 어제 일한 나의 대본은, 이상한 이유로 오늘은 아니었다. 변경 사항 없음.

    내 시스템 클럭이 2.5 (!!) 초 단축되어 NTP와 동기화되어 고정 되었기 때문에 분명히이 문제가 해결되었습니다.

    https://code.google.com/p/google-api-php-client/wiki/OAuth2#Solving_invalid_grant_errors를 참조하십시오.

  8. ==============================

    8.참고 사항 : 만료시 새로 고침 토큰이 있으면 스크립트가 refreshToken을 필요로하지 않도록 3.0 Google Analytics API가 자동으로 액세스 토큰을 새로 고칩니다.

    참고 사항 : 만료시 새로 고침 토큰이 있으면 스크립트가 refreshToken을 필요로하지 않도록 3.0 Google Analytics API가 자동으로 액세스 토큰을 새로 고칩니다.

    (auth / apiOAuth2.php의 Sign 기능 참조)

  9. ==============================

    9.저는 현재 버전의 Google API를 사용하여 스마트 코드로 예제를 사용했지만 작동하지 않았습니다. 나는 그의 API가 너무 오래되었다고 생각한다.

    저는 현재 버전의 Google API를 사용하여 스마트 코드로 예제를 사용했지만 작동하지 않았습니다. 나는 그의 API가 너무 오래되었다고 생각한다.

    그래서, 방금 API 예제 중 하나를 기반으로 내 버전을 작성했습니다 ... 액세스 토큰, 요청 토큰, 토큰 유형, ID 토큰, 만료 시간 및 생성 시간을 문자열로 출력합니다.

    클라이언트 자격 증명과 개발자 키가 올바른 경우이 코드는 기본적으로 작동합니다.

    <?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>
    
  10. ==============================

    10.때때로 Refresh Token은 $ client-> setAccessType ( "offline");을 사용하여 생성되지 않습니다.

    때때로 Refresh Token은 $ client-> setAccessType ( "offline");을 사용하여 생성되지 않습니다.

    이 시도:

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

    11.나는 google / google-api-php-client v2.0.0-RC7과 동일한 문제가 있습니다. 1 시간 동안 검색 한 후 json_encode를 사용하여이 문제를 해결했습니다. 이렇게 :

    나는 google / google-api-php-client v2.0.0-RC7과 동일한 문제가 있습니다. 1 시간 동안 검색 한 후 json_encode를 사용하여이 문제를 해결했습니다. 이렇게 :

        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()));
        }
    
  12. ==============================

    12.이것은 여기에서 아주 좋, 아마 누군가를 도울 수 있었다 :

    이것은 여기에서 아주 좋, 아마 누군가를 도울 수 있었다 :

    index.php          session_start (); require_once __DIR __. '/ client.php'; ($ _SESSION [ 'access_token']) && $ _SESSION [ 'access_token'] && isset ($ obj-> expires_in)) {ifset ($ obj-> error) ?> Google API 토큰 테스트 </ title> <meta charset = 'utf-8'/> <script src = "https://code.jquery.com/jquery-1.12.4.js"> </ script> <script> 검색 ( 'Music Mix 2010'); 기능 검색 (q) {     $ .ajax ({         'GET'을 입력하십시오.         url : 'action.php? q ='+ q,         성공 : 기능 (데이터) {             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'); $ 클라이언트 -> setAccessType ( '오프라인'); $ 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 [ '코드']) {     $ 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));     출구(); } else header ( 'Location :'.filter_var ($ client-> createAuthUrl (), FILTER_SANITIZE_URL)); 출구(); ?> 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'); $ 클라이언트 -> setAccessType ( '오프라인'); $ client-> setApprovalPrompt ( 'force'); $ client-> addScope (Google_Service_YouTube :: YOUTUBE_FORCE_SSL); // 쿠키 토큰 삭제 #setcookie ( 'refresh_token', @ $ _ SESSION [ 'refresh_token'], time () - 1, '/', filter_var ($ _ SERVER [ 'HTTP_HOST'], FILTER_SANITIZE_URL), true, true); // 세션 토큰 삭제 #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 토큰 테스트'); $ 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)) {     에코 '새로 고침';     출구(); } ($ _ GET [ 'q']) &&! empty ($ _ GET [ 'q']) && isset ($ obj-> expires_in) && isset ($ _ 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'=> '비디오 '));     echo json_encode ($ response [ 'modelData']);     출구(); } ?></p> </li> <li> <div>==============================</div><h2>13.Google의 인증에 따르면 : OAuth2는 'invalid_grant'를 계속 반환합니다.</h2> <p>Google의 인증에 따르면 : OAuth2는 'invalid_grant'를 계속 반환합니다.</p> <p>"첫 번째 성공적인 인증 후에 얻은 액세스 토큰을 재사용해야하며, 이전 토큰이 아직 만료되지 않은 경우 invalid_grant 오류가 발생하므로 다시 캐시 할 수 있도록 캐시하십시오."</p> <p>도움이되기를 바랍니다.</p> </li> <li> <div>==============================</div><h2>14.이 질문이 처음 게시 된 이후 Google에서 일부 변경했습니다.</h2> <p>이 질문이 처음 게시 된 이후 Google에서 일부 변경했습니다.</p> <p>다음은 현재 작동중인 예제입니다.</p> <pre><code> 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); } } } </code></pre> </li> <li> <div>==============================</div><h2>15.다음 코드 스 니펫을 사용하여 새로 고침 토큰을 가져옵니다.</h2> <p>다음 코드 스 니펫을 사용하여 새로 고침 토큰을 가져옵니다.</p> <pre><code> <?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']; ?> </code></pre> </li> </ul> <p>from <a href='https://stackoverflow.com/questions/9241213/how-to-refresh-token-with-google-api-client' target='_blank'>https://stackoverflow.com/questions/9241213/how-to-refresh-token-with-google-api-client</a> by cc-by-sa and MIT license</p></div> <!-- System - START --> <!-- System - END --> <!-- Adfit_PC - START --> <!-- Adfit_PC - END --> <!-- GoogleAdsenseForResponsive - START --> <div class="tt_adsense_bottom" style="margin-top:30px;"> <DIV class='ads_adsense_img' style='margin:40px 0px 40px 0px;'> <script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script> <!-- 디스플레이광고만 --> <ins class="adsbygoogle" style="display:block" data-ad-client="ca-pub-8393857339019314" data-ad-slot="7474886381" data-ad-format="auto"></ins> <script> (adsbygoogle = window.adsbygoogle || []).push({}); </script> </DIV> </div> <!-- GoogleAdsenseForResponsive - END --> <div class="container_postbtn #post_button_group"> <div class="postbtn_like"><script>window.ReactionButtonType = 'reaction'; window.ReactionApiUrl = '//cnpnote.tistory.com/reaction'; window.ReactionReqBody = { entryId: 513 }</script> <div class="wrap_btn" id="reaction-513"></div> <script src="https://tistory1.daumcdn.net/tistory_admin/userblog/tistory-dcd72a92cfa46d05916d7729aa74e508c9f6ce4f/static/script/reaction-button-container.min.js"></script><div class="wrap_btn wrap_btn_share"><button type="button" class="btn_post sns_btn btn_share" aria-expanded="false" data-thumbnail-url="https://t1.daumcdn.net/tistory_admin/static/images/openGraph/opengraph.png" data-title="Google API 클라이언트로 토큰을 새로 고치는 방법은 무엇입니까?" data-description="Google API 클라이언트로 토큰을 새로 고치는 방법은 무엇입니까? 저는 Google Analytics API (V3)를 가지고 놀았으며 som 오류가 발생했습니다. 첫째, 모든 것이 올바르게 설정되어 있고 테스트 계정으로 작업했습니다. 그러나 다른 프로필 ID (동일한 Google Accont / GA 계정)에서 데이터를 가져 오려면 403 오류가 발생합니다. 이상한 점은 일부 GA 계정의 데이터는 데이터를 반환하지만 다른 오류는이 오류를 생성한다는 것입니다. 나는 토큰을 취소하고 한 번 더 인증을 받았으며 이제는 모든 계정에서 데이터를 가져올 수있는 것처럼 보입니다. 문제 해결됨? 아니. 액세스 키가 만료 될 때마다 동일한 문제가 다시 발생합니다. 올바르게 이해했다면 새로 고침 토큰을 사용하여 새.." data-profile-image="https://t1.daumcdn.net/tistory_admin/static/manage/images/r3/default_L.png" data-profile-name="cnpnote" data-pc-url="https://cnpnote.tistory.com/entry/Google-API-%ED%81%B4%EB%9D%BC%EC%9D%B4%EC%96%B8%ED%8A%B8%EB%A1%9C-%ED%86%A0%ED%81%B0%EC%9D%84-%EC%83%88%EB%A1%9C-%EA%B3%A0%EC%B9%98%EB%8A%94-%EB%B0%A9%EB%B2%95%EC%9D%80-%EB%AC%B4%EC%97%87%EC%9E%85%EB%8B%88%EA%B9%8C" data-relative-pc-url="/entry/Google-API-%ED%81%B4%EB%9D%BC%EC%9D%B4%EC%96%B8%ED%8A%B8%EB%A1%9C-%ED%86%A0%ED%81%B0%EC%9D%84-%EC%83%88%EB%A1%9C-%EA%B3%A0%EC%B9%98%EB%8A%94-%EB%B0%A9%EB%B2%95%EC%9D%80-%EB%AC%B4%EC%97%87%EC%9E%85%EB%8B%88%EA%B9%8C" data-blog-title="복붙노트"><span class="ico_postbtn ico_share">공유하기</span></button> <div class="layer_post" id="tistorySnsLayer"></div> </div><div class="wrap_btn wrap_btn_etc" data-entry-id="513" data-entry-visibility="public" data-category-visibility="public"><button type="button" class="btn_post btn_etc2" aria-expanded="false"><span class="ico_postbtn ico_etc">게시글 관리</span></button> <div class="layer_post" id="tistoryEtcLayer"></div> </div></div> <button type="button" class="btn_menu_toolbar btn_subscription #subscribe" data-blog-id="2840920" data-url="https://cnpnote.tistory.com/entry/Google-API-%ED%81%B4%EB%9D%BC%EC%9D%B4%EC%96%B8%ED%8A%B8%EB%A1%9C-%ED%86%A0%ED%81%B0%EC%9D%84-%EC%83%88%EB%A1%9C-%EA%B3%A0%EC%B9%98%EB%8A%94-%EB%B0%A9%EB%B2%95%EC%9D%80-%EB%AC%B4%EC%97%87%EC%9E%85%EB%8B%88%EA%B9%8C" data-device="web_pc"><em class="txt_state">구독하기</em><strong class="txt_tool_id">복붙노트</strong><span class="img_common_tistory ico_check_type1"></span></button> <div data-tistory-react-app="SupportButton"></div> </div> <!-- PostListinCategory - START --> <div class="another_category another_category_color_gray"> <h4>'<a href="/category/PHP">PHP</a>' 카테고리의 다른 글</h4> <table> <tr> <th><a href="/entry/PHP-%EC%9B%80%EB%9D%BC%EC%9A%B0%ED%8A%B8%EB%A5%BC-UTF-8-%EB%AC%B8%EC%9E%90%EC%97%B4%EC%97%90%EC%84%9C-%EA%B0%80%EC%9E%A5-%EA%B0%80%EA%B9%8C%EC%9A%B4-7-%EB%B9%84%ED%8A%B8-ASCII%EB%A1%9C-%EB%B0%94%EA%BE%B8%EC%8B%AD%EC%8B%9C%EC%98%A4">PHP : 움라우트를 UTF-8 문자열에서 가장 가까운 7 비트 ASCII로 바꾸십시오.</a>  <span>(0)</span></th> <td>2018.09.13</td> </tr> <tr> <th><a href="/entry/%EC%97%85%EB%A1%9C%EB%93%9C%EC%8B%9C-%EB%B3%B4%EC%95%88-%EC%9C%84%ED%98%91">업로드시 보안 위협</a>  <span>(0)</span></th> <td>2018.09.13</td> </tr> <tr> <th><a href="/entry/GMAIL-%EB%A9%94%EC%9D%BC-%EC%84%9C%EB%B2%84%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EC%97%AC-PHP%EB%A1%9C-XAMPP%EB%A5%BC-%EC%8B%A4%ED%96%89%ED%95%98%EB%8A%94-localhost%EC%97%90%EC%84%9C-%EC%A0%84%EC%9E%90-%EB%A9%94%EC%9D%BC-%EB%B3%B4%EB%82%B4%EA%B8%B0">GMAIL 메일 서버를 사용하여 PHP로 XAMPP를 실행하는 localhost에서 전자 메일 보내기</a>  <span>(0)</span></th> <td>2018.09.13</td> </tr> <tr> <th><a href="/entry/%ED%81%B4%EB%9E%98%EC%8A%A4-%EC%9D%B4%EB%A6%84%EC%9C%BC%EB%A1%9C-DOM-%EC%9A%94%EC%86%8C-%EA%B0%80%EC%A0%B8-%EC%98%A4%EA%B8%B0">클래스 이름으로 DOM 요소 가져 오기</a>  <span>(0)</span></th> <td>2018.09.13</td> </tr> <tr> <th><a href="/entry/PHP%EC%97%90%EC%84%9C-%ED%85%8D%EC%8A%A4%ED%8A%B8%EC%9D%98-URL-%EC%B6%94%EC%B6%9C">PHP에서 텍스트의 URL 추출</a>  <span>(0)</span></th> <td>2018.09.13</td> </tr> </table> </div> <!-- PostListinCategory - END --> </div> <div class="entry-footer"> <div class="actionTrail"> <a href="#tb" onclick=""></a>, <a href="#rp" onclick=""></a> </div> <div data-tistory-react-app="Namecard"></div> </div> </div> </div><!-- entry close --> </article> </div><!-- container close --> <aside role="complementary" id="sidebar"> <div class="container"> <div class="sidebar-inner"> <div class="module module_plugin"> <!-- Adfit_PC - START --> <ins class="kakao_ad_area" style="display:none;" data-ad-unit = "DAN-ubre63wuo3sm" data-ad-width = "160" data-ad-height = "600"></ins> <script type="text/javascript" src="//t1.daumcdn.net/kas/static/ba.min.js" async></script> <!-- Adfit_PC - END --> </div> <!-- 검색 모듈 --> <div class="widget widget-search col-md-3 col-xs-12"> <h3><i class="icon-magnifier icons"></i> Search</h3> <input type="text" name="search" value="" onkeypress="if (event.keyCode == 13) { try { window.location.href = '/search' + '/' + looseURIEncode(document.getElementsByName('search')[0].value); document.getElementsByName('search')[0].value = ''; return false; } catch (e) {} }"/> <input value="검색" type="button" onclick="try { window.location.href = '/search' + '/' + looseURIEncode(document.getElementsByName('search')[0].value); document.getElementsByName('search')[0].value = ''; return false; } catch (e) {}" class="btn btn-default btn-sm"/> </div> <!-- 카테고리 모듈 --> <div class="widget widget-category col-md-3 col-xs-12"> <h3><i class="icon-direction icons"></i> 카테고리</h3> <ul class="tt_category"><li class=""><a href="/category" class="link_tit"> 분류 전체보기 </a> <ul class="category_list"><li class=""><a href="/category/PHP" class="link_item"> PHP </a></li> <li class=""><a href="/category/%EC%8A%A4%ED%81%AC%EB%9E%98%EC%B9%98%203.0" class="link_item"> 스크래치 3.0 </a></li> <li class=""><a href="/category/PYTHON" class="link_item"> PYTHON </a></li> <li class=""><a href="/category/SPRING" class="link_item"> SPRING </a></li> <li class=""><a href="/category/HADOOP" class="link_item"> HADOOP </a></li> <li class=""><a href="/category/SCALA" class="link_item"> SCALA </a></li> <li class=""><a href="/category/MONGODB" class="link_item"> MONGODB </a></li> <li class=""><a href="/category/REDIS" class="link_item"> REDIS </a></li> <li class=""><a href="/category/RUBY-ON-RAILS" class="link_item"> RUBY-ON-RAILS </a></li> <li class=""><a href="/category/SQL" class="link_item"> SQL </a></li> <li class=""><a href="/category/NODEJS" class="link_item"> NODEJS </a></li> <li class=""><a href="/category/JQUERY" class="link_item"> JQUERY </a></li> <li class=""><a href="/category/ANDROID" class="link_item"> ANDROID </a></li> <li class=""><a href="/category/SWIFT" class="link_item"> SWIFT </a></li> <li class=""><a href="/category/HTML" class="link_item"> HTML </a></li> <li class=""><a href="/category/CSS" class="link_item"> CSS </a></li> <li class=""><a href="/category/REACTJS" class="link_item"> REACTJS </a></li> <li class=""><a href="/category/VUEJS" class="link_item"> VUEJS </a></li> <li class=""><a href="/category/WORDPRESS" class="link_item"> WORDPRESS </a></li> <li class=""><a href="/category/ANGULAR" class="link_item"> ANGULAR </a></li> <li class=""><a href="/category/MICROSERVICE" class="link_item"> MICROSERVICE </a></li> <li class=""><a href="/category/DJANGO" class="link_item"> DJANGO </a></li> <li class=""><a href="/category/FLASK" class="link_item"> FLASK </a></li> <li class=""><a href="/category/APACHE" class="link_item"> APACHE </a></li> <li class=""><a href="/category/GO" class="link_item"> GO </a></li> <li class=""><a href="/category/JAVA" class="link_item"> JAVA </a></li> <li class=""><a href="/category/FLUTTER" class="link_item"> FLUTTER </a></li> <li class=""><a href="/category/REACTIVE" class="link_item"> REACTIVE </a></li> <li class=""><a href="/category/SPA" class="link_item"> SPA </a></li> </ul> </li> </ul> </div> <!-- 태그목록 모듈 --> <div class="widget widget-tag col-md-3 col-xs-12"> <h3><i class="icon-tag icons"></i> 태그목록</h3> <ul> <li><a href="/tag/HADOOP" class="cloud3"> HADOOP</a></li> <li><a href="/tag/php" class="cloud4"> php</a></li> <li><a href="/tag/sql" class="cloud2"> sql</a></li> <li><a href="/tag/javascript" class="cloud4"> javascript</a></li> <li><a href="/tag/mysql" class="cloud4"> mysql</a></li> <li><a href="/tag/spring-mvc" class="cloud4"> spring-mvc</a></li> <li><a href="/tag/spring" class="cloud1"> spring</a></li> <li><a href="/tag/java" class="cloud2"> java</a></li> <li><a href="/tag/PYTHON" class="cloud3"> PYTHON</a></li> <li><a href="/tag/jQuery" class="cloud4"> jQuery</a></li> </ul> </div> <!-- 최근 포스트 모듈 --> <div class="widget widget-post col-md-3 col-xs-12"> <h3><i class="icon-list icons"></i> 최근에 올라온 글 </h3> <ul> </ul> </div> </div> </div> </div><!-- container close --> </aside><!-- sidebar close --> <footer role="contentinfo"> <div class="container"> <div class="row"> <div class="col-xs-12 col-sm-12 col-md-12 footer-menu"> <a href="https://cnpnote.tistory.com/tag"><i class="icon-tag icons"></i> </a> <a href="https://cnpnote.tistory.com/rss" onclick="window.open(this.href); return false"><i class="icon-feed icons"></i> </a> </div> <div class="col-xs-12 col-sm-12 col-md-12 footer-copyright text-right"> <a href="https://cnpnote.tistory.com/"> cnpnote</a>'s Blog is powered by <a href="http://daum.net" onclick="window.open(this.href); return false">Daumkakao</a> / Designed by <a href="http://ongal.tistory.com">CEOSEO</a> </div> </div> </div> </footer> <!-- jQuery --> <script src="https://tistory1.daumcdn.net/tistory/2840920/skin/images/jquery-1.11.0.min.js?_version_=1583344392"></script> <div class="#menubar menu_toolbar "> <h2 class="screen_out">티스토리툴바</h2> <div class="btn_tool"><button class="btn_menu_toolbar btn_subscription #subscribe" data-blog-id="2840920" data-url="https://cnpnote.tistory.com" data-device="web_pc"><strong class="txt_tool_id">복붙노트</strong><em class="txt_state">구독하기</em><span class="img_common_tistory ico_check_type1"></span></button></div></div> <div class="#menubar menu_toolbar "></div> <div class="layer_tooltip"> <div class="inner_layer_tooltip"> <p class="desc_g"></p> </div> </div> <iframe id="editEntry" style="position:absolute;width:1px;height:1px;left:-100px;top:-100px" src="//cnpnote.tistory.com/api"></iframe> <!-- DragSearchHandler - START --> <script src="//search1.daumcdn.net/search/statics/common/js/g/search_dragselection.min.js"></script> <!-- DragSearchHandler - END --> <script type="text/javascript">(function($) { $(document).ready(function() { lightbox.options.fadeDuration = 200; lightbox.options.resizeDuration = 200; lightbox.options.wrapAround = false; lightbox.options.albumLabel = "%1 / %2"; }) })(tjQuery);</script> <div style="margin:0; padding:0; border:none; background:none; float:none; clear:none; z-index:0"></div> <script type="text/javascript" src="https://tistory1.daumcdn.net/tistory_admin/userblog/tistory-dcd72a92cfa46d05916d7729aa74e508c9f6ce4f/static/script/common.js"></script> <script type="text/javascript">window.roosevelt_params_queue = window.roosevelt_params_queue || [{channel_id: 'dk', channel_label: '{tistory}'}]</script> <script type="text/javascript" src="//t1.daumcdn.net/midas/rt/dk_bt/roosevelt_dk_bt.js" async="async"></script> <script type="text/javascript" src="https://tistory1.daumcdn.net/tistory_admin/userblog/tistory-dcd72a92cfa46d05916d7729aa74e508c9f6ce4f/static/script/menubar.min.js"></script> <script>window.tiara = {"svcDomain":"user.tistory.com","section":"글뷰","trackPage":"글뷰_보기","page":"글뷰","key":"2840920-513","customProps":{"userId":"0","blogId":"2840920","entryId":"513","role":"guest","trackPage":"글뷰_보기","filterTarget":false},"entry":{"entryId":"513","categoryName":"PHP","categoryId":"264984","author":"3264508","image":"","plink":"/entry/Google-API-%ED%81%B4%EB%9D%BC%EC%9D%B4%EC%96%B8%ED%8A%B8%EB%A1%9C-%ED%86%A0%ED%81%B0%EC%9D%84-%EC%83%88%EB%A1%9C-%EA%B3%A0%EC%B9%98%EB%8A%94-%EB%B0%A9%EB%B2%95%EC%9D%80-%EB%AC%B4%EC%97%87%EC%9E%85%EB%8B%88%EA%B9%8C","tags":["access-token","google-analytics-api","google-api","oauth-2.0","php"]},"kakaoAppKey":"3e6ddd834b023f24221217e370daed18","appUserId":"null"}</script> <script type="module" src="https://t1.daumcdn.net/tistory_admin/frontend/tiara/v1.0.0/index.js"></script> <script src="https://t1.daumcdn.net/tistory_admin/frontend/tiara/v1.0.0/polyfills-legacy.min.js" nomodule="true" defer="true"></script> <script src="https://t1.daumcdn.net/tistory_admin/frontend/tiara/v1.0.0/index-legacy.js" nomodule="true" defer="true"></script> </body> </html>