[Rails] Facebook/Twitter/Googleでのユーザー登録をDevise & Omniauthを使って爆速で実装する

# はじめに
Webアプリの作成で避けては通れないのがユーザー登録です。
更にそんなときについでにやりたくなるのがFacebookなどSNSを使ったユーザー登録ですよね。
何回もやっていて毎度調べるのが面倒だ～と思ったので、同じような境遇の方のための爆速で実装出来るまとめ記事です。

# この記事で出来ること
* Email/Passwordでのユーザー登録
* Facebookでのユーザー登録
* Twitterでのユーザー登録
* Googleアカウントでのユーザー登録

# 対象読者
* Railsにある程度慣れていて爆速でユーザー登録を実装したい方。

# 下準備
先に一通り必要なgemをインストールしておきます。Omniauthで必要が無いものがある場合は適宜省いてください。

```rb:Gemfile
gem 'devise'
gem 'omniauth-facebook'
gem 'omniauth-twitter'
gem 'omniauth-google-oauth2'
```

```sh
bundle install --path vendor/bundle
```

# Deviseでユーザー登録
a. Gemfileに[devise](https://github.com/plataformatec/devise)を追加します。

```rb
gem 'devise'
```

b. 以下のコマンドを実行します。

```sh
bundle install --path vendor/bundle
bundle exec rails g devise:install # generator
bundle exec rails g devise:views # deviseのviewをコピー
bundle exec rails g devise User # モデルを作成
bundle exec rails db:migrate
```

c. ApplicationController.rbにauthenticate_user!を追記します。

```rb:ApplicationController.rb
class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception
  before_action :authenticate_user! # 追記 (userの部分はmodel名)
end
```

d. serverを起動し、/users/sign_upにアクセスして動作を確認します。
![競馬AIたろう〜人工知能による競馬予想〜___競馬AIうまたろう〜人工知能による競馬予想〜_と_競馬AIたろう〜人工知能による競馬予想〜___競馬AIうまたろう〜人工知能による競馬予想〜.png](https://qiita-image-store.s3.amazonaws.com/0/71154/783823c8-2589-5176-dea8-4c58eeedd252.png)

登録するとユーザーが作られていことが確認できます。

```irb
pry> User.first
  User Load (0.4ms)  SELECT  `users`.* FROM `users` ORDER BY `users`.`id` ASC LIMIT 1
#<User:0x007feed8b78ee0> {
                        :id => 1,
                     :email => "xxxxx@gmail.com",
...
```
これだけでユーザー登録の機能は完了です！

# OmniAuth
続いてOmniAuthを実装していきます。

## Railsの設定
Rails側の必要な変更をまとめて行います。

a. oauthに必要なカラムをuserテーブルに追加します。

```sh
bundle exec rails g migration AddOmniauthToUsers provider:string uid:string
bundle exec rake db:migrate
```

b. initializerにomniauth用の設定を追記します。scopeなど必要に応じて変更してください。

```rb:initializers/devise.rb
  config.omniauth :facebook, ENV['FACEBOOK_KEY'], ENV['FACEBOOK_SECRET'], scope: 'email', info_fields: 'email', callback_url: "#{ENV['HOST']}/users/auth/facebook/callback"
  config.omniauth :twitter, ENV['TWITTER_API_KEY'], ENV['TWITTER_API_SECRET'], scope: 'email', oauth_callback: "#{ENV['HOST']}/users/auth/twitter/callback"
  config.omniauth :google_oauth2, ENV['GOOGLE_CLIENT_ID'], ENV['GOOGLE_CLIENT_SECRET'], scope: 'email', redirect_uri: "#{ENV['HOST']}/users/auth/google_oauth2/callback"
  OmniAuth.config.logger = Rails.logger if Rails.env.development? # debug用
```

c. userモデルにomniauthableを追記。コールバックメソッドのfrom_omniauthを追加します。

```user.rb
class User < ApplicationRecord
  devise :omniauthable, omniauth_providers: %i[facebook twitter google_oauth2]
  # omniauthのコールバック時に呼ばれるメソッド
  def self.from_omniauth(auth)
    where(provider: auth.provider, uid: auth.uid).first_or_create do |user|
      user.email = auth.info.email
      user.password = Devise.friendly_token[0,20]
    end
  end
end
```

d. config/routes.rbにrouteを追加します。

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

e. Users::OmniauthCallbacksControllerを作成します。

```controllers/users/omniauth_callbacks_controller.rb
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController

  # callback for facebook
  def facebook
    callback_for(:facebook)
  end

  # callback for twitter
  def twitter
    callback_for(:twitter)
  end

  # callback for google
  def google_oauth2
    callback_for(:google)
  end

  # common callback method
  def callback_for(provider)
    @user = User.from_omniauth(request.env["omniauth.auth"])
    if @user.persisted?
      sign_in_and_redirect @user, event: :authentication #this will throw if @user is not activated
      set_flash_message(:notice, :success, kind: "#{provider}".capitalize) if is_navigational_format?
    else
      session["devise.#{provider}_data"] = request.env["omniauth.auth"].except("extra")
      redirect_to new_user_registration_url
    end
  end

  def failure
    redirect_to root_path
  end

end
```

f. /app/views/devise/registrations/new.html.erbに導線を追加します。以下のように書くことでdevise.rbで追加したoauthのリンクを表示してくれます。

```erb:app/views/devise/registrations/new.html.erb
<%= render "devise/shared/links" %>
```

![競馬AIたろう〜人工知能による競馬予想〜___競馬AIうまたろう〜人工知能による競馬予想〜.png](https://qiita-image-store.s3.amazonaws.com/0/71154/ef6d6b0b-7bb1-a1d8-c5d1-8d0a0b33285e.png)


## 各プラットフォーム毎の設定
続いて各プラットフォームごとの設定を行います。

### [Facebook](https://github.com/mkdynamic/omniauth-facebook)

APPIDとSECRETは[アプリ一覧ページ](https://developers.facebook.com/apps)から対象のアプリを選択し、Settings⇨Basicsから確認できます

![keiba-ai-umataro_-_Settings_-_Facebook_for_Developers.png](https://qiita-image-store.s3.amazonaws.com/0/71154/59f0a96f-f488-e2d7-dc47-22d8cf1d3d44.png)

また、FacebookLogin ⇨ Settings ⇨ Valid Oauth Redirect URLsにテスト用にコールバックさせるurlを追記しておきます。
ドメインは何か適当なものを使い(自分は簡単にローカルのサーバ−を公開できる[ngrok](https://ngrok.com/)を使っています) 


![keiba-ai-umataro_-_Facebook_Login_-_Facebook_for_Developers.png](https://qiita-image-store.s3.amazonaws.com/0/71154/5139c356-33b1-6555-48d9-163efe102607.png)
Valid OAuth Redirect URIsを

```
https://yourdomain/users/auth/facebook/callback
```

としておきます。

※ 本番で利用する際はプライバシーポリシーページを追加して & StatusをONにする必要があります。
![keiba-ai-umataro.png](https://qiita-image-store.s3.amazonaws.com/0/71154/3e626f20-dd2e-6be4-1ef8-e45e6e4cb381.png)


### [Twitter認証](https://github.com/arunagw/omniauth-twitter)

a. [twitter app](https://apps.twitter.com/)にアクセスし、APIKEYとSECRETをコピー
![umataro-bot___Twitter_Application_Management.png](https://qiita-image-store.s3.amazonaws.com/0/71154/69169e78-1406-3aa8-5831-ee8fa4474242.png)

b. callback urlを設定。privacy policy, terms of serviceも何か埋めておく
![umataro-bot___Twitter_Application_Management.png](https://qiita-image-store.s3.amazonaws.com/0/71154/d59130dc-6cfb-6860-dedc-3bd4e67a6492.png)

c. permissionはread only、ユーザー登録にメルアドが必要なので「Request email addresses from users」チェック

![umataro-bot___Twitter_Application_Management.png](https://qiita-image-store.s3.amazonaws.com/0/71154/35d2e60b-973f-b9a1-0cd7-2b9c3cc6a82d.png)

### google
a. GCPのプロジェクトを作成し[API認証の設定](https://console.cloud.google.com/apis/credentials)からOauthクライアントIDを作成します。
![認証情報_-_mamorio.png](https://qiita-image-store.s3.amazonaws.com/0/71154/8c97e699-a79b-395f-8070-ce68424043b0.png)

b. 作成出来たらクライアントIDとクライアントシークレットをメモしておき、リダイレクトURIを設定します

![OAuth_クライアント_-_umataro.png](https://qiita-image-store.s3.amazonaws.com/0/71154/3de1c370-0434-31e5-e734-01b457d15dc2.png)

c. Google+のAPIを有効にしていないとエラーになるのでしていない場合は有効にしておきます。
![API_とサービス_-_Umataro.png](https://qiita-image-store.s3.amazonaws.com/0/71154/39b7402c-e397-385b-6402-8cb93a666035.png)

追記: これと直接関係しませんが例えば、calendarのapiも合わせて認証したい場合に
scopeにcalendarのみを書くとエラーになります。
必ずemailをscopeに含める必要があるようです([参考](https://github.com/zquestz/omniauth-google-oauth2/issues/118))

```rb
  config.omniauth :google_oauth2, ENV['GOOGLE_CLIENT_ID'], ENV['GOOGLE_CLIENT_SECRET'], scope: 'email,calendar', redirect_uri: "#{ENV['HOST_DOMAIN']}/users/auth/google_oauth2/callback"
```


# 確認
さてこれで全て完了です。
/users/sign_upにアクセスして
最後に動作の確認をして各oauthのリンクをクリックしてユーザー登録出来るか確認してみてください。
ユーザーが作成されてproviderとuidに値が入っていれば成功です。
(うまくいかない場合はrailsのlogを見てみると何がおかしいのかわかります)

# 最後に
いかがでしたでしょうか？
良くも悪くもRailsはなんでも簡単に出来てしまいますね。。。。
同じようにユーザー登録を実装したい方の役に立てれば幸いです。