carpool

Carpool is a single-sign-on (sso) solution for rack based applications. It is designed to allow you to designate one application as a "driver" which powers authentication, and sends session information to "passengers". It has been tested with both Rails 2.3 and Rails 3 (as rack middleware).

Carpool handles securely transferring user information across domains (using AES encryption), as well as redirection so that already-signed-in users will have a seamless experience. It is designed to be as unobtrusive as possible, allowing you to handle the actual login and session maintenance in any manner you would like.

Installation

Install the gem gem install carpool

Configuration

Configure the application you wish to designate as your driver. In Rails based applications, add Carpool::Driver to your middleware stack. The configuration takes three options.

  • One or more passengers. This takes two options, first, the domain name of the passenger application (the referrer), second a 'secret' designated to the passenger application. This secret can be anything, but must match in both the driver and passenger sites.
  • unauthorized_uri. If there isn't an existing session within the driver application, redirect to this location to handle logins etc
  • revoke_uri. This url is used to "logout" passengers from the driver

In environment.rb:

Rails.configuration.middleware.use Carpool::Driver do |config|
  config.passenger 'apasengerdomain.com', :secret => 'secret_key'
  config.unauthorized_uri = 'urlforunauthoried.passengers'
  config.revoke_uri       = 'signout'
end

Then configure the application that you would like to function as your passenger, adding Carpool::Passenger to your middleware stack. This takes two options.

  • driver_uri. This is the url/location of the 'driver' site. (ie: http://yourdriver.com)
  • secret. This is a shared secret between both the driver and the passenger to verify the passenger has permission to authenticate itself here.

In environment.rb:

Rails.configuration.middleware.use Carpool::Passenger do |config|
  config.driver_uri = 'http://yourdriver.com'
  config.secret = "secret_key"
end

Authenticating

Driver Application

Authentication in your driver application can be handled however you would like. When sessions are created, simply check to see if authentication was requested by a passenger website, or the actual application itself. To check this, use Carpool.auth_attempt? When authentication is initiated by a passenger, Carpool creates a 'seatbelt' object which represents the session details to be passed back after successful login/session.

# Create user session (authlogic format used as an example) 
user_session.save

if Carpool.auth_attempt?

  # This login request was generated from a passenger.
  # current_user represents our now logged in user.
  # env is the rack environment.

  # Fasten yer seatbelt to be taken back to the requesting app.
  seatbelt = Carpool::SeatBelt.new(env).fasten!(current_user)
  redirect_to seatbelt  # Redirect back to the passenger site (to /sso/authorize)

else
  # Handle local logins here
end

Seatbelt.fasten! generates a url representing a url back to the Passenger application, including a session payload that Carpool::Passenger uses to generate a session within itself.

Passenger Application

Passengers only need to be able to handle two aspects of the process, redirecting /login and handing the resulting seatbelt from your Driver application.

Redirecting login: To use the Driver application for logins, redirect users to Carpool.driver_uri

Processing the Seatbelt: On successful login the Drier will redirect the user back to /sso/authorize within the passenger application. On redirect, the header X-CARPOOL-PAYLOAD, and the parameters seatbelt and driver will be set. To be sure authentication has taken place, check for the X-CARPOOL-PAYLOAD header, then process the seatbelt.

# Remove our seatbelt because we've arrived! (ok really just process the result)
seatbelt = Carpool::SeatBelt.new(request.env).remove!

# User will contain any parameters encrypted via the Driver (see above).
user = seatbelt.user     

# Handle your session however using the user hash.
# Call the redirect_uri from our seatbelt to return users back to their original requesting url.
redirect_to seatbelt.redirect_uri

Rails users: make sure you setup a route to respond to /sso/authorize.

User Data

To make Carpool actually useful in your passenger applications, you would likely need to pass data from the Driver to the Passenger. The Carpool::Seatbelt.fasten! method takes one parameter, which can be any Ruby class that responds to the method encrypted_credentials. This method should return a hash containing any information you would like to access in your Passengers. This hash is then encrypted via AES using Nate Wiger's FastAES gem. Although this data is encrypted, it is not recommended that the user data hash include sensitive data such as credit card numbers, social security numbers etc.

class User
  def encrypted_credentials
    {
     :first_name => 'My', 
     :last_name => 'Name', 
     :id => id_for_database_use 
    }
  end
end

Caveats

To properly ensure the proper domains are being accessed/used, Carpool relies on the SERVER_NAME http header. Make sure that this header is properly set via your nginx/apache configuration. If using Passenger standalone, it seems like this variable will be _ so make sure that you properly handle that.

If you choose to, you can set SERVER_NAME to HTTP_HOST, but note that the HTTP_HOST variable can be spoofed by end users.

Note on Patches/Pull Requests

  • Fork the project.
  • Make your feature addition or bug fix.
  • Add tests for it. This is important so I don't break it in a future version unintentionally.
  • Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
  • Send me a pull request. Bonus points for topic branches.

Copyright (c) 2010 Brent Kirby / Kurb Media LLC. See LICENSE for details.