Omniauth-Facebook 的其他用户属性验证失败
Additional User Attribute Validation Failure with Omniauth-Facebook
大家好,在此先感谢您的专业知识和帮助。
我在 Rails 上的 Ruby 方面不是很熟练(仅仅 3 个月前才开始),并且正在构建一个相当复杂的应用程序。我遇到了关于 Omniauth-Facebook 的障碍。我有 Devise、omniauth-facebook、一个 omniauth_callbacks 控制器、一个 Devise registrations_controller、一个 Devise users_controller 和一个对多个字段进行验证的用户模型(请参阅下面的所有代码).在设置这一切时,我或多或少地遵循了这个 Youtube 视频:https://www.youtube.com/watch?v=VeUX3pWn28w 和许多其他分散的指南,当我尝试进行故障排除时。有些已经过时了,我什至不确定我的代码的哪些部分可能已经过时了。
我的注册表单的工作原理:用户可以填写我的自定义注册表单并正确添加到数据库中,所有 Devise 操作都正常运行并且所有自定义 table 属性都被记录下来。用户可以单击 "Signup with Facebook" 按钮并被定向到 Facebook 到 login/signup,其中返回提供者和 UID,以及(看起来)名字、电子邮件和密码。我的架构中默认有 State 和 Zip,因此不会引发验证异常。
似乎失败的地方:当 Facebook 发回信息时,它似乎在电子邮件返回时中断 ("User Exists")。即使我已经包含 "auth.info.last_name, auth.info.image, etc.",终端错误也不会显示从 Facebook 返回的那些额外字段,但验证不会抛出异常,包括电子邮件。在我的数据库中,我确保该电子邮件没有重复。由于我的数据库和验证,我需要的姓氏、性别、出生日期、地址和城市似乎也没有填写,这是可以理解的(last_name 除外),因为我无法从 Facebook 收到这些信息.
这就是我想要的:正如我上面提到的,Facebook 似乎没有返回 "last_name"、"address" 或 "image"。我还需要包括出生日期、性别和他们所在的城市。看来我无法从 omniauth-facebook 做到这一点,因为 Facebook 的 Auth Hash 不包括这些字段。我如何允许用户通过 Facebook 使用这些提供的字段进行注册,通过 Facebook 登录,然后被定向到一个页面,他们可以在其中输入我的数据库所需的缺少的附加信息?因此,在通过 facebook 注册后,如何将用户发送到另一个页面(没有抛出那些验证异常?),然后输入该附加信息(姓氏、出生日期、地址、地址 2 [如果需要]、性别和 city/state/zip、or/else 是否缺少任何其他字段)?为什么 "last_name" 会失败,但 "first_name" 没问题?
下面是你们可能需要的所有东西的图片,我认为可能需要。同样,我了解 Ruby 和 Rails 的基础知识,并认为自己处于中等水平,但需要帮助来理解 why/how 答案解决了我遇到的这个问题。遗憾的是,我对高级路由和控制器的工作原理缺乏了解,所以如果您有任何资源可以帮助我提高对该主题的了解,我也将不胜感激。
用户 Table (schema.rb):
create_table "users", force: :cascade do |t|
t.string "first_name", null: false
t.string "last_name", null: false
t.boolean "admin", default: false
t.boolean "subscribed", default: true
t.string "zip", default: "44107", null: false
t.string "phone_number"
t.string "address", null: false
t.string "city_name", default: "Lakewood", null: false
t.string "state", default: "Ohio", null: false
t.date "dob", null: false
t.integer "city_id"
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.string "confirmation_token"
t.datetime "confirmed_at"
t.datetime "confirmation_sent_at"
t.string "unconfirmed_email"
t.integer "failed_attempts", default: 0, null: false
t.string "unlock_token"
t.datetime "locked_at"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "address2"
t.boolean "gender"
t.string "provider"
t.string "uid"
t.string "name"
t.text "image"
t.index ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
t.index ["unlock_token"], name: "index_users_on_unlock_token", unique: true
end
用户模型(user.rb):
class User < ApplicationRecord
# Below - Devise Modules. We have not used = :timeoutable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable,
:validatable, :confirmable,
:omniauthable, omniauth_providers: [:facebook]
# Below - Additional validations of DB field entries presence at Model-level.
validates :first_name, presence: true, length: { maximum: 30 }
validates :last_name, presence: true, length: { maximum: 30 }
validates :dob, presence: { message: "(Date of Birth) must be entered" }
validates :zip, presence: true, length: { maximum: 11 }
validates :city_name, presence: true
validates :state, presence: true, length: { maximum: 15 }
validates :address, presence: true
validates :gender, presence: { message: "must be selected" }
# Below - Associates Users into a One to Many relationship with Cities.
belongs_to :city
def self.new_with_session(params, session)
super.tap do |user|
if data = session["devise.facebook_data"] && session["devise.facebook_data"]["extra"]["raw_info"]
user.email = data["email"] if user.email.blank?
end
end
end
def self.from_omniauth(auth)
where(provider: auth.provider, uid: auth.uid).first_or_create! do |user|
user.provider = auth.provider
user.uid = auth.uid
user.email = auth.info.email
user.first_name = auth.info.first_name
user.password = Devise.friendly_token[0,20]
user.last_name = auth.info.last_name
user.image = auth.info.image
user.address = auth.info.location
user.skip_confirmation!
user.save
end
end
# Below - Turns emails into downcase when saved from the controller into DB.
before_save { self.email = email.downcase }
end
会话控制器 (controllers/users/sessions_controller.rb):
class Users::SessionsController < Devise::SessionsController
# before_action :configure_sign_in_params, only: [:create]
class SessionsController < ApplicationController
def create
@user = User.find_or_create_from_auth_hash(auth_hash)
self.current_user = @user
redirect_to '/'
end
protected
def auth_hash
request.env['omniauth.auth']
end
end
注册控制器 (controllers/users/registrations_controller.rb):
class Users::RegistrationsController < Devise::RegistrationsController
before_action :configure_sign_up_params, only: [:create]
# before_action :configure_account_update_params, only: [:update]
# GET /resource/sign_up
def new
@user = User.new
end
# POST /resource
def create
super
end
# GET /resource/edit
def edit
super
end
# PUT /resource
def update
super
end
protected
# If you have extra params to permit, append them to the sanitizer.
def configure_sign_up_params
devise_parameter_sanitizer.permit(:sign_up, keys: [:first_name, :last_name, :gender, :dob, :admin, :phone_number, :address, :address2, :city_name, :state, :zip, :subscribed, :city_id, :name, :image, :uid, :provider])
end
end
用户控制器 (controllers/users_controller.rb):
class UsersController < ApplicationController
# Main Users Controller
# Index Action for all Users
def index
@users = User.all
end
# Show Action for individual user ID
def show
@user = User.find(params[:id])
end
end
Omniauth 回调控制器 (controllers/user/omniauth_callbacks_controller.rb):
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
# Main MOdel for Facebook OmniAuth User Login/Signup
def facebook
#raise request.env["omniauth.auth"].to_yaml
@user = User.from_omniauth(request.env["omniauth.auth"])
if @user.persisted?
sign_in_and_redirect @user, :event => :authentication
set_flash_message(:notice, :success, :kind => "Facebook") if is_navigational_format?
else
session["devise.facebook_data"] = request.env["omniauth.auth"]
redirect_to new_user_registration_url
end
#
end
def failure
redirect_to root_path
end
end
应用程序控制器 (controllers/application_controller.rb):
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
before_action :configure_permitted_parameters, if: :devise_controller?
# Below, permits strong params from signup process, sign_in, and Account_update
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:login, keys: [:email, :password])
devise_parameter_sanitizer.permit(:sign_up, keys: [:first_name, :last_name, :address, :address2, :dob, :city_name, :state, :zip, :gender, :phone_number, :subscribed, :email, :password, :password_confirmation, :city_id, :name, :image, :uid, :provider])
devise_parameter_sanitizer.permit(:account_update, keys: [:email, :address, :address2, :city_name, :state, :zip, :phone_number, :subscribed, :password, :password_confirmation, :current_password])
end
end
设计初始化器(config/initializers/devise.rb):
# ==> OmniAuth
# Add a new OmniAuth provider. Check the wiki for more information on setting
# up on your models and hooks.
config.omniauth :facebook, 'BLANK', 'BLANK', callback_url: 'MYWEBADRESS/users/auth/facebook/callback'
路线(routes.rb):
Rails.application.routes.draw do
# Main Routes file for all route, URL names and Action Methods.
# Below - Sets up custom login and log out path names for routes for user model.
devise_for :users, path_names: { sign_in: "login", sign_out: "logout" }, controllers: {
sessions: 'users/sessions',
:omniauth_callbacks => "users/omniauth_callbacks" }
# Not logged in visitors will be greeted by Index page
root 'visitors#index'
get 'cities/index'
# Callback Route after OmniAuth redirects back to Ossemble for User Signup
get '/auth/facebook/callback', to: 'sessions#create'
# Below - Sets up routes for admin model.
devise_for :admins
# Below - Creates all routes for City model.
resources :cities, except: [:destroy]
# Below - Creates show route for Users & Model
resources :users, only: [:show, :index]
end
最后是我的注册表 (views/devise/registrations/new.html.erb):
<%= form_for(resource, as: resource_name, url: registration_path(resource_name),
:html => {class: "form", role: "form"}) do |f| %>
<div class="container alert"> <!-- Begin - Error Message(s) Wrapper for User Signup -->
<%= devise_error_messages! %>
</div> <!-- End - Error Message(s) Wrapper for User Signup -->
<div class="container"> <!-- Begin -- Signup Form -->
<div class="form-row"> <!-- Begin - First, Last Name, Gender, & Date of Birth Form Row -->
<div class="form-group col-md-4">
<div class="control-label">
<%= f.label :first_name, "First Name" %>
</div>
<%= f.text_field :first_name, autofocus: true, class: "form-control", placeholder: "Enter First Name" %>
</div>
<div class="form-group col-md-4">
<div class="control-label">
<%= f.label :last_name, "Last Name" %>
</div>
<%= f.text_field :last_name, class: "form-control", placeholder: "Enter Last Name" %>
</div>
<div class="form-group col-md-2">
<div class="control-label">
<label class="control-label">
Gender
</label>
</div>
<div class="gender_form form-control center" >
<%= f.label :gender, "Male", id: "male_text", class: "gender_text" %>
<%= f.radio_button :gender, true, class: "gender_box"%>
<%= f.label :gender, "Female", id: "female_text", class: "gender_text" %>
<%= f.radio_button :gender, false, class: "gender_box" %>
</div>
</div>
<div class="form-group col-md-2">
<div class="control-label">
<%= f.label :dob, "Birth Date" %>
</div>
<%= f.date_field :dob, class: "form-control" %>
</div>
</div>
<br> <!-- End - of Name, Gender & DOB form row -->
<!-- Begin - Email & Password Form Row -->
<div class="form-row">
<div class="form-group col-md-4">
<div class="control-label">
<%= f.label :email, "Email Address" %>
</div>
<%= f.email_field :email, type:"text", class: "form-control", placeholder: "Enter Email Address: youremail@example.com" %>
</div>
<div class="form-group col-md-4">
<div class="control-label">
<%= f.label :password, "Password" %>
<!-- Begin - If statement checks for password minimum character length -->
<small>
(Minimum: <%= @minimum_password_length %> characters)
</small>
</div>
<%= f.password_field :password, autocomplete: "off", id:"inputPassword", class: "form-control", placeholder: "Create a Password" %>
<!-- End - password character length if statement -->
</div>
<div class="form-group col-md-4">
<div class="control-label">
<%= f.label :password_confirmation, "Confirm Password" %>
</div>
<%= f.password_field :password_confirmation, autocomplete: "off", id:"inputPasswordConfirm", class: "form-control", placeholder: "Re-enter Password" %>
</div>
</div>
<br> <!-- End - Email & Password Form Row -->
<!-- Begin - Address & Phone Number Form Row-->
<div class="form-row">
<div class="form-group col-md-4">
<div class="control-label">
<%= f.label :address, "Primary Address" %>
</div>
<%= f.text_field :address, autocomplete: "on", class: "form-control", placeholder: "Enter Address: 1234 Main Street" %>
</div>
<div class="form-group col-md-4">
<div class="control-label">
<%= f.label :address2, "Secondary Address" %> <small> (optional) </small>
</div>
<%= f.text_field :address2, autocomplete: "on", class: "form-control", placeholder: "Apartment, Suite #" %>
</div>
<!-- Phonne number currently hideen on form due to "style-" and "disabled :true" -->
<div class="form-group col-md-3" style="display: none">
<div class="control-label">
<%= f.label :phone_number, "Phone Number" %>
</div>
<%= f.phone_field :phone_number, disabled: true, class: "form-control", placeholder: "Enter Phone #: 555-555-5555" %>
</div>
</div> <br> <!-- End - Address & Phone Number Row -->
<!-- Beginning - Location Form Row -->
<div class="form-row">
<div class="form-group col-md-4">
<div class="control-label">
<%= f.label :city_name, "City" %>
<small>
(Currently Lakewood, OH Supported)
</small>
</div>
<!-- Below, we list all cities to be selected and we set the user's "city_id" field to the corresponding City table's City ID, completing the User's Association to City -->
<%= f.collection_select :city_id, City.all, :id, :name, {prompt: "Choose a City"}, class: "select_form form-control" %>
</div>
<div class="form-group col-md-2 ">
<div class="control-label">
<%= f.label :state, "State" %>
<small>
(Currently Ohio)
</small>
</div>
<%= f.collection_select :state, City.all, :state, :state, {prompt: "Choose a State"}, class: "select_form form-control" %>
</div>
<div class="form-group col-md-2">
<div class="control-label">
<%= f.label :zip, "Zip Code" %> <small> (5-Digit Zip)</small>
</div>
<%= f.collection_select :zip, City.all, :zip, :zip, {prompt: "Choose Zip-Code"}, maxlength: "5", class: "select_form form-control" %>
</div>
</div> <!-- End - Location Form Row -->
<!-- Begin - Subscribe Option -->
<div class="form-row">
<div class="form-group col-md-12 pull-left">
<div class="form-check">
<div class="control-label">
<%= f.label :subscribed, "Subscribe?" %>
<small>
<em>
Stay up to date with Ossemble!
</em>
</small>
<br>
<div class="form-row pull-left">
<div class="col-md-12">
<div>
<%= f.check_box :subscribed, class: "check_box" %>
<%= f.label "Yes!", class: "check_box_label" %>
</div>
</div>
</div>
</div>
</div>
</div>
</div> <!-- End - Subscribe Option -->
<div class="form-row"> <!-- Begin - Create Account Button Row (FB) -->
<div class="form-group col-md-2">
<div class="actions">
<%= f.submit "Create Account", class: "form_btn btn btn-success btn-lg btn-xlarge" %>
</div>
</div>
<div class="form-group col-md-4"> <!-- Begin - Signup With Facebook Button -->
<%- if devise_mapping.omniauthable? %> <!-- Begin - OmniAuth If Statement -->
<!-- Below - OmniAuth & FB Signup Button -->
**<%= link_to '<i class="fa fa-facebook fa-lg fa-fw fb_log_sign_icon" aria-hidden="true"></i> Facebook Signup'.html_safe,
user_facebook_omniauth_authorize_path,
class: "form_btn btn btn-primary btn-lg btn-xlarge" %> <br />
<% end -%>** <!-- End - OmniAuth If Statement -->
</div> <!-- End - Signup with Facebook Button -->
</div> <!-- End - Create Account Button Row (FB) -->
</div> <!-- End - Signup Form -->
<% end %>
我的浏览器和终端错误:
在您的 Devise 初始值设定项 (config/initializers/devise.rb
) 中,您需要使用 info_fields
参数来添加您希望 Facebook 加入的项目 return。默认情况下,Facebook 只有 return 的姓名、电子邮件和 ID。例如:
# Add a new OmniAuth provider. Check the wiki for more information on setting
# up on your models and hooks.
config.omniauth :facebook, ENV['FACEBOOK_APP_ID'], ENV['FACEBOOK_APP_SECRET'],
callback_url: 'https://ossemble-workspace-twistedben.c9users.io/users/auth/facebook/callback',
info_fields: 'email,name,birthday,locale,gender'
您可以获得 Facebook 用户对象 here 上可用项目的列表。某些项目(例如生日)需要用户的许可和 Facebook 对您的应用的特别批准。您需要在文档中彻底探索这一点。
此外,Facebook 用户可以拒绝您的应用访问 his/her 个人资料的任何部分。仅仅因为您请求来自 Facebook 的电子邮件,并不保证回电将包括电子邮件。您的应用需要妥善处理这些情况。
在您的情况下,您可能想要创建一个新的用户记录但不立即保存它。相反,将该对象传递给注册表单,让用户填写 his/her 个人资料中的空白。或者您可以在不验证的情况下保存新记录 (user.save(validate:false)
)。
此外,我建议将您的应用程序 ID 和应用程序密码存储在环境变量 (ENV[...]
) 中,而不是将其签入源代码或在线发布。您会想立即获得一个新的 Facebook 秘密。
大家好,在此先感谢您的专业知识和帮助。
我在 Rails 上的 Ruby 方面不是很熟练(仅仅 3 个月前才开始),并且正在构建一个相当复杂的应用程序。我遇到了关于 Omniauth-Facebook 的障碍。我有 Devise、omniauth-facebook、一个 omniauth_callbacks 控制器、一个 Devise registrations_controller、一个 Devise users_controller 和一个对多个字段进行验证的用户模型(请参阅下面的所有代码).在设置这一切时,我或多或少地遵循了这个 Youtube 视频:https://www.youtube.com/watch?v=VeUX3pWn28w 和许多其他分散的指南,当我尝试进行故障排除时。有些已经过时了,我什至不确定我的代码的哪些部分可能已经过时了。
我的注册表单的工作原理:用户可以填写我的自定义注册表单并正确添加到数据库中,所有 Devise 操作都正常运行并且所有自定义 table 属性都被记录下来。用户可以单击 "Signup with Facebook" 按钮并被定向到 Facebook 到 login/signup,其中返回提供者和 UID,以及(看起来)名字、电子邮件和密码。我的架构中默认有 State 和 Zip,因此不会引发验证异常。
似乎失败的地方:当 Facebook 发回信息时,它似乎在电子邮件返回时中断 ("User Exists")。即使我已经包含 "auth.info.last_name, auth.info.image, etc.",终端错误也不会显示从 Facebook 返回的那些额外字段,但验证不会抛出异常,包括电子邮件。在我的数据库中,我确保该电子邮件没有重复。由于我的数据库和验证,我需要的姓氏、性别、出生日期、地址和城市似乎也没有填写,这是可以理解的(last_name 除外),因为我无法从 Facebook 收到这些信息.
这就是我想要的:正如我上面提到的,Facebook 似乎没有返回 "last_name"、"address" 或 "image"。我还需要包括出生日期、性别和他们所在的城市。看来我无法从 omniauth-facebook 做到这一点,因为 Facebook 的 Auth Hash 不包括这些字段。我如何允许用户通过 Facebook 使用这些提供的字段进行注册,通过 Facebook 登录,然后被定向到一个页面,他们可以在其中输入我的数据库所需的缺少的附加信息?因此,在通过 facebook 注册后,如何将用户发送到另一个页面(没有抛出那些验证异常?),然后输入该附加信息(姓氏、出生日期、地址、地址 2 [如果需要]、性别和 city/state/zip、or/else 是否缺少任何其他字段)?为什么 "last_name" 会失败,但 "first_name" 没问题?
下面是你们可能需要的所有东西的图片,我认为可能需要。同样,我了解 Ruby 和 Rails 的基础知识,并认为自己处于中等水平,但需要帮助来理解 why/how 答案解决了我遇到的这个问题。遗憾的是,我对高级路由和控制器的工作原理缺乏了解,所以如果您有任何资源可以帮助我提高对该主题的了解,我也将不胜感激。
用户 Table (schema.rb):
create_table "users", force: :cascade do |t|
t.string "first_name", null: false
t.string "last_name", null: false
t.boolean "admin", default: false
t.boolean "subscribed", default: true
t.string "zip", default: "44107", null: false
t.string "phone_number"
t.string "address", null: false
t.string "city_name", default: "Lakewood", null: false
t.string "state", default: "Ohio", null: false
t.date "dob", null: false
t.integer "city_id"
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.string "confirmation_token"
t.datetime "confirmed_at"
t.datetime "confirmation_sent_at"
t.string "unconfirmed_email"
t.integer "failed_attempts", default: 0, null: false
t.string "unlock_token"
t.datetime "locked_at"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "address2"
t.boolean "gender"
t.string "provider"
t.string "uid"
t.string "name"
t.text "image"
t.index ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
t.index ["unlock_token"], name: "index_users_on_unlock_token", unique: true
end
用户模型(user.rb):
class User < ApplicationRecord
# Below - Devise Modules. We have not used = :timeoutable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable,
:validatable, :confirmable,
:omniauthable, omniauth_providers: [:facebook]
# Below - Additional validations of DB field entries presence at Model-level.
validates :first_name, presence: true, length: { maximum: 30 }
validates :last_name, presence: true, length: { maximum: 30 }
validates :dob, presence: { message: "(Date of Birth) must be entered" }
validates :zip, presence: true, length: { maximum: 11 }
validates :city_name, presence: true
validates :state, presence: true, length: { maximum: 15 }
validates :address, presence: true
validates :gender, presence: { message: "must be selected" }
# Below - Associates Users into a One to Many relationship with Cities.
belongs_to :city
def self.new_with_session(params, session)
super.tap do |user|
if data = session["devise.facebook_data"] && session["devise.facebook_data"]["extra"]["raw_info"]
user.email = data["email"] if user.email.blank?
end
end
end
def self.from_omniauth(auth)
where(provider: auth.provider, uid: auth.uid).first_or_create! do |user|
user.provider = auth.provider
user.uid = auth.uid
user.email = auth.info.email
user.first_name = auth.info.first_name
user.password = Devise.friendly_token[0,20]
user.last_name = auth.info.last_name
user.image = auth.info.image
user.address = auth.info.location
user.skip_confirmation!
user.save
end
end
# Below - Turns emails into downcase when saved from the controller into DB.
before_save { self.email = email.downcase }
end
会话控制器 (controllers/users/sessions_controller.rb):
class Users::SessionsController < Devise::SessionsController
# before_action :configure_sign_in_params, only: [:create]
class SessionsController < ApplicationController
def create
@user = User.find_or_create_from_auth_hash(auth_hash)
self.current_user = @user
redirect_to '/'
end
protected
def auth_hash
request.env['omniauth.auth']
end
end
注册控制器 (controllers/users/registrations_controller.rb):
class Users::RegistrationsController < Devise::RegistrationsController
before_action :configure_sign_up_params, only: [:create]
# before_action :configure_account_update_params, only: [:update]
# GET /resource/sign_up
def new
@user = User.new
end
# POST /resource
def create
super
end
# GET /resource/edit
def edit
super
end
# PUT /resource
def update
super
end
protected
# If you have extra params to permit, append them to the sanitizer.
def configure_sign_up_params
devise_parameter_sanitizer.permit(:sign_up, keys: [:first_name, :last_name, :gender, :dob, :admin, :phone_number, :address, :address2, :city_name, :state, :zip, :subscribed, :city_id, :name, :image, :uid, :provider])
end
end
用户控制器 (controllers/users_controller.rb):
class UsersController < ApplicationController
# Main Users Controller
# Index Action for all Users
def index
@users = User.all
end
# Show Action for individual user ID
def show
@user = User.find(params[:id])
end
end
Omniauth 回调控制器 (controllers/user/omniauth_callbacks_controller.rb):
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
# Main MOdel for Facebook OmniAuth User Login/Signup
def facebook
#raise request.env["omniauth.auth"].to_yaml
@user = User.from_omniauth(request.env["omniauth.auth"])
if @user.persisted?
sign_in_and_redirect @user, :event => :authentication
set_flash_message(:notice, :success, :kind => "Facebook") if is_navigational_format?
else
session["devise.facebook_data"] = request.env["omniauth.auth"]
redirect_to new_user_registration_url
end
#
end
def failure
redirect_to root_path
end
end
应用程序控制器 (controllers/application_controller.rb):
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
before_action :configure_permitted_parameters, if: :devise_controller?
# Below, permits strong params from signup process, sign_in, and Account_update
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:login, keys: [:email, :password])
devise_parameter_sanitizer.permit(:sign_up, keys: [:first_name, :last_name, :address, :address2, :dob, :city_name, :state, :zip, :gender, :phone_number, :subscribed, :email, :password, :password_confirmation, :city_id, :name, :image, :uid, :provider])
devise_parameter_sanitizer.permit(:account_update, keys: [:email, :address, :address2, :city_name, :state, :zip, :phone_number, :subscribed, :password, :password_confirmation, :current_password])
end
end
设计初始化器(config/initializers/devise.rb):
# ==> OmniAuth
# Add a new OmniAuth provider. Check the wiki for more information on setting
# up on your models and hooks.
config.omniauth :facebook, 'BLANK', 'BLANK', callback_url: 'MYWEBADRESS/users/auth/facebook/callback'
路线(routes.rb):
Rails.application.routes.draw do
# Main Routes file for all route, URL names and Action Methods.
# Below - Sets up custom login and log out path names for routes for user model.
devise_for :users, path_names: { sign_in: "login", sign_out: "logout" }, controllers: {
sessions: 'users/sessions',
:omniauth_callbacks => "users/omniauth_callbacks" }
# Not logged in visitors will be greeted by Index page
root 'visitors#index'
get 'cities/index'
# Callback Route after OmniAuth redirects back to Ossemble for User Signup
get '/auth/facebook/callback', to: 'sessions#create'
# Below - Sets up routes for admin model.
devise_for :admins
# Below - Creates all routes for City model.
resources :cities, except: [:destroy]
# Below - Creates show route for Users & Model
resources :users, only: [:show, :index]
end
最后是我的注册表 (views/devise/registrations/new.html.erb):
<%= form_for(resource, as: resource_name, url: registration_path(resource_name),
:html => {class: "form", role: "form"}) do |f| %>
<div class="container alert"> <!-- Begin - Error Message(s) Wrapper for User Signup -->
<%= devise_error_messages! %>
</div> <!-- End - Error Message(s) Wrapper for User Signup -->
<div class="container"> <!-- Begin -- Signup Form -->
<div class="form-row"> <!-- Begin - First, Last Name, Gender, & Date of Birth Form Row -->
<div class="form-group col-md-4">
<div class="control-label">
<%= f.label :first_name, "First Name" %>
</div>
<%= f.text_field :first_name, autofocus: true, class: "form-control", placeholder: "Enter First Name" %>
</div>
<div class="form-group col-md-4">
<div class="control-label">
<%= f.label :last_name, "Last Name" %>
</div>
<%= f.text_field :last_name, class: "form-control", placeholder: "Enter Last Name" %>
</div>
<div class="form-group col-md-2">
<div class="control-label">
<label class="control-label">
Gender
</label>
</div>
<div class="gender_form form-control center" >
<%= f.label :gender, "Male", id: "male_text", class: "gender_text" %>
<%= f.radio_button :gender, true, class: "gender_box"%>
<%= f.label :gender, "Female", id: "female_text", class: "gender_text" %>
<%= f.radio_button :gender, false, class: "gender_box" %>
</div>
</div>
<div class="form-group col-md-2">
<div class="control-label">
<%= f.label :dob, "Birth Date" %>
</div>
<%= f.date_field :dob, class: "form-control" %>
</div>
</div>
<br> <!-- End - of Name, Gender & DOB form row -->
<!-- Begin - Email & Password Form Row -->
<div class="form-row">
<div class="form-group col-md-4">
<div class="control-label">
<%= f.label :email, "Email Address" %>
</div>
<%= f.email_field :email, type:"text", class: "form-control", placeholder: "Enter Email Address: youremail@example.com" %>
</div>
<div class="form-group col-md-4">
<div class="control-label">
<%= f.label :password, "Password" %>
<!-- Begin - If statement checks for password minimum character length -->
<small>
(Minimum: <%= @minimum_password_length %> characters)
</small>
</div>
<%= f.password_field :password, autocomplete: "off", id:"inputPassword", class: "form-control", placeholder: "Create a Password" %>
<!-- End - password character length if statement -->
</div>
<div class="form-group col-md-4">
<div class="control-label">
<%= f.label :password_confirmation, "Confirm Password" %>
</div>
<%= f.password_field :password_confirmation, autocomplete: "off", id:"inputPasswordConfirm", class: "form-control", placeholder: "Re-enter Password" %>
</div>
</div>
<br> <!-- End - Email & Password Form Row -->
<!-- Begin - Address & Phone Number Form Row-->
<div class="form-row">
<div class="form-group col-md-4">
<div class="control-label">
<%= f.label :address, "Primary Address" %>
</div>
<%= f.text_field :address, autocomplete: "on", class: "form-control", placeholder: "Enter Address: 1234 Main Street" %>
</div>
<div class="form-group col-md-4">
<div class="control-label">
<%= f.label :address2, "Secondary Address" %> <small> (optional) </small>
</div>
<%= f.text_field :address2, autocomplete: "on", class: "form-control", placeholder: "Apartment, Suite #" %>
</div>
<!-- Phonne number currently hideen on form due to "style-" and "disabled :true" -->
<div class="form-group col-md-3" style="display: none">
<div class="control-label">
<%= f.label :phone_number, "Phone Number" %>
</div>
<%= f.phone_field :phone_number, disabled: true, class: "form-control", placeholder: "Enter Phone #: 555-555-5555" %>
</div>
</div> <br> <!-- End - Address & Phone Number Row -->
<!-- Beginning - Location Form Row -->
<div class="form-row">
<div class="form-group col-md-4">
<div class="control-label">
<%= f.label :city_name, "City" %>
<small>
(Currently Lakewood, OH Supported)
</small>
</div>
<!-- Below, we list all cities to be selected and we set the user's "city_id" field to the corresponding City table's City ID, completing the User's Association to City -->
<%= f.collection_select :city_id, City.all, :id, :name, {prompt: "Choose a City"}, class: "select_form form-control" %>
</div>
<div class="form-group col-md-2 ">
<div class="control-label">
<%= f.label :state, "State" %>
<small>
(Currently Ohio)
</small>
</div>
<%= f.collection_select :state, City.all, :state, :state, {prompt: "Choose a State"}, class: "select_form form-control" %>
</div>
<div class="form-group col-md-2">
<div class="control-label">
<%= f.label :zip, "Zip Code" %> <small> (5-Digit Zip)</small>
</div>
<%= f.collection_select :zip, City.all, :zip, :zip, {prompt: "Choose Zip-Code"}, maxlength: "5", class: "select_form form-control" %>
</div>
</div> <!-- End - Location Form Row -->
<!-- Begin - Subscribe Option -->
<div class="form-row">
<div class="form-group col-md-12 pull-left">
<div class="form-check">
<div class="control-label">
<%= f.label :subscribed, "Subscribe?" %>
<small>
<em>
Stay up to date with Ossemble!
</em>
</small>
<br>
<div class="form-row pull-left">
<div class="col-md-12">
<div>
<%= f.check_box :subscribed, class: "check_box" %>
<%= f.label "Yes!", class: "check_box_label" %>
</div>
</div>
</div>
</div>
</div>
</div>
</div> <!-- End - Subscribe Option -->
<div class="form-row"> <!-- Begin - Create Account Button Row (FB) -->
<div class="form-group col-md-2">
<div class="actions">
<%= f.submit "Create Account", class: "form_btn btn btn-success btn-lg btn-xlarge" %>
</div>
</div>
<div class="form-group col-md-4"> <!-- Begin - Signup With Facebook Button -->
<%- if devise_mapping.omniauthable? %> <!-- Begin - OmniAuth If Statement -->
<!-- Below - OmniAuth & FB Signup Button -->
**<%= link_to '<i class="fa fa-facebook fa-lg fa-fw fb_log_sign_icon" aria-hidden="true"></i> Facebook Signup'.html_safe,
user_facebook_omniauth_authorize_path,
class: "form_btn btn btn-primary btn-lg btn-xlarge" %> <br />
<% end -%>** <!-- End - OmniAuth If Statement -->
</div> <!-- End - Signup with Facebook Button -->
</div> <!-- End - Create Account Button Row (FB) -->
</div> <!-- End - Signup Form -->
<% end %>
我的浏览器和终端错误:
在您的 Devise 初始值设定项 (config/initializers/devise.rb
) 中,您需要使用 info_fields
参数来添加您希望 Facebook 加入的项目 return。默认情况下,Facebook 只有 return 的姓名、电子邮件和 ID。例如:
# Add a new OmniAuth provider. Check the wiki for more information on setting
# up on your models and hooks.
config.omniauth :facebook, ENV['FACEBOOK_APP_ID'], ENV['FACEBOOK_APP_SECRET'],
callback_url: 'https://ossemble-workspace-twistedben.c9users.io/users/auth/facebook/callback',
info_fields: 'email,name,birthday,locale,gender'
您可以获得 Facebook 用户对象 here 上可用项目的列表。某些项目(例如生日)需要用户的许可和 Facebook 对您的应用的特别批准。您需要在文档中彻底探索这一点。
此外,Facebook 用户可以拒绝您的应用访问 his/her 个人资料的任何部分。仅仅因为您请求来自 Facebook 的电子邮件,并不保证回电将包括电子邮件。您的应用需要妥善处理这些情况。
在您的情况下,您可能想要创建一个新的用户记录但不立即保存它。相反,将该对象传递给注册表单,让用户填写 his/her 个人资料中的空白。或者您可以在不验证的情况下保存新记录 (user.save(validate:false)
)。
此外,我建议将您的应用程序 ID 和应用程序密码存储在环境变量 (ENV[...]
) 中,而不是将其签入源代码或在线发布。您会想立即获得一个新的 Facebook 秘密。