google-apipassport-google-oauth2

Google: gapi authentication with oauth2 deprecation


I want to authenticate gapi client library so I could create a spreadsheet on behalf of another user, i.e. oauth.

This code used to work:

gapi.load('client:auth2', function() {
gapi.auth2.init({
    clientId: 'CLIENT_ID',
      scope: 'https://www.googleapis.com/auth/spreadsheets',
    }).then(function() {
      return gapi.auth2.getAuthInstance().signIn();
    }).then(function() {
      console.log('Signed in!');
      
    }, function(error) {
      console.error(error);
    });
  });

Now however it is saying that gapi.auth2 is deprecated, so I try to do following

google.accounts.id.initialize({
    client_id: 'CLIENT_ID',
    callback: handleCredentialResponse
  });
  google.accounts.id.prompt();

  function handleCredentialResponse(response) {
     gapi.client.setToken(response.credential)
  }

And then

      function createSpreadsheet() {


        gapi.client.sheets.spreadsheets
          .create({
            properties: {
              title: "My New Spreadsheet",
            },
          })
          .then((response) => {
            // The spreadsheet is created successfully
            console.log("Spreadsheet created:", response.result.spreadsheetUrl);
          })
          .catch((err) => {
            console.error("Failed to create spreadsheet", err);
          });
      }

And this gives me "Missing credential error"

I've also tried setting credential like this: gapi.auth.setToken({ access_token: token }) Same error.

What am I missing?


Solution

  • After some time I've figured it out and was able to authenticate gapi to use spreadsheets API.

    I was confused by ID token and Authorization token, those are 2 different things. Without further due, here's the working example:

    var token;
    
    function initGoogle() {
        // init client
        const client = google.accounts.oauth2.initTokenClient({
            client_id: "YOUR_CLIENT_ID",
            callback: handleCredentialResponse,
    
            scope: "https://www.googleapis.com/auth/spreadsheets",
        });
         
        // send authorize request
        // this prompts user to grant access
        client.requestAccessToken();
    }
    
    
    // this thing is needed to load spreadsheet client library JSON dicovery document
    async function fetchDiscoveryDoc() {
        const resp = await fetch(
            "https://sheets.googleapis.com/$discovery/rest?version=v4"
        );
        return resp.json();
    }
    
    
    async function handleCredentialResponse(response) {
    
        console.log("loading client library");
        await new Promise((resolve) => {
            gapi.load("client", () => {
                resolve();
            });
        });
    
        const discoDoc = await fetchDiscoveryDoc();
    
        console.log("Loading spreadsheets");
        
        // loading spreadsheet library using discovery document
        await new Promise((resolve, reject) => {
            gapi.client.load(discoDoc).then(() => {
                console.log("Spreadsheets loaded");
                resolve();
            });
        });
    
        console.log("Setting authorization token");
        gapi.client.setToken(response);
    
        console.log("All set. Client is ready to make authorized API calls. Creating spreadsheet");
    
        await gapi.client.sheets.spreadsheets.create({
            properties: {
                title: "My New Spreadsheet",
            },
        });
    }
    

    Note, that this is implicit authorization flow, that is less secure, but does not involve backend. Read more here: https://developers.google.com/identity/oauth2/web/guides/use-token-model