[Rails]deviseを使ったTwitter認証でログイン

今回は、Twitter　APIを利用して、deviseのログインでTwitterアカウントでログインできるようにしていきます。

APIを使うことで、かんたんにTwitterと連携ができます。

では、さっそくいきましょう！

#完成イメージ

完成イメージはこちらです
TwitterアカウントでログインをクリックするとTwitterアカウントでログインできます。

![スクリーンショット 2021-08-06 11.19.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1554142/34458491-5e23-de98-43a4-a5d8f8a3e88e.png)

動作確認したい方は、ぼくの[ポートフォリオ](https://oogiri-legendroad.com/users/sign_in)に実装してますので、確認してみてください。

#開発環境

> ruby 2.6.3
> Rails 5.2.6

#前提

今回のTwitterでのログインは、deviseを使用しますので、deviseを使ったログイン機能の実装を済ませておいてください。
[deviseのかんたん導入手順](https://qiita.com/nakachan1994/items/302bde68cfa453533be7)
[さくっとdeviseをつかってみる](https://qiita.com/nakachan1994/items/91af6ecaf7066697fb5f)

#TwitterAPI登録

まずは、TwitterAPIに登録することから始めます。
登録にはTwitterアカウントが必要なので、開発用であれば新規にアカウントを作成するのもアリかもです。

少し古い記事ですが、ぼくはこの記事を参考にしました。
画面キャプチャと実際の画面は少し違いましたが、問題なく登録できたので、問題ないと思います。
[Twitter API 登録 (アカウント申請方法) から承認されるまでの手順まとめ](https://qiita.com/kngsym2018/items/2524d21455aac111cdee)

その場ですぐ登録完了ではなく、審査がありますので、先に申請しておくことをおすすめします。
ぼくの場合は申請してから、１日後には承認されました。

####callbackURL

TwitterAPIの画面でcallbackURLを設定できます。

callbackURLに記載のURLのみが、Twitter認証が許可されますので、本番環境URLと開発環境URLを登録しておきましょう。（何個でも登録可能）


・本番環境
`https://[ドメインまたはIPアドレス]/users/auth/twitter/callback`

・開発環境
`https://localhost:3000/users/auth/twitter/callback`

・開発環境がcloud9の場合
cloud9でブラウザを開いたURLが入るので、`https://のあとからamazonaws.comまで`のURLをコピペ
`https://[amazonaws.comまでのURL]/users/auth/twitter/callback`

これで、本番環境、開発環境で登録しておけば、どちらの環境でも動作確認ができます。

####API KeyとAPI Secret Key

取得した`API Key`と`API Secret Key`はあとで、使うのでメモしておいてください。

#Gemのインストール

Gemは２つのGemをインストールしていきます。

`omniauth`というGemは、SNSなどの複数の外部サービスのログインや登録を管理できるGemです。

omniauthは、各SNSごとに別のGemが用意されているので併用して使います。

```Gemfile
gem 'omniauth', '1.9.1'
gem 'omniauth-twitter'
```

ここで注意してほしいのが、`omniauthのバージョン`を指定している点です。

現在の最新のバージョンは、2.いくらなのですが、仕様が変更して、`最新のバージョンだと今回の方法ではうまく動作しません。`

実際このバージョンを指定しなかったため、２日ほど時間を無駄にしました。。。（初心者にバージョン違いのエラーは厳しすぎる...）

```
$ bundle install
```

お忘れなく。

#API KeyとAPI Secret Keyを環境変数に

API KeyとAPI Secret Keyは、だれにも知られたくないので、.envファイルに記述して環境変数にしていきます。

####環境変数わからない方向けに、ざっと説明

環境変数にせずにファイルにそのままAPI KeyやAPI Secret Keyを記述して、GitHubにあげてしまうと、全世界に向けて大事な大事な情報を公開してしまいます。

なので、GitHubに大事な情報はあげないように設定をします。

手順としては、以下

- gem 'dotenv-rails'をインストール
- .envファイルを作成しその中に環境変数を定義
- gitignoreファイルに.envを追加

今回は、環境変数を定義するところから解説していきます。

####.envファイルに記述

環境変数として、
API Keyは、TWITTER_CONSUMER_KEY
API Secret Keyは、TWITTER_CONSUMER_SECRET

DOMAIN_NAMEには、動作確認したい環境のドメインまたはIPアドレスを入れてください。

```.env
TWITTER_CONSUMER_KEY = "メモしたAPI Key"
TWITTER_CONSUMER_SECRET = "メモしたAPI Secret Key"

DOMAIN_NAME = "ドメインまたはIPアドレス"
```

まずは開発環境で動作確認して問題なければ、本番環境のドメインまたはIPアドレスを入れるといいと思います。

EC2サーバーを使う場合は、cloud9では開発環境、ES2サーバー上では本番環境のドメインまたはIPアドレスに変更する方法もあります。(こっちのほうが、デプロイ後も開発環境で動作確認できるのでいいかも)

####gitignoreに追加

せっかく.envファイルに環境変数を定義しても、このままだとGitHubにあがってしまうので、gitignoreファイルに.envを追加してGitHubにあげないように設定します。

```gitignore
:
.env
```

#deviseの設定

deviseで、Twitter認証が行われるように.envファイルに環境変数として定義した値を用いて設定を記述します。

```config/initializers/devise.rb
:
  # Twitter認証
  config.omniauth :twitter, ENV['TWITTER_CONSUMER_KEY'], ENV['TWITTER_CONSUMER_SECRET'], scope: 'email', oauth_callback: "#{ENV['DOMAIN_NAME']}/users/auth/twitter/callback"
  OmniAuth.config.logger = Rails.logger if Rails.env.development? # debug用
end
```

omniauthでtwitterが使えるように設定を追加します。

```models/user.rb
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable,
         # Twitter API認証用に追加
         :omniauthable, omniauth_providers: [:twitter]
:
```

#Twitter認証時にUserモデルでデータを作る

Twitter認証でログインした際に、
はじめてのログインの場合は、Twitterの情報を元に新しくユーザーを登録する。
すでに作成済みの場合は、Userテーブルから同じデータを持ってくる。
という処理を記述していきます。

####Userテーブルにカラムを追加

Twitter認証をしているユーザーかを判断するために、uidカラムとproviderカラムを追加します。

```
$ rails g migration AddColumnsToUsers uid:string provider:string
$ rails db:migrate
```

####userモデルに処理を記述

userモデルに、Twitter認証時のログインに関する処理を記述していきます。

```models/user.rb
  # Twitter認証ログイン用
  # ユーザーの情報があれば探し、無ければ作成する
  def self.find_for_oauth(auth)
    user = User.find_by(uid: auth.uid, provider: auth.provider)

    user ||= User.create!(
      uid: auth.uid,
      provider: auth.provider,
      name: auth[:info][:name],
      email: User.dummy_email(auth),
      password: Devise.friendly_token[0, 20]
    )

    user
  end

  # ダミーのメールアドレスを作成
  def self.dummy_email(auth)
    "#{Time.now.strftime('%Y%m%d%H%M%S').to_i}-#{auth.uid}-#{auth.provider}@example.com"
  end
```

userテーブルにemailを保存する必要がある場合は、ダミーのメールアドレスを作成します。

####omniauthコントローラーの設定

コールバック時の処理を記述していきます。

```controllers/users/omniauth_callbacks_controller.rb
:
  # コメントアウト外して、処理追加(8行目あたり)
  def twitter
    callback_from :twitter
  end
:
  # 最後のendの直前に記載
  private

  # コールバック時に行う処理
  def callback_from(provider)
    provider = provider.to_s

    @user = User.find_for_oauth(request.env['omniauth.auth'])

    # persisted?でDBに保存済みかどうか判断
    if @user.persisted?
      # サインアップ時に行いたい処理があればここに書きます。
      flash[:notice] = I18n.t('devise.omniauth_callbacks.success', kind: provider.capitalize)
      sign_in_and_redirect @user, event: :authentication
    else
      session["devise.#{provider}_data"] = request.env['omniauth.auth']
      redirect_to new_user_registration_url
    end
  end
end
```

# ルーティング

omniauth_callbacksのルーティングを設定します。

devise_forに追加してください。

```config/routes.rb
  devise_for :users, controllers: {
    sessions: 'users/sessions',
    passwords: 'users/passwords',
    registrations: 'users/registrations',
    # Twitter API認証用
    :omniauth_callbacks => 'users/omniauth_callbacks',
  }
```

#ビュー

最後に、ビューにTwitter認証用のリンクを作成していきます。

今回は、deviseの_links.html.erbファイルに記述していきます。

他にもregistrationやsessionの、newファイルに直接記述する方法もあります。
ご自身のお好きなスタイルにあわせて、カスタマイズしてください。


```erb:views/devise/shared/_links.html.erb
:
<%- if devise_mapping.omniauthable? %>
  <%- resource_class.omniauth_providers.each do |provider| %>
    <!--以下１行を編集-->
    <%= link_to "#{OmniAuth::Utils.camelize(provider)}アカウントでログイン", omniauth_authorize_path(resource_name, provider), class:'fab fa-twitter', method: :post %>
  <% end %>
<% end %>
```

これで、この作成したリンクをクリックすることで、Twitter認証画面に飛んで、認証できればログインできるようになります！

#まとめ

はじめてAPIを使ったので、苦労しましたが、いい経験になりましたし、ユーザーも新しいアカウントを作る手間がなくなるので、使い勝手も上がりました！

苦労した点としては、
TwitterAPIとdeviseを組み合わせる方法が、deviseの無しの方法より難しかった。
なにより、omniauthのバージョン指定にはやられましたw

今後はTwitter以外のFacebookやGoogleなんかの認証にもチャレンジしていきたいですね！

同じようなところで詰まってる方の参考になれば幸いです。