Testing Authenticated/Unauthenticated Access

I’m wondering what pattern you use for testing authentication?

I currently test sign-in in a feature spec and then drop down to Controller specs to test access for each action.

So for each action of a controller that requires authentication I do something like:

  describe ".new" do

    context "authentication" do

      it {
        sign_in :user, create(:user)
        get :new
        should respond_with :success
      }

      it {
        get :new
        should redirect_to new_user_session_path
      }

    end

    context "authenticated" do

      before {
        sign_in :user, create(:user)
        get :new
      }

      it { should render_template :new }
      # etc

    end

  end

I like this approach because if I inadvertently expose an action to unauthenticated users, I get immediate feedback. However I’m concerned that adding an authorisation layer (with multiple roles with different permissions would balloon the number of tests). I’m wondering what approach you take to testing authentication and authorisation?

I usually reach for full-stack feature specs that sign in the user using the sign-in form.

If I have fine-grained permissions on a controller, I’ll write controller specs for that.

I feel like this is tricky. Feature specs that sign a user in/out verify that the sign in functionality work, but they don’t verify that you are properly protecting all of your controller actions by requiring a login to access them. Testing every action in every controller spec would be a pain.

I generally take the approach of making all controller actions require a login by default by adding before_action :authorize to my ApplicationController.

I test drive this with application_controller_spec which has an inline anonymous controller that inherits from application controller. The anonymous controller has the 7 standard actions and I verify that I am required to be logged in for each of those actions to work:

require 'spec_helper'

describe ApplicationController do
  describe 'authorization' do
    controller do
      def index; head :ok; end
      def show; head :ok; end
      def new; head :ok; end
      def create; head :ok; end
      def edit; head :ok; end
      def update; head :ok; end
      def destroy; head :ok; end
    end

    it { requires_signed_in_user_to { get :index } }
    it { requires_signed_in_user_to { get :new } }
    it { requires_signed_in_user_to { get :show, id: 1 } }
    it { requires_signed_in_user_to { get :edit, id: 1 } }
    it { requires_signed_in_user_to { put :update, id: 1 } }
    it { requires_signed_in_user_to { post :create } }
    it { requires_signed_in_user_to { delete :destroy, id: 1 } }

    def requires_signed_in_user_to(&block)
      yield

      expect(response).to redirect_to(sign_in_path)
    end
  end
end

Then I test drive any actions that require skipping the login filter with dedicated controller or feature specs as appropriate.

This isn’t perfect - I could do skip_before_action :authorize somewhere where it wasn’t test-driven and it wouldn’t be caught, but that seems mighty negligent behavior. I could also have a controller that inherits from something that doesn’t have ApplicationController in its inheritence hierarchy, but this doesn’t come up very often.

3 Likes

Thanks for your replies.