0

I am putting together a web app where I would like to display some bigquery data on my webpage

However, I am having trouble setting up the authentication for bigquery's javascript api. Most of the examples and resources, including Google's own docs dont make use of the new Google Identity Services.

So far I have html that looks like:

<html>
  <head>
    <title>Hello, world!</title>
    <link rel="stylesheet" type="text/css" href="style.css">
    <script src="https://apis.google.com/js/api.js"></script>
    <script src="https://accounts.google.com/gsi/client"></script>
  </head>
  <body>
    <button onclick="client.requestAccessToken();">Authorize me</button>
    <script type="text/javascript" src="interaction.js"></script>
  </body>
</html>

and javascript that looks like

//authorize
const client = google.accounts.oauth2.initTokenClient({
    client_id: 'my_id',
    scope: 'https://www.googleapis.com/auth/bigquery',
    callback: (tokenResponse) => {
        if (tokenResponse && tokenResponse.access_token) {
            console.log("token response")
            gapi.client.load('bigquery', 'v2', sendQuery)
        }
    },
});

//query
function sendQuery() {
    let request = gapi.client.bigquery.jobs.query({
        'query': "SELECT h3_id FROM `...` limit 10",
        'timeoutMs': 30000,
        'datasetId': "my_dataset",
        'projectId': "my_project",
        'useLegacySql': false
    });
    request.execute(response => {
        console.log(response)
    });
}

Ive replaced clientid and BQ parameters with placeholders for this stack post.

From what I can tell, the code doesn't run past gapi.client.load('bigquery', 'v2', sendQuery). But strangely I am not getting any errors in developer's console - making this hard to debug. Am I missing an additional step in the auth?

I have loosely been trying to follow this tutorial but adapt it with Google's Identity Services

user3750486
  • 225
  • 2
  • 8

2 Answers2

1

You need to load gapi client with gapi.load('client') before using gapi.client.

I could make your code work by adding:

gapi.load('client');

at the very top of you javascript (before const client = ...)

  • This is the simple solution I was looking for. Can confirm this works. I also posted an alternative approach I made work in the meantime. But I think this is the best solution. – user3750486 Feb 22 '23 at 18:24
0

I was able to accomplish authenticating to bigquery starting from the basic example in the Google Identity migration guide here

My code now looks like:

HTML:

<html>
  <head>
    <title>Hello, world!</title>
    <link rel="stylesheet" type="text/css" href="style.css">
    <script src="https://accounts.google.com/gsi/client" onload="initClient()" async defer></script>
  </head>
  <body>
    <script type="text/javascript" src="interaction.js"></script>
    <h1>Google Identity Services Authorization Token model</h1>
    <button onclick="getToken();">Get access token</button><br><br>
    <button onclick="queryBigQuery();">Query BQ</button><br><br>
    <button onclick="revokeToken();">Revoke token</button>
  </body>
</html>

Javascript:

var client;
var access_token;

function initClient() {
    client = google.accounts.oauth2.initTokenClient({
        client_id: '....',
        scope: 'https://www.googleapis.com/auth/bigquery.readonly \
                https://www.googleapis.com/auth/bigquery \
                https://www.googleapis.com/auth/cloud-platform \
                https://www.googleapis.com/auth/cloud-platform.read-only',
        callback: (tokenResponse) => {
            access_token = tokenResponse.access_token;
        },
    });
}

function getToken() {
    client.requestAccessToken();
}

function revokeToken() {
    google.accounts.oauth2.revoke(access_token, () => {
        console.log('access token revoked')
    });
}

// make post request to bigquery api
function queryBigQuery() {
    var xhr = new XMLHttpRequest();
    xhr.open('POST', `https://bigquery.googleapis.com/bigquery/v2/projects/${project_id}/queries`);
    xhr.setRequestHeader('Authorization', 'Bearer ' + access_token);
    xhr.setRequestHeader("Content-Type", "application/json");
    const requestBody = {
        query: "...",
        useLegacySql: false
    };

    //results 
    xhr.onreadystatechange = function() {
        if (this.readyState == 4 && this.status == 200) {
            // Typical action to be performed when the document is ready:
            console.log(xhr.responseText)
        }
    };
    xhr.send(JSON.stringify(requestBody));
}

The difference from the original post is that the access token generated in callback is passed to the global access_token variable and then used in the header of a standard XMLHttpRequest. Still not sure how to make this work with gapi..

General Grievance
  • 4,555
  • 31
  • 31
  • 45
user3750486
  • 225
  • 2
  • 8