Im using devise omniauthable for user authentication both with google and facebook. Google works just fine but facebook gets stucked on a redirect page, although on their platform the user logs in correctly (on facebook).
This is the log i get:
2014-09-01T15:26:41.996884+00:00 app[web.1]: (facebook) Request phase initiated.
2014-09-01T15:26:42.211524+00:00 app[web.1]: (facebook) Request phase initiated.
2014-09-01T15:26:41.994627+00:00 app[web.1]: Started GET "/users/auth/facebook?locale=es" for 190.15.201.45 at 2014-09-01 15:26:41 +0000
2014-09-01T15:26:42.205674+00:00 app[web.1]: Started GET "/users/auth/facebook?locale=es" for 190.15.201.45 at 2014-09-01 15:26:42 +0000
2014-09-01T15:26:42.217355+00:00 heroku[router]: at=info method=GET path="/users/auth/facebook?locale=es" host=myapp.herokuapp.com request_id=2b9aab45-c511-4ac9-b36f-4e6925cba3aa fwd="190.15.201.45" dyno=web.1 connect=2ms service=16ms status=302 bytes=1284
My routes:
devise_for :users, :controllers => { omniauth_callbacks: "users/omniauth_callbacks" }
My omniauth_callbacks_controller:
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
def self.provides_callback_for(provider)
class_eval %Q{
def #{provider}
#user = User.find_for_oauth(env["omniauth.auth"], current_user)
if #user.persisted?
sign_in_and_redirect #user, event: :authentication
set_flash_message(:notice, :success, kind: "#{provider}".capitalize) if is_navigational_format?
else
session["devise.#{provider}_data"] = env["omniauth.auth"]
redirect_to new_user_registration_url
end
end
}
end
[:google_oauth2, :facebook].each do |provider|
provides_callback_for provider
end
My user model:
class User < ActiveRecord::Base
devise :database_authenticatable, :confirmable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :omniauthable
belongs_to :company
before_save :create_user_company
def create_user_company
if self.company_id.nil?
company = Company.new
company.email = self.email
company.save
self.company_id = company.id
self.admin = true
end
end
def self.find_for_oauth(auth, signed_in_resource = nil)
identity = Identity.find_for_oauth(auth)
user = signed_in_resource ? signed_in_resource : identity.user
if user.nil?
email = auth.info.email
user = User.where(:email => email).first if email
if user.nil?
user = User.new(
name: auth.extra.raw_info.name,
email: email ? email : "temp_email#mail.com",
password: Devise.friendly_token[0,20]
)
user.skip_confirmation!
user.save!
end
end
if identity.user != user
identity.user = user
identity.save!
end
user
end
end
Devise initializer is the basic:
config.omniauth :facebook, ENV['FB_APP_ID'], ENV['FB_APP_SECRET']
But i also tried this (didnt work):
config.omniauth :facebook, ENV['FB_APP_ID'], ENV['FB_APP_SECRET'],{client_options: {ssl: {ca_file: Rails.root.join('lib/assets/cacert.pem').to_s}}}
And im using this gems:
gem 'omniauth'
gem 'omniauth-google-oauth2'
gem 'omniauth-facebook'
gem 'devise'
gem 'figaro' #for safe saving of env vars
I would appreciate any hint you might have.
Thanks in advance.
PS: this is not the answer
Ok, if anyone encounters the same problem, this fixed it:
Go into your facebook developer console (developers.facebook)
Enter your App:
On Setting you need to add a contact email.
On Status and Reviews you will find this question (former Sandbox): "Do you want to make this app and all its live features available to the general public?".
Click "Yes".
Related
I get this error message:
Completed 500 Internal Server Error in 5ms (ActiveRecord: 0.0ms)
NoMethodError (undefined method `avatar=' for #<User::ActiveRecord_Relation:0x007f87e4c304d8>):
app/controllers/api/v1/user_controller.rb:10:in `upload'
Model:
class User < ActiveRecord::Base
acts_as_token_authenticatable
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :confirmable
mount_uploader :avatar, AvatarUploader
validates_presence_of :avatar
validates_integrity_of :avatar
validates_processing_of :avatar
end
Controller:
module Api
module V1
class UserController < ApplicationController
before_action :set_user, only: [:show, :update, :destroy]
#before_filter :authenticate_user_from_token!
def upload
puts "here => " + params[:user][:email].to_s
#user = User.where(email: params[:user][:email])
#user.avatar = params[:user][:file]
#user.save!
p #user.avatar.url # => '/url/to/file.png'
p #user.avatar.current_path # => 'path/to/file.png'
p #user.avatar_identifier # => 'file.png'
end
...
environment.rb:
# Load the Rails application.
require File.expand_path('../application', __FILE__)
require 'carrierwave/orm/activerecord'
# Initialize the Rails application.
Rails.application.initialize!
The AvatarUploader was generated and the avatar:string column was added to the users table through the migration execution. I am not sure what's wrong with it.
Extra info: I use Rails: 4.2.4, Ruby: 2.2.1
Many thanks !
The error is pretty informative. When you call User.where(email: params[:user][:email]) you don't get a User object, you get an ActiveRecord_Relation object, wich can contain multiple ActiveRecord objects or be empty. To get a single User you want to use find_by instead of where, then you'll be able to get access to the avatar.
I am developing a web-app using Ruby on Rails (4.2.0) and I faced the implementation the login via Oauth (I followed the RailsCast tutorials #235 and #236). In particular for the authentication I implemented the following model
class Authentication < ActiveRecord::Base
# an authentication entry belongs to one user
belongs_to :user
# provider must be present
validates :provider, presence: true
# uid must be present
validates :uid, presence: true
# uniqueness of the couple user_id - provider
validates_uniqueness_of :user_id, :scope => :provider
end
and the associated create action for the authentication controller is
def create
omniauth = request.env["omniauth.auth"]
authentication =Authentication.find_by_provider_and_uid(omniauth['provider'],omniauth['uid'])
if authentication
flash[:success] = "Welcome back #{omniauth['info']['name']}!"
log_in(authentication.user)
redirect_to root_url
elsif current_user
current_user.authentications.create(provider: omniauth['provider'], uid: omniauth['uid'])
flash[:success] = "Welcome #{omniauth['info']['name']}!"
redirect_to root_url
else
user = User.new
user.authentications.build(:provider => omniauth ['provider'], :uid => omniauth['uid'])
if user.save
flash[:success] = "Signed in successfully."
redirect_to root_url
else
session[:omniauth] = omniauth.except('extra')
flash[:info] = "Just one step to go!"
redirect_to signup_url
end
end
end
While route.rb configuration file contains
match '/auth/:provider/callback' => 'authentication#create', via: [:get, :post]
Now I get stucked in writing down the controller test for two main reasons:
How can I make the post in test? With the following test unit
class AuthenticationControllerTest < ActionController::TestCase
test "post request" do
post :create
end
end
I get the following error
ActionController::UrlGenerationError: ActionController::UrlGenerationError: No route matches {:action=>"create", :controller=>"authentication"}
test/controllers/authentication_controller_test.rb:6:in `block in <class:AuthenticationControllerTest>'
test/controllers/authentication_controller_test.rb:6:in `block in <class:AuthenticationControllerTest>'
How can I create a "fake" env variable for the test?
Surfing the web I was able to find only tutorials using Capybara or Cucumber and nothing with the standard Rails tests.
Any kind of help will be really appreciated!
Andrea
I have a problem with devise_invitable 1.4.0 and strong parameters when I add additional custom parameters and I really hope somebody can guide me in the right direction. I am able to send invitations, but when an invited user accepts an invitation and enters a desired username, maiden name, password and confirmed password, the following error is shown:
Processing by Users::InvitationsController#update as HTML
Unpermitted parameters: username, name
The user is created as expected, but the 'username' and 'name' columns in the database are empty.
I have tried all the suggestions I could find for related issues, but none of the worked. I have noticed that if I change the app/controllers/users/invitations_controller.rb file in any way (eg inserting a blank space on an empty line) without restarting the webserver (Thin) the problem disappears - but the problem reappears when the webserver is restarted.
The various relevant files look like this:
routes.rb:
Rails.application.routes.draw do
root to: 'visitors#index'
#Tell rails to use the Devise controllers that were generated with this command:
# > rails generate devise:controllers users
#Using these generated controllers allows us to overwrite anything in the deault controllers.
devise_for :users, :path_names => {:sign_in => 'login', :sign_out => 'logout'}, controllers: {confirmations: "users/confirmations", passwords: "users/passwords", registrations: "users/registrations", sessions: "users/sessions", unlocks: "users/unlocks", :invitations => 'users/invitations'}
resources :users
end
config/initializers/devise.rb
Devise.setup do |config|
...
...
config.scoped_views = true
config.authentication_keys = [ :username ]
...
...
end
app/controllers/users/invitations_controller.rb:
class Users::InvitationsController < Devise::InvitationsController
private
# this is called when creating invitation
# should return an instance of resource class
def invite_resource
## skip sending emails on invite
resource_class.invite!(invite_params, current_inviter) do |u|
u.tenant = current_inviter.tenant
u.role = :user
end
end
def after_invite_path_for(resource)
users_path
end
def resource_params
params.permit(user: [:name, :email,:invitation_token, :username])[:user]
end
end
app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
around_filter :scope_current_tenant
before_filter :configure_permitted_parameters, if: :devise_controller?
if Rails.env.development?
# https://github.com/RailsApps/rails-devise-pundit/issues/10
include Pundit
# https://github.com/elabs/pundit#ensuring-policies-are-used
# after_action :verify_authorized, except: :index
# after_action :verify_policy_scoped, only: :index
rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
end
#############################################################################
private
#############################################################################
if Rails.env.development?
def user_not_authorized
flash[:alert] = "Access denied." # TODO: make sure this isn't hard coded English.
redirect_to (request.referrer || root_path) # Send them back to them page they came from, or to the root page.
end
end
def current_tenant
#current_tenant ||= current_user.tenant unless current_user.nil?
end
helper_method :current_tenant
def scope_current_tenant(&block)
if current_tenant.nil?
scope_visitor_schema
yield
else
current_tenant.scope_schema("public", &block)
end
end
def scope_visitor_schema()
original_search_path = ActiveRecord::Base.connection.schema_search_path
ActiveRecord::Base.connection.schema_search_path = 'public'
ensure
ActiveRecord::Base.connection.schema_search_path = original_search_path
end
#############################################################################
protected
#############################################################################
def configure_permitted_parameters
# Only add some parameters
devise_parameter_sanitizer.for(:account_update).concat [:name, :email]
# Override accepted parameters
devise_parameter_sanitizer.for(:accept_invitation) do |u|
u.permit(:name, :username, :password, :password_confirmation,
:invitation_token)
end
end
end
app/models/user.rb:
class User < ActiveRecord::Base
enum role: [:user, :admin]
after_initialize :create_tenant, :if => :new_record?
belongs_to :tenant
# has_many :invitations, :class_name => self.to_s, :as => :invited_by
scope :unconfirmed, -> { where(confirmed_at: nil) }
scope :confirmed, -> { where.not(confirmed_at: nil) }
# validate :username, presence: true, uniqueness: true, format: { with: /[a-zA-Z0-9]{4,20}/ }
def displayed_username
username.nil? ? "N/A" : username
end
def displayed_name
name.nil? ? "N/A" : name.titleize
end
def create_tenant
#The create_tenant method will also be called when looking up a user,
#so the following ensures a tenant is only created if it does not already
#exist - and the user has not been invited and assigned to an existing tenant:
if self.tenant.nil?
#Set role to 'admin' if a tenant is about to be created:
self.role = :admin #if self.tenant.nil?
self.tenant = Tenant.new
end
end
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :invitable, :database_authenticatable, :registerable, :confirmable,
:recoverable, :rememberable, :trackable, :validatable
end
I finally found a fix, which was to place the parameter sanitizer directly in users/invitations_controller.rb instead of the application_controller.rb.
class Users::InvitationsController < Devise::InvitationsController
before_filter :configure_permitted_parameters, if: :devise_controller?
private
def configure_permitted_parameters
devise_parameter_sanitizer.for(:accept_invitation) do |u|
u.permit(:username, :name, :email, :password, :password_confirmation, :invitation_token)
end
end
end
I created a starter app from RailsApps with the rails-devise-pundit example app. I am trying to write a user controller test because I plan to change some functionality and I want to make sure things still work. The pundit UserPolicy is not returning the correct value which is based on a role enum in the User class. The UserPolicy.index? method seen below is returning false when called from the first test in UsersControllerTest. Sorry there is a lot of code and detail here. I hope everyone can follow it.
Here's the failing test in UsersControllersTest. The response is a :redirect instead of :success.
require "test_helper"
class UsersControllerTest < ActionController::TestCase
def setup
#admin = users(:admin)
#admin.role = :admin
end
test "should get index page when authenticated as an admin" do
sign_in #admin
get :index
assert_response :success
end
...
end
Here's my user controller class just showing the index method where my problem is. authorize #users should call the UserPolicy.index? method.
class UsersController < ApplicationController
before_filter :authenticate_user!
after_action :verify_authorized, except: [:show]
def index
#users = User.all
authorize #users
end
...
end
My pundit user policy class. When I change the index? method so it returns true, the response in my UsersControllerTest is :success. So for some reason #user.admin? is not returning the correct value.
class UserPolicy
attr_reader :user, :record
def initialize(user, record)
#user = user
#record = record
end
def index?
#user.admin?
end
...
end
What is even stranger is that I created a UserPolicyTest class and when I test calling index? from there, I get the correct response. This test works correctly:
require 'test_helper'
class UserPolicyTest < ActiveSupport::TestCase
def setup
#admin = users(:admin)
#admin.role = :admin
end
def test_index
policy = UserPolicy.new #admin, nil
assert policy.index?
end
end
Here is my User model:
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable, :confirmable,
:recoverable, :rememberable, :trackable, :validatable
enum role: [:user, :vip, :admin]
after_initialize :set_default_role, :if => :new_record?
validates :name, presence: true
def set_default_role
self.role ||= :user
end
end
Here's my test fixture for an admin user:
admin:
email: admin#example.com
name: Mr Admin
role: admin
encrypted_password: $2a$10$PoBe1MvkoGJsjMVTEjKqgeBUp.xdfzWoiDjBzQhtLAj16NqIa2fOy
remember_created_at: nil
sign_in_count: 3
current_sign_in_at: 2014-01-02 08:31:23
last_sign_in_at: 2014-01-02 08:31:23
current_sign_in_ip: 127.0.0.1
last_sign_in_ip: 127.0.0.1
confirmation_token: nil
confirmed_at: 2014-01-02 08:31:23
confirmation_sent_at: 2014-01-02 08:30:59
created_at: 2014-01-02 08:30:59
updated_at: 2014-01-02 08:31:23
I found that setting the role in the fixture doesn't work. I'm guessing that's because of the after_initialize :set_default_role, :if => :new_record? line in my User model. If there's another reason or a better way to handle this, please let me know.
UPDATE: Maybe this is being caused by strong parameters. When I tried debugging my code with pry, I found that in the UsersControllerTest, after signing in, the admin user had a role of 2 which is correct. But when it got to User.Policy.index?, the role was 0. I may need to add the role field to the devise strong parameters. I saw something about how to do that a while back. It didn't look easy. If someone knows the answer before I get to it, please let me know.
After I changed the value of #admin.role in setup, I didn't save the user. After adding #admin.save to the setup method, the test passed.
I cannot get postmark to handle registration and forgot password emails:
user_mailer.rb
class UserMailer < ActionMailer::Base
include Devise::Mailers::Helpers
default from: "donotreply#barnpix.com"
def confirmation_instructions(record)
devise_mail(record, :confirmation_instructions)
end
def reset_password_instructions(record)
devise_mail(record, :reset_password_instructions)
end
def unlock_instructions(record)
devise_mail(record, :unlock_instructions)
end
# you can then put any of your own methods here
end
application.rb
config.action_mailer.delivery_method = :postmark
config.action_mailer.postmark_settings = { :api_key => ENV['9302106a-63xxxx-xxx-xx-'] }
user.rb
devise :database_authenticatable, :registerable, :recoverable,
:rememberable, :trackable, :validatable
devise.rb
config.mailer = "UserMailer" # UserMailer is my mailer class
I cannot get this to work at all. Any hints as to what I might be doing wrong or what I might be missing to get this to work ?
I think your problem is caused by this line:
config.action_mailer.postmark_settings = { :api_key => ENV['9302106a-63xxxx-xxx-xx-'] }
ENV is a hash of all environment variables. You should use names to access the values. I guess you’re using Postmark on Heroku, so it will be ENV['POSTMARK_API_KEY'] in that case.