问题
Summary:
I have a CORS problem. I'm trying to authenticate with Facebook from a React app with a Rails API.
My app runs on localhost:8080 and my api runs on Heroku.
I'm able to login to facebook and create a session on callback, but the cookie set by facebook isn't picked up by omniauth. I use omniauth-facebook client-side authentication.
I think that the problem is somewhere in my configuration of rack-cors. It works when I run my api on localhost:3000.
Earlier, I received the error csrf-detected
, but this was resolved by adding
provider_ignores_state: true
to omniauth configuration. Later, I found that xhrFields: { withCredentials: true }
was required for cors calls to pass cookies.
Finale solution: At the end, I resolve this issue by deploying my app to AWS 53 with domain example.com
and create CNAMES 'api.example.com` to my api on Heroku.
It takes a day to get CNAMES resolved. You can test your setup in terminal with commands host example.com
and host api.example.com
.
Alternative solution is to use nginx as proxy.
Versions:
Rails 4.2.2
Omniauth-facebook 3.0.0
Omniauth-oauth2 1.4.0
Rack-cors 0.4.0
Issue:
Getting error: no_authorization_code.
Routes:
namespace :api do
match "/auth/:provider/callback", to: "sessions#create", via: [:get, :post]
get "/auth/failure", to: "sessions#failure"
get "/logout", to: "sessions#destroy"
Application.rb:
config.middleware.insert_before 0, "Rack::Cors" do
allow do
origins "localhost:8000", "example.com"
resource "*",
:headers => :any,
:methods => [:get, :post, :delete, :put, :options],
:max_age => 1728000
end
end
Omniauth.rb:
OmniAuth.config.path_prefix = "/api/auth"
OmniAuth.config.logger = Rails.logger
OmniAuth.config.on_failure = Proc.new { |env|
OmniAuth::FailureEndpoint.new(env).redirect_to_failure
}
Rails.application.config.middleware.use OmniAuth::Builder do
provider :facebook,
Rails.application.secrets.facebook_key,
Rails.application.secrets.facebook_secret, {
provider_ignores_state: true,
client_options: {
:ssl => { :ca_file => "/usr/lib/ssl/certs/ca-certificates.crt" }
}
}
provider :identity,
fields: [:email],
on_failed_registration: lambda { |env|
Api::IdentitiesController.action(:new).call(env)
}
end
Secret.yml:
development:
facebook_key: <%= ENV["FACEBOOK_KEY"] %>
facebook_secret: <%= ENV["FACEBOOK_SECRET"] %>
Client:
$.ajax({
url: http://localhost:3000/api/auth/facebook,
dataType: "json",
type: "GET",
xhrFields: { withCredentials: true },
success: function(data) {
}.bind(this),
error: function(xhr, status, err) {
}.bind(this)
})
SessionsController:
module Api
class SessionsController < ApplicationController
skip_before_action :verify_authenticity_token
skip_before_action :restrict_access
skip_after_action :verify_authorized
def create
user = User.from_omniauth(auth_params)
session[:user_id] = user.id
render json: user, status: :ok
end
def destroy
session[:user_id] = nil
render json: {}, status: :ok
end
def failure
render json: { errors: params[:message] }, status: :unauthorized
end
private
def auth_params
request.env.fetch("omniauth.auth")
end end end
ApplicationsController:
class ApplicationController < ActionController::Base
include Pundit
before_action :restrict_access
after_action :verify_authorized, :except => :index
after_action :verify_policy_scoped, :only => :index
respond_to :json
protected
attr_reader :current_user
# Allows access to current_user in serializators.
serialization_scope :current_user
def restrict_access
authenticate_or_request_with_http_token do |token, options|
@current_user = User.find_by(token: token)
end
end
end
回答1:
I found out that my Ajax call need to include
xhrFields: { withCredentials: true }
in order to pass cookie to omniauth.
This is because that it is a cross-domain call.
See http://api.jquery.com/jquery.ajax/ for more details.
回答2:
Maybe the CORS middleware isn't working for you. I don't have experience with it, but maybe just add a before action in your application controller, and do the following... just to make sure you know the appropriate header is there.
response.headers["Access-Control-Allow-Origin"] = "http://localhost:8080"
回答3:
I was running into this same issue while using Devise to do omniauth in a rails app. Ultimately, the problem for me was that Facebook updated their API at the beginning of July, and it now requires you to specifically ask for fields in the info_fields
param.
I would try changing your omniauth.rb
to include:
provider :facebook,
Rails.application.secrets.facebook_key,
Rails.application.secrets.facebook_secret, {
:scope => "email, user_birthday",
:info_fields => "email, user_birthday", #<== this made the difference for me
provider_ignores_state: true
}
Here's a link from the devise wiki that mentions the issue. I know you're not using devise but it may be helpful. https://github.com/plataformatec/devise/wiki/OmniAuth:-Overview
来源:https://stackoverflow.com/questions/31032474/cors-issue-with-rails-4-omniauth-facebook-rack-cors-heroku