6

Here's how I do it, after getting the signin's client file :

// HTML 
<script type='text/javascript' src="https://apis.google.com/js/api:client.js" async defer></script>

I called gapi.load() function into a HTML button

// load the startApp function after the page loads
jQuery(function () {
    $(window).load(function () {
       startApp()
    })
})


var startApp = function () {

    gapi.load('auth2', function () {

        // Retrieve the singleton for the GoogleAuth library and set up the client.
        auth2 = gapi.auth2.init({
            client_id: 'xxxxxxxx-xxxxxxxxxx.apps.googleusercontent.com',
            cookiepolicy: 'single_host_origin',
            ux_mode: 'redirect',      // I don't want it to display a pop-up 
            scope: 'profile email'    // I just need to get user's name, profile picture and email address
        });

        // attach this function into a button element with id = "customBtn"
        attachSignin(document.getElementById('customBtn'));

    });
};

function attachSignin(element) {
    auth2.attachClickHandler(element, {},
        function (googleUser) {

            // it never calls this block of code.
            // this never runs
            console.log(googleUser.getBasicProfile().getName())
            var gProfile = googleUser.getBasicProfile();
            var name = gProfile.getName();
            var email = gProfile.getEmail();
            var imgUrl = gProfile.getImageUrl();
        }, function (error) {
            return alert("Google Sign in error!")
        });
}

It load the necessary functions into a button. If user click on that button, user will be redirected into Google's signin page. After user manages to sign in then Google will redirect the URL back into my website.

It should also send the user's profile info into my attachClickHandler() function within the attachSignin(). But it never happens since it reloads the page before the handler function gets called. It only works if I change the ux_mode: 'redirect' into default' popup

The best I can do right now is just to get the email address from the token_id parameter that Google give in URL after signin. The id_token field from the URL is a jwt that can be decoded

http://localhost:3006/login#scope=email%20profile%20https://www.googleapis.com/auth/userinfo.email%20https://www.googleapis.com/auth/userinfo.profile%20openid&id_token=xxxxxxxxx&client_id=xxxxxxxxxxxx-xxxxxx.apps.googleusercontent.com

So How to get the user's profile information with ux_mode set to redirect ?

DennyHiu
  • 4,861
  • 8
  • 48
  • 80
  • I can't believe it... it seems nobody knows how to get the user's profile information if `ux_mode` is set to `redirect` / no-popup – DennyHiu Apr 11 '21 at 10:10
  • what's it mean ? The code above works fine if `ux_mode` is set to `redirect`. User can input their email / password and then Google redirects user back into my website with additional parameters in URL.. The problem is I cannot get profile information from `id_token` and `client_id` alone – DennyHiu Apr 11 '21 at 10:37
  • As I can see from docs ux_mode has 2 options pop up and redirect. So you don't want to use ux_mode? and just use token_id and client id. Right? – Marios Nikolaou Apr 11 '21 at 10:43
  • I actually do not care about `token_id` or `client_id` as long as it can provide me with user profile (photo, email, name, etc). If both variable can be used to get the user profile, then I will gladly accept it as the answer – DennyHiu Apr 11 '21 at 14:01
  • Ok, check my answer and let me know, also read the link that I have provided. – Marios Nikolaou Apr 11 '21 at 14:04
  • OK. Thank you. I'll try your answer below – DennyHiu Apr 11 '21 at 14:05

1 Answers1

4

I modified your code so it works:

var startApp = function () {

    gapi.load('auth2', function () {

        // Retrieve the singleton for the GoogleAuth library and set up the client.
        auth2 = gapi.auth2.init({
            client_id: 'xxxxxxxx-xxxxxxxxxx.apps.googleusercontent.com',
            cookiepolicy: 'single_host_origin',
            ux_mode: 'redirect',      // I don't want it to display a pop-up 
            scope: 'profile email'    // I just need to get user's name, profile picture and email address
        });

        // attach this function into a button element with id = "customBtn"
        attachSignin(document.getElementById('customBtn'));

        
        // START NEW CODE
        auth2.currentUser.listen(function(googleUser) {
            if (googleUser && (gProfile = googleUser.getBasicProfile())) {
                var name   = gProfile.getName();
                var email  = gProfile.getEmail();
                var imgUrl = gProfile.getImageUrl();

                console.log({name, email, imgUrl});
            }
        });
        // END NEW CODE

    });
};

// Can remove callbacks if not using pop-up
function attachSignin(element) {
    auth2.attachClickHandler(element, {});
}

Explanation:

When using redirect instead of pop-up, listen on currentUser instead of the attachClickHandler() callbacks. The Google API will detect and consume the redirect parameters, firing the currentUser.listen handler.

Sources:

Leftium
  • 16,497
  • 6
  • 64
  • 99
  • YESSS!!! Clear-cut and to-the-point explanation. Also working code. It works! I hope this will help others too. Thank you sir! – DennyHiu Apr 14 '21 at 12:56
  • I tested this method and I can confirm that it's working on windows and android but not Safari on ios. The `auth2.currentUser.listen` is called but the `googleUser` is empty. There's also a warning abut `The source list for Content Security Policy directive 'script-src' contains an invalid source: ''strict-dynamic''. It will be ignored.` – DennyHiu Apr 14 '21 at 15:28
  • it seems that redirect mode and ` auth2.currentUser.listen()` is not working for safari for some reason. Both safari on MacOS desktop or mobile version do not work. Do you know what might cause it ? @Leftium – DennyHiu Apr 14 '21 at 15:39
  • @DennyHiu I was able to reproduce on my iPhone, but it's difficult for me to debug because I don't have a Mac. It might be a Safari-specific problem? I suggest opening another question. – Leftium Apr 14 '21 at 20:16
  • 1
    @DennyHiu It seems the redirect method no longer works in safari: "Due to the ITP policy, only the default popup flow works on Safari." (https://github.com/google/google-api-javascript-client/issues/589#issuecomment-793155811) – Leftium Apr 14 '21 at 20:25
  • @DennyHiu Lots of more issues to explore: https://github.com/google/google-api-javascript-client/issues?q=is%3Aissue+is%3Aopen+safari – Leftium Apr 14 '21 at 20:26
  • @DennyHiu This is the closest issue. Unfortunately the solution seems to be to use the raw HTTP API (vs JS SDK): https://github.com/google/google-api-javascript-client/issues/566#issuecomment-577919436 – Leftium Apr 14 '21 at 20:32
  • I have macbook and its developer mode don't give any clear reason why. It just doesn't work. Maybe the best we can do is making a different version with `popup` flow in Safari – DennyHiu Apr 14 '21 at 23:16
  • @DennyHiu The HTTP API seems to work: https://developers.google.com/identity/protocols/oauth2/javascript-implicit-flow The JS SDK is just a convenience wrapper around the HTTP API. – Leftium Apr 15 '21 at 03:09
  • How to do that ? would you mind to add example for HTTP API in your answer ? – DennyHiu Apr 15 '21 at 04:17
  • @DennyHiu I already linked to this: https://github.com/google/google-api-javascript-client/issues/566#issuecomment-577919436 It can be simplied to two steps: 1. Construct HTTP OAuth link. 2. Detect and handle URL params in redirect URL (3. Optionally call other API's with token to get more info) – Leftium Apr 15 '21 at 05:01
  • it turns out that using "popup" for iOS and MacOS is far easier solution for me. I just need to get the OS version from `navigator ` (explained in detail here: https://stackoverflow.com/questions/11219582/how-to-detect-my-browser-version-and-operating-system-using-javascript). And put a conditional check if it's a Mac or Safari the old `popup` mode will be used. – DennyHiu Apr 20 '21 at 06:22