devxlogo

OpenID and Rails: Authentication 2.0

OpenID and Rails: Authentication 2.0

penID is a service, framework, and protocol that is revolutionizing the realm of user authentication and identity services. Started in 2004 by Brad Fitzpatrick, OpenID is now a mature framework supported by major Internet organizations such as AOL, Google, IBM, Microsoft, VeriSign, and Yahoo. It offers a distributed, reliable, and open way for web sites to authenticate their users and saves web developers from the need to write yet another piece of authentication code.

This article describes what OpenID is and how it can benefit your web site development. It provides an example by demonstrating OpenID integration with the Ruby on Rails 2.0 framework.

What You Need
Ruby version 1.8.4 or later (1.8.6 recommended)
RubyGems (version 1.0.1 recommended)
Rails 2.0.2
MySQL
ruby-openid library

One Web-Wide Identity for the User
Authentication in web applications is the process of verifying someone’s identity and confirming that the user accessing the application really is who he claims to be. The most popular method of authentication is using a username and password: the username declares the user’s identity, while the password represents the verification token that ensures that only its holder can claim the identity of the associated username. The vast majority of web sites that need to verify their users’ identities adopt this form of authentication, each one implementing its own login or verification component.

Unfortunately, the username and password combination leads to extreme fragmentation of user credentials: users end up having tens of usernames and passwords for multiple sites (bank account, e-mail, blog, company web site, etc.). OpenID aims to solve this fragmentation once and for all. As stated on its web site, OpenID offers a way to define “a single digital identity across the Internet,” eliminating the need for multiple usernames across different web sites. OpenID keeps a user’s credentials in a single place, where other web sites can query them whenever they need to verify that user’s identity.

The OpenID service encompasses a few different entities that interact with each other:

  • An OpenID provider stores user credentials and exposes a standard API that other web sites can query to authenticate users. Popular free providers are myOpenID and VeriSign.
  • A relying party is a web site that wants to verify a user’s identity.
  • The end user is the person trying to authenticate him- or herself on the web site.

In the OpenID world, a user is uniquely identified by an OpenID identifier, which generally is a plain URL (think of it as the person’s unique username). The OpenID identifier usually points to the provider that is storing the user’s credentials. An example is yourname.myopenid.com.

Figure 1 and Figure 2 illustrate the differences between a traditional authentication scheme and the OpenID one, respectively.


Figure 1. A Traditional Authentication Scheme: The user has different credentials for each web site.
 
Figure 2. The OpenID Authentication Scheme: The user has only one set of credentials, shared among different web sites.

As the figures illustrate, the OpenID authentication flow is a bit different from traditional database-based authentication. OpenID authentication can be summarized in the following steps:

Figure 3. The OpenID Authentication Flow: The user has different credentials for each web site.
  1. A user wants to authenticate himself on a web site.
  2. The user provides his OpenID identifier to the web site.
  3. The web site translates the identifier into a canonical URL (such as http://yourname.myopenid.com).
  4. The web site redirects the browser to the generated URL, where the user can authenticate himself against the OpenID provider.
  5. The OpenID provider authenticates the user, either with a traditional method (username and password) or with more advanced techniques, such as certificate exchange.
  6. If successful, the OpenID provider redirects the browser back to the originating web site, along with the verified user credentials.
  7. The web site uses the credentials to identify the user and forwards him to its services.
  8. If unsuccessful, the OpenID provider redirects the browser back to the originating web site, along with an error key that the web site can use to handle the situation.

To guarantee security and prevent identity spoofing, the web site and the OpenID provider perform some extra steps. However, web developers who use the OpenID client library don’t need to get into these details, because the library handles them all transparently (the OpenID client library is used for the example in the section to follow).

The latest version of the OpenID specifications (Version 2.0) allows for further complexity by introducing the concept of provider discovery. You don’t need to delve into these details to be able to integrate your web site properly with OpenID. Figure 3 shows a schematic of the above sequence.

Connect Ruby to OpenID
The example for making web applications compatible with OpenID involves a traditional web site built upon Ruby on Rails 2.0. In following this example, you first will create a basic application and then improve it by adding credentials checking and user login. The basic application will be a basic web-based to-do list. It is purposefully simple, so that you can focus on the OpenID integration.

Make sure you have a locally running MySQL instance. Connect to it and create a development database by issuing the following command:

create database openid_development ;

The database will contain both the data used by your application and the additional tables required to interact with OpenID providers. Open a terminal and create an empty Rails application with this command:

# rails -d mysql openid

Next, verify that everything is working correctly by running the script/server command and accessing the application on http://localhost:3000/.

Create the basic scaffolding that will manage your to-do items by asking Rails to generate it. Each to-do item will have an owner (identified by a name and an email), a start and end date, and a description:

# script/generate scaffold Todo   person:string email:string start:date end:date description:text# rake db:migrate

Even though it is extremely simple, your sample application now handles to-do lists perfectly. It is now time to integrate it with OpenID, which will allow you to:

  • Provide a login screen for the application
  • Enrich the to-do creation page by reusing some of the credentials received by the OpenID provider

First, install the ruby-openid library. This library contains all the client code necessary for the interaction between your application and remote OpenID providers. You can use rubygems to install it:

# sudo gem install ruby-openid

If you inspect the installed gems, you will see something like this, depending on the version of the library installed (different versions make no difference for the purpose of this article):

# gem list --localruby-openid (2.0.4, 1.1.4)ruby-yadis (0.3.4)...rails (2.0.2)... other installed gems ...

The next step is to install the openid_authentication plugin, integrates Rails with the newly installed ruby-openid library. Since this plugin requires some database tables to work properly, you’ll also have to run rake to perform the required migrations:

# script/plugin install open_id_authentication# rake open_id_authentication:db:create# rake db:migrate

Add Some Intelligence to Your Authentication System
Now that all the pieces are in place, it is time to start modifying the application code. Open the file app/controllers/application.rb and add the following authorize method:

def authorize  unless session[:user_id]    flash[:notice] = "Please log in"    # save the URL the user requested so we can hop back to it    # after login    session[:jumpto] = request.parameters    redirect_to(:controller => "/login", :action => "index")  endend

This method will intercept all the users’ requests for actions that cannot be performed without valid credentials and redirect them to the login screen. By storing the request parameters into the session :jumpto symbol, this method also guarantees that, immediately after login, the user will be forwarded to the URL that he initially requested.

You now have to modify the todos_controller.rb that was created during the previous scaffolding. Add the before_filter as follows:

class TodosController  [ :new , :edit, :create, :update, :destroy]     ... rest of the controller as before ...end 

This ensures that all the actions that create or modify a to-do item are accessed only after a successful login.

You can now move to the core of the login mechanism by implementing the login controller. On a terminal, launch this:

# script/generate controller login

Open the generated app/controllers/login_controller.rb file, and edit it so that it matches the contents of Listing 1. This controller contains all the magic, so take a close look at it.

The index method either redirects the user to the web site main page (http://localhost:3000/todos) if he already performed login, or forwards him to the login page (http://localhost:3000/login). The view for the login page is still missing, so create the file app/views/login/index.html.erb with these contents:

   "login" , :action => "login" do |f| -%>      

As you can see, instead of the traditional username and password fields, there is now only one text field, which will accept the user’s OpenID identifier. The label openid_url is a conventional one that will allow the browser to remember the user’s OpenID identifier across different OpenID-enabled web sites.

Moving on, the login and logout methods, respectively, are responsible for handling the login process and for cleaning the session of all the user’s data once he logs out of your application. The using_open_id? function belongs to the ruby-openid library, and it detects whether the user is initiating or finalizing a login. Remember that, as described in Figure 3, the login process involves two calls to your web site: the first containing the OpenID identifier to initiate the login process and the second containing the user credentials as returned by the OpenID provider. Therefore, the login method ends up being called twice and the using_open_id? function detects both occurrences (more on this later).

The authenticate function contains the bulk of the authentication logic. It delegates to the authenticate_with_open_id function within the ruby-openid library. This function accepts a block that will execute once the login completes (either successfully or not)–that is, after the second call back to the web site. The block accepts three parameters:

  • The result object defines the effectiveness of the login process. It contains methods to investigate the various possible failures (user has canceled login, OpenID provider unavailable, etc.).
  • The identity_url object contains the user OpenID identifier.
  • The registration object contains additional information about the user, according to the Simple Registration Extension for OpenID (SREG) specification.

The Simple Registration Extension is an add-on to the OpenID specification that allows providers and web sites to exchange additional information about the users. This may include the user’s e-mail, real name, date of birth, and other personal data. To respect the user’s privacy, each user can instruct the OpenID provider not to disclose this kind of information. In the previous example, you used the :required directive (as opposed to the :optional one) to require a nickname and email address along with the user’s credentials.

SREG is another way in which OpenID improves the authentication experience when compared with traditional mechanisms, where each web site would require the user to complete a registration form, specifying again and again his personal data to each interested third party.

Lastly, the authenticate method stores all the user data into the :user_id session variable and redirects the user to the originally requested page with the redirect_to(:jumpto) statement.

As you already know, the login method will be called twice–the second time being a redirect instructed from the OpenID provider to the user browser. In order for this to work, you have to perform two last steps. First, you have to define an appropriate route in the routes.rb configuration file, as follow (notice that you’re using a named route):

map.openid "login",   :controller => "login" ,   :requirements => { :method => :get }

Next, you have to reference such a named route in the root_url method of the login controller, as follows:

def root_url  openid_urlend

This guarantees to the OpenID provider that the URL the user will be redirected to after the login belongs to the same domain (trusted root) as that from which the login request originated.

After this last step, your application now accepts only properly authenticated and identified users. You can use the additional information to improve the user experience. For example, you can enable your web site to pre-enter the user’s name and email address when he creates a new to-do item. Open again the todos_controller.rb file and modify the new method as follows:

def new  @todo = Todo.new  @todo.person = session[:user_id].nickname  @todo.email = session[:user_id].email  respond_to do |format|    format.html # new.html.erb    format.xml  { render :xml => @todo }  endend

Much More to Explore
If you have any previous experience with Rails, you may have noticed that the described implementation didn’t use any of the existing traditional authentication frameworks (that is, either restful_authentication or act_as_authenticated). This is again an indication that OpenID authentication does not depend on any previously established authentication logic.

In addition, if you want to avoid the dependency on the Rails open_id_authentication plugin or you need greater flexibility, you can access the ruby-openid library directly. Listing 2 shows an alternative (and longer) login controller that uses the ruby-openid function calls and public classes directly. To differentiate it even further, it uses the filesystem (instead of the database used up to this point) to store the temporary data required throughout the handshaking between your application and the OpenID provider.

The advantages of the OpenID service are clear: less code duplication, complexity, and maintenance overhead for the developer and a more coherent web experience for the user. However, this article went through only the very superficial layers of the OpenID universe, leaving a lot of other features open to exploration. These include the new OpenID 2.0 specification, the service discovery and delegation mechanisms, the Extensible Resource Descriptor Sequence (XRDS) format, stateless relying parties, and many others.

Also, many parts of the ruby-openid library were not explored. For example, it also supports setting up a Rails OpenID provider, instead of just the client library for a relying party as described in the article. Thanks to the openness of the OpenID initiative, you can further explore the service in as much details as you may need.

devxblackblue

About Our Editorial Process

At DevX, we’re dedicated to tech entrepreneurship. Our team closely follows industry shifts, new products, AI breakthroughs, technology trends, and funding announcements. Articles undergo thorough editing to ensure accuracy and clarity, reflecting DevX’s style and supporting entrepreneurs in the tech sphere.

See our full editorial policy.

About Our Journalist