CAS Authentication Filter plugin

Plugin details

RubyCAS-Client is a client for Yale's Central Authentication Service protocol -- an open source enterprise single sign on system for web applications. More info about the CAS system is available here: http://www.ja-sig.org/products/cas/

Websitehttp://rubyforge.org/projects/rubycas-client Repositoryhttp://rubycas-client.googlecode.com/svn/trunk/rubycas-client/ Author Karolinska Institutet Tags cas, Auth LicenseLGPL

Documentation

Install the plugin:
ruby script/plugin install http://rubycas-client.googlecode.com/svn/trunk/rubycas-client/

Using RubyCAS-Client in Rails controllers
===================================

Note that from this point on we are assuming that you have a working CAS server up and running at an https URI.

Somewhere in your config/environment.rb file add this:

  require 'cas_auth'
  CAS::Filter.cas_base_url = "https://login.example.com/cas"


You will also need to specify the server name where your CAS-protected app is running (i.e. the hostname of the app you are adding CAS protection to, not the CAS server):

  CAS::Filter.server_name = "yourapplication.example.com:3000"


Then, in your app/controllers/application.rb (or in whatever controller you want to add the CAS filter for):

  before_filter CAS::Filter


That's it. You should now find that you are redirected to your CAS login page whenever you try to access any action in your protected controller. You can of course qualify the before_filter as you would with any other ActionController filter. For example:

  before_filter CAS::Filter, :except => [ :unprotected_action, :another_unprotected_action ]


Once the user has been authenticated, their authenticated username is available under request.username (and also under session[:casfilteruser]). If you want to do something with this username (for example load a user record from the database), you can append another filter method that checks for this value and does whatever you need it to do.

A more complicated example
===================================

Here is a more complicated configuration showing most of the configuration options (this does not show proxy options however, which are covered in the next section):

  CAS::Filter.login_url = "https://login.example.com/cas/login"  # the URI of the CAS login page
  CAS::Filter.validate_url = "https://login.example.com/cas/serviceValidate"  # the URI where CAS ticket validation requests are sent
  CAS::Filter.server_name = "yourapplication.example.com:3000"  # the server name of your CAS-protected application
  CAS::Filter.renew = false                      # force re-authentication? see http://www.ja-sig.org/products/cas/overview/protocol
  CAS::Filter.wrap_request = true                # make the username available under request.username?
  CAS::Filter.gateway = false                    # act as cas gateway? see http://www.ja-sig.org/products/cas/overview/protocol
  CAS::Filter.session_username = :casfilteruser  # this is the hash in the session where the authenticated username will be stored


Note that in this example we explicitly specified the login and validate URLs instead of letting RubyCAS-Client figure them out based on CAS::Filter.cas_base_url.

Defining a 'logout' action
===================================

Your Rails application's controller(s) will probably have some sort of logout function. In it you will likely want reset the user's session for your application, and then redirect to the CAS server's logout URL. Here's an example of how to do this:

  def logout
  	reset_session
  	redirect_to CAS::Filter.logout_url(self, request.referer)
  end


Gatewayed authentication
===================================

RubyCAS-Client supports gatewaying as of version 1.1.0. Gatewaying allows for optional CAS authentication, so that users who already have a pre-existing CAS SSO session will be automatically authenticated for the gatewayed service, while those who do not, will be allowed to access the service without authentication. This is useful for example when you want to show some additional private content on a homepage to authenticated users, but also want unauthenticated users to be able to access the page without first logging in.

For more information on using gatewaying, see CAS::GatewayFilter.

How to act as a CAS proxy
===================================

CAS 2.0 has a built-in mechanism that allows a CAS-authenticated application to pass on its authentication to other applications. An example where this is useful might be a portal site, where the user logs in to a central website and then gets forwarded to various other sites that run independently of the portal system (but are always accessed via the portal). The exact mechanism behind this is rather complicated so I won't go over it here. If you wish to learn more about CAS proxying, a great walkthrough is available at http://www.ja-sig.org/wiki/display/CAS/Proxy+CAS+Walkthrough.

RubyCAS-Client fully supports proxying, so a CAS-protected Rails application can act as a CAS proxy.

Additionally, RubyCAS-Client comes with a controller that can act as a CAS proxy callback receiver. This is necessary because when your application requests to act as a CAS proxy, the CAS server must contact your application to deposit the proxy-granting-ticket (PGT). Note that in this case the CAS server CONTACTS YOU, rather than you contacting the CAS server (as in all other CAS operations).

Confused? Don't worry, you don't really have to understand this to use it. To enable your Rails app to act as a CAS proxy, all you need to do is this:

In your config/environment.rb:

  CAS::Filter.cas_base_url = "https://login.example.com/cas"
  CAS::Filter.proxy_callback_url = "https://yourrailsapp.com/cas_proxy_callback/receive_pgt"
  CAS::Filter.proxy_retrieval_url = "https://yourrailsapp.com/cas_proxy_callback/retrieve_pgt"


In config/routes.rb make sure that you have a route that will allow requests to /cas_proxy_callback/:action to be routed to the CasProxyCallbackController. This should work as-is with the standard Rails routes setup, but if you have disabled the default route, you should add the following:

  map.cas_proxy_callback 'cas_proxy_callback/:action', :controller => 'cas_proxy_callback'


Now here's a big giant caveat: your CAS callback application and your CAS proxy application must run on separate Rails servers. In other words, if you want a Rails app to act as a CAS ticket-granting proxy, the cas_proxy_callback controller must run on a different server. This is because Rails does not properly support handling of concurrent requests. The CAS proxy mechanism acts in such a way that if your proxy application and your callback controller were on the same server you would end up with a deadlock (the CAS server would be waiting for its callback to be accepted by your Rails server, but your Rails server wouldn't respond to the CAS server's callback until the CAS server responded back first).

The simplest workaround is this:

1. Create an empty rails app (i.e. something like rails cas_proxy_callback)
2. Make sure that you have the CAS plugin installed. If you installed it as a gem, you don't have to do anything since it is already installed. If you want to install as a plugin, see the instructions in the "Installing" section above.
3. Make sure that the server is up and running, and configure your proxy_callback_url and proxy_retrieval_url to point to the new server as described above (or rather, make Pound point to the new server, if that's how you're handling https).

That's it. The proxy_callback_controller doesn't require any additional configuration. It doesn't access the database or anything of that sort.

Once your user logs in to CAS via your application, you can do the following to obtain a service ticket that can then be used to authenticate another application:

  service_uri = "http://some.other.application"
  proxy_granting_ticket = session[:casfilterpgt]
  ticket = CAS::Filter.request_proxy_ticket(service_uri, proxy_granting_ticket).proxy_ticket


ticket should now contain a valid service ticket. You can use it to authenticate by sending it and the service URI as query parameters to your target application:

http://some.other.application?service=#{CGI.encode(ticket.target_service)}&ticket=#{ticket.proxy_ticket}

This is of course assuming that some.other.application is also protected by the CAS filter. Note that you should always URI-encode your service parameter inside URIs!

Note that CAS::Filter#request_proxy_ticket actually returns a CAS::ProxyTicketRequest object, which is why we need to call #proxy_ticket on it to retrieve the actual service ticket.

For extra security -- and you will likely want to do this on production machines in the wild -- in the proxied app's configuration (some.other.appliction in this example) you can specify the list of authorized proxies. For example, on your proxied app the CAS configuration might look something like this:

  CAS::Filter.cas_base_url = "https://login.example.com/cas"
  CAS::Filter.server_name = "some.other.application"
  CAS::Filter.authorized_proxies = ["https://yourrailsapp.com/cas/proxy_callback/receive_pgt"]


If no authorized proxies are given, the filter will accept receipts from any proxy.

Further Documentation

There is currently no advanced documentation for this plugin.

New documentation

Edit plugin | (0 older versions) | Last edited by: hardway, 7 months ago