Sinatra + Warden & Rails + Devise

Finally, I was able to get Sinatra + Warden & Rails + Devise all working together.

Here were the main aspects:

  • Get Devise working with Rails (Devise is a Rails app, won't work without it)
  • Setup the mapping (route) on Rack level to support both Rails and Sinatra
  • Share the sessions between Rails and Sinatra
  • Setup Warden and make it available to Sinatra

Obviously, many had attempted it, as observed from questions on stackoverflow.com. But, there was not any full solutions published. So, here you're, the first one. I hope this post will help the community, or lead other to post a better solution.

First, let's clarify what I want to get out of it.

I am building a new html5 app. In which, MVC is running on the browser. In many cases, I do not need Rails and the performance hit associated with it.

For my html5 app, the server perform two functions: a) serves static html, javascript, and css files. b) All data is served as JSON thru a RESTFul manner. The model code (similar to backbone.js) in the html5 app connect to the server for the JSON data.

With Rack, static files are served. So, we are set for function (a). For RESTFul data, Sinatra just fits the task very well. So, it is the weapons of choice for function (b).

However, in the Sinatra world, authenication implementations are not as well built as those in Rails'. I had actually completed a project with Sinatra on Warden with the help of dm-is-authenicate. I knew I was violating "Don't Repeat Yourself (DRY)" principle, and the task took days, and I was still lacking typical features like "Remember Me"; not to mention that I would have to implement all the feature I might need myself, such as OAuth.

 I went with that long route anyway, because the schedule was tight and I couldn't find any sucessful proclaimation (although, all experts I talked to believe it could be done). The risk of couldn't complete authenication in-time outweight some missing features. With the long route, at least I knew I could have the very very minimal feature in a few days. And, I would able to throw in some long days to put in some feature if needed.

With this setup, the session is shared between the Rails app and the Sinatra app. The only way to authenicate is thru Rails and Devise. Once it is authenicated, both app are authenicated. Same for logout.

You can try a demo here:

   http://sinatra-with-devise.heroku.com/

I also carefully crafted a commit tree on github to show you the exact steps you need to setup the simplest authenication, starting from 'rails new MyApp'.

   https://github.com/beedesk/sinatra-with-devise/commits/master

If you have a sucessful Devise and Rail setup, you might want to import the following changes. The only change you might also need is updating Gemfile and do a 'bundle update'.

 

Along the way, you might have seen some of these errors. Hopefully, by listing them, Google will direct a few of you here and save you some hairs:
- no marshal_dump is defined for class Proc
- rack-1.0.1/lib/rack/session/cookie.rb:64:in 'dump'
- undefined method `unauthenticated?' for nil:NilClass
- if env['warden'].unauthenticated?
- file: whiny_nil.rb location: method_missing line: 48
- manager.default_scope = Devise.default_scope