Posted at

RailsでFacebook認証する(Devise + OmniAuth)

More than 3 years have passed since last update.


環境

Rails 4.2.6

Devise 4.1.1

omniauth-facebook 3.0.0


やりたいこと

・Facebook認証をする

・Userモデルにnameカラムを追加する


Facebook認証する


1: 認証機能をインストール(Devise)

前も書きましたがdeviseの導入方法については詳しい記事がたくさんあるのでそちらを参考にしてください。

Qiita:Deviseの設定手順をまとめてみた。 その1 導入編

とか

Qiita:Rails 4.2 で ユーザー管理・認証 (devise)

とか。

まぁDeviseのGithubを参考にするのがベターだとは思います。


2: Facebook認証をするためのapp登録をする

Facebook Developersより登録し、「新しいアプリを追加」します。


3: ローカル環境からからアクセスするための設定。

設定>ベーシック>アプリドメインにローカル用の適当なドメインを追加します。→ test.example.com

Facebookログイン>有効なOAuthリダイレクトURIに先ほどのドメインを追加します。→test.example.com

開発環境のhostsファイルにlocalhostをtext.example.comとしてアクセスできるよう追記します。

Macなら/private/etc/hostsに対して、

Windowsならc:¥windows¥system32¥drivers¥etc¥hostsに対して以下を追記します


hosts

127.0.0.1       test.example.com



4: Facebook認証をインストール(omniauth-facebook)

omniauth-facebookでやります。

omniauth-facebookのgithubもあるのですが、Deviseと連携させる場合は、Deviseのwikiを見た方が良いです。

やることはこんな感じ

1. gem追加&bundle install

2. omniauth用のproviderカラムとUIDカラムをUserモデルに追加

3. Userモデル他を編集する


4-1.gem追加&bundle install


Gemfile

gem 'omniauth-facebook'


$ bundle install


4-2.omniauth用のproviderカラムとUIDカラムをUserモデルに追加

$ rails g migration AddOmniauthToUsers provider:string uid:string

$ rake db:migrate


4-3.Userモデル他を編集する

deviseのomniauthableを使えるようにする


app/models/user.rb

devise :omniauthable, :omniauth_providers => [:facebook]


ルーティングにcallbackとログアウトを追加する


routes.rb

  devise_for :users, controllers: { omniauth_callbacks: 'users/omniauth_callbacks' }

devise_scope :user do
delete :sign_out, to: 'devise/sessions#destroy', as: :destroy_user_session
end

callbackを作成する

Wikiに書いてある通りでも良いのですが、私はシンプルにこんな感じでつくっています。


/app/controllers/users/omniauth_callbacks_controller.rb

class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController

include Devise::Controllers::Rememberable

def facebook
auth = request.env['omniauth.auth']
user = User.find_or_create_by(
provider: auth.provider,
uid: auth.uid
)

remember_me(user)

sign_in_and_redirect user, event: :authentication
end

def failure
redirect_to root_path
end
end


ログインリンクを追加する


/app/views/適当.html.erb

<%= link_to "Sign in with Facebook", user_omniauth_authorize_path(:facebook) %>



4: Deviseから不要な機能を削除する

Userモデルよりrememberableとtrackableを除く機能を削除。

先ほどのomniauthableとまとめるとこんな感じ。


app/models/user.rb

devise :rememberable, :trackable, :omniauthable, omniauth_providers: [:facebook]


これでtest.example.comへアクセスすれば使えます。


ユーザーモデルにnameカラムを追加する

手順はこんな感じ

1. ユーザー名(name)カラムを追加する

2. callbackを修正する

3. ユーザー名編集画面を追加する


1.ユーザー名(name)カラムを追加する

$ rails g migration AddNameToUsers name:string

$ rake db:migrate


2.callbackを修正する

nameは編集される可能性があるので、find_by_or_createは使わずにfind_byとcreateを分ける。


/app/controllers/users/omniauth_callbacks_controller.rb

class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController

include Devise::Controllers::Rememberable

def facebook
auth = request.env['omniauth.auth']
user = User.find_by(
provider: auth.provider,
uid: auth.uid
) || User.create(
provider: auth.provider,
uid: auth.uid,
name: auth.info.name
)

remember_me(user)

sign_in_and_redirect user, event: :authentication
end

def failure
redirect_to root_path
end
end



3.ユーザー名編集画面を追加する

こんな感じで。


app/views/users/edit.html.erb

<h2>プロフィールを編集する</h2>

<%= form_for(current_user, html: { method: :patch }) do |f| %>
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name, autofocus: true %>
</div>

<div class="actions">
<%= f.submit '更新する' %>
</div>
<% end %>

<h3>退会する</h3>

<%= button_to "退会する", user_path( current_user ), data: { confirm: "本当に退会してよろしいですか??" }, method: :delete %>


※registerableを使っていないのでユーザーの編集/削除は自前で実装する。


app/controllers/users_controller.rb

class UsersController < ApplicationController

before_action :set_user, only: [ :update, :destroy ]

def edit
end

def update
if @user.update( user_params )
redirect_to root_path
else
render :edit
end
end

def destroy
if @user.destroy
redirect_to root_path
else
render :edit
end
end

private
def set_user
@user = User.find( params[:id] )
end

def user_params
params.require(:user).permit( :name )
end
end


ルーティングは:edit, :update, :destroyのみ。


routes.rb

  resources :users, only: [ :edit, :update, :destroy ]



Devise/registerableを使う場合

※Facebookによる登録とメールアドレスによる登録を併用する場合。

nameカラムを通すためにApplicationControllerにStrongParametersを追加しましょう。

(以下のコードはユーザー登録時とユーザー情報更新時に使う場合)


app/controllers/application_controller.rb

  before_action :configure_permitted_parameters, if: :devise_controller?

protected
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) << :name
devise_parameter_sanitizer.for(:account_update) << :name
end


ルーティングも追加


config/routes.rb

  devise_for :users, controllers: { omniauth_callbacks: 'users/omniauth_callbacks', registrations: 'registrations' }


パスワードカラムを持たないとDevise経由でのユーザーは更新できないため、以下のメソッドを用意して(上書きして)おきましょう。


app/controllers/regitrations_controller.rb

class RegistrationsController < Devise::RegistrationsController

protected
def update_resource(resource, params)
resource.update_without_current_password(params)
end
end

自前のviews/users/...html.erbは使わないので削除しておきましょう。


リンク

EasyRamble: Railsの認証プラグインDeviseでのStrongParametersについて

iii ThreeTreesLight: [rails][devise] devise userの更新対象追加 + current_password無し更新

Web屋さんのアレ: 【Facebook】Facebookアプリでlocalhostを指定する為のアレ【アプリ】

Simplie Post: RailsでDevise+OmniAuthによりメールアドレスを使わない認証を実装する方法