【发布时间】:2016-02-03 06:23:10
【问题描述】:
我正在尝试(再次)使用 Rails 4、devise 和 omniauth 设置身份验证。
我尝试按照这篇文章中的示例进行操作:Rails 4, Devise, Omniauth (with multiple providers)
我已经安装了这些 gem:
gem 'devise'
gem 'omniauth-google-oauth2'
gem 'omniauth-facebook'
gem 'omniauth-twitter'
gem 'omniauth-linkedin-oauth2'
gem 'oauth2'
我有一个用户模型和一个身份验证模型。
我有:
用户.rb:
has_many :authentications
def disapprove
self.approved = false
end
def approve
self.approved = true
end
社交 = { 脸书:'脸书', google_oauth2: '谷歌', linkedin:'Linkedin' }
def self.from_omniauth(auth, current_user)
authentication = Authentication.where(:provider => auth.provider,
:uid => auth.uid.to_s,
:token => auth.credentials.token,
:secret => auth.credentials.secret).first_or_initialize
authentication.profile_page = auth.info.urls.first.last unless authentication.persisted?
if authentication.user.blank?
user = current_user.nil? ? User.where('email = ?', auth['info']['email']).first : current_user
if user.blank?
user = User.new
user.skip_confirmation!
user.password = Devise.friendly_token[0, 20]
user.fetch_details(auth)
user.save
end
authentication.user = user
authentication.save
end
authentication.user
end
def fetch_details(auth)
self.first_name = auth.info.first_name
self.last_name = auth.info.last_name
self.email = auth.info.email
self.image = URI.parse(auth.info.image)
end
身份验证.rb
belongs_to :user
Routes.rb
devise_for :users,
:controllers => {
:registrations => "users/registrations",
:omniauth_callbacks => 'users/omniauth_callbacks',
}
用户/registrations_controller
class Users::RegistrationsController < Devise::RegistrationsController
#before_filter :check_permissions , :only => [ :new, :create, :cancel ]
#skip_before_filter :require_no_authentication
def check_permissions
authorize! :create, resource
end
def index
if params[:approved] == "false"
@users = User.find_all_by_approved(false)
else
@users = User.all
end
end
def create
@user = User.new(user_params) #(params[:user])
respond_to do |format|
if resource.save
# Tell the UserMailer to send a welcome email after save
# {@user.send_admin_mail
# @user.send_user_welcome_mail}
format.html { redirect_to(profile_path(@user.profile))}
#, notice: 'We have received your registration. We will be in touch shortly.') }
#format.json { render json: root_path, status: :created, location: @user }
else
#format.html { redirect_to(root_path, alert: 'Sorry! There was a problem with your registration. Please contact us to sort it out.') }
format.html { render action: 'new' }
format.json { render json: @user.errors, status: :unprocessable_entity }
end
end
end
private
def user_params
params.require(:user).permit(:first_name, :last_name, :email, :password )
end
# protected
# def after_sign_up_path_for(resource)
# 'subscribers/new'
# end
end
用户/Omniauth_callbacks 控制器
类用户::OmniauthCallbacksController
# try again following https://stackoverflow.com/questions/21249749/rails-4-devise-omniauth-with-multiple-providers
def all
user = User.from_omniauth(env['omniauth.auth'], current_user)
if user.persisted?
sign_in user
flash[:notice] = t('devise.omniauth_callbacks.success', :kind => User::SOCIALS[params[:action].to_sym])
if user.sign_in_count == 1
redirect_to profile_path(@user.profile)
else
redirect_to root_path
end
else
session['devise.user_attributes'] = user.attributes
redirect_to root_path
end
end
User::SOCIALS.each do |k, _|
alias_method k, :all
end
end
设计/新注册视图显示:
<%= simple_form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
<div class="row">
<div class="col-md-3 col-md-offset-3">
<div class="row">
<div class="col-md-12">
<%- if devise_mapping.omniauthable? %>
<div class="facebookauth">
<%= link_to "Join with Facebook", user_omniauth_authorize_path(:facebook) %>
</div>
<% end -%>
</div>
</div>
<div class="row">
<div class="col-md-12">
<%- if devise_mapping.omniauthable? %>
<div class="googleauth">
<%= link_to "Join with Google", user_omniauth_authorize_path(:google_oauth2) %>
</div>
<% end -%>
</div>
</div>
<div class="row">
<div class="col-md-12">
<%- if devise_mapping.omniauthable? %>
<div class="linkedinauth">
<%= link_to "Join with LinkedIn", user_omniauth_authorize_path(:linkedin) %>
</div>
<% end -%>
</div>
</div>
<div class="row">
<div class="col-md-12">
<%- if devise_mapping.omniauthable? %>
<div class="twitterauth">
<%= link_to "Join with Twitter", user_omniauth_authorize_path(:twitter) %>
</div>
<% end -%>
</div>
</div>
</div>
<div class="col-md-5">
<div class="emailform">
<div class="form-inputs", style="margin-left: 7%">
<%= devise_error_messages! %>
<%= f.input :first_name, :label_html => {:class => 'deviselabels'}, autofocus: true, required: false, :input_html => {:maxlength => 15, :size => 40, class: 'lineitemdevise'} %>
<%= f.input :last_name, :label_html => {:class => 'deviselabels'}, required: false, :input_html => {:maxlength => 15, :size => 40, class: 'lineitemdevise'} %>
<%= f.input :email, :label_html => {:class => 'deviselabels'}, required: false, autofocus: false, placeholder: "Please use your work or university address", :input_html => {:maxlength => 55, :size => 40, class: 'lineitemdevise'} %>
<%= f.input :password, :label_html => {:class => 'deviselabels'}, required: false, placeholder: "Minimum 8 characters", :input_html => {:maxlength => 15, :size => 40, class: 'lineitemdevise'} %>
</div>
<div class="form-actions">
<%= f.button :submit, "Join by email", :class => "dcpb" %>
</div>
<% end %>
</div>
</div>
我有另一个名为 profile.rb 的模型。
profile belongs_to user
问题:
- 这些都不起作用。当我点击每个社交媒体登录链接时,页面就会跳转到通过电子邮件注册的表单。
heroku 日志错误消息说:
(facebook) Authentication failure! invalid_credentials: OAuth2::Error, :
2015-11-03T07:05:48.237549+00:00 app[web.1]: {"error":{"message":"Error validating verification code. Please make sure your redirect_uri is identical to the one you used in the OAuth dialog request","type":"OAuthException","code":100,"fbtrace_id":"HD3mnzmSTEw"}}
- 当我使用电子邮件和密码通过电子邮件表单完成注册时,用户名被识别(导航栏显示 Hello ,但当我进入 rails 控制台时,用户未列出。
此外,当我单击用户名时,我收到一条错误消息,提示该配置文件不存在。 heroku 日志说:
(Couldn't find Profile with 'id'=3)
- 是否需要其他步骤才能使社交媒体注册工作以创建新用户?
我的下一次尝试:
我按照 Railscasts 视频中的方法更改了以上所有内容并再次尝试。
我现在使用用户模型和身份验证模型。
在 user.rb 中,我有:
has_many :authentications, :dependent => :delete_all
def apply_omniauth(omniauth)
authentications.build(:provider => omniauth['provider'], :uid => omniauth['uid'], :token => auth['credentials']['token'])
end
身份验证.rb
belongs_to :user
def self.from_omniauth(auth)
where(auth.slice(:provider, :uid)).first_or_create do | user |
authentication.provider = auth.provider
authentication.uid = auth.uid
authentication.user.first_name = auth.first_name
authentication.user.last_name = auth.last_name
authentication.user.image = auth.info.image
end
end
Authentications_controller:
class AuthenticationsController < ApplicationController
before_action :set_authentication, only: [:destroy]
def index
@authentications = current_user.authentications if current_user
end
def create
omniauth = request.env["omniauth.auth"]
authentication = Authentication.find_by_provider_and_uid(omniauth['provider'], omniauth['uid'])
if authentication
sign_in_and_redirect_user(:user, authentication.user.profile)
elsif current_user
current_user.authentications.create!(:provider => omniauth['provider'], :uid => omniauth['uid'])
redirect_to user.profile_url
else
user = User.new
user.omniauth(omniauth)
if user.save!
sign_in_and_redirect_user(:user, user.profile)
else
session[:omniauth] = omniauth.except('extra')
redirect_to new_user_registration_url
end
end
end
def destroy
@authentication.destroy
respond_to do |format|
format.html { redirect_to authentications_url }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_authentication
@authentication = current_user.authentications.find(params[:id])
end
end
在 routes.rb 中,我有:
devise_for :users,
:controllers => {
:registrations => "users/registrations",
}
patch '/auth/:provider/callback' => 'authentications#create'
Omniauth.rb
需要'omniauth-facebook' 需要“omniauth-google-oauth2”
OmniAuth.config.logger = Rails.logger
Rails.application.config.middleware.use OmniAuth::Builder do
provider :facebook, ENV['FACEBOOK_ID'], ENV['FACEBOOK_KEY'],
:scope => 'public_profile', info_fields: 'id,first_name,last_name,link,email',
:display => 'popup',
:client_options => {:ssl => {:ca_file => '/usr/lib/ssl/certs/ca-certificates.crt'}}
然后当我尝试这个时,我得到了这个错误:
(facebook) Authentication failure! invalid_credentials: OAuth2::Error, :
2015-11-05T06:4839+00:00 app[web.1]: {"error":{"message":"Error validating verification code. Please make sure your redirect_uri is identical to the one you used in the OAuth dialog request","type":"OAuthException","code":100,"fbtrace_id":"CrvXN22Z"}}
我觉得错误消息的下一部分很奇怪,因为它引用了我不再使用的回调控制器(整件事都被注释掉了,并且没有路由)。
Authentication failure! invalid_credentials: OAuth2::Error, :
2015-11-05T08:24:16.010951+00:00 app[web.1]: Processing by Devise::OmniauthCallbacksController#failure as HTML
2015-11-05T08:24:16.012648+00:00 app[web.1]: Redirected to http://www.dder.com/users/sign_in
进一步的尝试
我已经尝试使用omniauth 设置设计已经超过1.5 年了。这是我的最新尝试(遵循 sitepoint.com/rails-authentication-oauth-2-0-omniauth 上的 Sitepoint 教程)。我之前尝试过使用本教程,但没有成功,因此我做了一些调整以尝试使其适应有关该主题的其他教程的某些方面。
我现在有:
用户.rb
has_many :authentications, :dependent => :delete_all
def apply_omniauth(omniauth)
authentications.build(:provider => omniauth['provider'], :uid => omniauth['uid'], :token => auth['credentials']['token'])
end
身份验证.rb
belongs_to :user
def self.from_omniauth(auth)
where(auth.slice(:provider, :uid)).first_or_create do | user |
authentication.provider = auth.provider
authentication.uid = auth.uid
authentication.user.first_name = auth.first_name
authentication.user.last_name = auth.last_name
authentication.user.image = auth.info.image
end
end
身份验证控制器
class AuthenticationsController < Devise::OmniauthCallbacksController
before_action :set_authentication, only: [:destroy]
def index
@authentications = current_user.authentications if current_user
end
def create
omniauth = request.env["omniauth.auth"]
authentication = Authentication.find_by_provider_and_uid(omniauth['provider'], omniauth['uid'])
if authentication
sign_in_and_redirect_user(:user, authentication.user.profile)
elsif current_user
current_user.authentications.create!(:provider => omniauth['provider'], :uid => omniauth['uid'])
redirect_to user.profile_url
else
user = User.new
user.omniauth(omniauth)
if user.save!
sign_in_and_redirect_user(:user, user.profile)
else
session[:omniauth] = omniauth.except('extra')
redirect_to new_user_registration_url
end
end
end
def destroy
@authentication.destroy
respond_to do |format|
format.html { redirect_to authentications_url }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_authentication
@authentication = current_user.authentications.find(params[:id])
end
end
注册控制器
class Users::RegistrationsController < Devise::RegistrationsController
#before_filter :check_permissions , :only => [ :new, :create, :cancel ]
#skip_before_filter :require_no_authentication
# before_action :configure_permitted_parameters, if: :devise_controller? # Suggestion from Sitepoint tutorial - not currently implemented because not sure about the difference between this and set params.
def check_permissions
authorize! :create, resource
end
def index
if params[:approved] == "false"
@users = User.find_all_by_approved(false)
else
@users = User.all
end
end
def create
super
session[:omniauth] = nil unless @user.new_record?
end
# THIS IS A SUGGESTION FROM SITEPOINT TUTORIAL
# protected
# def configure_permitted_parameters
# devise_parameter_sanitizer.for(:sign_up) << [:first_name, :last_name]
# end
private
def user_params
params.require(:user).permit(:first_name, :last_name, :email, :password )
end
def build_resource(*args)
super
if session[:omniauth]
@user.apply_omniauth(session[:omniauth])
@user.valid?
end
end
end
路线
devise_for :users,
:controllers => {
:registrations => "users/registrations",
:omniauth_callbacks => "authentications"
# :omniauth_callbacks => 'users/omniauth_callbacks',
}
get '/auth/:provider/callback' => 'authentications#create'
我可以通过以下方式检查这些路线:
rake routes | grep auth
user_omniauth_authorize GET|POST /users/auth/:provider(.:format) authentications#passthru {:provider=>/facebook|linkedin|twitter|google_oauth2/}
user_omniauth_callback GET|POST /users/auth/:action/callback(.:format) authentications#:action
GET /auth/:provider/callback(.:format) authentications#create
视图中的新注册部分
<%- if devise_mapping.omniauthable? %>
<div class="twitterauth">
<%= link_to "Join with Twitter", user_omniauth_authorize_path(:twitter) %>
</div>
<% end -%>
我真的不确定这条路从何而来。不知道为什么这样命名。
视图中的新会话部分
<%- if devise_mapping.omniauthable? %>
<div class="twitterauth">
<%= link_to "Login with Twitter", user_omniauth_authorize_path(:twitter) %>
</div>
<% end -%>
当前错误:
AbstractController::ActionNotFound at /users/auth/twitter/callback
The action 'twitter' could not be found for AuthenticationsController
【问题讨论】:
-
你在Heroku上设置环境变量ENV['FACEBOOK_ID']、ENV['FACEBOOK_KEY']了吗?
-
是的 - 它们在 Heroku 中已正确配置
-
Oauth 2.01 有一个特定的问题。您需要在设备 oauth: config 中指定回调 url。 config.omniauth :facebook, "APP_ID", "APP_SECRET", callback_url: "CALLBACK_URL" 。如果这不起作用,您是否检查了 facebook 中的凭据和应用程序设置?您的域需要与本地环境中的域完全相同?
-
SO 上最长的问题
标签: ruby-on-rails devise omniauth