Search code examples
ruby-on-rails-3before-filter

ActionController sessions and subclasses


By default all rails controllers inherit the application controller.

In my application I want users to be able to authenticate from any page using http-basic auth, and also authenticate by posting, but only to one controller.

Currently things look like this (simplified):

class ApplicationController < ActionController::Base
    before_filter :set_current_user

    private

    def set_current_user
        Authorization.current_user = current_user
    end

    def current_user
        if session[:user].blank?
            authenticate_or_request_with_http_basic do |user,pass|
                session[:user] = authenticated_user_or_nil
            end
        end
        session[:user]
    end
end

class UserSessionsController < ApplicationController
    prepend_before_filter :set_user_from_params

    private

    def set_user_from_params
        session[:user] = authenticated_user_or_nil
    end
end

I set a breakpoint in both set_user_from_params and set_current_user and have verified they are indeed being called in the correct order.

set_user_from_params is in fact setting session[:user] correctly, but when I continue and hit the breakpoint inside set_current_user the session hash is empty!?

Is this intended behaviour or have I missed something really obvious?


Solution

  • If the request is a post request then, by default rails will check for the presence of a valid csrf token.

    A before_filter is inserted that does that check, if it fails then rails resets the session, which is consistent with your situation.

    You need to either ensure that your post request contains an authenticity token or (if appropriate) disable that check by skipping the verify_authenticity_token filter.

    One way of verifying that is to add

    def handle_unverified_request super Rails.logger.info "handling unverified request - csrf issue" end

    to your controller. If the issue is a csrf issue then this method will be called and will write to the log file confirming this has happened.