bvstone

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

Posted:

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

Update 3/11/2022 - Google has updated this function to use slightly different methods and is requiring updates. 

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://accounts.google.com/gsi/client" async defer></script>
<!-- end Google Login OAuth -->

This allows the code from Google to be loaded into your 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="g_id_onload"
   data-client_id="your.google.application.id"
   data-callback="handleCredentialResponse">
</div>
<div class="g_id_signin" data-type="standard"></div>

We now need a Javascript function to handle the credential response (handleCredentialRespose):

function handleCredentialResponse(response) {
   // decodeJwtResponse() is a custom function defined by you
   // to decode the credential response.
   const responsePayload = decodeJwtResponse(response.credential);

   //console.log('Full Name: ' + responsePayload.name);
   //console.log('Given Name: ' + responsePayload.given_name);
   //console.log('Family Name: ' + responsePayload.family_name);
   //console.log("Image URL: " + responsePayload.picture);
   //console.log("Email: " + responsePayload.email);

  $.cookie("gbfGoogleID",JSON.stringify(responsePayload));
	var id_token = response.credential;
	$('#userid').removeClass('validateField');
	$('#password').removeClass('validateField');
	$('#googleid').val(id_token);
	$('#loginID').val(randomString(256));
	showMessage('Logging in with Google ID ' + responsePayload.email);

	$('#updateForm')[0].submit();
}

What is returned is a JWT token that can be parsed for individual items.  But in our case we want to pass the entire token to our RPG program as we will call a Google Verification endpoint to validate that the data isn't being spoofed, and actually came from Google.

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 (JWT token) that is retrieved when a user clicks on the Sign In with Google Button. 

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;

  docNode = yajl_buf_load_tree(%addr(GetUri_Out:*DATA):          
                             %len(GetUri_Out):                 
                             errMsg);                          
                                                               
  if (docNode = *NULL);                                          
    errMsg = 'Error retrieving information from Google.';        
    exsr $Error;                                                 
  endif;                                                         

   errNode = YAJL_object_find(docNode:'error');                    
                                                                 
   if (errNode <> *NULL);                                          
     val = YAJL_object_find(errNode:'message');                    
     errMsg = yajl_get_string(val);                                
   endif;                                                          
                                                                 
   if (errMsg <> ' ');                                             
     yajl_tree_free(docNode);                                      
     exsr $Error;                                               
   endif;                                                       
                                                             
   val = YAJL_object_find(docNode:'aud');                       
   clientID = yajl_get_string(val);                             
                                                             
   if (clientID <>                                              
     'xxzxx' +        
     '.apps.googleusercontent.com');                          
     errMsg = 'Invalid client ID.(' + %trim(clientID) + ')';    
     yajl_tree_free(docNode);                             
     exsr $Error;                                         
   endif;                                                 
                                                       
   val = YAJL_object_find(docNode:'sub');                 
   gID = yajl_get_string(val);                            
                                                       
   if (gID = ' ');                                        
     errMsg = 'Google ID was blank.';                     
     yajl_tree_free(docNode);                             
     #popLib('GETURI');  
     exsr $Error;                                   
   endif;                                           
                                                 
   val = YAJL_object_find(docNode:'email');         
   gEMail = yajl_get_string(val);                   
                                                 
   if (gEMail = ' ');                               
     errMsg = 'Google EMail was blank.';            
     yajl_tree_free(docNode);                       
     exsr $Error;                                   
    endif;                             
                                    
   yajl_tree_free(docNode);           

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": "xxxxxx-hb24n2dstb40o45d4feuo2ukqmcc6381.apps.googleusercontent.com",
 "email": "billd1600@gmail.com",
 "at_hash": "X_B3Z3Fi4udZ2mf75RWo3w",
 "email_verified": "true",
 "aud": "xxxxxx-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.

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 no longer 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 to simply clear some cookies used for sessions:

function logout() {

	if (confirm('Are you sure you want to log out?')) {
      $.cookie("gbfuser", null, { path: '/' });  
      $.cookie("gbfGoogleID", null, { path: '/' });  
      window.location.assign("/"); 
	  	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 03/11/2022 at 11:12:35



Latest Posts:

Microsoft Office 365 Servers and Random Errors Issue Microsoft Office 365 Servers and Random Errors Issue
Posted by November 14, 2022
BVSTools >> BVSTools Software Discussion >> Email Tools (MAILTOOL) Specific Discussion
Sending/Resending Emails Using a MIME File with MAILTOOL Sending/Resending Emails Using a MIME File with MAILTOOL
Posted by November 8, 2022
BVSTools >> BVSTools Software Discussion >> Email Tools (MAILTOOL) Specific Discussion
Sending an HTML Email on Your IBM i Using MAILTOOL Sending an HTML Email on Your IBM i Using MAILTOOL
Posted by November 1, 2022
BVSTools >> BVSTools Software Discussion >> Email Tools (MAILTOOL) Specific Discussion
Transferring License Keys from One System to Another Transferring License Keys from One System to Another
Posted by October 31, 2022
BVSTools >> BVSTools Software Discussion
Calculating the Size of a File Before Base64 Encoding Calculating the Size of a File Before Base64 Encoding
Posted by August 13, 2022
Programming >> RPG Programming
GreenTools for Microsoft Apps (G4MS) v9.12 Now Includes Function to Send Emails using MIME File GreenTools for Microsoft Apps (G4MS) v9.12 Now Includes Function to Send Emails using MIME File
Posted by August 11, 2022
BVSTools >> BVSTools Announcements >> GreenTools for Microsoft Apps (G4MS) Specific Announcements
GreenTools for Google Apps (G4G) v15.20 Now Supports Shortcuts GreenTools for Google Apps (G4G) v15.20 Now Supports Shortcuts
Posted by August 6, 2022
BVSTools >> BVSTools Announcements >> GreenTools for G Suite (Google Apps) (G4G) Specific Announcements
GreenTools for Microsoft Apps (G4MS) Groups Admin Authority Instructions GreenTools for Microsoft Apps (G4MS) Groups Admin Authority Instructions
Posted by July 26, 2022
BVSTools >> BVSTools Software Discussion >> GreenTools for Microsoft Apps (G4MS) Specific Discussion
GreenTools for Microsoft Apps (G4MS) v9.10 Now Includes OneDrive Functions that Work With Groups/Shared Drives GreenTools for Microsoft Apps (G4MS) v9.10 Now Includes OneDrive Functions that Work With Groups/Shared Drives
Posted by July 19, 2022
BVSTools >> BVSTools Announcements >> GreenTools for Microsoft Apps (G4MS) Specific Announcements
GreenTools for Google Apps (G4G) v15.10 Now Includes Drive Functions that Work With Shared Drives GreenTools for Google Apps (G4G) v15.10 Now Includes Drive Functions that Work With Shared Drives
Posted by July 15, 2022
BVSTools >> BVSTools Announcements >> GreenTools for G Suite (Google Apps) (G4G) Specific Announcements
GreenTools for Microsoft Apps (G4MS) v9.00 Now Offers Functions to Bypass Registration Command and BVSTools Landing Page GreenTools for Microsoft Apps (G4MS) v9.00 Now Offers Functions to Bypass Registration Command and BVSTools Landing Page
Posted by July 4, 2022
BVSTools >> BVSTools Announcements >> GreenTools for Microsoft Apps (G4MS) Specific Announcements
What Objects Should I Omit from Replication to Ensure My License Keys Work on my HA/DR System? What Objects Should I Omit from Replication to Ensure My License Keys Work on my HA/DR System?
Posted by June 27, 2022
BVSTools >> BVSTools Software Discussion
GreenTools for Google Apps (G4G) v15.00 Now Offers Functions to Bypass Registration Command and BVSTools Landing Page GreenTools for Google Apps (G4G) v15.00 Now Offers Functions to Bypass Registration Command and BVSTools Landing Page
Posted by May 3, 2022
BVSTools >> BVSTools Announcements >> GreenTools for G Suite (Google Apps) (G4G) Specific Announcements
How Do I Switch From MAILTOOL Plus to GreenTools for Google (OAuth 2.0) or Microsoft Office 365? How Do I Switch From MAILTOOL Plus to GreenTools for Google (OAuth 2.0) or Microsoft Office 365?
Posted by April 18, 2022
BVSTools >> BVSTools Software Discussion >> Email Tools (MAILTOOL) Specific Discussion
PTFs Issued for SSL/TLS Issues PTFs Issued for SSL/TLS Issues
Posted by March 12, 2022
IBM Power Systems >> PTF Watch

Reply




Copyright 1983-2020 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).