Je veux utiliser OmniAuth pour récupérer l'utilisateur access_token et le secret de facebook, twitter et google en même temps. J'utilise Devise pour l'authentification, et je veux savoir comment demander des clés lorsque l'utilisateur est connecté et les stocker dans une base de données pour les utiliser plus tard.Comment utiliser OmniAuth seulement pour l'autorisation de différents Apis sans authentification dans Ruby on Rails
Répondre
Ajouter dans le fichier de pierres précieuses
gem 'devise'
gem 'omniauth'
gem 'omniauth-twitter'
gem 'omniauth-facebook'
gem 'omniauth-linkedin'
Générer des migrations et des modèles
rails generate devise:install
rails generate devise user
rails g migration add_name_to_users name:string
rails g model identity user:references provider:string uid:string
app/modèles/identity.rb
class Identity < ActiveRecord::Base
belongs_to :user
validates_presence_of :uid, :provider
validates_uniqueness_of :uid, :scope => :provider
def self.find_for_oauth(auth)
find_or_create_by(uid: auth.uid, provider: auth.provider)
end
end
app/config/initializers/devise.rb
Devise.setup do |config|
...
config.omniauth :facebook, "KEY", "SECRET"
config.omniauth :twitter, "KEY", "SECRET"
config.omniauth :linked_in, "KEY", "SECRET"
...
end
config/environnements/[environnement] .rb
# General Settings
config.app_domain = 'somedomain.com'
# Email
config.action_mailer.delivery_method = :smtp
config.action_mailer.perform_deliveries = true
config.action_mailer.default_url_options = { host: config.app_domain }
config.action_mailer.smtp_settings = {
address: 'smtp.gmail.com',
port: '587',
enable_starttls_auto: true,
user_name: 'someuser',
password: 'somepass',
authentication: :plain,
domain: 'somedomain.com'
}
config/routes.rb
devise_for :users, :controllers => { omniauth_callbacks: 'omniauth_callbacks' }
app/controllers/omniauth_callbacks_controller.rb
Par conséquent, pour les comptes avec plusieurs lien les fournisseurs la session current_user doit être déjà définie lorsque le rappel OAuth retourne, et transmis à User.find_for_oauth. Cela peut paraître compliqué, mais tous les thats nécessaires pour relier un autre fournisseur, Facebook par exemple, est à redirect_to user_omniauth_authorize_path (facebook), tandis que l'utilisateur est déjà connecté
class OmniauthCallbacksController < Devise::OmniauthCallbacksController
def self.provides_callback_for(provider)
class_eval %Q{
def #{provider}
@user = User.find_for_oauth(env["omniauth.auth"], current_user)
if @user.persisted?
sign_in_and_redirect @user, event: :authentication
set_flash_message(:notice, :success, kind: "#{provider}".capitalize) if is_navigational_format?
else
session["devise.#{provider}_data"] = env["omniauth.auth"]
redirect_to new_user_registration_url
end
end
}
end
[:twitter, :facebook, :linked_in].each do |provider|
provides_callback_for provider
end
def after_sign_in_path_for(resource)
if resource.email_verified?
super resource
else
finish_signup_path(resource)
end
end
end
app/modèles/user.rb
class User < ActiveRecord::Base
TEMP_EMAIL_PREFIX = '[email protected]'
TEMP_EMAIL_REGEX = /\[email protected]/
# Include default devise modules. Others available are:
# :lockable, :timeoutable
devise :database_authenticatable, :registerable, :confirmable,
:recoverable, :rememberable, :trackable, :validatable, :omniauthable
validates_format_of :email, :without => TEMP_EMAIL_REGEX, on: :update
def self.find_for_oauth(auth, signed_in_resource = nil)
# Get the identity and user if they exist
identity = Identity.find_for_oauth(auth)
# If a signed_in_resource is provided it always overrides the existing user
# to prevent the identity being locked with accidentally created accounts.
# Note that this may leave zombie accounts (with no associated identity) which
# can be cleaned up at a later date.
user = signed_in_resource ? signed_in_resource : identity.user
# Create the user if needed
if user.nil?
# Get the existing user by email if the provider gives us a verified email.
# If no verified email was provided we assign a temporary email and ask the
# user to verify it on the next step via UsersController.finish_signup
email_is_verified = auth.info.email && (auth.info.verified || auth.info.verified_email)
email = auth.info.email if email_is_verified
user = User.where(:email => email).first if email
# Create the user if it's a new registration
if user.nil?
user = User.new(
name: auth.extra.raw_info.name,
#username: auth.info.nickname || auth.uid,
email: email ? email : "#{TEMP_EMAIL_PREFIX}-#{auth.uid}-#{auth.provider}.com",
password: Devise.friendly_token[0,20]
)
user.skip_confirmation!
user.save!
end
end
# Associate the identity with the user if needed
if identity.user != user
identity.user = user
identity.save!
end
user
end
def email_verified?
self.email && self.email !~ TEMP_EMAIL_REGEX
end
end
config/routes.rb
match '/users/:id/finish_signup' => 'users#finish_signup', via: [:get, :patch], :as => :finish_signup
app/controllers/users_controller.rb
class UsersController < ApplicationController
before_action :set_user, only: [:show, :edit, :update, :destroy]
...
# GET /users/:id.:format
def show
# authorize! :read, @user
end
# GET /users/:id/edit
def edit
# authorize! :update, @user
end
# PATCH/PUT /users/:id.:format
def update
# authorize! :update, @user
respond_to do |format|
if @user.update(user_params)
sign_in(@user == current_user ? @user : current_user, :bypass => true)
format.html { redirect_to @user, notice: 'Your profile was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: @user.errors, status: :unprocessable_entity }
end
end
end
# GET/PATCH /users/:id/finish_signup
def finish_signup
# authorize! :update, @user
if request.patch? && params[:user] #&& params[:user][:email]
if @user.update(user_params)
@user.skip_reconfirmation!
sign_in(@user, :bypass => true)
redirect_to @user, notice: 'Your profile was successfully updated.'
else
@show_errors = true
end
end
end
# DELETE /users/:id.:format
def destroy
# authorize! :delete, @user
@user.destroy
respond_to do |format|
format.html { redirect_to root_url }
format.json { head :no_content }
end
end
private
def set_user
@user = User.find(params[:id])
end
def user_params
accessible = [ :name, :email ] # extend with your own params
accessible << [ :password, :password_confirmation ] unless params[:user][:password].blank?
params.require(:user).permit(accessible)
end
end
app/views/utilisateurs/finish_signup.html.erb
<div id="add-email" class="container">
<h1>Add Email</h1>
<%= form_for(current_user, :as => 'user', :url => finish_signup_path(current_user), :html => { role: 'form'}) do |f| %>
<% if @show_errors && current_user.errors.any? %>
<div id="error_explanation">
<% current_user.errors.full_messages.each do |msg| %>
<%= msg %><br>
<% end %>
</div>
<% end %>
<div class="form-group">
<%= f.label :email %>
<div class="controls">
<%= f.text_field :email, :autofocus => true, :value => '', class: 'form-control input-lg', placeholder: 'Example: [email protected]' %>
<p class="help-block">Please confirm your email address. No spam.</p>
</div>
</div>
<div class="actions">
<%= f.submit 'Continue', :class => 'btn btn-primary' %>
</div>
<% end %>
</div>
app/controllers/application_controller.rb
La méthode suivante est facultative, mais il est utile si vous voulez vous assurer que l'utilisateur a fourni toutes les informations nécessaires avant d'accéder à une ressource spécifique.
Vous pouvez l'utiliser dans un before_filter comme ceci: before_filter: ensure_signup_complete, seulement: [: nouveau: créer,: mise à jour,: détruire]
class ApplicationController < ActionController::Base
...
def ensure_signup_complete
# Ensure we don't go into an infinite loop
return if action_name == 'finish_signup'
# Redirect to the 'finish_signup' page if the user
# email hasn't been verified yet
if current_user && !current_user.email_verified?
redirect_to finish_signup_path(current_user)
end
end
end