【问题标题】:Google API OAuth 2.0 CURL returning "Required parameter is missing: grant_type"Google API OAuth 2.0 CURL 返回“缺少必需参数:grant_type”
【发布时间】:2012-01-02 03:22:55
【问题描述】:

我正在尝试为 Web 服务器应用程序实施 Google 的 OAuth 2.0 身份验证。

我可以从 Google 获取代码,但是当我发回此代码以尝试获取访问令牌时,它总是给我错误“缺少必需的参数:grant_type。错误 400”,即使 grant_type 在那里。

此外,如果我将 content-length 指定为 0 以外的任何值,则会引发其他错误。

这是执行此 curl 帖子的代码:

$url = 'https://accounts.google.com/o/oauth2/token';
$ch = curl_init($url);

curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);  
curl_setopt($ch, CURLOPT_FAILONERROR, false);  
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST'); 

curl_setopt($ch, CURLOPT_HTTPHEADER, array(
    'Content-Type: application/x-www-form-urlencoded',
    'Content-length: 0'
));

curl_setopt($ch, CURLOPT_POSTFIELDS, array( 
    'code='. urlencode($code),
    'client_id=' . urlencode($clientID),
    'client_secret=' . urlencode($clientSecret),
    'redirect_uri=http%3A%2F%2Flocalhost%2Fexperiments%2FnewGALogin.php',
    'grant_type=authorization_code'
)); 

【问题讨论】:

    标签: php curl oauth google-api


    【解决方案1】:

    我试图在原始问题和此处提供的答案中使用 PHP 代码,并不断收到来自 Google 令牌服务器的关于缺少“grant_type”的投诉,即使它肯定已被传入。事实证明问题是CURLOPT_HTTPHEADER 不喜欢/不需要“内容长度:0”。希望这个完整的工作代码可以避免其他人同样的头痛......

    // This is what Google's OAUTH server sends to you
    $code = $_GET['code'];
    
    // These come from your client_secret.json file
    $clientID = "your client id.apps.googleusercontent.com";
    $clientSecret = "your client secret";
    $redirectURI = "your redirect URI";
    $token_uri = 'https://accounts.google.com/o/oauth2/token';
    
    
    $ch = curl_init($token_uri);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);  
    curl_setopt($ch, CURLOPT_FAILONERROR, false);  
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
    
    curl_setopt($ch, CURLOPT_HTTPHEADER, array(
        'Content-Type: application/x-www-form-urlencoded'
    ));
    
    // Build the URLEncoded post data
    $postFields = http_build_query(array( 
        'client_secret' => $clientSecret,
        'grant_type' => 'authorization_code',
        'redirect_uri' => $redirectURI,
        'client_id' => $clientID,
        'code' => $code
    ));
    curl_setopt($ch, CURLOPT_POSTFIELDS, $postFields); 
    
    $response = curl_exec($ch);
    
    // Save response, especially the "refresh_token"
    $pathToAccessToken = "/your/path/to/access_token.json";
    file_put_contents($pathToAccessToken, $response);
    

    仅供参考,响应 JSON 看起来像这样:

    {
      "access_token" : "xxxWhateverGibberish", 
      "token_type" : "Bearer", 
      "expires_in" : 3600, 
      "refresh_token" : "yyyMoreGibberish" 
    }
    

    之后,我可以使用如下代码成功查询日历(我的原始 OAuth 请求调用的 API 范围):

    function getClient() {
      $client = new Google_Client();
      $client->setApplicationName(APPLICATION_NAME);
      $client->setScopes(SCOPES);
      $client->setAuthConfigFile(CLIENT_SECRET_PATH);
      $client->setAccessType('offline');
    
      // Load previously authorized credentials from a file.
      $pathToAccessToken = "/your/path/to/access_token.json";
      $accessToken = file_get_contents($pathToAccessToken);
      $client->setAccessToken($accessToken);
    
      // Refresh the token if it's expired.
      if ($client->isAccessTokenExpired()) {
        $client->refreshToken($client->getRefreshToken());
        file_put_contents($pathToAccessToken, $client->getAccessToken());
      }
    
      return $client;
    }
    
    $client = getClient();
    $service = new Google_Service_Calendar($client);
    
    // Print the next 10 events on the user's calendar.
    $calendarId = 'primary';
    $optParams = array(
          'maxResults' => 10,
          'orderBy' => 'startTime',
          'singleEvents' => TRUE,
          'timeMin' => date('c'),
    );
    $results = $service->events->listEvents($calendarId, $optParams);
    
    if (count($results->getItems()) == 0) {
        print "No upcoming events found.\n";
    } else {
        print "Upcoming events:\n";
        foreach ($results->getItems() as $event) {
            $start = $event->start->dateTime;
            if (empty($start)) {
              $start = $event->start->date;
            }
            printf("%s (%s)\n", $event->getSummary(), $start);
        }
    }       
    

    【讨论】:

      【解决方案2】:

      原始问题和一些答案的核心问题是使用密钥CURLOPT_POSTFIELDScurl_setopt 调用中接受的不同值。

      当输入是一个数组时,生成的Content-Type 将是multipart/form-data,它不符合 OAuth 2.0 规范,服务器将忽略它。当输入是查询编码的字符串(例如使用 http_build_query 构建)时,Content-Type: 将是 application/x-www-form-urlencoded,这是规范要求的。

      请参阅“注释”部分:http://php.net/manual/en/function.curl-setopt.php

      【讨论】:

        【解决方案3】:

        在研究了这个问题后,似乎数组格式不接受grant_type。 (是的,查询字符串方法有效,但构建起来很麻烦。)

        如果您热衷于将 POST 字段保留在数组中,则可以将 http_build_query() 添加到数组中。

        curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query(array( 
            'code' => $code,
            'client_id' => $clientID,
            'client_secret' => $clientSecret,
            'redirect_uri' => 'http%3A%2F%2Flocalhost%2Fexperiments%2FnewGALogin.php',
            'grant_type' => 'authorization_code'
        ))); 
        

        【讨论】:

          【解决方案4】:

          我不想相信它,但奇怪的是,只需从 CURLOPT_POSTFIELDS 从数组切换到 '&' 连接字符串(具有相同的数据!),我的 OAuth 服务器终于可以识别 grant_type。

          【讨论】:

            【解决方案5】:

            试试

            curl_setopt($ch, CURLOPT_POSTFIELDS, array( 
                'code' => $code,
                'client_id' => $clientID,
                'client_secret' => $clientSecret,
                'redirect_uri' => 'http%3A%2F%2Flocalhost%2Fexperiments%2FnewGALogin.php',
                'grant_type' => 'authorization_code'
            )); 
            

            curl_setopt($ch, CURLOPT_POSTFIELDS,
                'code=' . urlencode($code) . '&' .
                'client_id=' . urlencode($clientID) . '&' .
                'client_secret=' . urlencode($clientSecret) . '&' .
                'redirect_uri=http%3A%2F%2Flocalhost%2Fexperiments%2FnewGALogin.php' . '&' .
                'grant_type=authorization_code'
            ); 
            

            【讨论】:

              【解决方案6】:

              请仔细阅读CURLOPT_POSTFIELDS 的文档:

              ... 以字段名称为键,字段数据为值的数组

              你只是做某事,但不是那样。试试:

              curl_setopt($ch, CURLOPT_POSTFIELDS, array( 
                  'code' => $code,
                  'client_id' => $clientID,
                  ...
              

              在这种情况下,您不需要urlencode

              【讨论】:

                猜你喜欢
                • 2015-07-13
                • 1970-01-01
                • 2016-12-14
                • 1970-01-01
                • 1970-01-01
                • 2012-06-27
                • 2014-10-14
                • 2014-06-29
                • 2019-09-14
                相关资源
                最近更新 更多