Tuesday, August 28, 2007

Rails and SSL (https://)

So I recently had to have some secure routes for a site I am currently working on. If you are using deprec and deploying to a slicehost, getting the SSL certificate stuff working is a snap. If you want your whole site to be over SSL, then all you do is put in a permanent redirect to socket 443 in your app_name.conf file. But, if you need to do something interesting like go back and forth between http and https, then you need to do a little digging.

Doing a google search doesn't bring up anything particularly useful in terms of going back and forth between http and https, but one thing did catch my eye. In your routes you can pass a protocol option to set which protocol you are going to use (something like http, file, svn, https, etc...). I also had to dig through some code to find out how to pass that option in, since it gets set two different ways depending on what kind of route you are using. If you do a map.resource(s) route, then you have to set the option as :requirements => {:protocol => ROUTES_PROTOCOL}, whereas if you just do a map.route_name type route, then you just pass in :protocol => ROUTES_PROTOCOL.

You should also be careful about when you are setting that protocol, as trying to access https://localhost:3000 just doesn't work (such as in development mode). I just set a conditional constant at the top of my routes file like so: ROUTES_PROTOCOL = (ENV["RAILS_ENV"] =~ /development/ ? "http" : "https").

Two more caveats:
  • First caveat is that if you want to go back and forth between http and https, you MUST explicitly set all of your routes to have http or https protocol. If you don't, then when rails generates your routes it will just use the protocol of the current page. This is annoying, but I can see why they would do this (if you are looking at something securely, then just look at everything securely).

  • Second caveat is that you must use the route_name_url throughout your application instead of route_name_path, as some are accustomed to. The _url named route gives you the full url (duh), which is what you want if you are on a page using http and want to go to https (whereas _path only gives you the uri, and will use the protocol and host of the current page). If you already use _url, then you are one step ahead in the game.

Here is a sample of some of my routes using the protocol option:

# Public routes using http protocol
map.with_options :protocol => "http" do |http|
http.login '/login', :controller => 'sessions', :action => 'new'
http.logout '/logout', :controller => 'sessions', :action => 'destroy'
http.resources :users, :sessions, :requirements => {:protocol => "http"}

# Public routes using https protocol
map.with_options :protocol => ROUTES_PROTOCOL do |https|
https.form_step '/form/*slug', :controller => 'forms', :action => 'show'
https.form_edit '/edit/*path', :controller => 'forms', :action => 'edit'
https.resources :forms, :requirements => {:protocol => ROUTES_PROTOCOL}