Google OAuth 2.0 with Ionic Framework

On a recent project I was working on, I wanted to jump on board with the rest of the world and use OAuth for the authentication service. After hours of researching and coming up with a scheme to do this, I found that the documentation on this process was very limited. It took a while before I finally had everything sorted out. I don’t want anyone to suffer through the same painful experience that I went through, so I wanted to share my experience with everyone. I hope this post will save you hours of unnecessary frustration.

Introduction to Google OAuth 2.0

To start off, here is a diagram of the basic web application work flow to Google OAuth 2.0.

1) Client directs a browser to a Google URL, which will need to contain query parameters
2) Google handles the authentication process and returns an access code to the client
3) The client takes the access code and respond to the Google Server to exchange for the access token

Implementing Google OAuth on Ionic

You might have noticed that the workflow shown above is for web application and you might be wondering why a web application workflow is used for a mobile application framework like Ionic. That’s because we are going to use the Cordova InAppBrowser plugin to do our OAuth. (Make sure you have the Cordova InAppBrowser plugin installed--if not, go to https://github.com/apache/cordova-plugin-inappbrowser)

Here we go: first, we will use the InAppBrowser to direct to a Google URL where the user can sign in and give consent. In order to do this, you will have to create a url which will need to include your Google API information. Here is what you will need:

Google = {
 url: 'https://accounts.google.com/o/oauth2/auth'
 client_id: YOUR GOOGLE CLIENT ID
 redirect_uri: 'http://localhost'
 scope: 'email' 
}

(for more info on scope https://developers.google.com/+/api/oauth#login-scopes)

With the object above, create a URL string that you will later use to open an inAppBrowser window. We are now ready for the first step of the OAuth workflow, which is to direct the inAppBrowser to the google URL. We need to save a reference to the opened window because we will have to use the saved reference to add event listeners for google's response. Here is how you would create the URL and create a window browser:

var url = Google.url+'?client_id='+ Google.client_id + '&response_type=code' +'&redirect_uri=' + Google.redirect_uri + '&scope=' + Google.scope;

var browser = $window.open(url, '_blank', 'location=no,toolbar=no');

Now that the browser is created, you will need to add event listeners to listen after the user logged in with google and google responded with a URL. The method I found most useful was to listen for the 'loadstart' event. Once the load start event is fired and google returns a url, your code will be in the url. Now you just have to grab the url and parse the string, so you can grab the user code.

browser.addEventListener('loadstart',function(e) {
  var url = e.url;
  var code = /\?code=(.+)$/.exec(url);
  if (code) {
    browser.close();
    //use 'code' to trade for a token
  }
}

With the user code, you can now exchange the code for the token. It is recommended that you use your server to retrieve the token because in this retrieval step, you will need the client id and the client secret. You do not want this code to be on the client side because you could potentially be exposing your id and secret. Now with the node, you can send an http post request to the server and have your server go to the google server to retrive the token. You can post to the server using $http with something like this:

if (code) {
  $http ({
    method: 'POST',
    url: postUrl,
    data: {
      code: code[1]
    }
  });
  browser.close();
}

In my case, we were using node.js and express, so this is what the code would look like from the server side:

//create Object for Post request
var reqObj = {
  method: 'POST',
  url: constants.Google.authorize,
  form: {
    code: code,
    client_id: constants.Google.client_id,
    client_secret: constants.Google.client_secret,
    redirect_uri: constants.Google.redirect_uri,
    grant_type: 'authorization_code'
  }
};

//post require using express middleware request
request(reqObj)
  .success(function(data,error){
    data = JSON.parse(data[1]);
    resolve(data.access_token);
  });
comments powered by Disqus