初心者でも簡単！SNS認証を実装（いざ実装編）

#　はじめに

では、いよいよ実装していきます！今回は、今チームで作っているフリマアプリに実装していきます。


#　Gemを導入


OmniAuthの脆弱性に対応するためのGemを導入します。

```ruby:Gemfile
gem 'omniauth-facebook'
gem 'omniauth-google-oauth2'
gem "omniauth-rails_csrf_protection"
```

```ruby:ターミナル
bundle install
```

FacebookとGoogleで使用するAPIキーを環境変数に設定していきます。
前回取得した、API側の設定でメモしたAPP_IDとAPP_SECRETを準備し、$ vim ~/.bash_profileを実行し、以下の内容を追記します。（まだ、APIキーを取得していない場合は、SNS認証を実装（Google編）かSNS認証を実装（（Faceboook編）を見てください！）

```ruby:~/.bash_profile
export FACEBOOK_CLIENT_ID='メモしたアプリID'
export FACEBOOK_CLIENT_SECRET='メモしたapp secret'
export GOOGLE_CLIENT_ID='メモしたクライアントID'
export GOOGLE_CLIENT_SECRET='メモしたクライアントシークレット'
```
変更後は$ source ~/.bash_profileを実行します。
環境変数が設定できたら、アプリ側にそれを読み込む記述を加えます。

```ruby:config/initializers/devise.rb
config.omniauth :facebook,ENV['FACEBOOK_CLIENT_ID'],ENV['FACEBOOK_CLIENT_SECRET']
config.omniauth :google_oauth2,ENV['GOOGLE_CLIENT_ID'],ENV['GOOGLE_CLIENT_SECRET']
```

#SNS認証に必要なモデル、コントローラの準備

次に、uidやproviderをはじめとする、SNS認証時の情報を保存するSnsCredentialモデルを作成します。この時に、Userモデルとのアソシエーションのために、外部キーとしてuser_idを持たせます。

```ruby:ターミナル
$ rails g model sns_credential provider:string uid:string user:references
$ rails db:migrate
```

モデルにomniauthを使うためのオプションとアソシエーションを追記します。
以下の（）の部分を追記しました。


```ruby:models/user.rb
class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, （:validatable, :omniauthable, omniauth_providers: [:facebook, :google_oauth2]）

  has_one :parsonal_user, dependent: :delete
  （has_many :sns_credentials）
```

```ruby:models/sns_credential.rb
class SnsCredential < ApplicationRecord
  # associations
  belongs_to :user, optional: true
end
```
続いてdeviseのコントローラをカスタマイズするためのコントローラを作成します。

```ruby:ターミナル
$ rails g devise:controllers users
```
以下のようにdeviseのルーティングを変更します
変更した箇所は（）の中です

```ruby:ターミナル
 devise_for :users, （controllers: {
    omniauth_callbacks: 'users/omniauth_callbacks',
    registrations: 'users/registrations',）
    sessions: 'users/sessions'
    }
```

#SNS認証を実装するためメソッドの実装

まずは、基本の動きをコントローラに記述していきます。

```ruby:controllers/users/omniauth_callbacks_controller.rb
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
  def facebook
    authorization
  end

  def google_oauth2
    authorization
  end

  def failure
    redirect_to root_path
  end

  private
  def authorization
    sns_info = User.from_omniauth(request.env['omniauth.auth'])
    @user = sns_info[:user]

    if @user.persisted?
      sign_in_and_redirect @user, event: :authentication # this will throw if @user is not activated
    else
      @sns_id = sns_info[:sns].id
      render 'devise/registrations/new'
    end
  end
```
次に、User.from_omniauthメソッドの中身を作ります。

```ruby:user.rb
  def self.from_omniauth(auth)
    sns = SnsCredential.where(provider: auth.provider, uid: auth.uid).first_or_create
    user = sns.user || User.where(email: auth.info.email,).first_or_initialize(
      nick_name: auth.info.name,
        email: auth.info.email
    )
    if user.persisted?
      sns.user = user
      sns.save
    end
    { user: user, sns: sns }
  end
end
```

最後にhaml側に以下の記述を追記します。
追記した箇所は（）の中です。

```ruby:views/devise/sessions/new.html.haml
.p-login-main
    .p-login-user
      .p-login-user__account
        %p アカウントをお持ちでない方はこちら
        = link_to signup_registration_path, class: "p-login-user__account--link" do
          新規会員登録
      .p-login-user__inner
        .p-login-user__inner--btn
          %button.facebook-login
            = fa_icon 'facebook-square'
            (= link_to 'Facebookでログイン', user_facebook_omniauth_authorize_path, method: :post)
          %button.google-login
            = fa_icon 'google'
            (= link_to 'Googleでログイン', user_google_oauth2_omniauth_authorize_path, method: :post)
<img width="1257" alt="スクリーンショット 2020-03-29 15.36.31.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/591360/4bc12249-3308-0bb8-d1d3-33227bd345e4.png">
<img width="1264" alt="スクリーンショット 2020-03-29 15.41.54.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/591360/c15faa04-b595-9df9-a64e-ccc87b0a1cd8.png">

```
```ruby:views/users/new.html.haml
ruby:views/users/new.html.haml

         .p-reg-btn__mail
            = fa_icon 'envelope'
            = link_to 'メールアドレスで登録する', signup_registration_path, class: 'p-reg-btn__link'
          .p-reg-btn__facebook
            = fa_icon 'facebook-square'
            = link_to 'Facebookで登録する', user_facebook_omniauth_authorize_path, class: 'p-reg-btn__link', method: :post
          .p-reg-btn__google
            = fa_icon 'google'
            = link_to 'Googleで登録する', user_google_oauth2_omniauth_authorize_path, class: 'p-reg-btn__link--color', method: :post

```
ここまでです。
以下のようにfacebookまたはgoogleで登録するを押すと、名前とメールアドレスが自動で入ります。
今回は、プライバシーの関係で〇〇で表現しています。（笑）

<img width="1257" alt="スクリーンショット 2020-03-29 15.36.31.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/591360/2e5a3a18-f6b5-aae6-7f60-7bb961848644.png">

<img width="1264" alt="スクリーンショット 2020-03-29 15.41.54.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/591360/d391c946-f2da-9471-3da8-3980ca1ea6d7.png">


次回はここで少し工夫したことをやっていきます。