【備忘録】deviseの通常ログインは先に実装しましょう
はじめに
こんにちは!大ちゃんの駆け出し技術ブログです。
自分のPFがslackログインのみでしたが、それに通常ログイン機能を加えました。deviseを使用した認証の実装では本来通常ログイン ⇒ 外部認証の順番で実装を行うかと思います。しかし、自分は既にslack認証を実装していたので通常ログインとの互換性に差異があり、少しつまづいてしまったのでこの記事で記録しておきます。
導入前のUserモデルの影響
導入前のUserモデルのバリデーションの設定です。
validates :name, presence: true validates :email, presence: true, uniqueness: { case_sensitive: true } validates :provider, presence: true validates :uid, presence: true, uniqueness: { case_sensitive: true } validates :encrypted_password, presence: true belongs_to :team
slack認証を実装していたので、providerとuidがslack認証時に追加されます。そのため、providerとuidは共に必須カラムとしていました。
def self.from_omniauth(auth, user_info) user = find_or_initialize_by(provider: auth.provider, uid: auth.uid) user.password = Devise.friendly_token[0, 20] # ランダムなパスワードを作成 user.name = user_info.dig('user', 'name') user.email = user_info.dig('user', 'email') user.image = user_info.dig('user', 'image_192') user.check_team_existence(user_info.dig('team')) user.save! user end
また、belongs_to :team
とあるようにslackのワークスペースにユーザーが所属する設定をしています。
後々この3つのprovider, uid, team_id(team)が既に存在していたために通常ログインフローで引っかかりました。
いざ実装
deviseの記事はググればいくらでも出てくるので基本的deviseの導入は割愛します。
ユーザー登録画面は以下のように実装しました。deviseで生成されるerbファイルをslimに変換しています。
h2 | Sign up = form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| = render "users/shared/error_messages", resource: resource .field = f.label :name br = f.text_field :name, autofocus: true, autocomplete: "name" .field = f.label :email br = f.email_field :email, autofocus: true, autocomplete: "email" .field = f.label :password - if @minimum_password_length em | ( = @minimum_password_length | characters minimum) br = f.password_field :password, autocomplete: "new-password" .field = f.label :password_confirmation br = f.password_field :password_confirmation, autocomplete: "new-password" .field = f.label :image br = f.file_field :image, autofocus: true, autocomplete: "image" .actions = f.submit "Sign up" = render "users/shared/links"
さてさて、通常通りにこれを登録しようとしてもエラーになります。表示されるエラーメッセージは元Userモデルのバリデーション が影響したものです。
- チームを入力してください
- uidを入力してください
- providerを入力してください
providerとuidについてはnull: false, presence: true
を取る他に方法がないかなと思いました。これらはslackログインの時に取得できますので、通常ログイン時との互換性を合わせるなら必須カラムを解除するしかないなと。
validates :name, presence: true validates :email, presence: true, uniqueness: { case_sensitive: true } validates :encrypted_password, presence: true belongs_to :team
問題はteam
の方です。belongs_to :team
をコメントアウトで消すわけにはいきませんし、、、
問題はuserに属するteamがuser登録時に存在しないことです。なのでゴリ押しで先にデフォルトのチームデータを入れ、userのデフォルトのteam_idをマイグレーションファイルに記述しました。(絶対ベストプラクティスじゃない、、、)
t.references :team, foreign_key: true, :default => "1"
結論
今回のようにゴリ押しする他実装方法がないのはdeviseのログインの実装を外部認証⇒通常ログインの順番で実装したためです。通常ログイン⇒外部認証で実装を行った方が、後々の互換性を合わせる際に楽になるかと思います。結論、普通に通常ログインから実装しようという話でした。