googleads / google-ads-php

Google Ads API Client Library for PHP
https://developers.google.com/google-ads/api/docs/client-libs/php
Apache License 2.0
277 stars 260 forks source link

OAUTH_TOKEN_HEADER_INVALID with Web client or Service account despite fetchAuthToken() success #1000

Closed stephenr85 closed 2 months ago

stephenr85 commented 4 months ago

I posted on stack overflow, but this may be a bug, considering I can get an authentication token directly, but the call fails.

I'm on php 8.2.13 Using V16 of Google Ads API.

Your client library and Google Ads API versions:

Your environment: PHP: 8.2.13 Laravel Sail/Docker env: Darwin Kernel Version 22.5.0: Mon Apr 24 20:53:19 PDT 2023; root:xnu-8796.121.2~5/RELEASE_ARM64_T6020 arm64 Transport: rest (grpc not installed)

Description of the bug: I've set up an OAuth2 web project as well as a Service Account. Both IDs have been added with domain-wide delegation with the adwords scope in my Google Workspace.

I have a Google Ads Manager account with a Developer Key (it's in test mode, if that matters?). The email I'm trying to authenticate with for either method is an Admin in the domain and on the relevant Ads Manager and Ads accounts.

No matter what I do, I get the following error.

{
    "message": "Request is missing required authentication credential. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https:\/\/developers.google.com\/identity\/sign-in\/web\/devconsole-project.",
    "code": 16,
    "status": "UNAUTHENTICATED",
    "details": [
        {
            "@type": "type.googleapis.com\/google.ads.googleads.v16.errors.GoogleAdsFailure",
            "errors": [
                {
                    "errorCode": {
                        "authenticationError": "OAUTH_TOKEN_HEADER_INVALID"
                    },
                    "message": "Oauth token HTTP header is malformed."
                }
            ],
            "requestId": "9QCN5RO9mRkmS1ULtDAKMA"
        }
    ]
}

If I try to use the service account email as the impersonateEmail parameter, I get a NOT_ADS_USER. I've invited it to the Ads Manager account, but am not sure how to accept that invite. I think it would result in the same OAUTH_TOKEN_HEADER_INVALID, anyways.

This is within a Laravel project.

$oauth2 = (new OAuth2TokenBuilder())
                ->withClientId(config('google.client_id'))
                ->withClientSecret(config('google.client_secret'))
                ->withRefreshToken(config('googleads.refresh_token'))
                ->build();
$oauth2 = (new OAuth2TokenBuilder())
                ->withJsonKeyFilePath(realpath(config('google.service.file')))
                ->withScopes('https://www.googleapis.com/auth/adwords')
                ->withImpersonatedEmail(config('googleads.impersonated_email'))
                ->build();

What's strange is that, using either OAuth2Token instance, I can run fetchAuthToken(), and I see that the object gets its access_token. I can also see in Google\ApiCore\CredentialsWrapper::getAuthorizationHeaderCallback that it is added as the authorization Bearer token.

Here's how my GoogleAdsClient is built:

$this->client = (new GoogleAdsClientBuilder())
                ->withOAuth2Credential($oauth2)
                ->withDeveloperToken(config('google.developer_key'))
                ->usingGapicV2Source(true)
                ->build();

And here's the request that fails with the OAUTH_TOKEN_HEADER_INVALID:

$requestArgs = [
            // Set the language resource using the provided language ID.
            'language' => $this->getLanguageConstant(),
            'customer_id' => $this->getCustomerId(),
            // Add the resource name of each location ID to the request. - currently an empty array
            'geo_target_constants' => $this->getGeoTargetConstants(),
            // Set the network. To restrict to only Google Search, change the parameter below to
            'keyword_plan_network' => KeywordPlanNetwork::GOOGLE_SEARCH,
        ] + $requestOptionalArgs;

        $response = $ideaClient->generateKeywordIdeas(
            new GenerateKeywordIdeasRequest($requestArgs)
        );

I've been going in circles on this for over a day. Thank you in advance!

fiboknacky commented 3 months ago

What version of google/gax do you use? And do you have a full log (with sensitive information redacted) that shows how your OAuth header looks like?

fiboknacky commented 2 months ago

Closing due to inactivity.