Closed GLStephen closed 7 years ago
Hello,
One more issue, 'approval_prompt' => 'force' is not working as expected.
If I pass the 'approval_prompt' => 'force', it is still not asking user to authorize app forcefully If I already had login with already authorized adwords account.
Also It is not returning refresh_token with $user->GetOAuth2Info() under below code.
$OAuth2Handler = $user->GetOAuth2Handler(); $user->SetOAuth2Info($OAuth2Handler->GetAccessToken($user->GetOAuth2Info(), $code, $redirectUri)); $user->GetOAuth2Info();
Thanks,
@chiragvels replied to your comment in #5 as @GLStephen's issue is for the experimental branch.
@GLStephen this shouldn't be related to #162 as that was for the non-experimental branch and doesn't use the same OAuth2 library. The experimental branch uses our Google Auth library.
The OAuth2 object should be setting the grant_type automatically for you based on the existence of a refresh token.
So my guess is your UserRefreshCredentials stored in your ads session is somehow losing its refresh token? It would be good to log if a refresh token is actually present when you get that error.
Can you show me how you're constructing your credentials and ads session? Are you following our examples for this?
@vtsao This is how I am building the session. It is a bit different than the example you provide, but I don't see a material difference.
public function getDFPSession($network){
$user = User::find($network->user_id); // get user from db
// Generate a refreshable OAuth2 credential for authentication.
$oAuth2Credential = (new OAuth2TokenBuilder())
->withClientId('client_id')
->withClientSecret('client_secret')
->withRefreshToken($user->refresh_token)
->build();
// Construct an API session configured from a properties file and the OAuth2
// credentials above.
$session = (new DfpSessionBuilder())
->withLogger(Log::getMonolog())
->withApplicationName('CPMEnator')
->withNetworkCode($this->network->dfp_network_id)
->withEndpoint('https://ads.google.com/')
->withOAuth2Credential($oAuth2Credential)
->build();
$this->dfp_session = $session; // session is stored globally for all future usages
}
I was able to add a bit more debug logging and got this on the next instance of the failure:
Google\AdsApi\Dfp\DfpSession Object ( [logger:Google\AdsApi\Dfp\DfpSession:private] => Monolog\Logger Object ( [name:protected] => local [handlers:protected] => Array ( [0] => Monolog\Handler\StreamHandler Object ( [stream:protected] => Resource id #400 [url:protected] => /srv/dev/www.cpmenator.com/master/storage/logs/laravel.log [errorMessage:Monolog\Handler\StreamHandler:private] => [filePermission:protected] => [useLocking:protected] => [dirCreated:Monolog\Handler\StreamHandler:private] => 1 [level:protected] => 100 [bubble:protected] => 1 [formatter:protected] => Monolog\Formatter\LineFormatter Object ( [format:protected] => [%datetime%] %channel%.%level_name%: %message% %context% %extra%
[allowInlineLineBreaks:protected] => 1
[ignoreEmptyContextAndExtra:protected] => 1
[includeStacktraces:protected] =>
[dateFormat:protected] => Y-m-d H:i:s
)
[processors:protected] => Array
(
)
)
)
[processors:protected] => Array
(
)
)
[networkCode:Google\AdsApi\Dfp\DfpSession:private] => 243879457
[applicationName:Google\AdsApi\Dfp\DfpSession:private] => CPMEnator
[endpoint:Google\AdsApi\Dfp\DfpSession:private] => https://ads.google.com/
[oAuth2Credential:Google\AdsApi\Dfp\DfpSession:private] => Google\Auth\Credentials\UserRefreshCredentials Object
(
[auth:protected] => Google\Auth\OAuth2 Object
(
[authorizationUri:Google\Auth\OAuth2:private] =>
[tokenCredentialUri:Google\Auth\OAuth2:private] => GuzzleHttp\Psr7\Uri Object
(
[scheme:GuzzleHttp\Psr7\Uri:private] => https
[userInfo:GuzzleHttp\Psr7\Uri:private] =>
[host:GuzzleHttp\Psr7\Uri:private] => www.googleapis.com
[port:GuzzleHttp\Psr7\Uri:private] =>
[path:GuzzleHttp\Psr7\Uri:private] => /oauth2/v4/token
[query:GuzzleHttp\Psr7\Uri:private] =>
[fragment:GuzzleHttp\Psr7\Uri:private] =>
)
[redirectUri:Google\Auth\OAuth2:private] =>
[clientId:Google\Auth\OAuth2:private] => <clientid>
[clientSecret:Google\Auth\OAuth2:private] => <secret>
[username:Google\Auth\OAuth2:private] =>
[password:Google\Auth\OAuth2:private] =>
[scope:Google\Auth\OAuth2:private] =>
[state:Google\Auth\OAuth2:private] =>
[code:Google\Auth\OAuth2:private] =>
[issuer:Google\Auth\OAuth2:private] =>
[audience:Google\Auth\OAuth2:private] =>
[sub:Google\Auth\OAuth2:private] =>
[expiry:Google\Auth\OAuth2:private] => 3600
[signingKey:Google\Auth\OAuth2:private] =>
[signingAlgorithm:Google\Auth\OAuth2:private] =>
[refreshToken:Google\Auth\OAuth2:private] => 1/SeZtaNOaW<restofthecorrecttoken>
[accessToken:Google\Auth\OAuth2:private] =>
[idToken:Google\Auth\OAuth2:private] =>
[expiresIn:Google\Auth\OAuth2:private] =>
[expiresAt:Google\Auth\OAuth2:private] =>
[issuedAt:Google\Auth\OAuth2:private] =>
[grantType:Google\Auth\OAuth2:private] =>
[extensionParams:Google\Auth\OAuth2:private] => Array
(
)
)
)
[soapSettings:Google\AdsApi\Dfp\DfpSession:private] => Google\AdsApi\Common\SoapSettings Object
(
[compressionLevel:Google\AdsApi\Common\SoapSettings:private] =>
[wsdlCacheType:Google\AdsApi\Common\SoapSettings:private] =>
[proxyHost:Google\AdsApi\Common\SoapSettings:private] =>
[proxyPort:Google\AdsApi\Common\SoapSettings:private] =>
[proxyUser:Google\AdsApi\Common\SoapSettings:private] =>
[proxyPassword:Google\AdsApi\Common\SoapSettings:private] =>
[sslVerify:Google\AdsApi\Common\SoapSettings:private] => 1
[sslCaFile:Google\AdsApi\Common\SoapSettings:private] =>
)
)
Client error: POST https://www.googleapis.com/oauth2/v4/token
resulted in a 400 Bad Request
response:
{
"error": "invalid_request",
"error_description": "Required parameter is missing: grant_type",
"error_uri": ""
}
Thanks for the additional information. This is strange, I'm not sure how the grant type is being nulled out, because when you first create the OAuth2 credentials, it sets a grant type and it's never changed after that: https://github.com/google/google-auth-library-php/blob/0420299c61f2b6eef1da465342a1b66b34b36c13/src/OAuth2.php
Can you confirm that it's setting a grant type when you first attempt to get an access token?
Also, are you writing a web application or having multiple threads access the same session object?
This is the UserRefreshCredentials object on initial creation, which works fine until it fails after a while. Can I add a grant type to it manually somehow and see if having it on the object works for future refreshes? This is a web app, where I'm using an oAuth2 flow to get a refresh token for multiple users and storing them. Multiple threads are not accessing the object. Currently, the app is only in dev and I'm the only one using it.
[oAuth2Credential:Google\AdsApi\Dfp\DfpSession:private] => Google\Auth\Credentials\UserRefreshCredentials Object ( [auth:protected] => Google\Auth\OAuth2 Object ( [authorizationUri:Google\Auth\OAuth2:private] => [tokenCredentialUri:Google\Auth\OAuth2:private] => GuzzleHttp\Psr7\Uri Object ( [scheme:GuzzleHttp\Psr7\Uri:private] => https [userInfo:GuzzleHttp\Psr7\Uri:private] => [host:GuzzleHttp\Psr7\Uri:private] => www.googleapis.com [port:GuzzleHttp\Psr7\Uri:private] => [path:GuzzleHttp\Psr7\Uri:private] => /oauth2/v4/token [query:GuzzleHttp\Psr7\Uri:private] => [fragment:GuzzleHttp\Psr7\Uri:private] => )
[redirectUri:Google\Auth\OAuth2:private] =>
[clientId:Google\Auth\OAuth2:private] => <clientid>
[clientSecret:Google\Auth\OAuth2:private] => <secret>
[username:Google\Auth\OAuth2:private] =>
[password:Google\Auth\OAuth2:private] =>
[scope:Google\Auth\OAuth2:private] =>
[state:Google\Auth\OAuth2:private] =>
[code:Google\Auth\OAuth2:private] =>
[issuer:Google\Auth\OAuth2:private] =>
[audience:Google\Auth\OAuth2:private] =>
[sub:Google\Auth\OAuth2:private] =>
[expiry:Google\Auth\OAuth2:private] => 3600
[signingKey:Google\Auth\OAuth2:private] =>
[signingAlgorithm:Google\Auth\OAuth2:private] =>
[refreshToken:Google\Auth\OAuth2:private] => 1/SeZtaNO<rest of token>
[accessToken:Google\Auth\OAuth2:private] =>
[idToken:Google\Auth\OAuth2:private] =>
[expiresIn:Google\Auth\OAuth2:private] =>
[expiresAt:Google\Auth\OAuth2:private] =>
[issuedAt:Google\Auth\OAuth2:private] =>
[grantType:Google\Auth\OAuth2:private] =>
[extensionParams:Google\Auth\OAuth2:private] => Array
(
)
)
)
This is the UserRefreshCredentials object on initial creation
It won't set a grant type until you attempt to first grab an access token with it. Can you call fetchAuthToken() on the OAuth2 credential after you first create it and then dump its contents to ensure it has a grant type as a sanity check.
And yes, you can set the grant type manually yourself when debugging, but you shouldn't have to do that in prod.
It looks like the Grant Type is missing after the fetchAuthToken() call.
Google\Auth\Credentials\UserRefreshCredentials Object ( [auth:protected] => Google\Auth\OAuth2 Object ( [authorizationUri:Google\Auth\OAuth2:private] => [tokenCredentialUri:Google\Auth\OAuth2:private] => GuzzleHttp\Psr7\Uri Object ( [scheme:GuzzleHttp\Psr7\Uri:private] => https [userInfo:GuzzleHttp\Psr7\Uri:private] => [host:GuzzleHttp\Psr7\Uri:private] => www.googleapis.com [port:GuzzleHttp\Psr7\Uri:private] => [path:GuzzleHttp\Psr7\Uri:private] => /oauth2/v4/token [query:GuzzleHttp\Psr7\Uri:private] => [fragment:GuzzleHttp\Psr7\Uri:private] => )
[redirectUri:Google\Auth\OAuth2:private] =>
[clientId:Google\Auth\OAuth2:private] => <clientid>
[clientSecret:Google\Auth\OAuth2:private] => <secret>
[username:Google\Auth\OAuth2:private] =>
[password:Google\Auth\OAuth2:private] =>
[scope:Google\Auth\OAuth2:private] =>
[state:Google\Auth\OAuth2:private] =>
[code:Google\Auth\OAuth2:private] =>
[issuer:Google\Auth\OAuth2:private] =>
[audience:Google\Auth\OAuth2:private] =>
[sub:Google\Auth\OAuth2:private] =>
[expiry:Google\Auth\OAuth2:private] => 3600
[signingKey:Google\Auth\OAuth2:private] =>
[signingAlgorithm:Google\Auth\OAuth2:private] =>
[refreshToken:Google\Auth\OAuth2:private] =>
[accessToken:Google\Auth\OAuth2:private] => ya29.CjCKA-No<rest of access token>
[idToken:Google\Auth\OAuth2:private] =>
[expiresIn:Google\Auth\OAuth2:private] => 3600
[expiresAt:Google\Auth\OAuth2:private] =>
[issuedAt:Google\Auth\OAuth2:private] => 1478203637
[grantType:Google\Auth\OAuth2:private] =>
[extensionParams:Google\Auth\OAuth2:private] => Array
(
)
)
)
Interestingly, the access token was not previously in the object as the code executed, but now that I've explicitly called fetchAuthToken I see it in the full session object. Still, no grantType at any point.
This is the code snippet:
$oAuth2Credential = (new OAuth2TokenBuilder())
->withClientId('clientid')
->withClientSecret('secret')
->withRefreshToken($user->refresh_token)
->build();
$oAuth2Credential->fetchAuthToken();
print_r($oAuth2Credential);
hi.
I had similar error on v14.0.0, and fix.
In my case, sent params not correctly, because arg_separator.output
was set &
.
It changed to use http_build_query
on v13.0.0 (see bellow)
@GLStephen I wasn't able to reproduce this issue - did you ever figure out what the problem was?
My apologies, I thought I had posted a "I think it is working" response. I can verify now that it is working. I changed my code to this:
$oAuth2Credential = (new OAuth2TokenBuilder())
->withClientId('stuff')
->withClientSecret('secret_stuff')
->withRefreshToken($this->site->refresh_token)
->build();
$oAuth2Credential->fetchAuthToken();
Oddly enough, without that $oAuth2Credential->fetchAuthToken(); it worked for some length of time and then failed. I could not find the source of the time, but it seemed to be session expiration. With that initial fetch added it works when the session is refreshed later and the failure stopped.
Thanks for following up, glad it's working now!
This is in the experimental branch. I am getting intermittent errors when running long API processes where it seems a token refresh is failing. The error is this:
[GuzzleHttp\Exception\ClientException] Client error:
POST https://www.googleapis.com/oauth2/v4/token
resulted in a400 Bad Request
response: { "error": "invalid_request", "error_description": "Required parameter is missing: grant_type", "error_uri": "" }Exception trace: () at /master/vendor/guzzlehttp/guzzle/src/Exception/RequestException.php:107 GuzzleHttp\Exception\RequestException::create() at /master/vendor/guzzlehttp/guzzle/src/Middleware.php:65 GuzzleHttp\Middleware::GuzzleHttp{closure}() at /master/vendor/guzzlehttp/promises/src/Promise.php:203 GuzzleHttp\Promise\Promise::callHandler() at /master/vendor/guzzlehttp/promises/src/Promise.php:156 GuzzleHttp\Promise\Promise::GuzzleHttp\Promise{closure}() at /master/vendor/guzzlehttp/promises/src/TaskQueue.php:61 GuzzleHttp\Promise\TaskQueue->run() at /master/vendor/guzzlehttp/promises/src/Promise.php:246 GuzzleHttp\Promise\Promise->invokeWaitFn() at /master/vendor/guzzlehttp/promises/src/Promise.php:223 GuzzleHttp\Promise\Promise->waitIfPending() at /master/vendor/guzzlehttp/promises/src/Promise.php:266 GuzzleHttp\Promise\Promise->invokeWaitList() at /master/vendor/guzzlehttp/promises/src/Promise.php:225 GuzzleHttp\Promise\Promise->waitIfPending() at /master/vendor/guzzlehttp/promises/src/Promise.php:62 GuzzleHttp\Promise\Promise->wait() at /master/vendor/guzzlehttp/guzzle/src/Client.php:104 GuzzleHttp\Client->send() at /master/vendor/google/auth/src/HttpHandler/Guzzle6HttpHandler.php:33 Google\Auth\HttpHandler\Guzzle6HttpHandler->invoke() at /master/vendor/google/auth/src/OAuth2.php:431 Google\Auth\OAuth2->fetchAuthToken() at /master/vendor/google/auth/src/Credentials/UserRefreshCredentials.php:89 Google\Auth\Credentials\UserRefreshCredentials->fetchAuthToken() at /master/vendor/googleads/googleads-php-lib/src/Google/AdsApi/Commo n/OAuth2TokenRefresher.php:60 Google\AdsApi\Common\OAuth2TokenRefresher->getOrFetchAccessToken() at /master/vendor/googleads/googleads-php-lib/src/Google/AdsApi/Dfp /DfpHeaderHandler.php:73 Google\AdsApi\Dfp\DfpHeaderHandler->generateHttpHeaders() at /master/vendor/googleads/googleads-php-lib/src/Google/AdsApi/Common/AdsSo apClient.php:72 Google\AdsApi\Common\AdsSoapClient->soapCall() at /master/vendor/googleads/googleads-php-lib/src/Google/AdsApi/Dfp/v201608/LineItemC reativeAssociationService.php:173
The error is inconsistent, but appears similar to that which was fixed in the master branch as issue #162 I tried to patch in changes manually from that fix, but these branches are too dissimilar for it to be reliably followed.
composer.json is:
"require": { "php": ">=5.5.9", "laravel/framework": "5.2.", "kennedytedesco/validation": "~3.0", "google/auth": "^0.7.0", "googleads/googleads-php-lib": "dev-experimental" }, "require-dev": { "fzaninotto/faker": "~1.4", "mockery/mockery": "0.9.", "phpunit/phpunit": "~4.0", "symfony/css-selector": "2.8.|3.0.", "symfony/dom-crawler": "2.8.|3.0." },