Skip to content

grant_type Paramater is Missing - 400 Bad Request #177

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
GLStephen opened this issue Oct 17, 2016 · 13 comments
Closed

grant_type Paramater is Missing - 400 Bad Request #177

GLStephen opened this issue Oct 17, 2016 · 13 comments
Assignees

Comments

@GLStephen
Copy link

GLStephen commented Oct 17, 2016

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 a 400 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."
},

@chiragvels
Copy link

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,

@vtsao vtsao self-assigned this Oct 18, 2016
@vtsao vtsao added the beta label Oct 20, 2016
@vtsao
Copy link
Contributor

vtsao commented Oct 20, 2016

@chiragvels replied to your comment in #5 as @GLStephen's issue is for the experimental branch.

@vtsao
Copy link
Contributor

vtsao commented Oct 20, 2016

@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?

@GLStephen
Copy link
Author

GLStephen commented Nov 3, 2016

@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
}

@GLStephen
Copy link
Author

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": ""
}

@vtsao
Copy link
Contributor

vtsao commented Nov 3, 2016

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?

@GLStephen
Copy link
Author

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
                    (
                    )

            )

    )

@vtsao
Copy link
Contributor

vtsao commented Nov 3, 2016

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.

@GLStephen
Copy link
Author

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

@shoota
Copy link

shoota commented Nov 15, 2016

hi.
I had similar error on v14.0.0, and fix.
In my case, sent params not correctly, because arg_separator.output was set &amp;.
It changed to use http_build_query on v13.0.0 (see bellow)

8a1b83f#diff-56c31de18b46725ac7194a2894c13857L109

@vtsao
Copy link
Contributor

vtsao commented Dec 22, 2016

@GLStephen I wasn't able to reproduce this issue - did you ever figure out what the problem was?

@vtsao vtsao removed the beta label Dec 22, 2016
@GLStephen
Copy link
Author

GLStephen commented Dec 22, 2016

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.

@vtsao
Copy link
Contributor

vtsao commented Dec 22, 2016

Thanks for following up, glad it's working now!

@vtsao vtsao closed this as completed Dec 22, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants