abacritt / angularx-social-login

Social login and authentication module for Angular 17
636 stars 387 forks source link

Get access token from Google login #645

Open anuj-scanova opened 1 year ago

anuj-scanova commented 1 year ago

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?

ruizalexandre commented 1 year ago

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

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

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

Hope this comment will help you. See you đź‘‹

anuj-scanova commented 1 year ago

@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.

MasterBroki commented 1 year ago

@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!

anuj-scanova commented 1 year ago

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

shyallegro commented 1 year ago

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

anuj-scanova commented 1 year ago

@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.

gwp-rob commented 1 year ago

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.

rohankumar1524 commented 1 year ago

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.

XclaimR commented 1 year ago

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?

gwp-rob commented 1 year ago

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.

flogh commented 1 year ago

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.

gwp-rob commented 1 year ago

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" }

diegoot-dev commented 1 year ago

Any news?

Can't get access token without another popup.

ciekawy commented 1 year ago

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 commented 1 year ago

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

stale[bot] commented 1 year ago

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.

samarthkrtya commented 11 months ago

@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 ?

anuj-scanova commented 11 months ago

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

tomfordweb commented 3 months ago

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.
        })
    );

}