41
votes

I have been trying to implement sign in with Google in angular 2 in a separate login component. I am unable to implement it with the documentation available in Google https://developers.google.com/identity/sign-in/web/sign-in

Google sign in does work when I declare my script tags and google callback function inside my index.html file. But I require a separate component to be able to render the sign in with google button and receive the callback in it to further process the access token which is received for a user

7

7 Answers

74
votes

Add this line in your app index.html file

INDEX.html

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

Component.ts file

declare const gapi: any;
  public auth2: any;
  public googleInit() {
    gapi.load('auth2', () => {
      this.auth2 = gapi.auth2.init({
        client_id: 'YOUR_CLIENT_ID.apps.googleusercontent.com',
        cookiepolicy: 'single_host_origin',
        scope: 'profile email'
      });
      this.attachSignin(document.getElementById('googleBtn'));
    });
  }
  public attachSignin(element) {
    this.auth2.attachClickHandler(element, {},
      (googleUser) => {

        let profile = googleUser.getBasicProfile();
        console.log('Token || ' + googleUser.getAuthResponse().id_token);
        console.log('ID: ' + profile.getId());
        console.log('Name: ' + profile.getName());
        console.log('Image URL: ' + profile.getImageUrl());
        console.log('Email: ' + profile.getEmail());
        //YOUR CODE HERE


      }, (error) => {
        alert(JSON.stringify(error, undefined, 2));
      });
  }

ngAfterViewInit(){
      this.googleInit();
}

Template html file

<button id="googleBtn">Google</button>

View the demo on Plunker

19
votes

src/index.html

In the index.html file of your app you need to add this in the <head> section:

<meta name="google-signin-scope" content="profile email">
<meta name="google-signin-client_id" content="YOUR_CLIENT_ID.apps.googleusercontent.com">
<script src="https://apis.google.com/js/platform.js" async defer></script>

typings/browser/ambient/gapi/

You need to add gapi & gapi.auth2 to your typings:

npm install --save @types/gapi.auth2
npm install --save @types/gapi

(see this borysn's question to understand this a little better).

src/app/+login/login.component.ts

This is the file of my component, here you need to use the ngAfterViewInit() to use the gapi an get the auth. And you can follow the implementation here developers.google...sign-in/web/build-button

As an example, this is my template:

<div id="my-signin2"></div>

and sign in function:

ngAfterViewInit() {
    gapi.signin2.render('my-signin2', {
        'scope': 'profile email',
        'width': 240,
        'height': 50,
        'longtitle': true,
        'theme': 'light',
        'onsuccess': param => this.onSignIn(param)
    });
}

public onSignIn(googleUser) {
    var user : User = new User();

    ((u, p) => {
        u.id            = p.getId();
        u.name          = p.getName();
        u.email         = p.getEmail();
        u.imageUrl      = p.getImageUrl();
        u.givenName     = p.getGivenName();
        u.familyName    = p.getFamilyName();
    })(user, googleUser.getBasicProfile());

    ((u, r) => {
        u.token         = r.id_token;
    })(user, googleUser.getAuthResponse());

    user.save();
    this.goHome();
};

UPDATE: After some time, and taking in consideration the comments, this answer needed a small update.

15
votes

Lexical scoping with an arrow (=>) function makes the use of let that = this; unnecessary.

A cleaner version of Pravesh's example, without the need for the that scoping work-around, would be:

Index.html

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

Component.ts

declare const gapi: any;

@Component({
  selector: 'google-signin',
  template: '<button id="googleBtn">Google Sign-In</button>'
})
export class GoogleSigninComponent implements AfterViewInit {

  private clientId:string = 'YOUR_CLIENT_ID.apps.googleusercontent.com';

  private scope = [
    'profile',
    'email',
    'https://www.googleapis.com/auth/plus.me',
    'https://www.googleapis.com/auth/contacts.readonly',
    'https://www.googleapis.com/auth/admin.directory.user.readonly'
  ].join(' ');

  public auth2: any;

  public googleInit() {        
    gapi.load('auth2', () => {
      this.auth2 = gapi.auth2.init({
        client_id: this.clientId,
        cookiepolicy: 'single_host_origin',
        scope: this.scope
      });
      this.attachSignin(this.element.nativeElement.firstChild);
    });
  }

  public attachSignin(element) {
    this.auth2.attachClickHandler(element, {},
      (googleUser) => {
        let profile = googleUser.getBasicProfile();
        console.log('Token || ' + googleUser.getAuthResponse().id_token);
        console.log('ID: ' + profile.getId());
        // ...
      }, function (error) {
        console.log(JSON.stringify(error, undefined, 2));
      });
  }

  constructor(private element: ElementRef) {
    console.log('ElementRef: ', this.element);
  }

  ngAfterViewInit() {
    this.googleInit();
  }
}

Template

<div id="googleBtn">Google</div>

Working Plnkr

8
votes

As of now, the angular latest version came and mostly we are using angular 4/5/6, so thought to give this simple solution to login by social so someone who really want it

Angular 4/5/6 Social Login

In your AppModule, import the SocialLoginModule

import { SocialLoginModule, AuthServiceConfig } from "angularx-social-login";
import { GoogleLoginProvider, FacebookLoginProvider, LinkedInLoginProvider} from "angularx-social-login";


let config = new AuthServiceConfig([
  {
    id: GoogleLoginProvider.PROVIDER_ID,
    provider: new GoogleLoginProvider("Google-OAuth-Client-Id")
  },
  {
    id: FacebookLoginProvider.PROVIDER_ID,
    provider: new FacebookLoginProvider("Facebook-App-Id")
  },
  {
    id: LinkedInLoginProvider.PROVIDER_ID,
    provider: new FacebookLoginProvider("LinkedIn-client-Id", false, 'en_US')
  }
]);

export function provideConfig() {
  return config;
}

@NgModule({
  declarations: [
    ...
  ],
  imports: [
    ...
    SocialLoginModule
  ],
  providers: [
    {
      provide: AuthServiceConfig,
      useFactory: provideConfig
    }
  ],
  bootstrap: [...]
})
export class AppModule { }

And use it in your components

by importing below modules

import { AuthService } from "angularx-social-login";
import { SocialUser } from "angularx-social-login";

For complete reference you can look their Github

it has really simple page for demo

7
votes

There is also another way to connect with google :

Add theses lines in the index.html :

<meta name="google-signin-client_id" content="YOUR-GOOGLE-APP-ID.apps.googleusercontent.com">
<script src="https://apis.google.com/js/platform.js"></script>

and then here is a sample code to write on a component (or a service if you want) :

import {Component} from "@angular/core";
declare const gapi : any;


@Component({ ... })
export class ComponentClass {
   constructor() {
      gapi.load('auth2', function () {
         gapi.auth2.init()
      });
   }

   googleLogin() {
      let googleAuth = gapi.auth2.getAuthInstance();
      googleAuth.then(() => {
         googleAuth.signIn({scope: 'profile email'}).then(googleUser => {
            console.log(googleUser.getBasicProfile());
         });
      });
   }
}
7
votes

pretty much none of this worked for me because I want the google button made by Google. @mathhew eon's code worked but that does not use their button.

so I threw the google data-success function on the window and it works PERFECT! It also has the added advantage of if the user is already logged in it will auto call the googleLogin function.

html

<div class="g-signin2" data-onsuccess="googleLogin" data-theme="dark"></div>

In your index.html put this in the head:

<meta name="google-signin-client_id" content="YOUR-GOOGLE-APP-ID.apps.googleusercontent.com">
<meta name="google-signin-scope" content="profile email AND WHATEVER OTHER SCOPES YOU WANT">
<script src="https://apis.google.com/js/platform.js" async defer></script>

then in your ngOnInit

ngOnInit() {
    (window as any).googleLogin = this.googleLogin
}
public googleLogin(userInfo) {
    console.log(userInfo)
}
2
votes

Everything is the same in previous answered except I added

declare var gapi: any; otherwise, you will get the error.

src/index.html

In the index.html file of your app you need to add this in the section:

<meta name="google-signin-scope" content="profile email">
<meta name="google-signin-client_id" content="YOUR_CLIENT_ID.apps.googleusercontent.com">
<script src="https://apis.google.com/js/platform.js" async defer></script>

typings/browser/ambient/gapi/

You need to add gapi & gapi.auth2 to your typings:

npm install --save @types/gapi.auth2
npm install --save @types/gapi

(see this borysn's question to understand this a little better).

src/app/+login/login.component.ts

This is the file of my component, here you need to use the ngAfterViewInit() to use the gapi an get the auth. And you can follow the implementation here developers.google...sign-in/web/build-button

As an example, this is my template:

<div id="my-signin2"></div>

and sign in function:

 declare var gapi: any;

ngAfterViewInit() {
   gapi.signin2.render('my-signin2', {
      'scope': 'profile email',
      'width': 240,
      'height': 50,
      'longtitle': true,
      'theme': 'light',
      'onsuccess': param => this.onSignIn(param)
  });
}

public onSignIn(googleUser) {
   var user : User = new User();

      ((u, p) => {
         u.id            = p.getId();
         u.name          = p.getName();
         u.email         = p.getEmail();
         u.imageUrl      = p.getImageUrl();
         u.givenName     = p.getGivenName();
         u.familyName    = p.getFamilyName();
      })(user, googleUser.getBasicProfile());

      ((u, r) => {
         u.token         = r.id_token;
      })(user, googleUser.getAuthResponse());

      user.save();
      this.goHome();
};