Forums >> Programming >> Web Programming >>
Google Sign-In Integration with your IBM i RPG Web Application



Posted:
bvstone

Google Sign-In Integration with your IBM i RPG Web Application

 
Google Sign-In Integration with your IBM i RPG Web Application

More applications these days require some sort of authentication or method of "signing on" to the application so that we can track the user and customize the display for each individual.

This has been done in the past using hidden fields in forms (for example, each dynamic page will contain a hidden field with a user id) or even more useful, using Cookies to track each user.

Now there seems to be a push to go with "one sign on for everything."  The major players offering up this service are Google, Facebook and Linked In.  You've probably seen sites where you can create a user id, but they also allow you to "Sign on with Google" or one of the other players.  Not only does this make things easier for visitors of the sight by allowing them to sign up and/or sign on with a single click (not having to remember yet another user id and password), it increases security through the use of OAuth authentication and may also reduce the anonymity.

Recently for the FieldExit site we added this feature as well, allowing you to sign on with a Google ID.  This means you don't have to go through the entire signup process.  In as little as one click you are able to sign in to this sight and share your IBM i related applications, solutions and ideas.

Google has made this quite simple with some JavaScript functions that you can include in your web application.  To make things more secure you also should have a back end process that communicates with Google's servers to verify the information.  While this isn't necessary, Google strongly advises it be done.  We chose to go that route and decided to share how we implemented this Google Sign-In process here at FieldExit.com.

Getting Started

The first step, as outlined in the documentation provided by Google, is to create a Project and Client ID for your Sign-In application.  This is easier than it sounds and Google provides some decent documentation for this.

Once that is done, we need to include a JavaScript library in our application.  Because we use Server Side Includes (SSIs) to piece together our sites, adding this to each page on our site is a simple as adding a new line to our "JavaScript" SSI file:

<!-- Google Login OAuth -->
<script src="https://apis.google.com/js/platform.js?onload=onLoadGoogle" async defer></script>
<!-- end Google Login OAuth -->

There are probably some new attributes and tags here that most of us aren't used to, and if you want to read about them feel free.  But the main thing we need to notice is the onload query that is used here.  This tells the browser that once this is loaded to run a specific JavaScript function.  In this case the onLoadGoogle function that we included in our standard JavaScript application looks like this:

function onLoadGoogle() {
	gapi.load('auth2', function() {
		gapi.auth2.init().then(function () {
			//console.log('Init complete.');
			gapi.signin2.render('myGoogleSignin', {
				'scope': 'https://www.googleapis.com/auth/plus.login',
				'width': 200,
				'height': 50,
				'longtitle': true,
				'theme': 'dark',
				'onsuccess': onSignIn,
				'onfailure': onSignInFailure
			});	
			showUserPicture();
		});
	});
	
}

What this does is loads the Google APIs and render the "Sign-In with Google" button on our sign in page.  It also calls another function named showUserPicture() which we implemented so that when you do sign in with a Google ID, the picture that normally shows on your Google Applications also shows up on our page.  We will discuss that later.  Also, this method we're using is a little more involved than the simple examples Google gives.  But that is so we have more control over what is going on.  How simple or involved you choose to go will most likely change once you get deeper into an application.  But, using this method we have more control and access to the Google ID and information that is available.

Next, we needed to add some Meta Tags that are used by Google to Identify ourselves and our application ID.  Again, this meant adding only a couple of lines to one of our SSI "Puzzle Pieces" so that they appeared on every page on our site:

<meta name="google-signin-scope" content="profile email">
<meta name="google-signin-client_id" content="ourGoogleApplicationID">

The "ourGoogleApplicationID" is replaced by the application ID we created earlier.  This is used by the Google APIs to know who we are (our website) and what our application is named, and what Google APIs are used in this application.

Signing In

Once these things are in place we need to add our sign in button to our web page.  When a user clicks the Log In or Sign Up link in the top right hand corner of the site, they are taken out our login page.  At first it just allowed you to sign in if you already had an ID for FieldExit or allowed you to create a new account.

But by adding this simple HTML, we now have a button that allows you to sign in using your Google Account:

<div id="myGoogleSignin"></div>
<span class="small2">
  If you don't see a Google Sign in button, 
  try using the latest version of Chrome or Firefox
</span><br>

Now, first I'd like to explain the little note about using the latest version of Chrome or Firefox.  When we first started using this functionality we were developing using Chrome.  Once we got things where we wanted we tested in Firefox.  The Google Sign In button did not appear.  Then we tested in IE.  Again, nothing.  We refreshed our version of Firefox and then things started working.  But we still have no luck with IE.  In doing some research the issue seems to be because Google recently changed to a two step sign in process instead of the previous one step where you'd enter your ID and password on the same page.  So, Firefox is caught up, but IE has not yet.

With that out of the way (and hopefully helping others from spending hours trying to figure out why it wasn't working) we see that we have a DIV container named "myGoogleSignin".  If we look back to the JavaScript that is run when the Google Library is loaded we will see a reference to this div name in the render() method:

gapi.signin2.render('myGoogleSignin', {
				'scope': 'https://www.googleapis.com/auth/plus.login',
				'width': 200,
				'height': 50,
				'longtitle': true,
				'theme': 'dark',
				'onsuccess': onSignIn,
				'onfailure': onSignInFailure
			});

This allows us to customize the button and also instruct the browser what to do when a user clicks on the button.  The onsuccess and onfailure tags tell the browser which JavaScript functions to run in the case of success and failure.

The "Real Work"

Now, in order to understand what happens we need to first look at the HTML form that is used on our sign in page.  In the next source clip we've simplified it and removed some of the formatting so that the important pieces are more obvious:

<form id="updateForm" action="/forum/login" method="POST">
  <input name="id" id="loginID" type="hidden">
  <input name="googleid" id="googleid" type="hidden">
  User ID:<input class="validateField" id="userid" name="userid">
  <div class="error" id="useridError"></div>
  Password:<input class="validateField" id="password" name="password" type="password">
  <div class="error" id="passwordError"></div>
  <a class="button" id="loginButton" href="#">Log In</a>
</form>

First we see we have two hidden fields, id and googleid.  The id field is used internally for tracking the user, but the googleid field is used to store the Google ID that is retrieved when a user clicks on the Sign In with Google Button.  Remember the onsuccess tag of our button?  It called a JavaScript function named onSignIn.  That function looks like this:

function onSignIn(googleUser) {
	var profile = googleUser.getBasicProfile();
	var id_token = googleUser.getAuthResponse().id_token;
	$('#userid').removeClass('validateField');
	$('#password').removeClass('validateField');
	$('#googleid').val(id_token);
	$('#loginID').val(randomString(256));
	showMessage('Logging in with Google ID ' + profile.getEmail());
	$('#updateForm')[0].submit();
	//console.log('Logged in as: ' + googleUser.getBasicProfile().getName());
}

We're using jQuery to do a few things.  First we remove the validateField class from the user id and password fields so that our validation routines won't be called when the form is submitted.  Next, we set the value of the hidden field googleid to the value of the ID token retrieved using the Google functions.  As stated in their documentation you can also retrieve all the other information you want, but for security reasons you should only use the ID and then validate that on the back end to make sure you're application is getting the real data from Google, and the information isn't being "spoofed" by another entity.

Finally, we show a message (which uses BlockUI, a jQuery library) to show the user we're signing on with their email address and we submit the form which calls an e-RPG program named LOGIN.

The LOGIN program validates the data, but most importantly uses a web service application (in this case our GETURI application) to make an HTTP request to Google to validate the ID that we retrieved from our web page.  The important part of the application (which uses the eRPG SDK) is as follows:

inUserID = #getData('userid');
inPW = #getData('password');
inID = #getData('id');
inGoogleID = #getData('googleid');


if (inGoogleID <> ' ' );
  exsr $GoogleID;
else;
  // do normal login
endif;

//-------------------------------------------------------------/
// Get Google ID (Clicked Google Signon Button)                /
//-------------------------------------------------------------/
begsr $GoogleID;

  #pushLib('GETURI');

  Clear GetUri_In;
  GetUri_Data = 'id_token=' + %trim(inGoogleID);
  GI_URI = 'https://www.googleapis.com/oauth2/v3/tokeninfo';
  GI_Port = 443;
  GI_Data = '*PARM';
  GI_ReqMeth = 'GET';
  GI_SSL = '*YES';
  GI_SprHead = '*YES';
  GI_CCSID = 1252;
  GI_Debug = '*YES';
  GI_DebugFile = '/tmp/googlesignondebug.txt';
  GI_OutType = '*RETURN';
  GI_CodPag = 1252;

  callp(e) #geturicall(GetUri_In:GetUri_Out:GetUri_Head:GetUri_Data:
                       GetUri_MsgCd:GetUri_Msg);

  if (%error);
    errMsg = 'GETURI Error: ' + %trim(GetUri_Msg);
    exsr $Error;
  endif;

  #js2_init();
  rc = #js2_loadString(%addr(GetUri_Out):
                       %len(%trim(GetUri_Out)):errMsg);

  if (rc <= 0);
    errMsg = 'Error retrieving information from Google.';
    exsr $Error;
  endif;

  #js2_setValue('tag':'error:message');
  errMsg = #js2_getDataStr();

  if (errMsg <> ' ');
    #js2_cleanup();
    exsr $Error;
  endif;

  #js2_setValue('tag':'aud');
  clientID = #js2_getDataStr();

  if (clientID <> ourClientID);
    #js2_cleanup();
    errMsg = 'Invalid client ID.';
    exsr $Error;
  endif;

  #js2_setValue('tag':'sub');
  gID = #js2_getDataStr();

  if (gID = ' ');
    #js2_cleanup();
    errMsg = 'Google ID was blank.';
    exsr $Error;
  endif;

  #js2_setValue('tag':'email');
  gEMail = #js2_getDataStr();

  if (gEMail = ' ');
    #js2_cleanup();
    errMsg = 'Google EMail was blank.';
    exsr $Error;
  endif;

  #js2_cleanup();

  #popLib('GETURI');

endsr;

To validate the Google ID retrieved we call a specific Google API using GETURI (or any other HTTP client).  We then check for errors that may have occurred during the call.  If everything worked we should receive some JSON data that looks like the following:

{
 "iss": "https://accounts.google.com",
 "sub": "110169484474386276334",
 "azp": "1008719970978-hb24n2dstb40o45d4feuo2ukqmcc6381.apps.googleusercontent.com",
 "email": "billd1600@gmail.com",
 "at_hash": "X_B3Z3Fi4udZ2mf75RWo3w",
 "email_verified": "true",
 "aud": "1008719970978-hb24n2dstb40o45d4feuo2ukqmcc6381.apps.googleusercontent.com",
 "iat": "1433978353",
 "exp": "1433981953"
}

In our case we are using our JSON parser named JSONTOOL.  We load the data returned from GETURI into our JSON parser and then check to see if there is an error message in the JSON.  Because we've worked with a few of Googles APIs with our GreenTools for Google Apps (G4G) application we know that Google tends to return error messages in a standard format. 

If there is no error we retrieve the ID token from the JSON data and compare it against the ID token that was read into the application.  If that checks out (which means it's not being "spoofed"), we then parse out the email address and use that as the ID for the user that is signed in.

Once we have this email address following this back end server processing we can be sure that it's a valid account and allow the user to be signed on in which case they can add content on our site.

Showing the User's Picture

Now that we've been through the "meat and potatoes" I thought I would also explain something else we chose to do.  As you've noticed when you're signed onto a Google web page (such as GMail, Google Drive, etc) you'll see your avatar in the corner.  We thought we would also add that to our site.

If you recall back to the JavaScript function that is called when the Google APIs are loaded we were calling a function named showUserPicture().  The source for that looks like the following:

function showUserPicture() {
	var auth2 = gapi.auth2.getAuthInstance();

	if (auth2.isSignedIn.get()) {
		var user = auth2.currentUser.get();
		var profile = user.getBasicProfile();
		var imgUrl = profile.getImageUrl();
		var name = profile.getName();
		var email = profile.getEmail();
		$('#googleIDPicture').html('<img src="' + imgUrl + 
           '" style="border-radius: 16px; height: 32px; width: 32px; ' + 
           'vertical-align:middle;" title="Google Account: ' + name + 
           '\n(' + email + ')">');
	}

}

Using available Google APIs we are able to retrieve the information of the user signed in with their Google Account.  Then we simply update the contents of a DIV container in our header portion of our web site (again, using SSI) with the image and information of the user.  If you sign in to FieldExit with your Google ID, you should see your Google ID avatar in the top corner.

For most users the text will show their email address and not a user id.  But, for users that already have an account on FieldExit and wish to use their Google ID, we can easily map your Google ID to your already existing FieldExit ID, as we have done here.

Signing Out

If a user wishes to sign out Google provides a function for that as well.  Our site provides a "Log Out" link at the top right that a user can click to sign out.  With this new sign in process we also need to make sure we disconnect from the Google account if that was the sign on method.  So, when the user clicks the Log Out link, the following JavaScript is run:

function logout() {

	if (confirm('Are you sure you want to log out?')) {
		var auth2 = gapi.auth2.getAuthInstance();
		auth2.signOut().then(function () {
			//console.log('User signed out.');
		});
		
		return true;
	} else {
		return false;
	}
}

The user is prompted with a confirmation window and if the choose to sign out we call the signOut() application for the specific Google instance that is being used.  

So, as you can see implementing a 3rd party sign on to your application is not only fairly simple and straightforward, it can make your user's experience even better as it is one less user id and password that they will have to remember.  And if done correctly, should provide secure authentication and even user tracking to your applications!


Last edited 07/12/2015 at 16:44:24


Reply




Copyright 1983-2017 BVSTools
GreenBoard(v3) Powered by the eRPG SDK, MAILTOOL Plus!, GreenTools for Google Apps, jQuery, jQuery UI, BlockUI, CKEditor and running on the IBM i (AKA AS/400, iSeries, System i).