Aurelia Google SignIn Button

I just started using Aurelia and I am having a problem with Google Sign in. It looks like I might be able to create my own Google button but I'd rather get it to work this way if it is possible. Here is my code:

<script src="https://apis.google.com/js/platform.js" async defer></script>
...
<body aurelia-app="src/main">
...
<span id="googleButtonPlaceholder" class="g-signin2" data-onsuccess="onSignIn"></span>

I have the function setup in my Aurelia class but I do not know if/how I can call it. I have tried ${onSignIn()} which just calls the function when it loads, ${onSignIn}, onSignIn(), onSignIn, data-onsuccess.bind="onSignin()" but nothing seems to work. Is there a way to pass the Aurelia function to the Google data-onsuccess attribute?

As a note, I am switching from Angular 1.5.8 where this previously worked.

Answers:

Answer

Here's an example: https://gist.run?id=5da90f48b43b9c5867c8d2ace0f6371f

app.html

<template>
  <require from="google-signin-button"></require>

  <google-signin-button success.call="signinSuccess(googleUser)"
                        error.call="signinError(error)">
  </google-signin-button>

  <h1>${message}</h1>
</template>

app.js

export class App {
  message = 'Not signed in.';

  signinSuccess(googleUser) {
    const name = googleUser.getBasicProfile().getName();
    this.message = `Signed in: ${name}`;
  }

  signinError(error) {
    this.message = `Error: ${error}`;
  }
}

google-signin-button.js

import {inject, noView, bindable} from 'aurelia-framework';

const googleSigninClientID = '927519533400-mfupo3lq9cjd67fmmvtth7lg7d8l50q9.apps.googleusercontent.com';

function preparePlatform() {
  // https://developers.google.com/identity/sign-in/web/build-button

  // The name of the global function the platform API will call when
  // it's ready.
  const platformCallbackName = 'setGooglePlatformReady';

  // An "API ready" promise that will be resolved when the platform API 
  // is ready.
  const ready = new Promise(  
    resolve => window[platformCallbackName] = resolve);

  // Inject the client id meta tag
  const meta = document.createElement('meta');
  meta.name = 'google-signin-client_id';
  meta.content = googleSigninClientID;
  document.head.appendChild(meta);

  // Inject an async script element to load the google platform API.
  // Notice the callback name is passed as an argument on the query string.
  const script = document.createElement('script');  
  script.src = `https://apis.google.com/js/platform.js?onload=${platformCallbackName}`;  
  script.async = true;  
  script.defer = true;  
  document.head.appendChild(script);

  return ready;
}

const platformReady = preparePlatform();

@noView()
@inject(Element)
export class GoogleSigninButton {  
  @bindable success = googleUser => { };
  @bindable error = error => { };
  @bindable scope = 'profile email';
  @bindable theme = 'dark';
  @bindable width = 240;
  @bindable height = 50;

  constructor(element) {
    this.element = element;
  }

  attached() {
    platformReady.then(this.renderButton);
  }

  renderButton = () => {
    gapi.signin2.render(this.element, {
      scope: this.scope,
      width: this.width,
      height: this.height,
      longtitle: true,
      theme: this.theme,
      onsuccess: googleUser => {
        console.info(googleUser);
        this.success({ googleUser });
      },
      onfailure: error => {
        console.error(error);
        this.failure({ error });
      }
    });
  }
}
Answer

@JeremyDanyow had a great answer but after I went to bed and read a little more about Aurelia, I thought of a solution to try before seeing his answer so I thought I'd share an alternate approach for those interested.

index.html

<main aurelia-app="src/main">
</main>

<script src="https://apis.google.com/js/platform.js" async defer></script>

app.html

<template>
    <span id="my-signin2"></span>
    <!-- other stuff -->
</template>

app.js

attached() {
   this.render();
}

render() {
   gapi.signin2.render('my-signin2', {
      'scope': 'profile email',
      'theme': 'dark',
      'onsuccess': this.onSuccess,
      'onfailure': this.onFailure
   });
}

onSuccess(googleuser) {
  let gUser = googleuser.getBasicProfile(),
      id_token = googleuser.getAuthResponse().id_token;
}

onFailure(error) {
   console.log(error);
}

This approach differs slightly from what Google shows on their website where they have you give platform.js an onload function to render the button. Instead, I create the button in the template and then once the template is done being loaded, attached() is called, which in turn, calls the function I would have had platform.js call onload.

Answer

Try data-onsuccess.call="onSignIn()".

Answer

After following @JeremyDanyow's example around a few corners I came up with this

It works ok for simple usage, but needs help...

  1. when there is another window open using a google login there is an error loading something in the iframe google adds (this doesn't seem to break it)

  2. the listeners don't work for more than a couple of login/logouts at most

Here's hoping that someone else can improve upon this.

google-signin-button.js

import { inject, noView, bindable } from 'aurelia-framework';

import { LogManager } from 'aurelia-framework';
const Console = LogManager.getLogger('google-signin-button');

// Integrating Google Sign-In into your web app
// https://developers.google.com/identity/sign-in/web/reference
// https://console.developers.google.com/apis/credentials

// inspiration: https://developers.google.com/identity/sign-in/web/build-button
function preparePlatform(): Promise<Function> {
  // Inject an async script element to load the google platform API.
  const script = document.createElement('script');
  script.src = `https://apis.google.com/js/platform.js?onload=gapi_ready`;
  script.async = true;
  script.defer = true;
  document.head.appendChild(script);
  // return a promise that will resolve with the onload callback
  return new Promise(resolve => window['gapi_ready'] = resolve);
}

@noView
@inject(Element)
export class GoogleSigninButton {

  @bindable authenticated = (signedIn: Boolean) => { };
  @bindable authorized = (GoogleUser: any) => { };

  @bindable scope = 'profile email';
  @bindable clientId = 'none';

  @bindable theme = 'dark';
  @bindable width = 240;
  @bindable height = 50;

  public element: Element;

  constructor(element) {
    this.element = element;
  }

  public wasAuthenticated: Boolean;
  sendAuthenticated(signedIn: Boolean) {
    if (signedIn !== this.wasAuthenticated) {
      this.authenticated(signedIn);
      this.wasAuthenticated = signedIn;
    }
  }

  public wasAuthorized: any;
  sendAuthorized(googleUser: any) {
    if (googleUser !== this.wasAuthorized) {
      this.authorized(googleUser);
      this.wasAuthorized = googleUser;
      this.sendAuthenticated(true);
    }
  }

  attached() {
    // inject the script tag
    preparePlatform()
      .then(() => {
        // load the auth lib
        // Console.debug('gapi created, loading auth2');
        window['gapi'].load('auth2', () => {
          // init the auth lib
          // Console.debug('gapi.auth2 loaded, intializing with clientId:', this.clientId);
          window['gapi'].auth2.init({
            client_id: this.clientId
          })
            .then(
              (googleAuth: any) => {
                // Console.debug('gapi.auth2 intialized');
                // listen for user signed in/out
                googleAuth.isSignedIn.listen((signedIn: Boolean) => {
                  // Console.debug('googleAuth.isSignedIn.listener', signedIn);
                  this.sendAuthenticated(signedIn);
                });
                // listen for who signed in
                googleAuth.currentUser.listen((googleUser: any) => {
                  // Console.debug('googleAuth.currentUser.listener', googleUser);
                  this.sendAuthorized(googleUser);
                });
                // draw the button
                window['gapi'].signin2.render(this.element, {
                  scope: this.scope,
                  width: this.width,
                  height: this.height,
                  longtitle: true,
                  theme: this.theme,
                  onsuccess: (googleUser: any) => {
                    // Console.debug('gapi.signin2.render success', googleUser);
                    this.sendAuthorized(googleUser);
                  },
                  // drawing button failure
                  onfailure: (error: any) => {
                    Console.error('gapi.signin2.render failure', error);
                  }
                });
              },
              // intialization error
              (errObj: any) => {
                Console.error('gapi.auth2.init -> errObj', errObj);
              }
            );
        });
      });
  }
}

some-usage.js

import environment from '../environment';

import { LogManager } from 'aurelia-framework';
const Console = LogManager.getLogger('Login');

import { inject } from 'aurelia-framework';
import { AuthService } from 'aurelia-authentication';
import { EventAggregator } from 'aurelia-event-aggregator';

import './login.scss';

@inject(AuthService, EventAggregator)
export class Login {

  public authService: AuthService;
  public eventAggregator: EventAggregator;

  public googleSigninClientID: string = 'none';

  constructor(authService: AuthService, eventAggregator: EventAggregator) {
    this.eventAggregator = eventAggregator;
    this.authService = authService;

    this.googleSigninClientID = environment.googleSigninClientID;
  };

  isAuthenticated(signedIn: Boolean) {
    Console.warn('isAuthenticated', signedIn);
  }

  isAuthorized(googleUser: any) {
    Console.warn('isAuthorized', googleUser);
  }

}

some-usage.html

<template>
  <require from="../resources/elements/google-signin-button"></require>
  <section>
    <div class="container-fluid">
      <center>
        <google-signin-button client-id.bind="googleSigninClientID" authenticated.bind="isAuthenticated" authorized.bind="isAuthorized"> </google-signin-button>
      </center>
    </div>
  </section>
</template>

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.