Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use the Google API client for JavaScript with a One-Tap sign in flow?

I'm using Google One-Tap sign in to authenticate users, and after the user is authenticated I get an access token. I know that I can use this access token in order to work with the Google API client for JavaScript ("GAPI"). But I can't find any way to work with GAPI using this access token.

Is there any way to use GAPI assuming I already have an logged in user?

What I'm trying to do is access the user calendar after simply authenticating with One-Tap authentication and giving consent for the calendar once.

like image 263
mesqueeb Avatar asked Feb 23 '18 17:02

mesqueeb


1 Answers

First of all:
There is no way to "authenticate" the google JS api client with the response that is returned by the One-Tap sign in.

Luckily we don't need to authenticate with the gapi JS client because we use a handy function called gapi.auth2.authorize!

How to authorize the gapi client

It's important to first read the docs and understand their warning:

Do not use this method alongside the recommended gapi.auth2.init and signIn flow. These are two distinct behaviors (Authorization for gapi.auth2.authorize vs Authentication for gapi.auth2.init/signIn) and will have unexpected issues if used within the same application.

Now the question is how to completely avoid the init/signIn method.

Step 1
Sign the user into the Google One-Tap sign in.

Step 2
Load the gapi client: window.gapi.load('client')

Step 3
Pass the credential.id (the email address) returned by Google One-Tap as the login_hint param in the call to authorize. This will preselect the account and we can try to not show any new login pop-up (prompt).

Example:

gapi.auth2.authorize({
  client_id,
  prompt: 'none',
  response_type: 'permission', // Access Token.
  scope: 'CALENDAR_SCOPE',
  login_hint: credential.id
}, function(result) {})

Using prompt: 'none', you can try to fetch a token without any UI. This is useful to check whether you need to show an Authorize button, and also useful before making any call to the Calendar API to get a fresh token.

Step 4
Before we can make any call to gapi.client.calendar we need to initialize the client with just the calendar discoveryDocs.

gapi.client.init({discoveryDocs})

This is the most tricky part and is not properly documented anywhere! The only thing we can retrieve from the api.client.init() documentation is the following line:

If OAuth client ID and scope are provided, this function will load the gapi.auth2 module to perform OAuth

This basically means: as soon as you give either clientID or scope gapi.client.init will try and start the traditional authentication method.
In our case: we don't need to pass the clientID or scope as we've already done this in step 3.

So how does the client know which module to initialize? → By only passing the discoveryDocs of the module you want to use! In this case the calendar discoveryDocs.

Step 5
Now you're done! You can make requests with e.g. gapi.client.calendar.events.list()


Full example

A full code example can be found here below:

const config =  {
  response_type: 'permission',
  scope: 'CALENDAR_SCOPE',
  client_id: clientId,
  login_hint: credential.id,
  promt: 'none',
}
gapi.auth2.authorize(config, function(response) {
  // No need to `setToken`, it's done for you by auth2.
  let calConfig = {discoveryDocs} // only of google calendar
  window.gapi.client.init(calConfig).then(function() {
    // then execute a calendar call:
    window.gapi.client.calendar.events.list({'calendarId': 'primary'})
  })
})
like image 73
mesqueeb Avatar answered Nov 11 '22 23:11

mesqueeb