A Quick and Simple Alternative to GCF / CPIP
Can you get three GCF connectors working in 10 minutes? Don't feel bad - its probably not possible. Restarting the webserver and CPIP systems alone take half that time. What if there were an alternative to GCF that allowed you to produce connectors that this rate? After a brainstorming session, we developed a simple solution that allowed us to do just that.
What is a GCF connector actually doing?
A GCF connector has several modes. The only one that we are concerned with at the moment, is the pickup.response mode. To give you a quick review, the pickup.response method has a mock HTML form written in the system.properties file. The CPIP server then spits this out in a user's browser, and Javascript submits the form on the user's behalf. Sounds great, except that you have to do all of those other horrible little tasks, like:
- Appending your system to "es.systems"
- Placing your properties file inside of cpipconnector.properties
- Importing your configman settings for your system into "es.*"
- Creating an XML file to verify authentication, and logout
- Bouncing the webserver and CPIP server
You may think, "This is worth doing to have a reliable and secure system". If you feel this is the case, I encourage you to try something if you are using pickup.response:
Log into your Portal, and click a system that uses GCF's pickup.response. Now, disable Javascript, and look at your browser's history. Go back to the history item that is your system's mock form. Now, view the source. You will see that your username and password are in the source in clear text. This means that if anyone logs in using your Portal to any system on a public computer, then entire process is compromised!
So after all that work, you still have a system with a crippling security hole.
Another Approach
First things first. Our simplified approach assumes a few things about the system you are logging into:
- The username and password are predictable
- You don't care about session management (when you log out of your Portal, it doesn't affect your external system)
- The password is retrievable in a cleartext format (ex Banner's GOBTPAC PIN)
- You have some mechanism to know who is logged into the Portal (cookie you set, etc)
- You can get around the SOP of Javascript to do an AJAX request (ie proxy server, etc)
FYI - we are currently working on a more advanced system that address the first two limitations.
How does it work? We create an HTML file (thats right - plain ol' HTML - no server restarting needed!) that is a mockup of the login form for the website. Instead of placing credentials inside the form, we will fill this in later via AJAX. Doing this keeps the credentials out of the source code - fixing our security hole from GCF. We fire off an AJAX request when you pull up the HTML page, and it's response swaps out our empty username and password input fields with completed ones for the current user.
For the security conscience, this AJAX request is only served with a valid cookie that we check for in the user's browser. Once the user closes the window, the cookie is destroyed, so the system is effectively disabled from people snooping around in the history later.
Take a look at this HTML connector:
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Please wait...</title>
<script type="text/javascript" src="http://your.portal.name/js/prototype.js"></script>
</head>
<body>
<iframe id="iframe" src="https://clayton-csm.symplicity.com/students/" style="display: none;"></iframe>
<form action="https://clayton-csm.symplicity.com/students/" method="post" id="form">
<input type="hidden" name="username" id="username" />
<input type="hidden" name="password" id="password" />
</form>
<script type="text/javascript">
$('iframe').onload = function() {
new Ajax.Request('/proxy.jsp?url=https://your.lookup.website/credential/studentid/pin', {
onComplete: function() {
$('form').submit();
}
});
}
</script>
</body>
</html>
Lets walk through this:
- There is a script tag to load Prototype.js for abstracting AJAX requests
- A hidden iFrame to load the cookies, etc from the actual website
- A mock form (with empty credentials). These form fields have id attributes
- An AJAX request is fired when the iframe finishes loading the external website. This request goes to a service you can run anywhere and returns a response that updates the form fields with a filled in username and password
- When the AJAX stuff is finished, it submits the form automatically.
The AJAX that should be returned by this request would look something like this:
$('username').value = 'bsimpson';
$('password').value = 'sEcReTpAsSwOrD';
This response is evaluated, and updates the form accordingly. The form is then submitted with all the information filled in for the user.
Earlier I mentioned that you need a mechanism for knowing who is logged into the Portal. Ours is a by setting a cookie. This cookie is a domain-wide cookie (the domain is ".clayton.edu"), and our external website can pick read this, verify its contents, and serve the request based on the information contained in the cookie. The only way to set this cookie is by logging into the portal. More information is below in the resources section.
Try it for yourself - hopefully you won't have to write a GCF connector ever again!
Resources
- Javascript SOP - http://en.wikipedia.org/wiki/Same_origin_policy
- Prototype.js - http://www.prototypejs.org/api/ajax/request
- Setting a cookie - http://www.lumdev.net/node/2672
Feedback is welcome! We feel like we are on the right track with this process, but this approach leaves much room for improvement in our next version.

Another Approach
Check out this article posted to LumDev a couple of years ago: http://www.lumdev.net/node/606
Basically it allows you to pass parameters to your CPIP connector via cookie and configure things like destination URL, name of the username field, name of the password field and any extra fields needed for authentication. The good thing about this is that aside from setting the cookie values, it doesn't require any coding to implement or any specific AJAX library; it integrates into the current GCF with any connectors you may be using and similar to your solution allows you to CPIP on the fly by making HTML updates without any server restarts or mucking around with additional external systems.
The bad thing about is that it is (like CPIP in general) locked to a specific credential set. (This will work for most situations where your users are authenticating to systems that use the same back-end directory). Your way has more flexibility in this regard.
You wrote: "You will see that your username and password are in the source in clear text." One note on this: if someone logs in and doesn't have a valid session they shouldn't be able to access this page; and the browser shouldn't be caching it due to various pragma no cache headers that are used normally with GCF.
With any luck you've made your entire CPIP process HTTPS so transmission shouldn't be a problem either.
Interesting
When a user clicks a link for a SSO application using your method, are you setting the cookies on the fly right before this code is executed?
Also, do pickup.response connectors honor the session management features of the more full featured methods? If so, does your method require a different system.xml file for each external application?
Very interesting approach - you have significantly reduced the setup time of adding new systems into your Portal. Thanks for sharing.
Another Approach to CPIP
>When a user clicks a link for a SSO application using your method, are you setting the cookies on the fly right
>before this code is executed?
That's correct.
>Also, do pickup.response connectors honor the session management features of the more full featured
>methods? If so, does your method require a different system.xml file for each external application?
This would be nice, but unfortunately, there is no serious session management. One solution to this that we've created is that whenever we launch a connection to an external system, we do so in a separate window. Luminis maintains a list (in JS) of windows that it cycles through when you logout and closes each one. We add each of our external systems to this window list. (There are definitely situations where this is insufficient; these are for sites that maintain logged in state even when you've closed their window. I can think of a few possible ways to get around this, but we haven't started looking at this seriously as of yet).
Brian
Window lists
>Luminis maintains a list (in JS) of windows that it cycles through when you logout and closes each one.
Interesting - I didn't know that it did this. Is there any specific naming convention for these windows, or a specific JS call to add my applications to this list? A quick glance at our portal shows calls to a function OpenWin() which is defined in nested-tables.xsl. (Inline Javascript, and some of the worst written code I have ever seen at that...)
In this function, it shows a call to "navigator.cpChildWindowList[navigator.cpChildWindowList.length] = ###Window". is this what you are referring to?