OAUTH 2 Login with MultiValue BASIC Part 2
In the last article, I gave an overview of OAUTH 2 and the different ways login can be done. In this article I'll talk about some of the data files and routines you need to setup to make this all work.
The Control Record
Part of what makes OAUTH 2 work is the additional information that is sent along with each URL request. Each request requires a token to be included but - to make things more interesting - your system will also need to keep track of other security information in order to request the current tokens. This data needs to be available for OAUTH 2 to work correctly.
On my systems at International Spectrum, I have created a control file call OAUTH.CONTROL.SUPT. The control record will contain information needed when requesting new Auth Tokens to be sent to the web service. It also includes the rules for accessing these web services. The record structure is found in below.
001 - Consumer Key
002 - Consumer Secret
003 - Token Request URL
004 - Token Access URL
006 - Final Auth Token
008 - Refresh Token Returned
009 - Session Handle Returned
010 - Date Authorized
011 - Time Authorized
012 - Token Expires In (Seconds)
013 - OAUTH Version (2.0)
015 - Request Token (Internal Uses)
016 - Request Secret (Internal Uses)
017 - Request Pin Number
018 - Request Pin URL
019 - User Name
020 - User Password
021 - OAUTH Login Flow
022 - Extra OAUTH Field Names (Returned)
023 - Extra OAUTH Field Values (Returned)
025 - Redirect URL
Much of the information in this control record is a combination of transactional, or transitory, data only needed during the authentication process, and fixed parts that should always be unchanged. Since this file contains passwords and identifying information, you may decide to encrypt the individual fields or just encrypt the whole file using "Encryption at Rest."
Another thing to keep in mind is that your may have more than one OAUTH 2 control record per web service. A classic example of this is the Salesforce REST service. If you need your applications to update to Salesforce separately for each user, then you need to have a control record for each individual user. This may be required for audits and compliance.
You can see an example of what information is needed, and how Google+'s OAUTH 2 uses it in below.
001 xxxxxxxxxsbsqnupkl3p0bhrievchdo8j58.apps.googleusercontent.com
002 tGkk2QnhKbtpxxxxxxxx
003 https://accounts.google.com/o/OAUTH 2/auth?scope=email profile
004 https://accounts.google.com/o/OAUTH 2/token
013 2.0
021 native
Where Is All This Stuff?
Before you can do anything, you have to sign up with the web service you wish to authenticate with in order to get your consumer key and secret. Since I'm using Google here, and you'll likely want to try these examples as well, let's do a quick run through to get access to the Google API.
To get started with Google APIs, you need to go to Google's Development Console ( Figure 1 ): https://console.developers.google.com/project
Google has an in-depth help document that will take you through all the steps at https://developers.google.com/+/web/api/rest/oauth#acquiring-and-using-an-api-key
Once you have your project created and you have signed your life away via Google's "Terms and Agreements" dialog, you can get your keys from the "Credentials" sub-menu under the "APIs & Auth" section by adding your credential request. Be sure you are adding an "OAUTH 2.0 Client IDs" request. There are other kinds, but they are not needed for this example.
Now comes the bit where we code.
The HttpClient
Having done the manual steps, we now need to teach our computer how to talk with the web service. HttpClient is the communication tool for this job. Just to be clear, I am not talking about a web browser. They are not the same thing.
A web browser is a program that is used to render HTML. The browser does have an HttpClient inside of it, but just as an engine isn't a car, the two are not synonymous. HttpClient is a connector — the middleware — that links to a web service, asks for something, retrieves it, and then closes the connection. The information needs to be passed off to another program — for example: a web browser — which can do something with it.
There are lots of different options for building an HttpClient. It can be created using raw socket calls or using a Telnet client. All forms of MultiValue databases have a way to either establish an HttpClient or provide an interface with the tools needed to create an HttpClient.
You can find examples of source code for building HttpClients at:
http://www.intl-spectrum.com/resource/category/190/httpclient.aspx.
One tried and true solution is to use a command-line tool called cURL. A version of this command-line tool can be found on Windows, Linux, AIX, and most any other O/S. If you visit the URL above, you'll see an article and source code for using cURL as the HttpClient.
Once you have your HttpClient, you will need to wrap it into a new program. This code example can be found at the above URL, and is called IS.HTTPCLIENT. For the purposes of this article, I'm going to create a new program called IS.OAUTH.HTTPCLIENT with the same parameters as the example, except that I'm going to add an additional one called OAUTH.ID, which will contain the ID of the OAUTH.CONTROL.SUPT record.
SUBROUTINE
IS.OAUTH.HTTPCLIENT(OAUTH.ID,HTTP.METHOD,URL,HEADER.DATA,POST.DATA,RESP.HTTP.STATUS,RESP.HEADER.DATA,RESP.DATA,HTTP.ERROR)
The first thing that the program will need to do after reading the OAUTH.CONTROL record, is to check to see if there is any Bearer information to be added to the HTTP request. The Bearer information is the OAuth Token returned from the OAuth Login service which is stored in OAUTH.CONTROL<6>.
If you have Bearer information, you will need to check to see if it has expired using the Auth Date (in OAUTH.CONTROL<10>), Auth Time (in OAUTH.CONTROL<11>), and ExpiresIn Seconds (in OAUTH.CONTROL<12>). See an example of how to do this below.
HAS.EXPIRED = 0
*
BEGIN CASE
CASE (ITEM.OAUTH<CONTROL$ACCESS.TOKEN> EQ '')
HAS.EXPIRED = 3
CASE (ITEM.OAUTH<CONTROL$DATE.AUTH> EQ '')
HAS.EXPIRED = 2
CASE (ITEM.OAUTH<CONTROL$TOKEN.EXPIRE.IN> LE 0)
HAS.EXPIRED = 0 ;* Never expires
CASE 1
AUTH.EXPIRE = (ITEM.OAUTH<CONTROL$DATE.AUTH> * 86400)
AUTH.EXPIRE += ITEM.OAUTH<CONTROL$TIME.AUTH>
AUTH.EXPIRE += ITEM.OAUTH<CONTROL$TOKEN.EXPIRE.IN>
*
EXPIRE.CHECK = (DATE() * 86400)
EXPIRE.CHECK += TIME()
*
IF (EXPIRE.CHECK > AUTH.EXPIRE) THEN
HAS.EXPIRED = 1
END
END CASE
If the Token has not expired, then you need to add it to the HTTP headers so that IS.HTTPCLIENT can pass it along
LAST.HEADER.POS = DCOUNT(HEADER.DATA<1>,@VM) + 1
HEADER.DATA<1,LAST.HEADER.POS> = 'Authorization'
HEADER.DATA<2,LAST.HEADER.POS> = ITEM.OAUTH<CONTROL$ACCESS.TOKEN>
*
CALL IS.HTTPCLIENT(HTTP.METHOD,URL,HEADER.DATA,POST.DATA,RESP.HTTP.STATUS,RESP.HEADER.DATA,RESP.DATA,HTTP.ERROR)
Next…
Now that you have the basic structure and requirements to handle an OAuth web service, the next article will talk about the handshaking needed to acquire the Bearer token.