In a project I’m working on I’m using spree as a mountable engine. The host application has its own administration area, and I wanted to share the spree authentication with my app.

Spree uses devise to handle authentication. The code which is responsible for the authentication part of the app is in the auth module of Spree.

To share authentication with your application you have to:

  • setup devise in your routes.rb file. I copied this code from the routes.rb file included in the spree/auth module:
HostApplication::Application.routes.draw do
  devise_for :user,
     :class_name => 'Spree::User',
     :controllers => { :sessions => 'spree/user_sessions',
                       :registrations => 'spree/user_registrations',
                       :passwords => 'spree/user_passwords' },
     :skip => [:unlocks, :omniauth_callbacks],
     :path_names => { :sign_out => 'logout' }
  # ...
end
  • add before_filter :authenticate_user! to the controller you want to be protected.

This way you’re setup with authentication; it’s time to move on with authorization.

  • add load_and_authorize_resource! to the controller you want to be protected.

  • register new abilities to the Spree CanCan configuration using the register_ability method. Here is an example:

# create a file in config/initializers, e.g. add_abilities_to_spree.rb,
# with the following content:

Spree::Ability.register_ability MyAppAbility

# create a file under app/models (or lib/) to define your abilities (in
# this example I protect only the HostAppCoolPage model):

class MyAppAbility
  include CanCan::Ability

  def initialize(user)
    if user.has_role?('admin')
      can manage, :host_app_cool_pages
    end
  end
end
  • add to your application_controller.rb file the code needed to handle authorization exceptions:
class ApplicationController < ActionController::Base
  protect_from_forgery

  def current_ability
    @current_ability ||= Spree::Ability.new(current_user)
  end

  rescue_from CanCan::AccessDenied do |exception|
    redirect_to :root, :alert => exception.message
  end
end

And you’re done!