I started receiving a lot of error notifications recently from my ExceptionNotfier plugin for an error with ActionController::InvalidAuthenticityToken. It turned out the error was occurring because one of my users was pasting a link to my app in an MS Office document and when Office sees the link it makes a request that Rails could not handle. Here I'll show you a simple fix you can use to avoid these errors with much credit going to an article at
Dealing with Microsoft Office Protocol Discovery in Rails
.
My execptions looked something like this (lots of boring details omitted)
A ActionController::InvalidAuthenticityToken occurred in events#1164:
ActionController::InvalidAuthenticityToken
[RAILS_ROOT]/vendor/rails/actionpack/lib/action_controller/request_forgery_protection.rb:86:in `verify_authenticity_token'
-------------------------------
Environment:
-------------------------------
* HTTP_USER_AGENT : Microsoft Data Access Internet Publishing Provider Protocol Discovery
* REQUEST_METHOD : OPTIONS
The problem is that Rails doesn't understand the method 'OPTIONS' (see
rails/actionpack/lib/action_controller/routing.rb
#around line 270 of routing.rb
module ActionController
module Routing
HTTP_METHODS = [:get, :head, :post, :put, :delete]
end
end
Fixing the problem is fairly simple. You insert a before_filter into your application controller to intercept and handle requests with the option method before the rails code realizes it can't handle the request.
class ApplicationController < ActionController::Base
before_filter :options_for_microsoft_office_protocol_discovery
...
def options_for_microsoft_office_protocol_discovery
render :nothing => true, :status => 200 if request.method == :options
end
end
Its also easy to write a simple spec in rspec to verify the behavior. There is one trick which is that rails/actionpack/lib/action_controller/test_process.rb defines helper methods for get, post, put, delete & head that we can't use so we need to call the underlying method directly but the signature for that underlying method changed with Rails 2.3 (
commit) so depending what version you're using you'll need one of 2 flavors.
# Rails 2.3 and above
it 'should not throw an exception on OPTIONS request (from ms office protocol discovery)' do
process '/any/goofy/path', nil, nil, nil, 'OPTIONS'
response.should be_success
end
# Rails < 2.3 version
it 'should not throw an exception on OPTIONS request (from ms office protocol discovery)' do
@request.env['REQUEST_METHOD'] = 'OPTIONS'
process '/any/goofy/path'
response.should be_success
end
You can also test from the command line using curl
curl -X OPTIONS http://localhost:3000/
Update: It turns out that Rails stores the acceptable methods in 2 different places
actionpack/lib/action_controller/request.rb which does include all of get head put post delete options and also (see
rails/actionpack/lib/action_controller/routing.rb which only includes :get, :head, :post, :put, :delete (options is missing). This means this fix will only work for OPTIONS requests and not any other type as ActionController::Request request_method will throw an exception before getting to the filter code above.