'Using Google One Tap in Angular

I'd like to use Google One Tap in my Angular 11 app. Following the documentation I added <script async defer src="https://accounts.google.com/gsi/client"></script> to my html and then used the following code in my app.component.html:

<div id="g_id_onload"
    data-client_id="MY_GOOGLE_CLIENT_ID"
    data-callback="handleCredentialResponse",
    data-cancel_on_tap_outside="false">
</div>

The popup works fine, though I can't seem to log in. If I create a function handleCredentialResponse in app.component.ts, I get the following error: [GSI_LOGGER]: The value of 'callback' is not a function. Configuration ignored.

If I instead try to use the JavaScript API, Typescript throws the following error: Property 'accounts' does not exist on type 'typeof google'

What should I do to be able to using Google One Tap in Angular?



Solution 1:[1]

I had a similar problem when I used the HTML API approach, so I ended up using the JavaScript API instead.

Here's what I did:

First, make sure to install the @types/google-one-tap package.

As you mentioned, I'm also importing the script in my index.html file, like so:

<body>
  <script src="https://accounts.google.com/gsi/client" async defer></script>
  <app-root></app-root>
</body>

Now, moving on to your main component which in my case is app.component.ts, import the following first:

import { CredentialResponse, PromptMomentNotification } from 'google-one-tap';

Then, you can add this on the ngOnInit(). Make sure to read the documentation to get more details on the onGoogleLibraryLoad event:

// @ts-ignore
window.onGoogleLibraryLoad = () => {
  console.log('Google\'s One-tap sign in script loaded!');

  // @ts-ignore
  google.accounts.id.initialize({
    // Ref: https://developers.google.com/identity/gsi/web/reference/js-reference#IdConfiguration
    client_id: 'XXXXXXXX',
    callback: this.handleCredentialResponse.bind(this), // Whatever function you want to trigger...
    auto_select: true,
    cancel_on_tap_outside: false
  });

  // OPTIONAL: In my case I want to redirect the user to an specific path.
  // @ts-ignore
  google.accounts.id.prompt((notification: PromptMomentNotification) => {
    console.log('Google prompt event triggered...');

    if (notification.getDismissedReason() === 'credential_returned') {
      this.ngZone.run(() => {
        this.router.navigate(['myapp/somewhere'], { replaceUrl: true });
        console.log('Welcome back!');
      });
    }
  });
};

Then, the handleCredentialResponse function is where you handle the actual response with the user's credential. In my case, I wanted to decode it first. Check this out to get more details on how the credential looks once it has been decoded: https://developers.google.com/identity/gsi/web/reference/js-reference#credential

handleCredentialResponse(response: CredentialResponse) {
// Decoding  JWT token...
  let decodedToken: any | null = null;
  try {
    decodedToken = JSON.parse(atob(response?.credential.split('.')[1]));
  } catch (e) {
    console.error('Error while trying to decode token', e);
  }
  console.log('decodedToken', decodedToken);
}

Solution 2:[2]

Google One Tap js library tries to find callback in the global scope and can't find it, because your callback function is scoped somewhere inside of your app, so you can attach your callback to window, like window.callback = function(data) {...}. Also, since you are attaching it to window, it's better to give the function a less generic name.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 who_khiers