Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Google Authentication Deprecation | "Your client application uses libraries for user authentication or authorization that will soon be deprecated." #489

Closed
kopyl opened this issue Apr 16, 2022 · 36 comments · Fixed by #507
Labels
help_wanted Help requested from the community on this issue willfix A fix will be provided for this issue

Comments

@kopyl
Copy link

kopyl commented Apr 16, 2022

I got this warning in the console:

https://imgur.com/a/I3mACzR

Here is the link:

https://developers.google.com/identity/gsi/web/guides/gis-migration

Here is how I log in with Google:

private authService: SocialAuthService,

...

this.authService.initState.subscribe((value: boolean) => {
            this.authService
                .signIn(GoogleLoginProvider.PROVIDER_ID)
                .then((data) => {
                    localStorage.setItem("google_auth", JSON.stringify(data))
                    this.initJWTauth(data)
                })
        })

Could you please fix Google SocialAuthService so we don't need to make any change in the code to make the warning disappear?

@jaibatrik jaibatrik added help_wanted Help requested from the community on this issue willfix A fix will be provided for this issue labels Apr 17, 2022
@viheag21
Copy link

I have the same error

@Octarines
Copy link

Octarines commented Apr 29, 2022

I also have this issue and am somewhat confused by the comments in #473 as to whether it is supposed to be resolved?

The merge listed above seems to imply the fix should be in the latest version of this package (though he lists v4.1.0 which is the last version number of the old package location). I currently have installed the latest version of the new package location (i.e. @abacritt/angularx-social-login v1.0.2) and I am still getting the error message in the console.

Is the fix actually in @abacritt/angularx-social-login yet?

A clarification on the version numbering since the package location migration would help here too :)

image

@sommmen
Copy link
Contributor

sommmen commented May 4, 2022

Hm - so i'm using the version compatible with angular v11:
https://www.npmjs.com/package/angularx-social-login/v/3.5.7
And i also have this issue.

@jaibatrik will the migration to the new google library also be moved to the older versions to remain compatble with older angular versions or not?

@Jeremybdel
Copy link

Hi ! I'm using angular cli 13.3.5 but got the same error here !

@Vigneshprabu-augment
Copy link

Hi, I'm using angular CLI 9.1.12 but got the same error here, Please help me how to fix this issue

@jaibatrik
Copy link
Contributor

Looking for PR to fix it in the lates version at least. It will be great if someone can send a PR for this.

@Elya2305
Copy link

Same issue here

@kurpav
Copy link

kurpav commented May 20, 2022

Are there any updates on this?

@Heatmanofurioso
Copy link
Collaborator

I've deployed the changes.
They should be present in https://www.npmjs.com/package/@abacritt/angularx-social-login

We had to change the packaging in NPM, so it's now under the @abacritt org

@oliverfrost
Copy link

oliverfrost commented May 23, 2022

Does new release resolve the issue for someone?

I've downloaded the latest version and here is what I have at app initialisation.

{
details: "You have created a new client application that uses libraries for user authentication or authorization that will soon be deprecated. New clients must use the new libraries instead; existing clients must also migrate before these libraries are deprecated. See the [Migration Guide](https://developers.google.com/identity/gsi/web/guides/gis-migration) for more information."
error: "idpiframe_initialization_failed"
}

Sign In/ Sign Up with Google also fails with an error:

zone.js:1213 Uncaught Error: Uncaught (in promise): Object: {"error":"popup_closed_by_user"}
    at resolvePromise (zone.js:1213:1)
    at zone.js:1120:1
    at abacritt-angularx-social-login.mjs:283:25
    at ZoneDelegate.invoke (zone.js:372:1)
    at Object.onInvoke (core.mjs:25608:1)
    at ZoneDelegate.invoke (zone.js:371:1)
    at Zone.run (zone.js:134:1)
    at zone.js:1276:1
    at ZoneDelegate.invokeTask (zone.js:406:1)
    at Object.onInvokeTask (core.mjs:25595:1)

It opens modal window, allows to enter and submit credentials and then fails.

My setup:

    "@abacritt/angularx-social-login": "^1.1.0",
    "@angular-devkit/build-angular": "13.3.6",
    "@angular/cdk": "13.3.7",
    "@angular/cli": "13.3.6",
    "@angular/common": "13.3.9",
    "@angular/compiler": "13.3.9",
    "@angular/core": "13.3.9",
    "@angular/forms": "13.3.9",
    "@angular/platform-browser": "13.3.9",
    "@angular/platform-browser-dynamic": "13.3.9",
    "@angular/router": "13.3.9",

Nothing unusual. Just imports the SocialLoginModule, registers provider and calls this.authService.signIn(GoogleLoginProvider.PROVIDER_ID);. Just like in a demo project.

@AyuDiego
Copy link

Does new release resolve the issue for someone?

I've downloaded the latest version and here is what I have at app initialisation.

{
details: "You have created a new client application that uses libraries for user authentication or authorization that will soon be deprecated. New clients must use the new libraries instead; existing clients must also migrate before these libraries are deprecated. See the [Migration Guide](https://developers.google.com/identity/gsi/web/guides/gis-migration) for more information."
error: "idpiframe_initialization_failed"
}

Sign In/ Sign Up with Google also fails with an error:

zone.js:1213 Uncaught Error: Uncaught (in promise): Object: {"error":"popup_closed_by_user"}
    at resolvePromise (zone.js:1213:1)
    at zone.js:1120:1
    at abacritt-angularx-social-login.mjs:283:25
    at ZoneDelegate.invoke (zone.js:372:1)
    at Object.onInvoke (core.mjs:25608:1)
    at ZoneDelegate.invoke (zone.js:371:1)
    at Zone.run (zone.js:134:1)
    at zone.js:1276:1
    at ZoneDelegate.invokeTask (zone.js:406:1)
    at Object.onInvokeTask (core.mjs:25595:1)

It opens modal window, allows to enter and submit credentials and then fails.

My setup:

    "@abacritt/angularx-social-login": "^1.1.0",
    "@angular-devkit/build-angular": "13.3.6",
    "@angular/cdk": "13.3.7",
    "@angular/cli": "13.3.6",
    "@angular/common": "13.3.9",
    "@angular/compiler": "13.3.9",
    "@angular/core": "13.3.9",
    "@angular/forms": "13.3.9",
    "@angular/platform-browser": "13.3.9",
    "@angular/platform-browser-dynamic": "13.3.9",
    "@angular/router": "13.3.9",

Nothing unusual. Just imports the SocialLoginModule, registers provider and calls this.authService.signIn(GoogleLoginProvider.PROVIDER_ID);. Just like in a demo project.

happens the same on me :C

@Tim-mhn
Copy link

Tim-mhn commented May 26, 2022

Happens to me as well, even though I have downloaded the latest version of the package.

I went into the code of the google-login-provider.ts and it still uses the old JS script https://apis.google.com/js/api.js. It should be https://accounts.google.com/gsi/client instead.

The flow has changed a lot with the new update. I was able to update the initialize and signIn functions to retrieve the accessToken (see below). However, it seems much harder to get the idToken and, thus, the profile information (name, email, emailVerified ...). It appears that Google is forcing to use their own button component which comes inside an iframe, making it very hard to have a pure JS solution.

Basic implementation to get the accessToken

initialize(): Promise<void> {
    return new Promise((resolve, reject) => {
      try {
      this.loadScript(
          GoogleLoginProvider.PROVIDER_ID,
          'https://accounts.google.com/gsi/client',
          () => {
            this.client = google.accounts.oauth2.initTokenClient({
              ...this.initOptions,
              client_id: this.clientId,
              callback: (r) => {
                console.log(r);
              },
              // allowed_parent_origin: 'http://localhost:4200' // had to add this to make it work on localhost for the moment 
            });
            resolve();

     } catch (err) {
          reject(err);
    }
}

 signIn(signInOptions?: any): Promise<SocialUser> {
    const options = { ...this.initOptions, ...signInOptions };
    return new Promise((resolve, reject) => {
      this.client.callback = (response) => {
        // response  has the following shape 
        // { access_token, authuser, expires_in, prompt, provider, scope, token_type } 
        console.log(response)
        resolve()
      };
      this.client.requestAccessToken();
})
}

NB
I even tried to create an invisible google button and click it from JS but I'm unauthorized to do this since it's inside an iframe :/

NB2
There is a temporary trick to make newly created apps with the deprecated Google Sign-In JS Library.
You can add any string in plugin_name in the options. As it says here, New Client IDs created before July 29th, 2022 can set plugin_name to enable use of the Google Platform Library and the library will be unavailable from March 31, 2023.

To be used like this

 gapi.client.init({
         client_id: this.clientId,
         plugin_name: 'put anything here',
         ...otherOptions
              })

Some useful links

JS API
Google Identity Services migration

@Heatmanofurioso
Copy link
Collaborator

@Tim-mhn Thanks for your work on this.

I haven't explored the new API myself, but would you be okay with me "or someone from the community" to adapt this and update the lib with it?

@AyuDiego
Copy link

AyuDiego commented May 26, 2022 via email

@ShemiNechmad
Copy link

Hi there, working with Angular 8 version and angularx-social-login 2.2.1.
Having the same issue.
Would like to know if anyone has a professional thought of what is possible to do to solve that.
Thank you

@Tim-mhn
Copy link

Tim-mhn commented May 27, 2022

I have been working on it since yesterday. I was able to implement a JS-only solution and retrieve the user's profile information (name, email ...) and an authorization token. But i haven't been able to get the id token, which might be necessary for some users of this library (it's the case for our team). I will keep you updated if i make some progress next week.

In the mean time, you can always add plugin_name: "any string you want" to the options as mentionned above, it will temporarily do the trick

@AyuDiego
Copy link

AyuDiego commented May 27, 2022 via email

@bartromgens
Copy link

@Tim-mhn Thanks for your efforts. I also need the id token. Seems we have to use the Sign in with Google API.

Authentication and authorization have been split into two APIs in the new library you linked. The google.accounts.oauth2.initTokenClient API only returns the access token for calling Google APIs. If you need an id_token, you should use the Sign in with Google API instead.
- source

But that indeed seems to require the renderButton call to get the token response. Something like this:

    google.accounts.id.initialize({
      client_id: '',
      callback: handleCredentialResponse,
      allowed_parent_origin: 'http://localhost:4200'
    });
    google.accounts.id.renderButton(parentElement);

Which indeed is not what we want in this SocialAuthService.signIn interface.

I currently see 3 options:

  • Use some trickery with clicking a hidden button somehow
  • Find some undocumented way to do the same in pure javascript
  • Switch to the official Google Button, which breaks the existing concept/interface

@Tim-mhn
Copy link

Tim-mhn commented May 27, 2022

@Tim-mhn Thanks for your efforts. I also need the id token. Seems we have to use the Sign in with Google API.

Authentication and authorization have been split into two APIs in the new library you linked. The google.accounts.oauth2.initTokenClient API only returns the access token for calling Google APIs. If you need an id_token, you should use the Sign in with Google API instead.
- source

But that indeed seems to require the renderButton call to get the token response. Something like this:

    google.accounts.id.initialize({
      client_id: '',
      callback: handleCredentialResponse,
      allowed_parent_origin: 'http://localhost:4200'
    });
    google.accounts.id.renderButton(parentElement);

Which indeed is not what we want in this SocialAuthService.signIn interface.

I currently see 3 options:

* Use some trickery with clicking a hidden button somehow

* Find some undocumented way to do the same in pure javascript

* Switch to the official Google Button, which breaks the existing concept/interface

Thanks for your feedback. I am under the same impression :/

  • For your first suggestion, i tried programmatically clicking on the button that Google renders. But it seems Impossible since it's embedded in an iframe (CORS policies). Let us know if you get the same error or find a trick.

  • The AuthToken allows you to call other Google APIs (see the list ). I havent found any that could return something such as an idToken but there's a lot to go through so I could've missed something.

@bartromgens
Copy link

bartromgens commented May 28, 2022

I can imagine it is impossible to click the iframe embedded button. Not my expertise though.

Getting an id token in a different way using the OAuth2 library is probably discouraged because this requires a correct (secure) implementation:

This document describes how to perform the server flow for authenticating the user. The implicit flow is significantly more complicated because of security risks in handling and using tokens on the client side. If you need to implement an implicit flow, we highly recommend using Google Sign-In.

The Microsoft library implements client side Authorization Code Flow with PKCE:

This version of the library uses the OAuth 2.0 Authorization Code Flow with PKCE.

Google doesn't seem to offer the same.

If we need to build our custom implementation of the implicit flow (or better the Authorization Code Flow with PKCE, if possible) with the Google API, we need to be careful to do it securely. I understand the basics of the authentication flows and some vulnerabilities, but this is not my expertise. I can see that the Google button is the more secure way.

Edit:
Google does support PKCE with a code_challenge url parameter and a code_verifier, see https://developers.google.com/identity/protocols/oauth2/native-app#step1-code-verifier

I think this can be used to get a code with a request to https://accounts.google.com/o/oauth2/v2/auth
and the use the code to get a id token from https://oauth2.googleapis.com/token. This just doesn't seem to be included in the Google library.

It would be a generic OpenID Connect Authorization Code Flow with PKCE.

@Tim-mhn
Copy link

Tim-mhn commented May 30, 2022

Edit: Google does support PKCE with a code_challenge url parameter and a code_verifier, see https://developers.google.com/identity/protocols/oauth2/native-app#step1-code-verifier

I think this can be used to get a code with a request to https://accounts.google.com/o/oauth2/v2/auth and the use the code to get a id token from https://oauth2.googleapis.com/token. This just doesn't seem to be included in the Google library.

It would be a generic OpenID Connect Authorization Code Flow with PKCE.

I'm not sure if this could work since you need to pass in the client_secret to the oauth2.googleapis.com/token request, that doesn't seem like a safe solution :(

On a side-note, this is the code that was able to come up with to get the user's information using the openidconnect.googleapis.com/v1/userinfo endpoint

initialize() {
   return new Promise((resolve, reject) => {
      this.loadScript(
          GoogleLoginProvider.PROVIDER_ID,
          'https://accounts.google.com/gsi/client',
          () => {
            this.client = google.accounts.oauth2.initTokenClient({
              client_id: this.clientId,
              scope: 'openid email profile',
              callback: (tokenResponse) => {},
              allowed_parent_origin: 'http://localhost:4200',
            });
       })

      resolve();
}


signIn() {
   return new Promise((resolve, reject) => {

      // client.callback is called after client.requestAccessToken completes with its response ({ access_token} object)
      this.client.callback = (tokenResponse) => {
         const { access_token } = tokenResponse;
         var xhr = new XMLHttpRequest();
         xhr.addEventListener('load', (e) => {
          const user = this.getUserProfile(JSON.parse(xhr.response));
          console.log(user);
          resolve(user);
        });
        xhr.addEventListener('error', (err) => {
          console.error('error from request', err);
          reject(err);
        });
        xhr.open('GET', 'https://openidconnect.googleapis.com/v1/userinfo');
        xhr.setRequestHeader('Authorization', 'Bearer ' + access_token);
        xhr.send();
   }

   this.client.requestAccessToken();

}


 private getUserProfile(userInfo: {
    email: string;
    email_verified: boolean;
    family_name: string;
    given_name: string;
    locale: string;
    name: string;
    picture: string;
    sub: string;
  }): SocialUser {

    const user = new SocialUser();
    user.name = userInfo.name;
    user.email = userInfo.email;
    user.photoUrl = userInfo.picture;
    user.firstName = userInfo.given_name;
    user.lastName = userInfo.family_name;
    return user;

}

@bartromgens
Copy link

I'm not sure if this could work since you need to pass in the client_secret to the oauth2.googleapis.com/token request, that doesn't seem like a safe solution :(

Using the client_secret on the client side is indeed not a good idea.

@Jin-K
Copy link
Contributor

Jin-K commented May 31, 2022

On a side-note, this is the code that was able to come up with to get the user's information using the openidconnect.googleapis.com/v1/userinfo endpoint

initialize() {
   return new Promise((resolve, reject) => {
      this.loadScript(
          GoogleLoginProvider.PROVIDER_ID,
          'https://accounts.google.com/gsi/client',
          () => {
            this.client = google.accounts.oauth2.initTokenClient({
              client_id: this.clientId,
              scope: 'openid email profile',
              callback: (tokenResponse) => {},
              allowed_parent_origin: 'http://localhost:4200',
            });
       })

      resolve();
}


signIn() {
   return new Promise((resolve, reject) => {

      // client.callback is called after client.requestAccessToken completes with its response ({ access_token} object)
      this.client.callback = (tokenResponse) => {
         const { access_token } = tokenResponse;
         var xhr = new XMLHttpRequest();
         xhr.addEventListener('load', (e) => {
          const user = this.getUserProfile(JSON.parse(xhr.response));
          console.log(user);
          resolve(user);
        });
        xhr.addEventListener('error', (err) => {
          console.error('error from request', err);
          reject(err);
        });
        xhr.open('GET', 'https://openidconnect.googleapis.com/v1/userinfo');
        xhr.setRequestHeader('Authorization', 'Bearer ' + access_token);
        xhr.send();
   }

   this.client.requestAccessToken();

}


 private getUserProfile(userInfo: {
    email: string;
    email_verified: boolean;
    family_name: string;
    given_name: string;
    locale: string;
    name: string;
    picture: string;
    sub: string;
  }): SocialUser {

    const user = new SocialUser();
    user.name = userInfo.name;
    user.email = userInfo.email;
    user.photoUrl = userInfo.picture;
    user.firstName = userInfo.given_name;
    user.lastName = userInfo.family_name;
    return user;

}

Honestly I think this is the exact way to go (for implicit authorization) if you want to preserve the current custom button, otherwise it should be their button.

They say it's ok to call the OAuth 2.0 API endpoints here

@ShemiNechmad
Copy link

Meantime, if anybody needs it,
here is a repository with an example of Google Sign In with Angular (any version should work) and Google Identity, directly.
https://github.com/ShemiNechmad/GoogleSignInAngular

@Heatmanofurioso
Copy link
Collaborator

Thanks for the repo @ShemiNechmad
I'm going to try and work on an updated implementation this weekend, and update the lib if no one proposes a solution before that

@Jin-K
Copy link
Contributor

Jin-K commented Jun 2, 2022

Hi @Heatmanofurioso I reopened that PR I closed some days ago. #507
I worked a bit more on it and now it uses the auto-login & auto-logout features of the Google Identity library.
The only "problem" is the button generated by the lib that seems impossible to avoid.

Can anyone propose Pull Requests in this repo ? Or should we be listed as contributor first ?

@Heatmanofurioso
Copy link
Collaborator

I believe you need to be a contributor @Jin-K
And I'm not the owner. I'm just a user, who decided to try and maintain it, and got added as a contributor. "Been doing a bad job at it due to lack of time.. sorry"

But you can just use that PR, and I can effectively accept it if we agree on it

@edoremo00
Copy link

@ShemiNechmad thanks for your help man. Do you know how can I ask user to grant additional permissions to read from their calendar for example? (scopes) in the previous version of the google library I used to be able to that. I found that in the gis library there are two methods in which you can pass a scope parameter, however they don't return a valid JWT token. these methods are : google.accounts.oauth2.initTokenClient and google.accounts.oauth2.initCodeClient. the problem is that I need a JWT token to pass to my backend in order to validate it with a .NET library made by Google which is Google.Apis.Auth package.

@Jin-K
Copy link
Contributor

Jin-K commented Jun 5, 2022

@edoremo00 google.accounts.oauth2.initTokenClient & google.accounts.oauth2.initCodeClient are 2 methods for the authorization flow, they don't give you a jwt token but an access token to use for their APIs.

to get a jwt token that you can decode by yourself later, you need to call google.accounts.id.initialize() first with a callback that you pass, then google.accounts.id.renderButton(): That will generate a button and when you click on it, some magic happens and your passed callback should then be invoked.

ref: https://developers.google.com/identity/gsi/web/guides/overview

I strongly recommend to install @types/google.accounts as devDependency to benefit of the documentation on the Google Identity library

@Tim-mhn
Copy link

Tim-mhn commented Jun 7, 2022

Meantime, if anybody needs it, here is a repository with an example of Google Sign In with Angular (any version should work) and Google Identity, directly. https://github.com/ShemiNechmad/GoogleSignInAngular

@edoremo00 you can check out this repo, you have a working html-based example. The crendential returned in the callback (see handleCredentialResponse ) is a JW Token

@edoremo00
Copy link

@Tim-mhn thank you for the repo. I used it however the problem remains. the credential returned is a JWT token but you can't request scopes for using a particular API. passing that JWT to a Google API will result in a 401 unauthorized as you didn't request the scope for it but you just made a Login. Previously you were able to request additional scopes in the login phase.

@edoremo00
Copy link

@Jin-K so now it's a two step thing? first I need to log in the user and than if I want to use a Google API, like Calendar I need to call either google.accounts.oauth2.initTokenClient or google.accounts.oauth2.initCodeClient for using the Api? seems like a downgrade to me. in the previous version(GAPI) you were able to do Login and request for additional scopes in one method

@Jin-K
Copy link
Contributor

Jin-K commented Jun 7, 2022

@Jin-K so now it's a two step thing? first I need to log in the user and than if I want to use a Google API, like Calendar I need to call either google.accounts.oauth2.initTokenClient or google.accounts.oauth2.initCodeClient for using the Api? seems like a downgrade to me. in the previous version(GAPI) you were able to do Login and request for additional scopes in one method

It's a downgrade I agree 😥

@edoremo00
Copy link

@Jin-K do you know the difference between initTokenclient and initCodeclient?

@Jin-K
Copy link
Contributor

Jin-K commented Jun 10, 2022

@edoremo00 I already replied that, and I'm not working for Google. I also can't understand why the made it so complex to integrate 2 of their own products.

If you want to get both at the same time and avoid their iframed button, you should try what @Tim-mhn proposes: getting the access token first with initTokenClient, and immediatelly get the id token via a http request.

@Heatmanofurioso Heatmanofurioso changed the title "Your client application uses libraries for user authentication or authorization that will soon be deprecated." Google Authentication Deprecation | "Your client application uses libraries for user authentication or authorization that will soon be deprecated." Jun 13, 2022
@Heatmanofurioso Heatmanofurioso pinned this issue Jun 13, 2022
@Heatmanofurioso Heatmanofurioso linked a pull request Jun 13, 2022 that will close this issue
@Heatmanofurioso
Copy link
Collaborator

@abacritt/angularx-social-login:1.2.0 has been deployed.

The functionality has slightly changed, but it should be working fine.
We're going to try and work on adding a new Google Provider without the button, and that will later be added to the lib.

If there's any issues further one, we can always reopen this issue

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help_wanted Help requested from the community on this issue willfix A fix will be provided for this issue
Projects
None yet
Development

Successfully merging a pull request may close this issue.