angularx-social-login icon indicating copy to clipboard operation
angularx-social-login copied to clipboard

Get access token from Google login

Open anuj-scanova opened this issue 3 years ago • 19 comments

Using version 1.2.5 and trying to upgrade existing Google login using a new mechanism.

Here is my HTML implementation

<asl-google-signin-button [type]="'standard'" [size]="'medium'" [text]="'signin_with'" [theme]="'outline'"></asl-google-signin-button>

and component.ts

ngOnInit() {
    this.socialAuthService.authState.subscribe((user) => {
      this.getAccessToken();                            <--- Called to get access token
      console.log('authState user: ', user);            <--- Prints user email, name, full name, id, idToken, provider, photoUrl
      console.log('Token from state: ', user.authToken)       <--- Prints undefined
    });
  }

  getAccessToken(): void {
    this.socialAuthService.getAccessToken(GoogleLoginProvider.PROVIDER_ID).then(accessToken => {
      console.log('access token: ', accessToken);        <-- Does not print anything
    });
  }

The readme file reads

You are notified when user logs in or logs out. You receive a SocialUser object when the user logs in and a null when the user logs out. SocialUser object contains basic user information such as name, email, photo URL, etc. along with the auth_token. You can communicate the auth_token to your server to authenticate the user in server and make API calls from server.

How can I get the access token to authenticate user in the backend as well?

anuj-scanova avatar Dec 02 '22 15:12 anuj-scanova

You have the token inside the user object when you listen authState changes.

this.socialAuthService.authState.subscribe returns an user object.

  • If user equals null, your client is not logged (or just sign out !)
  • If user not empty, inside you will find a property idToken.

With idToken you can add in a HttpInterception a Bearer value.

Hope this comment will help you. See you 👋

ruizalexandre avatar Dec 09 '22 18:12 ruizalexandre

@ruizalexandre I tried with the idToken but it does not seem to be a valid access token.

I had to add email, profile scopes, then get accessToken from the .getAccessToken() method.

anuj-scanova avatar Dec 10 '22 04:12 anuj-scanova

@anuj-scanova I tried your workaround and it works fine, except that google asks the users to authenticate 2 times, once when clicking the button and once when calling .getAccessToken(). Do you have the same issue? Why can't the AccessToken be provided in the property SocialUser::authToken instead of having to call a second method, just like the facebook API?

Thanks!

MasterBroki avatar Dec 21 '22 16:12 MasterBroki

@MasterBroki Yes, in my case as well, Google asks for authentication twice.

anuj-scanova avatar Dec 21 '22 16:12 anuj-scanova

@MasterBroki for me I get the JWT token on SocialUser.idToken, isn't that what your looking for?

shyallegro avatar Dec 22 '22 08:12 shyallegro

@shyallegro We want the access_token which will be used from the backend to fetch user details using Google API and authenticate from the backend. In the previous version, the library was returning access_token.

anuj-scanova avatar Jan 10 '23 06:01 anuj-scanova

I have the same issue, when calling the .getAccessToken() method a new login prompt is presented. So you have to click twice to get an access token for calling Googles' scoped apis. This is not very userfriendly.

gwp-rob avatar Feb 07 '23 13:02 gwp-rob

I have checked the angularx-social-login code(working example https://stackblitz.com/edit/angularx-social-login-gc2ivh) and the difference is that in the old one there is addition of authToken

getLoginStatus(loginStatusOptions) {
    const options = Object.assign(Object.assign({}, this.initOptions), loginStatusOptions);
    return new Promise((resolve, reject) => {
        if (this.auth2.isSignedIn.get()) {
            const user = new SocialUser();
            const profile = this.auth2.currentUser.get().getBasicProfile();
            const authResponse = this.auth2.currentUser.get().getAuthResponse(true); // get complete authResponse object
            this.setUserProfile(user, profile);
            user.response = authResponse;
            const resolveUser = authenticationResponse => {
                user.authToken = authenticationResponse.access_token; // <--------- Addition of authToken
                user.idToken = authenticationResponse.id_token;
                resolve(user);
            };
            if (options.refreshToken) {
                this.auth2.currentUser.get().reloadAuthResponse().then(resolveUser);
            }
            else {
                resolveUser(authResponse);
            }
        }
        else {
            reject(`No user is currently logged in with ${GoogleLoginProvider.PROVIDER_ID}`);
        }
   });
 }

which is not there in new version. Any major reason to remove this? I have the same problem with onetaplogin, even the popup showing again after login successfully.

rohankumar1524 avatar Feb 16 '23 09:02 rohankumar1524

When implementing what @anuj-scanova suggested, I'm receiving the access token intermittently. Sometimes it comes and sometimes it's undefined. Is there any solution or code snippet I can follow?

Also has any workaround been found to prevent the login prompt from coming twice?

XclaimR avatar Mar 16 '23 06:03 XclaimR

I did not find a decent solution for this yet. At the moment I use this config:

GoogleInitOptions = { oneTapEnabled: true, prompt: ''}

With the onetap enabled and the prompt set to an empty string, the user is only prompted once when authenticating, the second time (when access token is requested) the popup is displayed only a second or so and then automatically continues.

But, the second popup is still visible for a while and the browser now also initially blocks the second popup. So still not something I would implement in production sites.

gwp-rob avatar Mar 17 '23 12:03 gwp-rob

Guys, you can call https://www.googleapis.com/oauth2/v3/tokeninfo?id_token=${idToken} with the idToken field to retrieve back end side user profile data.

flogh avatar Apr 15 '23 14:04 flogh

This does not include the access toke however, the response:

{ "iss": "https://accounts.example.com/", "nbf": "##########", "aud": "##########", "sub": "##########", "hd": "demo.example.com", "email": "admin.##########@demo.example.com", "email_verified": "true", "azp": "##########", "name": "##########", "picture": "https://lh3.googleusercontent.com/a/##########=s96-c", "given_name": "##########", "family_name": "##########", "iat": "##########", "exp": "##########", "jti": "##########", "alg": "RS256", "kid": "##########", "typ": "JWT" }

gwp-rob avatar Apr 15 '23 15:04 gwp-rob

Any news?

Can't get access token without another popup.

diegoot-dev avatar May 30 '23 14:05 diegoot-dev

it seems here similar problem has been solved in the accepted answer:

https://stackoverflow.com/questions/72612704/how-to-get-oauth-token-after-google-one-tap-sign-in-jwt-token-response-of-one-t

ciekawy avatar Jun 21 '23 12:06 ciekawy

just pushed some changes that makes things bit better. The code though is just minimum changes to test the idea from SO so would need to be properly reworked. Unfortunately do not have more time at this moment but maybe someone can make use of it/move it forward

with those changes no need to call getAccessToken(), just the user.authToken should already contain the access_token, also as per note on the PR, there is still popup showing for a moment (at leas in my case) but without user interaction needed

ciekawy avatar Jun 21 '23 13:06 ciekawy

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Sep 17 '23 01:09 stale[bot]

@anuj-scanova i am facing same issue that cannot get access token which i want to use backend api call.

Can you please help me how to get access token ?

samarthkrtya avatar Oct 25 '23 07:10 samarthkrtya

@samarthkrtya We switched to another library https://github.com/i7N3/google-oauth-gsi and it is working great for us.

anuj-scanova avatar Oct 25 '23 08:10 anuj-scanova

I had the same issue and came across this thread, wanted to share my solution as this still seems to be an missing as of 2.2.0.

@Component({
    template: `<asl-google-signin-button type="standard" size="large"></asl-google-signin-button>`
})
MyComponent {
    socialLogin$: Observable<SocialUser> = this.authService.authState.pipe(
        concatMap((socialUser) =>
            from(this.authService.getAccessToken(GoogleLoginProvider.PROVIDER_ID)).pipe(
                map((authToken) => ({
                    ...socialUser,
                    authToken,
                }))
            )
        ),
        tap((socialUser) => {
            console.log(socialUser.authToken);
            // do any action you need.
        })
    );

}

tomfordweb avatar Jun 25 '24 18:06 tomfordweb