Google API OAuth 2.0 CURL返回“必需的参数丢失:grant_type”

问题描述:

我正在尝试为Web服务器应用程序实施Google的OAuth 2.0身份验证。Google API OAuth 2.0 CURL返回“必需的参数丢失:grant_type”

我可以从Google ok获得代码,但是当我发布此代码尝试获取访问令牌时,即使grant_type存在,它也会给我提供错误“必需的参数丢失:grant_type。Error 400” 。

此外,如果我指定内容长度为0以外的任何内容,则会引发其他错误。

下面是在做这个卷曲后的代码:

$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' 
)); 

尝试

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' 
); 
+0

您的第二个解决方案有效,谢谢:) – hud

请阅读文档CURLOPT_POSTFIELDS小心:

...与字段名的关键和现场数据的数组值

你只是做了一些,但没有。尝试:

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

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

我不想去相信,但奇怪的是,简单地从CURLOPT_POSTFIELDS开关从阵列的“&”连接字符串(与相同的数据!)让我的OAuth服务器最终识别grant_type。

在研究了这个问题之后,好像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' 
))); 

的核心问题与原来的问题和一些问题的答案是使用密钥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

我试图用在这里提供的原题加答案PHP代码,并保持约一失踪“grant_type”的谷歌令牌服务器的抱怨,甚至虽然它肯定被传入。事实证明,问题是CURLOPT_HTTPHEADER不喜欢/需要'Content-length: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" 
} 

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

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