I have the following problem: My app is running as a Facebook app, but on the first time that the user accept's it's Facebook permissions, it's redirected to the right URL, but outside the Facebook (canvas). How can I fix it?
Here is my actual settings:
devise.rb:config.omniauth :facebook, ENV['FACEBOOK_APP_ID'], ENV['FACEBOOK_SECRET'], scope: "email,publish_stream"
app/controllers/omniauth_callback_controller.rb
class OmniauthCallbacksController < Devise::OmniauthCallbacksController
def passthru
render :file => "#{Rails.root}/public/404.html", :status => 404, :layout => false
end
def facebook
#user = User.find_for_facebook_oauth(request.env["omniauth.auth"], current_user)
if #user.persisted?
flash[:notice] = I18n.t "devise.omniauth_callbacks.success", :kind => "Facebook"
sign_in_and_redirect #user, :event => :authentication
else
session["devise.facebook_data"] = request.env["omniauth.auth"]
redirect_to root_path
end
end
def after_sign_in_path_for(resource)
inicio_path
end
end
routes.rb
Pl::Application.routes.draw do
ActiveAdmin.routes(self)
mount Resque::Server, :at => "/resque"
devise_for :admin_users, ActiveAdmin::Devise.config
devise_for :users, :controllers => { :omniauth_callbacks => "omniauth_callbacks" }
devise_scope :user do
get '/users/auth/:provider' => 'users/omniauth_callbacks#passthru'
end
match 'states' => 'game_plays#states'
match 'cities' => 'game_plays#cities'
match 'parties' => 'game_plays#parties'
match 'prefeito' => 'game_plays#prefeito'
match 'prefeitos' => 'game_plays#prefeitos'
match 'vereadores' => 'game_plays#vereadores'
match 'parties' => 'game_plays#parties'
match 'qualities' => 'game_plays#qualities'
match 'start' => 'game_plays#start'
match 'generated_image' => 'game_plays#generated_image'
match 'save_game_play' => 'game_plays#save_game_play'
match 'final' => 'game_plays#final', :as => :final
match 'inicio' => 'game_plays#index'
root :to => 'game_plays#bem_vindo'
end
Any sugestions?
This is from my own experience a couple months ago with this issue and I hope it helps.
From Facebook's point of view, this is exactly what it is suppose to do. It does not recognize the difference between where the request is coming from. You have to change the redirect URL on the request for it to stay in the iframe.
The easiest way I found to do this was to have the iframe url go to my_website.com/fb-app, which sets a session variable to let you know you are in the canvas and ensures they are already logged in through facebook. It then redirects them to the canvas site where the site continues as normal and the user never notices anything.
def fbapp
session[:fbapp] = true
if user_signed_in? == false
redirect_to user_omniauth_authorize_path(:facebook)
else
redirect_to $APP_CANVAS_URL + "?auth_token=" + current_user.authentication_token
end
end
Then, in your Application_controller.rb you need to change the redirect path. This is what it looked like in my application, note $APP_CANVAS_URL should be something along the lines of https://apps.facebook.com/your_app_here
def after_sign_in_path_for(resource_or_scope)
if session[:fbapp] == true
$APP_CANVAS_URL + "?auth_token=" + current_user.authentication_token
else
root_path
end
end
I doubt this is the best way, but it's what I got to work for me after hours of frustration. I hope it helps.
Related
I have omniauth-facebook to sign up/sign in. After signin, I want to redirect the user to the page where they hit the auth block. I'm using sign_in_and_redirect but it seems to be calling the last url, which in this case is
http://localhost:3000/auth/facebook?callback_url=localhost%2Fauth%2Ffacebook%2Fcallback
So it keeps doing:
Redirected to http://localhost:3000/auth/facebook?callback_url=localhost%2Fauth%2Ffacebook%2Fcallback
And it keeps calling this callback in a loop until it crashes. Here is the omniauth controller:
def facebook
generic_callback( 'facebook' )
end
def generic_callback( provider )
#identity = Identity.find_for_oauth env["omniauth.auth"]
if #identity.user != nil
#user = #identity.user || current_user
else
#user = User.from_omniauth(request.env["omniauth.auth"])
end
if #user.nil?
#user = User.create( email: #identity.email || "" )
#identity.update_attribute( :user_id, #user.id )
end
if #user.email.blank? && #identity.email
#user.update_attribute( :email, #identity.email)
end
if #user.persisted?
#identity.update_attribute( :user_id, #user.id )
#user = User.find(#user.id)
sign_in_and_redirect #user, event: :authentication
return
else
session["devise.#{provider}_data"] = env["omniauth.auth"]
redirect_to new_user_registration_url
return
end
end
The devise helper:
def sign_in_and_redirect(resource_or_scope, *args)
options = args.extract_options!
scope = Devise::Mapping.find_scope!(resource_or_scope)
resource = args.last || resource_or_scope
sign_in(scope, resource, options)
redirect_to after_sign_in_path_for(resource)
end
Routes
resources :users
devise_for :users, controllers: { registrations: "users/registrations", sessions: "users/sessions", :omniauth_callbacks => "users/omniauth_callbacks" }, path: '', path_names: { sign_in: 'sign_in', sign_out: 'logout', sign_up: 'sign_up'}
match '/auth/:provider/callback', to: 'sessions#create', via: [:get, :post]
How can I make it sign in regularly?
Ok, here's the problem. This line creates the recursion:
match '/auth/:provider/callback', to: 'sessions#create', via: [:get, :post]
Change it to:
get 'auth/facebook/callback', to: 'users/omniauth_callbacks#facebook'
If you'll refactor #generic_callback method a bit to use params[:provider] instead of the provider argument, you'll then be able to get rid of hard-coding of the omniauth providers in your routes:
get 'auth/:provider/callback', to: 'users/omniauth_callbacks#generic_callback'
To clarify the problem:
In the implementation described in the question users/omniauth_callbacks#generic_callback is not called at all. Instead you're trying to call sessions#create which is used to sign in users with the app credentials (usually, email and password), whereas in the omniauth case user should be created and authenticated in a specific way (via generic_callback and sign_in_and_redirect in your case).
Simply put:
You don't have to call sessions#create to create user session here, #generic_callback with #sign_in_and_redirect does just that.
so i followed this tutorial: http://www.munocreative.com/nerd-notes/winvoice
also this:https://github.com/isaacsanders/omniauth-stripe-connect
and when i skip the form, i get sent to my uri that i have set in my connect settings but i get this:
No route matches [GET] "/users/auth/stripe_connect/callback"
this is my routes.rb file
Rails.application.routes.draw do
root 'index#home'
devise_for :users, :controllers => { :omniauth_callbacks => "omniauth_callbacks" }
end
this is my omniauth_callbacks_controller.rb
class OmniauthCallbacksController < Devise::OmniauthCallbacksController
def stripe_connect
#user = current_user
if #user.update_attributes({
provider: request.env["omniauth.auth"].provider,
uid: request.env["omniauth.auth"].uid,
access_code: request.env["omniauth.auth"].credentials.token,
publishable_key: request.env["omniauth.auth"].info.stripe_publishable_key
})
# anything else you need to do in response..
sign_in_and_redirect #user, :event => :authentication
set_flash_message(:notice, :success, :kind => "Stripe") if is_navigational_format?
else
session["devise.stripe_connect_data"] = request.env["omniauth.auth"]
redirect_to new_user_registration_url
end
end
any other details you need me to give, just ask. Thanks.
I'm trying to use devise to authenticate a simple email/password login so users can access the rest of the API via auth tokens. I'm running into trouble with devise simply returning You need to sign in or sign up before continuing. Here's my code:
class LoginController < ApplicationController
respond_to :json
def login
resource = warden.authenticate!(:scope => resource_name, :recall => "#{controller_path}#failure")
render :status => 200,
:json => { :success => true,
:info => "Logged in",
:user => current_user
}
end
def failure
render :status => 401,
:json => { :success => false,
:info => "Login Failed",
:data => {} }
end
def resource_name
:user
end
def resource
#resource ||= User.new
end
def devise_mapping
#devise_mapping ||= Devise.mappings[:user]
end
end
routes.rb
devise_scope :user do
post 'register' => 'registration#create', :as => :registers
post 'login' => 'login#login', :as => :login
end
I'm sending the following post data:
{
"user" : {
"email" : "testPost4#fake.com",
"password" : "Password1"
}
}
Having browsed various posts I've added:
config.navigational_formats = [:json]
to the devise.rb file but it didn't solve the problem.
Adding skip_before_filter :authenticate_user! doesn't work either.
I wasn't able to get this working so I have reverted to the much simpler approach of checking and signing in manually.
def login
params = login_params
user = User.find_by_email(params['email'].downcase)
if user.valid_password?(params['password']) then
sign_in(user)
success
else
failure
end
end
I am using omiauth with ROR and i get the following error,
omniauth auth/failure message=invalid_credentials,
I am able to connect with linkedin, i am asked for user credentials after giving everything the page redirect is happening and i get the above error.
Here is my callback:
class SessionsController < ApplicationController
def create
auth = request.env["omniauth.auth"]
user = User.find_by_provider_and_uid(auth["provider"], auth["uid"]) || User.create_with_omniauth(auth)
session[:user_id] = user.id
redirect_to root_url, :notice => "Signed in!"
end
def destroy
session[:user_id] = nil
redirect_to root_url, :notice => "Signed out!"
end
end
and the routes.rb is
Lovelinkedin::Application.routes.draw do
root :to => "users#index"
match "/auth/:provider/callback" => "sessions#create"
match "/auth/failure" => "users#index"
match "/signout" => "sessions#destroy", :as => :signout
end
and my omniauth.rb is
Rails.application.config.middleware.use OmniAuth::Builder do
provider :linkedin, 'xxxxx', 'ffffffff'
end
Please help me.
thanks in advance
What is your OmniAuth.config.full_host equal to in your omniauth.rb file?
if its redirecting to a secure site then it should be something like this:
"https://#{SITE_BASE}"
else just take off the s if its not a secure site
I just created my first engine. It adds a couple of new routes like so:
Rails.application.routes.draw do
scope :module => 'contact' do
get "contact", :to => 'contacts#new'
get "contact/send_email", :to => 'contacts#send_email', :as => 'send_email'
end
end
Then, in /websites/Engines/contact/app/controllers/contacts_controller.rb, I have:
module Contact
class ContactsController < ApplicationController
# Unloadable marks your class for reloading between requests
unloadable
def new
#contact_form = Contact::Form.new
end
def send_email
#contact_form = Contact::Form.new(params[:contact_form])
if #contact_form.valid?
Notifications.contact(#contact_form).deliver
redirect_to :back, :notice => 'Thank you! Your email has been sent.'
else
render :new
end
end
end
end
I loaded it up in the client app's console to prove to myself some basics were working and quickly got this load error (which I then confirmed by reproducing the issue in a browser):
ruby-1.8.7-p302 > Contact::Form.new
=> #<Contact::Form:0x2195b70>
ruby-1.8.7-p302 > app.contact_path
=> "/contact"
ruby-1.8.7-p302 > r = Rails.application.routes; r.recognize_path(app.contact_path)
LoadError: Expected /websites/Engines/contact/app/controllers/contacts_controller.rb to define ContactsController
And there you have it; /contact gets to the engine's contacts_controller.rb but the fact the controller is inside the module Contact makes it unrecognizable.
What am I doing wrong?
Your app/controllers/contacts_controller.rb is actually defining the Contact::ContactsController, not the ContactsController that Rails expects.
The problem is with your routes, they should be defined like this:
Rails.application.routes.draw do
scope :module => 'contact' do
get "contact", :to => 'contact/contacts#new'
get "contact/send_email", :to => 'contact/contacts#send_email', :as => 'send_email'
end
end
Thanks to #ryan-bigg and #nathanvda their answers in conjunction fixed this issue for me. In short, I ended up using the following routes:
Rails.application.routes.draw do
scope :module => 'contact' do
get "contact", :to => 'contacts#new'
post "contact/send_email", :to => 'contacts#send_email', :as => 'send_email'
end
end
with the following controller:
module Contact
class ContactsController < ApplicationController
def new
#contact_form = Contact::Form.new
end
def send_email
#contact_form = Contact::Form.new(params[:contact_form])
if #contact_form.valid?
Contact::Mailer.contact_us(#contact_form).deliver
redirect_to :back, :notice => 'Thank you! Your email has been sent.'
else
render :new
end
end
end
end
but what seemed to be the final piece was #nathanvda's suggestions to move the contacts_controller from:
/app/controllers/contacts_controller.rb
to
/app/controllers/contact/contacts_controller.rb
Thank you both for your help!