如何使用 OmniAuth 允许每个用户进行多次 Instagram 身份验证?
How do I allow several Instagram authentications per user with OmniAuth?
我需要允许用户使用 3 个 Instagram 帐户进行身份验证。我在 Ruby 和 Rails 上开发,我正在使用 Instagram OAuth。
在我的 Devise
配置中,我添加了 config.omniauth :instagram, ENV['INSTAGRAM_client'], ENV['INSTAGRAM_secret'], {:scope => 'basic'}
。这仅适用于一次身份验证。我的问题是如何设置 2 个不同的端点以使用不同的帐户进行身份验证,以便我可以使用此 gem 分别处理它们以及如何访问它们?
创建多重身份验证(或多提供商)应用程序的第一步是将您的用户模型与身份验证分开。
确保在开始之前阅读 https://github.com/plataformatec/devise/wiki/OmniAuth:-Overview。
我们想要如下内容:
class User < ActiveRecord::Base
has_many :authentications
end
# Represents an OAuth authentication account
class Authentication < ActiveRecord::Base
belongs_to :user
alidates_uniqueness_of :uid, scope: :provider
end
这是基于多提供商设置 - 如果您只打算使用 Instagram,则可以省略提供商列。您还可以根据需要命名模型 - OathAccount
或 InstagramAccount
或任何您喜欢的名称。
我们可以生成认证模型:
rails g authentication uid:string provider:string user:references
我们还想添加一个数据库索引来加快查询速度,并确保每个 Twitter 帐户只有一个身份验证。因此,让我们编辑迁移:
class CreateAuthentications < ActiveRecord::Migration
def change
create_table :authentications do |t|
# ...
end
# add this line
add_index :authentications, [:provider, :uid], unique: true
end
end
现在我们需要处理 OAuth 回调 - 它显示在提供商对话框之后,他们可以在其中选择批准或拒绝应用程序。让我们设置一条路线:
devise_for :users, skip: [:sessions], controllers: {
omniauth_callbacks: 'users/omniauth_callbacks'
}
所以现在在用户登录后将被路由到 /users/omniauth_callbacks/twitter
。
然后我们创建一个控制器:
# app/controllers/users/omniauth_callbacks_controller.rb
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
def instagram
# OmniAuth creates a normalized
# hash of the credentials and info supplied by the provider.
@auth_params = request.env["omniauth.auth"]
# @todo check if authentication exists
# @todo find user for authentication
# @todo create account for new users
# @todo sign user in
end
end
为了处理所有这些逻辑,我们可能需要调用服务对象。这些很简单
ruby 接受输入并完成任务的对象。
# app/services/authenticate_service
class AuthenticateService
# @param [Hash|OmniAuth::AuthHash] auth_hash
# @param [User|nil] current_user
# @return [Authentication]
def call(auth_hash, current_user = nil)
auth = Authentication.find_or_initialize_by(uid: auth_hash[:uid], provider: auth_hash[:provider])
if !current_user || !auth.user
# You need to adapt this to the provider and your user model
user = User.new(
name: auth_hash[:info][:full_name]
)
end
auth.update(user: current_user ? current_user : user)
end
end
这比我们在控制器中拥有所有逻辑要容易得多。
我们可以注入任何哈希并使用我们想要的用户对象。
只是一个小警告 - 我们需要告诉 rails 自动加载我们的服务
# config/application.rb
config.autoload_paths += Dir[Rails.root.join('app', 'services', '{**}')]
现在让我们调用我们的服务并让用户登录
# app/controllers/users/omniauth_callbacks_controller.rb
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
def instagram
@auth = AuthenticateService.new.call(request.env['omniauth.auth'], current_user)
@user = @auth.user
if user_signed_in?
flash[:notice] = 'You have added a new authentication!'
redirect_to :root
else
sign_in_and_redirect(:user, @user)
end
end
end
推荐阅读:
我需要允许用户使用 3 个 Instagram 帐户进行身份验证。我在 Ruby 和 Rails 上开发,我正在使用 Instagram OAuth。
在我的 Devise
配置中,我添加了 config.omniauth :instagram, ENV['INSTAGRAM_client'], ENV['INSTAGRAM_secret'], {:scope => 'basic'}
。这仅适用于一次身份验证。我的问题是如何设置 2 个不同的端点以使用不同的帐户进行身份验证,以便我可以使用此 gem 分别处理它们以及如何访问它们?
创建多重身份验证(或多提供商)应用程序的第一步是将您的用户模型与身份验证分开。
确保在开始之前阅读 https://github.com/plataformatec/devise/wiki/OmniAuth:-Overview。
我们想要如下内容:
class User < ActiveRecord::Base
has_many :authentications
end
# Represents an OAuth authentication account
class Authentication < ActiveRecord::Base
belongs_to :user
alidates_uniqueness_of :uid, scope: :provider
end
这是基于多提供商设置 - 如果您只打算使用 Instagram,则可以省略提供商列。您还可以根据需要命名模型 - OathAccount
或 InstagramAccount
或任何您喜欢的名称。
我们可以生成认证模型:
rails g authentication uid:string provider:string user:references
我们还想添加一个数据库索引来加快查询速度,并确保每个 Twitter 帐户只有一个身份验证。因此,让我们编辑迁移:
class CreateAuthentications < ActiveRecord::Migration
def change
create_table :authentications do |t|
# ...
end
# add this line
add_index :authentications, [:provider, :uid], unique: true
end
end
现在我们需要处理 OAuth 回调 - 它显示在提供商对话框之后,他们可以在其中选择批准或拒绝应用程序。让我们设置一条路线:
devise_for :users, skip: [:sessions], controllers: {
omniauth_callbacks: 'users/omniauth_callbacks'
}
所以现在在用户登录后将被路由到 /users/omniauth_callbacks/twitter
。
然后我们创建一个控制器:
# app/controllers/users/omniauth_callbacks_controller.rb
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
def instagram
# OmniAuth creates a normalized
# hash of the credentials and info supplied by the provider.
@auth_params = request.env["omniauth.auth"]
# @todo check if authentication exists
# @todo find user for authentication
# @todo create account for new users
# @todo sign user in
end
end
为了处理所有这些逻辑,我们可能需要调用服务对象。这些很简单 ruby 接受输入并完成任务的对象。
# app/services/authenticate_service
class AuthenticateService
# @param [Hash|OmniAuth::AuthHash] auth_hash
# @param [User|nil] current_user
# @return [Authentication]
def call(auth_hash, current_user = nil)
auth = Authentication.find_or_initialize_by(uid: auth_hash[:uid], provider: auth_hash[:provider])
if !current_user || !auth.user
# You need to adapt this to the provider and your user model
user = User.new(
name: auth_hash[:info][:full_name]
)
end
auth.update(user: current_user ? current_user : user)
end
end
这比我们在控制器中拥有所有逻辑要容易得多。 我们可以注入任何哈希并使用我们想要的用户对象。
只是一个小警告 - 我们需要告诉 rails 自动加载我们的服务
# config/application.rb
config.autoload_paths += Dir[Rails.root.join('app', 'services', '{**}')]
现在让我们调用我们的服务并让用户登录
# app/controllers/users/omniauth_callbacks_controller.rb
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
def instagram
@auth = AuthenticateService.new.call(request.env['omniauth.auth'], current_user)
@user = @auth.user
if user_signed_in?
flash[:notice] = 'You have added a new authentication!'
redirect_to :root
else
sign_in_and_redirect(:user, @user)
end
end
end
推荐阅读: