google / google-api-javascript-client

Google APIs Client Library for browser JavaScript, aka gapi.
Apache License 2.0
3.18k stars 1.05k forks source link

gapi calls APIs with wrong token that leads to errors #729

Open Ark-kun opened 3 years ago

Ark-kun commented 3 years ago

high-level scenario: I want to call Kubernetes APIs: list clusters in user's project.

I can do that successfully using the API explorer. However when I do it myself in Javascript using gapi, I receive error 403. I've verified that the URL that gapi calls is correct, but the auth token causes the issue. For some reason the server complains about the API not being available in a different project (the project where Client ID was created).

I've verified the behavior using the command line:

The following command works correctly

curl -H "authorization: Bearer $(gcloud auth print-access-token)" https://content-container.googleapis.com/v1/projects/target-project/locations/-/clusters

However when I replace the token with the one obtained by gapi, I get the same error as before (notice that the URL is the same - only token is different)

curl -H "authorization: Bearer <gapi-token>" https://content-container.googleapis.com/v1/projects/target-project/locations/-/clusters

I've also tried the API explorer: https://cloud.google.com/kubernetes-engine/docs/reference/rest/v1/projects.locations.clusters/list It was able to call the API successfully. It accessed the same URL, just with a different token and got results successfully.

To sum it up:

What do I need to change in my code to make it work (to make it generate/use correct access token)?

My code:

        var apiKey = 'XXX';

        var discoveryDocs = [
            "https://people.googleapis.com/$discovery/rest?version=v1",
            "https://container.googleapis.com/$discovery/rest?version=v1",
        ];

        var clientId = 'YYY';
        var scopes = 'profile cloud-platform';

        function onGoogleSignInSuccess(googleUser) {
            console.log('Logged in as: ' + googleUser.getBasicProfile().getName());
            loadAndInitGoogelApiClient();
        }
        function onGoogleSignInFailure(error) {
            console.log(error);
        }
        function renderGoogleSignInButton() {
            gapi.signin2.render('google-signin-button', {
                // 'scope': 'profile email',
                'scope': 'profile email https://www.googleapis.com/auth/cloud-platform',
                'onsuccess': onGoogleSignInSuccess,
                'onfailure': onGoogleSignInFailure
            });
        }
        function loadAndInitGoogelApiClient() {
            gapi.load('client:auth2', initGoogelApiClient);
        }
        function initGoogelApiClient() {
            gapi.client.init({
                // Do not specify the API key. Otherwise the container API discovery fails with error 403
                // apiKey: apiKey,
                discoveryDocs: discoveryDocs,
                clientId: clientId,
                scope: scopes
            }).then(function () {
                callKubernetesApi();
            });
        }
        function callKubernetesApi() {
            console.log(gapi.client);
            gapi.client.container.projects.locations.clusters.list({
                'parent': 'projects/target-project/locations/-',
            }).then(function (resp) {
                var p = document.createElement('p');
                var name = resp.result.clusters[0].name;
                p.appendChild(document.createTextNode('Cluster: ' + name));
                document.getElementById('kubernetes-api-content').appendChild(p);
            });
        }

Error (403):

{
  "error": {
    "code": 403,
    "message": "This API method requires billing to be enabled. Please enable billing on project #<OTHER  PROJECT> by visiting https://console.developers.google.com/billing/enable?project=<OTHER  PROJECT> then retry. If you enabled billing for this project recently, wait a few minutes for the action to propagate to our systems and retry.",
    "status": "PERMISSION_DENIED",
    "details": [
      {
        "@type": "type.googleapis.com/google.rpc.Help",
        "links": [
          {
            "description": "Google developers console billing",
            "url": "https://console.developers.google.com/billing/enable?project=<OTHER  PROJECT>"
          }
        ]
      },
      {
        "@type": "type.googleapis.com/google.rpc.ErrorInfo",
        "reason": "BILLING_DISABLED",
        "domain": "googleapis.com",
        "metadata": {
          "service": "container.googleapis.com",
          "consumer": "projects/<OTHER  PROJECT>"
        }
      }
    ]
  }
}

As you see, the error complains about the <OTHER PROJECT>, not target-project (where the billing is enabled).

sergentj commented 3 years ago

Hi, I don't think this is an issue specific to the JS client. Did you try contacting GKE support for help with this issue?

Ark-kun commented 3 years ago

I don't think this is an issue specific to the JS client.

Are you sure? The difference is in the token that gapi generates.

I suspect that the token generated by gapi is somehow tied to the project which contains clientId and API_KEY instead of a token associated with the authenticated user.

What do you think about my suspicion?

Is there a way to make gapi generate a token for the authenticated user? Similar to gcloud auth print-access-token