Lorsque vous utilisez l'API Twitter, vous pouvez utiliser votre propre (compte enregistré) access_token, access_token_secret ↓ pour obtenir des informations publiques sur les utilisateurs et des tweets. Cependant, vous devez utiliser un access_token et un access_token_secret uniques pour chaque utilisateur afin d'obtenir des informations sur les utilisateurs privés et pour permettre aux utilisateurs de tweeter via l'application. Dans cet article, j'écrirai une implémentation qui stocke et utilise access_token et access_token_secret des utilisateurs individuels dans la base de données.
On suppose que l'authentification Twitter a été implémentée dans [Rails] Sortery for Twitter authentication \ -Qiita.
【Mise en garde】 Cet article suppose qu'il n'y a aucun utilisateur au moment de la mise en œuvre. Si vous avez déjà un utilisateur (si vous ne pouvez pas réinitialiser les données utilisateur), l'accès_token de cet utilisateur sera nul, vous devrez donc ajouter un peu de branchement conditionnel ennuyeux.
Ruby 2.6.6 Rails 5.2.4.3 Sorcery 0.15.0
Dans mon cas, j'ai supposé que l'authentification externe était uniquement pour Twitter, j'ai donc changé l'association en has_one
comme suit.
app/models/user.rb
class User < ApplicationRecord
authenticates_with_sorcery!
- has_many :authentications, dependent: :destroy
- accepts_nested_attributes_for :authentications
+ has_one :authentication, dependent: :destroy
+ accepts_nested_attributes_for :authentication
...
end
Si vous travaillez avec d'autres services et que l'utilisateur has_many: authentifications, remplacez la description telle que ʻuser.authentication.hogepar ʻuser.authentications.find_by! (Fournisseur: 'twitter')
.
Ajoutez les colonnes access_token et access_token_secret à la table d'authentification. Si vous utilisez un lien autre que Twitter, supprimez l'option facultative «null: false».
db/migrate/20200613022618_add_access_token_columns_to_authentications.rb
class AddAccessTokenColumnsToAuthentications < ActiveRecord::Migration[5.2]
def change
add_column :authentications, :access_token, :string, null: false
add_column :authentications, :access_token_secret, :string, null: false
end
end
Ce qui a changé par rapport à l'article précédent, c'est l'intérieur de la méthode create_user_from
.
app/controllers/api/v1/oauths_controller.rb
class OauthsController < ApplicationController
skip_before_action :require_login # applications_avant avec contrôleur_action :require_Si la connexion est définie
def oauth
login_at(auth_params[:provider])
end
def callback
provider = auth_params[:provider]
if auth_params[:denied].present?
redirect_to root_path, notice: "J'ai annulé ma connexion"
return
end
#Créez un nouvel utilisateur si vous ne pouvez pas vous connecter avec les informations d'identification envoyées (s'il n'y en a pas)
create_user_from(provider) unless (@user = login_from(provider))
redirect_to root_path, notice: "#{provider.titleize}Je me suis connecté avec"
end
private
def auth_params
params.permit(:code, :provider, :denied)
end
def create_user_from(provider)
@user = build_from(provider) # ①
@user.build_authentication(uid: @user_hash[:uid],
provider: provider,
access_token: access_token.token,
access_token_secret: access_token.secret) # ②
@user.save! # ③
reset_session
auto_login(@user)
end
① build_from
est une méthode de sorcellerie.
Mettez les données passées de provider
(: twitter
) à Sorcery en tant qu'attributs de l'instance User (@ user
).
② Créez une instance d'authentification.
Puisque @ user_hash
, ʻaccess_tokencontient les données reçues de Twitter, utilisez-le. Au fait,
build_authentication est une méthode de has_one, donc dans le cas de has_many, veuillez la définir sur ʻuser.authentications.build
.
[11] pry(#<OauthsController>)> @user_hash
=> {:token=>"111111111111111111111",
:user_info=>
{"id"=>1048451188209770497,
"id_str"=>"1048451188209770497",
"name"=>"END",
"screen_name"=>"aiandrox",
"location"=>"Okayama tout le temps → Yamanashi un peu → Tokyo Imakoko",
"description"=>"Une personne récemment devenue ingénieur, travaillant comme enseignant au primaire ou comme intermédiaire. Profitez de résoudre le mystère.#RUNTEQ",
"url"=>"https://t.co/zeP2KN6GMM",
...
}
}
[12] pry(#<OauthsController>)> access_token
=> #<OAuth::AccessToken:0x00007f2fc41402d0
@consumer=
#<OAuth::Consumer:0x00007f2fc405c008
@debug_output=nil,
@http=#<Net::HTTP api.twitter.com:443 open=false>,
@http_method=:post,
@key="aaaaaaaaaaaaaaaaa",
@options=
{:signature_method=>"HMAC-SHA1",
:request_token_path=>"/oauth/request_token",
:authorize_path=>"/oauth/authenticate",
:access_token_path=>"/oauth/access_token",
:proxy=>nil,
:scheme=>:header,
...
③ Enregistrez chaque authentification associée.
Les instances d'authentification créées par @ user.build_authentication
, @ user.authentications.build
, etc. sont enregistrées avec l'utilisateur lors de son enregistrement.
Il est dangereux de sauvegarder access_token et access_token_secret tels qu'ils sont dans la base de données, donc enregistrez-les cryptés.
app/models/authentication.rb
class Authentication < ApplicationRecord
before_save :encrypt_access_token
belongs_to :user
validates :uid, presence: true
validates :provider, presence: true
def encrypt_access_token
key_len = ActiveSupport::MessageEncryptor.key_len
secret = Rails.application.key_generator.generate_key('salt', key_len)
crypt = ActiveSupport::MessageEncryptor.new(secret)
self.access_token = crypt.encrypt_and_sign(access_token)
self.access_token_secret = crypt.encrypt_and_sign(access_token_secret)
end
end
Découpez la logique liée au client Twitter à la classe de service. Si l'utilisateur est inclus dans l'argument, utilisez le jeton d'accès de l'utilisateur, etc., et si l'utilisateur n'est pas passé, utilisez le jeton d'accès par défaut, etc.
app/services/twitter_api_client.rb
require 'twitter'
class ApiClient
def self.call(user = nil)
new.call(user)
end
def call(user)
@user = user
@client ||= begin
Twitter::REST::Client.new do |config|
config.consumer_key = Rails.application.credentials.twitter[:key]
config.consumer_secret = Rails.application.credentials.twitter[:secret_key]
config.access_token = access_token
config.access_token_secret = access_token_secret
config.dev_environment = 'premium'
end
end
end
private
attr_reader :user
def access_token
@access_token ||= user ? crypt.decrypt_and_verify(user.authentication.access_token)
: Rails.application.credentials.twitter[:access_token]
end
def access_token_secret
@access_token_secret ||= user ? crypt.decrypt_and_verify(user.authentication.access_token_secret)
: Rails.application.credentials.twitter[:access_token_secret]
end
def crypt
key_len = ActiveSupport::MessageEncryptor.key_len
secret = Rails.application.key_generator.generate_key('salt', key_len)
ActiveSupport::MessageEncryptor.new(secret)
end
end
Vous pouvez appeler une instance de Client avec TwitterApiClient.call (user)
.
Lorsque vous écrivez sous forme de code, utilisez quelque chose comme TwitterApiClient.call (utilisateur) .update (" Je tweet avec @gem! ")
.