Cordova Ionic Google Oauth Login For Your Mobile App

In this blog post, we will see how to add google login for your mobile app. We will see how to add it all three platforms desktop, android and ios

Since we are using google oauth for authentication, we don’t need to use any external cordova plugin. Hence the same code will automatically work for all platforms.
Before starting, we first need to create a google app and generate “client_id” and “secret”.

Steps to do this are as follows
1. First Open https://console.developers.google.com
2. Create a new project as shown below, write any name you prefer.
Projects 2015-07-20 18-50-10
3. Go to API’s and Auth -> Credentials, then click on “Create New Client ID” under OAuth.
Fill in details there, and then setup Authorized redirect URL and authorized javascript origins as per your app url. For mobile app, simple use http://localhost
Credentials - Excellence Blog Test App 2015-07-20 19-07-01
after doing this your client id, secret and redirect url are generated.

Once the client_id, secret and redirct_uri have been created, next we need to write our javascript code.
The source code the google login can be found at https://github.com/manishiitg/Excellence-Blog-Cordova-Ionic-Google-OAuth-Login/blob/master/js/service/google_login.js
Its a reusable angularjs service, which can be used in a plug/play mode in any of your projects.

Lets look in details of how the code works.
There are two angualrjs services,
a. timeStorage
b. googleLogin

timeStorage is not the main service, it is just alternate for COOKIE using localStorage. We can set an expiry date for localStorage using this service which is required in the current case.
We will only discuss the googleLogin service below.

Google OAuth Process

Step1 Authorize: First step in the oauth process is authorize. Here we send our client_id, secret and redirect url to google oauth authorize url. If everything is setup properly in the google developer console, you will see the consent screen. After user gives his consent, he will be redirected to the “redirect_uri” with access_code as a request parameter.

Step2 Validate Token: After redirection to the redirect_uri, we need to get the access_code from URL and generate a access token and expire date. Access token is valid for some time, as given in expiry date. Generating multiple access token in a short time frame can result in errors.

Step3 Getting Data: After we get the access token, we need to call another google api with the access token and get the user data required.

These are 3 steps to follow for google oauth api to work. The main problem face with javascript based is in step1, when we need to extra access_code after redirection. This is quite is easy in php, but difficult in javascript. Lets look at the code step by step on how to do this.

Authorize OAuth

Lets look at the code for authorize

            var access_token = timeStorage.get('google_access_token');
            if (access_token) {
                $log.info('Direct Access Token :' + access_token);
                service.getUserInfo(access_token, def);
            }

Here we simple check if a previous access token is present already in localStorage and it has not expired. We use it directly then.

                var params = 'client_id=' + encodeURIComponent(service.client_id);
                params += '&redirect_uri=' + encodeURIComponent(service.redirect_uri);
                params += '&response_type=code';
                params += '&scope=' + encodeURIComponent(service.scope);
                var authUrl = 'https://accounts.google.com/o/oauth2/auth?' + params;
                var win = window.open(authUrl, '_blank', 'location=no,toolbar=no,width=800, height=800');

Here we generate the URL for google and open it in a new window.
When developing thie module on mobile, its very important to install the phonegap inappbrowser plugin. Without this the code won’t work properly. Run “cordova plugin add cordova-plugin-inappbrowser” to install the plugin.

Next

                    if (ionic.Platform.isWebView()) {
                    console.log('using in app browser');
                    win.addEventListener('loadstart', function (data) {
                        console.log('load start');
                        if (data.url.indexOf(context.redirect_uri) === 0) {
                            console.log('redirect url found ' + context.redirect_uri);
                            win.close();
                            var url = data.url;
                            var access_code = context.gulp(url, 'code');
                            if (access_code) {
                                context.validateToken(access_code, def);
                            } else {
                                def.reject({error: 'Access Code Not Found'});
                            }
                        }

                    });
                } else {
                    console.log('InAppBrowser not present');
                    var pollTimer = $interval(function () {
                        try {
                            console.log("google window url " + win.document.URL);
                            if (win.document.URL.indexOf(context.redirect_uri) !== -1) {
                                console.log('redirect url found');
                                win.close();
                                $interval.cancel(pollTimer);
                                pollTimer = false;
                                var url = win.document.URL;
                                $log.debug('Final URL ' + url);
                                var access_code = context.gulp(url, 'code');
                                if (access_code) {
                                    $log.info('Access Code: ' + access_code);
                                    context.validateToken(access_code, def);
                                } else {
                                    def.reject({error: 'Access Code Not Found'});
                                }
                            }
                        } catch (e) {
                        }
                    }, 100);
                }

If the platform is cordova/webview we wait for the “win.addEventListener(‘loadstart’)” event. This even is called when ever a new url gets opened in the inappbrowser. So when google redirects to the redirect url set by us, we read the access code of it.
On on a normal browser, we don’t have the ‘loadstart’ event hence we keep checking the browser URL every the window url every 100 mili seconds. Again when the URL matches the redirect url, we close the window and retrive the access code.

Token Validation

            service.validateToken = function (token, def) {
            var http = $http({
                url: 'https://www.googleapis.com/oauth2/v3/token',
                method: 'POST',
                headers: {'Content-Type': 'application/x-www-form-urlencoded'},
                params: {
                    client_id: this.client_id,
                    client_secret: this.secret,
                    redirect_uri: this.redirect_uri,
                    code: token,
                    grant_type: 'authorization_code'
                }
            });
            var context = this;
            http.then(function (data) {
                $log.debug(data);
                var access_token = data.data.access_token;
                var expires_in = data.data.expires_in;
                expires_in = expires_in * 1 / (60 * 60);
                timeStorage.set('google_access_token', access_token, expires_in);
                if (access_token) {
                    $log.info('Access Token :' + access_token);
                    context.getUserInfo(access_token, def);
                } else {
                    def.reject({error: 'Access Token Not Found'});
                }
            });
        };

This code is quite simple, we fire an ajax request to retrieve the access token. We save the access token in our localStorage service and set the expiry time as well.

Getting User Information

            service.getUserInfo = function (access_token, def) {
            var http = $http({
                url: 'https://www.googleapis.com/oauth2/v3/userinfo',
                method: 'GET',
                params: {
                    access_token: access_token
                }
            });
            http.then(function (data) {
                $log.debug(data);
                var user_data = data.data;
                var user = {
                    name: user_data.name,
                    gender: user_data.gender,
                    email: user_data.email,
                    google_id: user_data.sub,
                    picture: user_data.picture,
                    profile: user_data.profile
                };
                def.resolve(user);
            });
        };

Again this function is quite simple, we fire an ajax request to get the user information based on access token we have.

Google OAuth Scope

In the above code, we had use a space separated list of scope as well. The list of scope basically tell what all information the google api will return and these also display in the first consent screen as well. For a complete list of scope and api debugging visit the link below
https://developers.google.com/oauthplayground/