CAS authentication for Redmine

It’s been a while since I wanted to integrate Redmine with my CAS server at $WORK. Unfortunately, Redmine only supports its integrated login/password, LDAP auth and OpenID. There are existing demands on this topic (#2965 for instance) but Redmine developers always refused to implement such specific authentications in the core itself. I approve this choice, I think they should even drop OpenID, and maybe LDAP support (hard to maintain so many things, the core should be leaner).

Existing solutions

I’m only aware of two existing solutions if you want to authenticate your Redmine users against CAS :

  • a blog post titled ‘CASify Redmine’ : it explains how to modify the core to use rubycas-client, definitely not a long-term solution
  • a plugin called redmine_cas : it works, but it doesn’t exactly fit my needs :
    • it replaces Redmine standard authentication completely : I just want CAS authentication as a comfort option, as some of my users are not registered on the CAS server
    • it cannot be configured in the interface : as a datacenter which offers a SaaS product for dozens of users, I want them to be able to configure the CAS server themselves (we have multiple addresses depending on wether your users are on the intranet, extranet or internet)
    • there’s an annoying but when clicking on “logout” : undefined method `destroy' for {}:Hash, though it may be easy to fix

To be honest, I also wanted to try to the OmniAuth authentication framework and see if it could help integrating Redmine with external authentication sources (not only CAS).

OmniAuth

OmniAuth (blog post) is an awesome authentication framework built as a Rack middleware.

It has a pretty simple workflow for external authentication sources. If we take the example of CAS :

  • call /auth/cas and you will be redirected to the CAS server (with the needed back url)
  • after you logged in to your CAS server, you’re redirected to /auth/cas/callback and free to do whatever you want with the params returned, stored in request.env["omniauth.auth"] ; this hash stores at least the “provider” (“cas”) and the “uid” (“jeanbaptiste.barth” for instance)

OmniAuth is covered in 3 railscasts, but the one that has been really helpful is #241 Simple OmniAuth.

The “redmine_omniauth_cas” plugin

I spent some hours last week-end to build the “redmine_omniauth_cas” plugin. The code is on github. It relies on OmniAuth for (optionally) authenticating your users against a CAS server. The CAS server has to be configured in the plugin configuration section. It’s only compatible with Redmine 1.2.0 since latest versions of OmniAuth are not compatible with Rack 1.0.1.

There are currently some limitations I plan to improve in the next versions :

  • implement ticket validation when first opening your browser (for now you’ll be considered as logged out if your session has expired on Redmine but your ticket is still valid on the CAS server)
  • add a plugin option to hide ‘normal’ login/password form
  • authorize on-the-fly registration

I also plan to contribute to OmniAuth and especially the CAS provider. The current implementation is functional but not so flexible, since it doesn’t support OmniAuth’s mechanisms to setup the CAS options at runtime.

Conclusion

This plugin is a first try at using OmniAuth with Redmine, but I could easily imagine building plugins to authenticate against other sources. I hope it will be useful for some of you. I think it could even be a viable option to throw OpenID support out of the core, and let it live its life in a clean plugin. Stay tuned !