【rails】OAuthを用いて、GitHub認証ログイン機能を実装する

# やりたいこと

- deviseのomniauthableの機能でgithubログインを実装する
- [ログイン](http://localhost:3000/users/sign_in),[サインアップ](http://localhost:3000/users/sign_up)からgithubログインができるようにする。

- 挙動
    - サインアップ、ログイン画面より「GitHub認証」のボタンを押下
    - GitHubのログイン画面へ遷移しid,パスを入力しログインボタンを押下
    - github上で認証するためのトークンを発行し、それをWebサイト側で検証
    - 上記が認証されるとトップ画面へ遷移する。
    
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2265379/515b9f63-e3b8-c9e4-0d21-6a0f981afc17.png)

    

# 調査

- 必要なこと
    - omniauthableの実装に必要なgemをインストールする。
    - GitHubにてOAuthアプリを作成する。
    - 上記で生成されたCLIENT_IDとSECRET_PASSをenvファイルへ記述
    - controllerにて上記認証情報の処理を記述(むずい)
    - モデルのメソッドにて上記paramsをuserテーブルへの保存するためのメソッドを記述
    - User modelに`:omniauthable` を追加
    - viewの実装を行う

# 実際にやったこと

## ライブラリ関連

- Gemfileに以下を記述→bundle install

```ruby
gem 'omniauth'
gem 'omniauth-github'
gem 'omniauth-rails_csrf_protection'
group :development, :test do
  gem 'dotenv-rails'
end
```

[[Rails]deviseとomniauthによるgithubログイン](https://zenn.dev/redheadchloe/articles/879f143ee54602#gemをインストールする)

## OAuthのID,パス生成

- https://github.com/settings/developers よりOAuthアプリを作成(Homepage URL:http://localhost:3000/, URL:http://localhost:3000/auth/github/callback)
    
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2265379/0f4e124f-a6af-9b10-fabc-c94cd6feb7e2.png)


[【2021年4月現在】GitHubのClient IDとClient secretsを取得する手順 - No day younger than today](https://fuga-ch85.hatenablog.com/entry/2021/04/04/164302)

- [OAuthのアプリケーション設定](https://github.com/settings/applications/2487165)より「Client ID」と「Client secret」のidをそれぞれ取得し、環境変数として.envへ記述。

```jsx
#上記にて自分で設定したkeyを設定
GITHUB_ID = '*********'
GITHUB_SECRET = '********'
```

- 記述した.envファイルは外部に漏れないように  `gitignore` ファイルに記述

```ruby
# Ignore env File
/.env
```

- `/initializers` に上記ENVファイルにて設定した環境変数を記述し読み込めるようにする

```ruby
  # ==> OmniAuth
  # Add a new OmniAuth provider. Check the wiki for more information on setting
  # up on your models and hooks.
  config.omniauth :github, ENV['GITHUB_ID'], ENV['GITHUB_SECRET'], scope: 'user,public_repo'

```

## DB関連

- `rails g migration AddOmniauthToUsers` にてmigrationファイルを生成
- →以下記述し、 `rails db:migrate`

```ruby
class AddOmniauthToUsers < ActiveRecord::Migration[7.0]
  def change
    add_column :users, :uid, :string
    add_column :users, :provider, :string, null: false, default: ""

    add_index :users, %i[uid provider], unique: true
  end
end
```

- `app/models/user.rb` に,  `omniauthable` モジュールと、validationを記述

```ruby
# frozen_string_literal: true

class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  validates :name, presence: { message: "can't blank" }
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable,
         :confirmable, :lockable, :timeoutable, :trackable,:omniauthable, omniauth_providers: %i[github]
                                                            #追加

  validates :birth_day, presence: { message: "can't blank" }
  validates :tel_number, presence: { message: "can't blank" }
　　
  #追加
  validates :uid, uniqueness: { scope: :provider }, if: -> { uid.present? }
end
```

## routeの設定

```ruby
Rails.application.routes.draw do
  〜略
  devise_for :users, controllers: { omniauth_callbacks: 'users/omniauth_callbacks'}
  
end
```

## controllerの設定

```ruby
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
  skip_before_action :verify_authenticity_token, only: :github
  
  def github
    @user = User.from_omniauth(request.env['omniauth.auth'])

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

  def failure
    redirect_to root_path
  end
end
```

## user.rbモデルにメソッド設定

```ruby
# frozen_string_literal: true

class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  validates :name, presence: { message: "can't blank" }
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable,
         :confirmable, :lockable, :timeoutable, :trackable,:omniauthable, omniauth_providers: %i[github]
  validates :birth_day, presence: { message: "can't blank" }
  validates :tel_number, presence: { message: "can't blank" }

  validates :uid, uniqueness: { scope: :provider }, if: -> { uid.present? }

  #追記
  def self.form_omniauth(auth)
    where(uid: auth.uid, provider: auth.provider).first_or_create! do |user|
      user.email = auth.info.email
      user.password = Devise.friendly_token[0,20]
      user.name = auth.info.name
      user.birth_day = Date.parse('1993-04-21')
      user.tel_number = '12345678'.to_i
    end
  end
end

```

- first_or_createについて

[覚えておくと幸せになれるActiveRecord::Relationメソッド6選 - Qiita](https://qiita.com/Yama-to/items/4696e9d43ebec6012129#1-first_or_createブロック付き)

## viewの設定

- `app/views/shared/_link.html.slim` のomniauthボタンのroutingの変更

```ruby
　#〜省略

- if devise_mapping.omniauthable?
  - resource_class.omniauth_providers.each do |provider|　　　　　#変更
    = button_to " #{OmniAuth::Utils.camelize(provider)}で続ける", user_github_omniauth_authorize_path(resource_name, provider), data: { turbo: false } ,class: "btn btn-primary"
  br
```

- Githubログイン用のボタンがサインアップ画面とログイン画面に表示される

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2265379/c9ff9743-b9b5-b4ca-c9a0-7aa4d6a8cabf.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2265379/1f380901-2c5d-04a8-f94c-d64b763b0b8d.png)

# 結果

- 「Githubで続ける」を押下してログインしようと試みると`Not found. Authentication passthru.` とエラーが表示されて認証が通らなかった。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2265379/5a2dc159-db31-999f-34c4-2c9b810f03d1.png)


# 原因究明と解決

- `passthru`は聞きなれない単語だが `passthrough`(=通過する、素通りする)と同じ意味

[passthru - Weblio 英和・和英辞典](https://ejje.weblio.jp/content/passthru)

- ログを確認すると認証用パラメータは生成されているように見える。
    
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2265379/b91b9e0e-d1e3-b3d5-ef0d-02caa9b5b1fa.png)
  

- つまり生成された認証paramasが入力→出力へ「内部で何もせず素通り(pass through)」しているということ?　正しくparamsが送られてない？

[パススルーとは - IT用語辞典](https://e-words.jp/w/パススルー.html)

- deviseの公式ドキュメントを確認すると、ボタンのmethodにpostを指定するように記述されていた。ただ、button_to場合は必要なく、link_toの場合は  `method: :post` の追記が必要
- `button_to` を用いて下記設定したが解消されず。

```ruby
- if devise_mapping.omniauthable?
  - resource_class.omniauth_providers.each do |provider|
    = button_to " #{OmniAuth::Utils.camelize(provider)}で続ける", user_github_omniauth_authorize_path ,class: "btn btn-primary"
  br

```

https://github.com/heartcombo/devise/wiki/OmniAuth:-Overview#facebook-example

- `config/initializers` 直下に `omniauth.rb` を作成し、以下記述

```ruby
Rails.application.config.middleware.use OmniAuth::Builder do
  OmniAuth.config.allowed_request_methods = [:post, :get]
end
```

→これによりpostとgetメソッドが許可されるので、送られてきた認証paramasが素通りせず、受け取られるはず

- サインアップ画面が表示されたのでid,passを入力すると、、、

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2265379/9f617233-e60c-a874-1619-5e2f41e5944f.png)


- 今度はrouting errorが表示された。エラー画面では `/auth/github/callback` のrouteがないと怒られている。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2265379/06393ae3-00cd-3c7e-2e6b-22988d62ba19.png)

- 改めて `config/route` を確認すると, `omniauth_callbacks: 'users/omniauth_callbacks'` と設定しているので、callbackの接続するパスは `users/auth/github/callback` となるはず。

```ruby
Rails.application.routes.draw do

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

- https://github.com/settings/developers のAuthorizeのcallback URLを見るとusersがないので改めて http://localhost:3000/users/auth/github/callback と設定
    
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2265379/b15fee34-c289-dcce-e689-fc74dee13c04.png)

    

- 今度は認証画面に遷移することができた。Authorizeをクリックすると無事にログインすることができた！！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2265379/ffe61cc4-c26a-bfc8-0c98-15a62f043ec6.png)






# 参考

https://github.com/omniauth/omniauth

https://github.com/omniauth/omniauth-github

https://docs.github.com/ja/apps/oauth-apps/building-oauth-apps/authorizing-oauth-apps

https://zenn.dev/fujishiro/scraps/e6bde6deec5c57

https://maku.blog/p/ubkt3ai/

https://www.youtube.com/watch?v=w4gUaBMDm7A&ab_channel=だれでもエンジニア/山浦清透