31 Mar

Single Sign On with Devise and Casino

Last update on 2015-03-31

Nowadays it is very common to deal with multiple distributed services that need a Central Authentication System and a Single Sign In/Out feature.

In this blog post we will present one of the simplest, yet effective solutions, which is the use of the CAS protocol. CAS allows the authentication to be centralized and it is well suited for organizations that already have an existing user base.

For our actual implementation of CAS SSO we used the CASino ruby gem. We are going to see how we can integrate this with an existing project using devise and Rails 3 and 4. The overall single sign on mechanism for our service is shown in the image below.

Single Sign On diagram

CASino

First of all lets talk about CASino. CASino is an easy to use Single Sign On (SSO) web application written in ruby. It supports multiple backends and authentication methods through modules called Authenticators.

They provide a ready to use Rails 3 application called CASinoApp that we can download (clone) and configure easily with the instructions provided in their website, which serves as the authentication server. To summarise, we only need to configure a database authenticator to connect with a Users collection in a MongoDB (you can use any other ODM/ORM too) database using the email and password fields in the cas.yml file. For this example we used a third party authenticator to support MongoDB called casino-moped_authenticator, instead of the default one for ActiveRecord.

# cas.yml

development:
  frontend:
    sso_name: 'SSO Example Server'
    footer_text: 'Powered by <a href="http://rbcas.com/">CASino</a>'
  authenticators:
     sso_example_database:
      authenticator: "Moped"
      options:
        database_url: "mongodb://localhost:27017/sso_example"
        collection: "users"
        username_column: "email"
        password_column: "encrypted_password"

Rails 3 example application

To enable the SSO with CAS in a current application using Devise we use a replacement for the database_authenticatable module of devise called devise_cas_authenticatable. This comes as a gem which we just normally install as usual in our gemfile.

#Gemfile
# devise_cas_authenticatable
gem 'devise_cas_authenticatable'

Once it is installed we head on to configure the project to use it.

Devise

We need to configure the config/initializers/devise.rb with some already bundled configurations explained in their website, we use:

#config/initializers/devise.rb
# ==> Configuration for SSO server authentication
# Url pointing to the CASino SSO server
config.cas_base_url = "http://localhost:4000"
# Tell devise_cas_authenticatable not create users if they do not exist
config.cas_create_user = false
# Instead username we use email for login field
config.cas_username_column = "email"
# After logout we use a 'destination' url to be redirected
config.cas_logout_url_param = "destination"
# The url for redirect after logout
config.cas_destination_url = "http://localhost:3000/"
# Parameter required for CASino logout to work
config.cas_destination_logout_param_name = "service"
# Enable single sign out
config.cas_enable_single_sign_out = true

Routes

In the config/routes.rb file we configure the user sessions to use the devise_cas_authenticatable sessions controller.

devise_for :users, :controllers => {sessions: 'devise/cas_sessions' }

User model

Finally the User model must be modified to avoid the regular database authentication, we replace the database_authenticatable module with cas_authenticatable.

devise :cas_authenticatable #, your other devise modules

Rails 4 application

The devise_cas_authenticatable module works with Rails 4 but there are still some issues that need to be resolved.

First of all we need to override the Devise::CasSessionsController that comes with devise_cas_authenticatable

In the config/routes.rb file:

devise_for :users, skip: [:sessions], controllers: { cas_sessions: 'our_cas_sessions' }

In controllers/our_cas_sessions_controller.rb

class OurCasSessionsController < Devise::CasSessionsController
  # Skip redirect_to_sign_in to fix
  # flash message not showing up in Rails 4
  # (https://github.com/nbudin/devise_cas_authenticatable/issues/81)

  skip_before_filter :redirect_to_sign_in,
  only: [:new, :destroy, :single_sign_out, :service, :unregistered]

  # Skip verify_signed_out_user for Devise >= 3.3.0
  skip_before_filter :verify_signed_out_user
end

Single Sign Out

The CASino application also supports a Single Sign Out solution, but for this to work we cannot use the default cookie session store mechanism, instead we need to use a persisted session store. For the session store we recommend using redis and the redis-rails gem can help us with this.

To change the session store in our application we edit the config/initializers/session_store.rb file:

# config/initializers/session_store.rb

Rails.application.config.session_store :redis_store,
redis_server: "redis://127.0.0.1:6379/0/_sso_rails3_example_app_session"

Note that we have specified the redis_server to use in the initializer file, which will be needed if you plan to use this in a distributed environment.

For Rails 4 we had another problem with the devise_cas_authenticatable because the DeviseCasAuthenticatable::SingleSignOut module does not work with redis store, for that reason we did a fork of the project and fixed that, so in the Gemfile we change the gem to use the fork:

# Gemfile

# Use redis to store Rails Sessions
gem 'redis-rails'
# For CAS authentication with SSO example server
gem 'devise_cas_authenticatable', git: 'https://github.com/jpamaya/devise_cas_authenticatable'

Wrap up

To test this Single Sign On solutions is better to have at least two client applications configured to see the magic of the Single Sign In and Single Sign Out mechanism. We have prepared three example apps ready for you to try out this already: one with a casino server example and two others for the client application examples, one for Rails 3 and another one for Rails 4.

Thats all for now, hope you find this useful as a way of implementing Single Sign On (and Single Sign Out) in a simple way, see you in the next blog post!

comments powered by Disqus