Simple Comments and OpenID (1/5) | WebReference

Simple Comments and OpenID (1/5)

current pageTo page 2To page 3To page 4To page 5

Simple Comments Meets OpenID

By Dan Ragle

[This is a supplementary article for the Simple Comments script. For general information pertaining to the script, including the latest release download, system requirements, etc., visit the Simple Comments main page.]

Beginning with version .960, Simple Comments supports visitor logins using OpenID identifiers. For those unfamiliar with OpenID in general, I described the basics in the initial v.960 release bulletin, and I refer you to that article for relevant background information. You might also have a look at the OpenID introduction at the OpenID Wiki site. It does a good job of describing the details of the OpenID process without getting bogged down in specification-speak.

In this article, I'll discuss some of the specific architectural and developmental considerations that went into the process of OpenID-enabling the Simple Comments system. Whether you're a Simple Comments user or just a Perl coder with an interest in OpenID implementations you're welcome to follow along. Be sure to add your thoughts and experiences in our comment submission form, located at the end of the article!

Relying Parties and OpenID Providers

As mentioned in the previous article, OpenID authentication requires communication between three primary players: The end user who wishes to authenticate themselves to a site or application, the site or application that needs to verify the user and a 3rd party willing to vouch for the end user's authentication. In OpenID parlance, the site or application that needs to verify the end user is called a Relying Party, or RP, and the site willing to vouch for the user's authentication is called an OpenID Provider, or OP. The ID itself--i.e., the string the end user is claiming as their own, unique identifier--is called an OpenID Identifier, or Claimed Identifier. The goal of OpenID authentication is to positively verify that the identifier the end user is claiming as their own actually belongs to them (and no one else).

Obtaining this verification requires both direct and indirect communications between the RP and the OP.

Direct vs. Indirect Communications

Direct communications occur between an RP and OP at various points in the OpenID protocol, and provide a means for the RP to 'discuss' something privately with the OP in a way that others--including the end user--cannot interfere with. These communications are straightforward in Perl, and can generally be performed by way of the LWP::UserAgent module. For example, here's a snippet that posts a small amount of data (in the same general manner that a Web form might) to the fictitious application

use LWP::UserAgent ();
# Initialize the useragent
my $ua = LWP::UserAgent->new();
# Set some default values
push (@{$ua->requests_redirectable},'POST');
$ua->agent('LWP Test');
# Setup data to be posted
my $post_form = { 'name' => 'foo',
                  'pass' => 'bar' };
# And send the post
my $res = $ua->post('', $post_form);
# Test the result
if ($res->is_success) {
   print $res->content;
else {
   print 'Error retrieving document: ' . $res->status_line;

Following the initialization of the LWP::UserAgent object, we set up a default timeout value, allow post requests to be redirectable (i.e., enable the module to follow server redirects on post requests, if they're provided), and set up our user-agent string (which will typically show up in the host server's logs). The post method handles all the dirty work, returning an HTTP Response object with the results of the communication (i.e., whatever information the host returned as a result of the request). For full details I refer you to your perldoc documentation for LWP::UserAgent.

With OpenID, we don't know who the OP we'll be communicating with is, nor do we know if we can trust them. Original OpenID author Brad Fitzpatrick recommends the use of the Perl module LWPx::ParanoidAgent, a subclass of LWP::UserAgent that adds such features as a global timeout and protection from connecting to local IP ranges, among other goodies. Simple Comments automatically uses LWPx::ParanoidAgent if it's available; and will otherwise fall back to LWP::UserAgent for direct OP communications.

Indirect communications occur when the end user must be redirected to the OP (and vice versa for the return communication), with additional information provided by the RP/OP going along for the ride. Indirect communications are a critical part of the OpenID specification, since they provide both privacy for the end user (the RP cannot see the resulting interactions between the OP and end user), and they provide a means for the OP to recognize if the end user has already previously authenticated themselves. Since the end user is redirected to the OP, they (the OP) have access to any previously saved session cookies or identifiers that may be stored in the user's browser1 for that OP.

To redirect an end user's browser to the OP, the RP first determines where the end user needs to be redirected to (i.e., what URL), and then builds that URL including any additional parameters it needs to send to the OP. Then, the redirect itself can be as simple as executing the redirect method of the CGI object. The whole process might look something like this:

use CGI ();
# Initialize the CGI object
my $cgi = CGI->new();
# Setup the redirection URL
my $redirectURL = '';
# And send the user to it
print $cgi->redirect($redirectURL);

When a browser receives a redirect request in this manner, it automatically takes the user to the new target URL ($redirectURL in the code above). This typically happens in a transparent fashion; in some cases the end user will only know the redirect has happened by checking the address in the address bar of their browser. This type of GET redirect--where any additional parameters are tacked onto the end of the target URL--is sufficient for most OpenID redirection needs, and should be used whenever possible.

Some browsers, however, limit the length of data that can be included in a GET request. Thus, an alternative form of redirect may be used in which a standard HTML page is presented to the end user with a form that points to the target URL and a button asking the user to click to proceed to the target URL (the form is nearly always autosubmitted using JavaScript, so the user only needs to click if JavaScript is unavailable in their browser). While not nearly as glamorous as the redirect above, this redirect method has the distinct advantage in that additional parameters that need to be communicated to the RP can be presented as hidden parameters of the form, as opposed to presented literally as part of the URL. i.e., following the above example, the HTML form sent to the browser might look something like this:

   <title>OpenID Authentication Redirect</title>
   <script type="text/javascript">
      window.onload=function() { document.forms['openid_redirect_form'].submit(); };
<p>We are about to redirect you to another site. If you are 
   not redirected automatically then you can do so directly 
   by clicking the <strong>Continue</strong> button below.</p>
<form action="" 
      method="post" name="openid_redirect_form">
   <input type="Submit" value="Continue">
   <input type="hidden" name="param1" value="value1" />
   <input type="hidden" name="param2" value="value2" />

Simple Comments includes a function (called openid_redirect) which examines the length of the URL that you wish to redirect to. If that URL is longer than 2,000 characters, a form similar to the above is built and presented to the end user. Otherwise, the end user is redirected using the preferred CGI method described above.

On the next page, we'll examine the two different types of OpenID Identifiers a user may present to your login form and introduce you to XRI proxy resolvers.

1. To simplify the discussion, I'll assume throughout this article that the end user is using a standard Web browser to communicate/authenticate with the RP/OP, but theoretically, the end user might be using any type of user agent that supports redirection and HTTP/HTTPS communications.

current pageTo page 2To page 3To page 4To page 5

Created: July 31, 2008
Revised: July 31, 2008