1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【Rails】Twitterクローンを作ってみた 〜GitHubログイン(OmniAuth)〜

Last updated at Posted at 2022-11-27

はじめに

【Rails】Twitterクローンを作ってみた 〜画像アップロード(ActiveRecord)〜の続きです。こちらを前提に進めていきますので、まだご覧になっていない方はそちらからご覧ください!※こちらはdockerでの環境構築を省きます。

サービス環境

  • ruby 3.0.0
  • Rails 6.0.4
  • docker
  • mysql 8.0.2
  • Slim, SCSS

OmniAuth

条件

  • Git Hubアカウントでログインできる
    DeviseのモジュールOmniauthableを使用しGit Hubログインをできるようにしてください。

実装流れ(簡略)

  1. https://fuga-ch85.hatenablog.com/entry/2021/04/04/164302
    こちらを参考にGitHubのClient IDとClient secretsを取得してください。
  2. Client IDとClient secretsを.envファイルに保存。.gitignoreに.envファイル追記。
    (.env.exampleファイルを作り、どの環境変数を設定すれば良いかわかるようにすると親切)
  3. Gemファイルにgem 'omniauth-github'
    gem 'omniauth-rails_csrf_protection'を追記し、bundle install
  4. マイグレーションファイル作成
  5. db:migarate→失敗→db:resetdb:migrate
  6. config/initializers/devise.rbでomniauthの設定をする
  7. ルーティング作成
  8. models/user.rbでユーザー登録のメソッド作成
  9. controllers/users/omniauth_callbacks_controller.rbでGitHubログインのメソッド作成
  10. controllers/users/registrations_controller.rbでパスワードなしでのアカウント更新を可能にするメソッド作成
  11. ルーティング作成
  12. サインアップ画面(html, css)の修正

サインアップ画面

スクリーンショット 2022-11-23 20.20.17.png

GitHubサインイン後の画面

スクリーンショット 2022-11-23 20.22.17.png

解説

マイグレーションファイルの作成

class AddOmniauthToUsers < ActiveRecord::Migration[6.0]
  def change
    add_column :users, :uid, :string
    add_column :users, :provider, :string, null: false, default: ""
    
    add_index :users, [:uid, :provider], unique: true
  end
end

uidとproviderカラムを作成します。uid は oAuth を使用してログインしたユーザーの一意の ID で構成されます。uidにnull: falseを追記していない理由は通常ログインの際にuidが無くともログインできるようにするためです。エラー原因の1つになる、とても重要なものになります。そしてuserモデルにバリデーションを加えるのを忘れずにしましょう!

models/user.rb

class User < ApplicationRecord
  ...
  validates :uid, uniqueness: { scope: :provider }, if: -> { uid.present? }
  
  # authの中身はGitHubから送られてくる大きなハッシュ。この中に名前やメアドなどが入っている。
  # providerカラムとuidカラムが送られてきたデータと一致するユーザーを探す。
  # もしユーザーが見つからない場合は新規作成する。
  def self.find_for_github_oauth(auth)
    where(provider: auth.provider, uid: auth.uid).first_or_create! do |user|
      # 名前を取得するときはこのように書く(今回はUserモデルにname属性がないのでエラーなる) 
      # user.name = auth.info.name
      user.email = auth.info.email
      # 任意の20文字の文字列を作成する
      user.password = Devise.friendly_token[0, 20]
    end
  end
  ...
end

ここで重要になるものがif条件です。uidが存在していないときにこのバリデーションが作用するとまたしても通常ログインの際エラーが出てログインできなくなります。私はここのif条件を忘れてしまっていたため、エラー地獄のひどい目に遭いました笑
さらにGitHubからもらったデータでユーザー登録するメソッドを作ります。しかしながらこの状態だとGitHubログインした際、プロフィール編集ができません。なぜならプロフィール編集に現在のパスワードが必要になっています。しかし現在のパスワードはGitHubのパスワードではなくこのメソッドで作ったランダムのパスワードです。つまりGitHubでログインしたユーザーはプロフィール編集できない仕様になっています。この問題を後ほどのcontrollers/users/registrations_controller.rbで解決していきます!

db:migrateでのエラー

userテーブルにデータが入っているとエラーが出ます。なぜこのようなことが起きたのかというと、先程のマイグレーションファイルでuidに対してnull: falseを追記してしまったままであったからです。これは現データのuser.uidに対してNOT NULL制約がかかってしまったためでした。その場ではrails db:resetをして現データを削除してからrails db:migrateすることにより解決できました。

ルーティング(routes.rb)

Rails.application.routes.draw do
  
  devise_for :users, controllers: { 
    registrations: "users/registrations",
    omniauth_callbacks: "users/omniauth_callbacks"
  }
  ...
end

ハッシュロケットは可能な限り使わないようにしましょう!

controllers/users/omniauth_callbacks_controller.rb

class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
  # You should configure your model like this:
  # devise :omniauthable, omniauth_providers: [:github]

  # You should also create an action method in this controller like this:
  def github
    # request.env["omniauth.auth"]にGitHubから送られてきたデータが入っている
    @user = User.find_for_github_oauth(request.env["omniauth.auth"])

    unless @user.persisted? # ログイン失敗
      session["devise.github_data"] = request.env["omniauth.auth"]
      return redirect_to new_user_registration_url
    end
    
    if @user.profile.blank? # プロフィール登録画面への誘導
      sign_in @user, event: :authentication
      return redirect_to new_profile_path
    end
    
    sign_in_and_redirect @user, event: :authentication #プロフィールを作成していれば、ログイン
    set_flash_message(:notice, :success, kind: "Github") if is_navigational_format?
  end

  # More info at:
  # https://github.com/heartcombo/devise#omniauth

  # GET|POST /resource/auth/github
  # def passthru
  #   super
  # end

  # GET|POST /users/auth/github/callback
  # def failure
  #   super
  # end

  # protected

  # The path used when OmniAuth fails
  # def after_omniauth_failure_path_for(scope)
  #   super(scope)
  # end
end

コメントアウトしてあるsuperに関してメソッドを追記しない場合はコメントアウトのままで大丈夫です。

controllers/users/registrations_controller.rb

class Users::RegistrationsController < Devise::RegistrationsController
  ...
  protected
    def update_resource(resource, params)
      return super if params["password"]&.present?
      # 現在のパスワードなしでアカウントの更新をする
      resource.update_without_password(params.except("current_password"))
    end
end

上記コードによりランダムなパスワードが振り分けされても(GitHubのパスワードで無くとも)プロフィール詳細を編集できるようになりました!

.env.exampleファイル

GITHUB_ID = 
GITHUB_SECRET = 

最後に、どの環境変数を設定すれば良いかわかるようにしておくと親切です。※秘匿情報は書かない

実装を通して学んだこと

1. uidとproviderカラム

omniauthを使ったログインではこの二つのカラムをセットにして考えるということを学びました。uid は oAuth を使用してログインしたユーザーの一意の ID で構成されることから、通常ログインでは絶対に絡めないようにすること。validationで必ずif条件によりuidが存在するかどうか判断してproviderカラムにユニーク制約をかけること(if条件をつけないと通常ログインでもユニーク制約がかかり、エラーが発生する)。

2. omniauthでのパスワード問題

GitHubログインをした際は、その時点で適当なパスワードを振るように設定することと、パスワードが必要な操作をする際にパスワードなしで、操作できるようにコントローラの編集が必要なことを学びました。

3. db:migarate失敗

db:resetからのdb:migrateで解決。

感想

今回は非常に時間がかかり、苦労しました。まずはomniauth?oAuth?というとこから始まりました笑
GitHubログインができた!と喜んでいると、通常のログインが機能しなくなっていたり、てんてこまいになりましたが解説の通り、マイグレーションファイルやモデルのバリデーションをいじることによりなんとか解決できました。実際にこの章を実装してみるとよくわかりますがとても得られるものが多いなと感じました。特にマイグレーションファイルのNOT NULL制約であったり、モデルのバリデーション機能に関してはとても良い復習になりました。是非実装してみてください!
次回はツイートのいいね機能についてアウトプットしていこうと思います!読んでいただきありがとうございました。

参考記事

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?