Rails5.1でGithubログインを実装する

# 0. はじめに

お初にお目にかかります!!
toramaruJPです(｀・ω・´)

昨日オリジナルアプリを作成しました！！
日頃からQiitaさんにはお世話になっているので、少しでも貢献できたらと思い、オリジナルアプリに導入した**Githubログイン実装方法**を記載したいと思います。
改善点等あった際には、ご指摘して下さると嬉しいです(*´꒳`*)

# 1. 実装概要
（１）Github連携の準備をしよう！(Github上)
（２）Github連携の準備をしよう！(実装上)
（３）Deviseを用いて、Githubログインを実装しよう!

# 2. 実装詳細
### （１）Github連携の準備をしよう！(Github上)
<img width="1010" alt="スクリーンショット 2018-10-01 18.14.28.png" src="https://qiita-image-store.s3.amazonaws.com/0/225769/a72b5324-e50e-df0c-5819-357edfe29674.png">
まず、上記画像の右上の"Settings"タブを押します。
<img width="1024" alt="スクリーンショット 2018-10-01 18.14.55.png" src="https://qiita-image-store.s3.amazonaws.com/0/225769/4f160b1e-c436-32ff-3bcf-0981447907f2.png">
次に、左下の"Developer settings"タブを押します。
<img width="1037" alt="スクリーンショット 2018-10-01 18.09.32.png" src="https://qiita-image-store.s3.amazonaws.com/0/225769/e5f69410-0948-7064-6c83-0e3018d98d61.png">
そうすると、上記画像のような画面になるので、右上の"New OAuth APP"を押します。
<img width="1028" alt="スクリーンショット 2018-10-01 18.22.13.png" src="https://qiita-image-store.s3.amazonaws.com/0/225769/6ff15524-266d-41e8-4f1e-ca4bd8f8b814.png">
Localで動かす場合には、画像のように
・"Application name"には"アプリケーション名-development"
・"Homepage URL"には"http://localhost:3000/"
・"Authorization callback URL"には"http://localhost:3000/users/auth/github/callback"
と記載します。

最後に、"Register application"ボタンを押すと、完了です。
そうすると、"Client ID"と"Client Secret"が作成されます。
後ほど使うので、メモしておきましょう。
※　"Client ID"と"Client Secret"は、作成されたアプリケーション名をクリックした先の詳細に記載されてあるので、そこで確認できます。

### （２）Github連携の準備をしよう！(実装上)
#### (a) アプリを立ち上げよう！
開発で使用するバージョンは以下の通りです。

```
Rails v5.1.6
ruby v2.3.1
```

下記コマンドで現在のバージョンを確認できます。

```
$ ruby -v
$ rails -v
```

rails newコマンドを使用してアプリの立ち上げを行います。

```
$ rails _5.1.6_ new oauth -d postgresql
```

下記のコマンド実行後、"http://localhost:3000/"
にアクセスをして、サーバーが立ち上がるか確認しましょう。

```
$ rails s
```

#### (b) OAuth認証に必要なGemをインストールしよう！
下記をGemfileに追記し、"bundle install"を行なってください。

```:Gemfile
省略

gem 'devise'
gem 'omniauth'
gem 'omniauth-github'

省略
```

```
$ bundle install
```

各ReadMe
・devise - https://github.com/plataformatec/devise
・omniauth - https://github.com/omniauth/omniauth
・omniauth-github - https://github.com/omniauth/omniauth-github

#### (c) Deviseの設定をしよう！
以下のコマンドを打ち、deviseに必要な設定ファイルを作成します。

```
$ rails g devise:install
```

これで、準備が整いました。

### （３）Deviseを用いて、Githubログインを実装しよう!
#### (a) ユーザーテーブルを作成しよう！

```
$ rails g devise user
```

作成されたマイグレーションファイルを下記のように編集しましょう。

```
class DeviseCreateUsers < ActiveRecord::Migration[5.1]
  def change
    create_table :users do |t|
      ## Database authenticatable
      t.string :name,               null: false, default: ""
      t.string :email,              null: false, default: ""
      t.string :encrypted_password, null: false, default: ""
      t.string :provider, null: false, default: ""
      t.string :uid, null: false, default: ""

      ## Recoverable
      t.string   :reset_password_token
      t.datetime :reset_password_sent_at

      ## Rememberable
      t.datetime :remember_created_at

      ## Trackable
      # t.integer  :sign_in_count, default: 0, null: false
      # t.datetime :current_sign_in_at
      # t.datetime :last_sign_in_at
      # t.inet     :current_sign_in_ip
      # t.inet     :last_sign_in_ip

      ## Confirmable
      # t.string   :confirmation_token
      # t.datetime :confirmed_at
      # t.datetime :confirmation_sent_at
      # t.string   :unconfirmed_email # Only if using reconfirmable

      ## Lockable
      # t.integer  :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts
      # t.string   :unlock_token # Only if unlock strategy is :email or :both
      # t.datetime :locked_at


      t.timestamps null: false
    end

    add_index :users, :name,                 unique: true
    add_index :users, :email,                unique: true
    add_index :users, :reset_password_token, unique: true
    add_index :users, [:uid, :provider], unique: true
    # add_index :users, :confirmation_token,   unique: true
    # add_index :users, :unlock_token,         unique: true
  end
end
```

マイグレーションファイルを編集できたら
下記コマンドを実行してテーブルの作成を行います。

```
$ rails db:migrate
```

#### (b) users/registration_controllerを作成して、deviseのコントローラーを継承しよう！

下記のコマンドを実行します。

```
$ rails g controller users::registrations
```

作成されたファイルを、以下のように編集してください。

```:app/controllers/users/registrations_controller
class Users::RegistrationsController < Devise::RegistrationsController
  def build_resource(hash={})
    hash[:uid] = User.create_unique_string
    super
  end
end
```

※　上記"build_resource(hash={})"メソッドは、継承元の同メソッドをオーバーライドしています。具体的には、uidにランダムな値を代入しています。
下記において、継承元の同メソッドが確認できます。
https://github.com/plataformatec/devise/blob/bcdd54cc5ebd3413eeb3d17e3b634e8ace0d09f3/app/controllers/devise/registrations_controller.rb

#### (c) create_unique_stringメソッドを作成しよう！。

下記ファイルに、下記メソッドを追記してください。

```:app/models/user.rb
  def self.create_unique_string
    SecureRandom.uuid
  end
```

#### (d) ルーティングを追加しよう！

```:config/routes.rb
  devise_for :users, controllers: {
    registrations: "users/registrations"
  }
```

#### (e) modelを編集しよう！

下記ファイルを、下記のように編集してください。

```:app/models/user.rb
  devise :database_authenticatable, :registerable,
       :recoverable, :rememberable, :validatable,
       :omniauthable, omniauth_providers: %i(github)
```

下記ファイルに、下記メソッドを追記してください。

```:app/models/user.rb
  def self.find_for_github_oauth(auth, signed_in_resource=nil)
    user = User.find_by(provider: auth.provider, uid: auth.uid)

    unless user
      user = User.new(provider: auth.provider,
                      uid:      auth.uid,
                      name:     auth.info.name,
                      email:    User.dummy_email(auth),
                      password: Devise.friendly_token[0, 20]
      )
    end
    user.save
    user
  end
```

```:app/models/user.rb
  def self.dummy_email(auth)
    "#{auth.uid}-#{auth.provider}@example.com"
  end
```

現時点における該当modelは下記のようになります。

```:app/models/user.rb
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable,
         :omniauthable, :confirmable, omniauth_providers: %i(google facebook github)

  def self.create_unique_string
    SecureRandom.uuid
  end

  def self.find_for_github_oauth(auth, signed_in_resource=nil)
    user = User.find_by(provider: auth.provider, uid: auth.uid)

    unless user
      user = User.new(provider: auth.provider,
                      uid:      auth.uid,
                      name:     auth.info.name,
                      email:    User.dummy_email(auth),
                      password: Devise.friendly_token[0, 20]
      )
    end
    user.save
    user
  end

  def self.dummy_email(auth)
    "#{auth.uid}-#{auth.provider}@example.com"
  end
```

#### (f) githubで取得したOAuth認証に必要な値（"Client ID"と"Client Secret"）を読み込む設定をしよう！

下記ファイルに、下記記述を追記してください。

```:config/initializers/devise.rb
Devise.setup do |config|

　　省略

　　config.omniauth :github, ENV["GITHUB_ID"], ENV["GITHUB_SECRET"], scope: 'user,public_repo'

　　省略

end
```

#### (g) .envファイルを作成しよう！

アプリケーションディレクトリ直下に下記ファイルを作成し、下記記述を追加してください。

```:.env
　　GITHUB_ID=メモした"Client ID"
　　GITHUB_SECRET=メモした"Client Secret"
```

セキュリティ上、githubにプッシュさせない為、下記ファイルに下記記述を追記してください。

```:.gitignore
　　.env
```

.envに記載した環境変数を認識させる為に、"dotenv"というgemをインストールしてください。

```:Gemfile
　　gem 'dotenv-rails'
```

```
 $ bundle install
```

#### (h) ログインボタンのリンクを設置するViewを作成しよう！

下記コマンドをターミナルで実行する。

```
 $ rails g controller oauth_test index
```

作成されたViewファイルにログインボタンのリンクを設置します。

```:app/views/oauth_test/index.html.erb
 <p><%= notice %><%= alert %></p>
 <% if user_signed_in? %>
   <%= link_to "ログアウト", destroy_user_session_path, method: :delete %>
 <% else %>
   <%= link_to 'githubでサインアップしてね', user_github_omniauth_authorize_path %>
 <% end %>
```

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

```:config/routes.rb
  devise_for :users, controllers: {
    registrations: "users/registrations",
    omniauth_callbacks: "users/omniauth_callbacks"
  }
```

#### (i) コールバック処理のコントローラーを作成しよう！

下記ディレクトリに下記ファイルを作成し、下記を記述してください。

```:app/controllers/users/omniauth_callbacks_controller.rb
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
  def github
    @user = User.find_for_github_oauth(request.env["omniauth.auth"], current_user)

    if @user.persisted?
      sign_in_and_redirect @user, :event => :authentication
      set_flash_message(:notice, :success, :kind => "Github") if is_navigational_format?
    else
      session["devise.github_data"] = request.env["omniauth.auth"]
      redirect_to new_user_registration_url
    end
  end
end
```

#### (j) コールバック先のルーティングを設定しよう！

下記ファイルに下記記述を追記します。

```:config/routes.rb
 root :to => 'oauth_test#index'
```

#### (k) フラッシュメッセージを表示させよう！

下記ファイルに下記記述を追記します。

```:app/views/layouts/application.html.erb

 省略

 <body>
   <% if notice %>
     <p class="alert alert-notice"><%= notice %></p>
   <% end %>
   <% if alert %>
     <p class="alert alert-error"><%= alert %></p>
   <% end %>

   <%= yield %>

省略
```

以上になります！！！！！！
これで、"githubでサインアップしてね"リンクをクリックすれば、githubログイン認証してくれるはずです！！！！！

できなかったら(　ﾟдﾟ)(　ﾟдﾟ)
問題点を教えて頂けると嬉しいです(｀・ω・´)

# ３. 最後に
初めて書きましたが、大変ですねこれ(　ﾟдﾟ)
書くのとまとめるのが大変(　ﾟдﾟ)
いっぱい書いてる人凄いな(　ﾟдﾟ)

ただ、自分の為にもなるのでこれからも書き続けます！！
最後まで見て頂いてありがとうございます！！