0

I'm developing a mountable engine (called SimpleUser) which uses Devise, OmniAuth and OmniAuth-Facebook. First I made a test app with the gems about and every worked fine. Next, I started building the engine from scratch, using the code of the test app as an example.

Everything is almost done, except for the connection with Facebook (it uses the Javascript popup). When I click in "log in" the FB popup is displayed, I grant the app, it redirects to the route specified (see routes), but throws this error:

NoMethodError in SimpleUser::AuthController#create
undefined method `[]' for nil:NilClass

The error occurs in that action, in the line authentication = Authentication.find_by_provider_and_uid(auth['provider'], auth['uid']) where auth is nil (auth comes from auth = request.env["omniauth.auth"]).

One thing I check is that the Callback phase it's no initialised. This is the log of the test app:

Started GET "/auth/facebook/callback" for 127.0.0.1 at 2013-03-14 08:52:56 -0600 (facebook) Callback phase initiated. Processing by AuthController#create as HTML Parameters: {"provider"=>"facebook"}

This is the log of the engine app:

Started GET "/simple_user/auth/facebook/callback" for 127.0.0.1 at 2013-03-14 08:51:19 -0600
Processing by SimpleUser::AuthController#create as HTML
  Parameters: {"provider"=>"facebook"}

For manage OmniAuth, I added the gem to the .gemspec and to the Gemfile; also, I require the gems in the engine, and within a generator of the engine I move a template of omniauth.rb to config/initializers of the parent app during installation. This is what I have:

AuthController (located in app/controllers/simple_user/auth_controller.rb)

module SimpleUser
  class AuthController < ApplicationController

    def create
      auth = request.env["omniauth.auth"]   
      authentication = Authentication.find_by_provider_and_uid(auth['provider'], auth['uid'])

      if authentication
        flash[:notice] = "Signed in successfully."
        sign_in(:user, authentication.user)
        redirect_to root_url
      else
        user = User.build_new_auth(auth)
        #user.apply_omniauth(auth)
        if user.save(:validate => false)
          flash[:notice] = "Account created and signed in successfully."
          sign_in(:user, user)
          redirect_to root_url
        else
          flash[:error] = "Error while creating the user account. Please try again."
          redirect_to root_url
        end
      end
    end

  end
end

Engine

module SimpleUser

  require 'rubygems'
  require 'devise'
  require 'cancan'
  require 'rolify'
  require 'omniauth'
  require 'omniauth-facebook'
  require 'simple_form'

  class Engine < ::Rails::Engine
    isolate_namespace SimpleUser

    config.before_configuration do
      env_file = File.join(Rails.root, 'config', 'fb_config.yml')
      YAML.load(File.open(env_file)).each do |key, value|
        ENV[key.to_s] = value.to_s
      end if File.exists?(env_file)

      env_file = File.join(Rails.root, 'config', 'devise_config.yml')
      YAML.load(File.open(env_file)).each do |key, value|
        ENV[key.to_s] = value.to_s
      end if File.exists?(env_file)
    end
  end
end

Generator

module SimpleUser
  module Generators
    class InstallGenerator < ::Rails::Generators::Base
      source_root File.expand_path("../templates", __FILE__)
      desc "Install SimpleUser"

      def copy_config_file
        copy_file "fb_config.yml", "config/fb_config.yml"
        copy_file "devise_config.yml", "config/devise_config.yml"
        copy_file "omniauth.rb", "config/initializers/omniauth.rb"
      end

      def copy_migrations
        rake "simple_user:install:migrations"
        SimpleUser::Engine.load_seed
      end

    end
  end
end

Template of the omniauth.rb

require 'omniauth'
require 'omniauth-facebook'

OmniAuth.config.logger = Rails.logger

Rails.application.config.middleware.use OmniAuth::Builder do
    provider :facebook, ENV['FACEBOOK_KEY'], ENV['FACEBOOK_SECRET'], :scope => ENV['FACEBOOK_SCOPE']
end

Routes (on engine)

match 'auth/:provider/callback', to: 'auth#create'
match 'auth/failure', to: redirect('/')

Routes (on dummy app)

mount SimpleUser::Engine => "/simple_user", :as => "simple_user"

.gemspec dependencies

  s.add_dependency "rails", "~> 3.2.12"
  s.add_dependency "devise"
  s.add_dependency "cancan"
  s.add_dependency "rolify"
  s.add_dependency "omniauth"
  s.add_dependency "omniauth-facebook", "1.4.1"
  s.add_dependency "simple_form"

  #s.add_development_dependency "mysql2"
  s.add_development_dependency "sqlite3"
  s.add_development_dependency "jquery-rails"
  s.add_development_dependency "debugger"

Gemfile

source "http://rubygems.org"

gemspec

gem 'devise'
gem 'cancan'
gem 'rolify'
gem 'omniauth'
gem 'omniauth-facebook', '1.4.1'
gem 'simple_form'

# Development
gem 'jquery-rails'
gem 'debugger'

I think the problem is the callback that is not initialised, and the reason may be that OmniAuth doesn't get loaded, but I don't know if it is and how to solve it.

You can check the project in https://github.com/pablomarti/simple_user, and if you want to clone it and test you can use the generator rails g simple_user:install, and you can see the code of test/dummy also to get the idea.

Thank you very much in advance.

pablomarti
  • 2,087
  • 2
  • 22
  • 35

1 Answers1

0

The solution was to remove the omniauth.rb and include the middleware of OmniAuth in the engine, so the engine looks like this:

module SimpleUser

  require 'rubygems'
  require 'devise'
  require 'cancan'
  require 'rolify'
  require 'omniauth'
  require 'omniauth-facebook'
  require 'simple_form'

  class Engine < ::Rails::Engine
    isolate_namespace SimpleUser

    middleware.use OmniAuth::Builder do
      provider :facebook, ENV['FACEBOOK_KEY'], ENV['FACEBOOK_SECRET'], :scope => ENV['FACEBOOK_SCOPE']
    end

    config.before_configuration do
      env_file = File.join(Rails.root, 'config', 'fb_config.yml')
      YAML.load(File.open(env_file)).each do |key, value|
        ENV[key.to_s] = value.to_s
      end if File.exists?(env_file)

      env_file = File.join(Rails.root, 'config', 'devise_config.yml')
      YAML.load(File.open(env_file)).each do |key, value|
        ENV[key.to_s] = value.to_s
      end if File.exists?(env_file)
    end

  end
end

Thanks to Dmitry Lihachev for his answer https://stackoverflow.com/a/8413724/347501 in a similar problem.

Community
  • 1
  • 1
pablomarti
  • 2,087
  • 2
  • 22
  • 35