AWSへデプロイ後に、暗号化したパスワードでログインできない現象への対処法

#AWSへデプロイ後に、bcryptで暗号化したパスワードでログインできない現象についての対処法
プログラミング初学者です。Ruby on Railsで作成したアプリをAWSのEC2インスタンスにデプロイした後で、作成したアカウントにログインできない現象が発生しました。パスワード周りのエラーの解消に丸一日かかってしまいましたので、また同じ現象で困らないためにもこの記事にまとめておきたいと思います。

##発生した環境

Rubyバージョン：2.5.8
Railsバージョン：6.1.1
gem bcryptバージョン：3.1.16

##発生した現象

デプロイ後に新規登録したアカウントに、始めはログインできていたのですが、しばらく経ってからログインしようとすると「メールアドレスかパスワードに誤りがあります」と表示されログインできなくなりました。

##試したこと

###ログを確認
```アプリのディレクトリ
$ vim log/production.log
```

いくつかのエラーログを確認しましたので、順に確認していきました。

###Mysql2::Error
>ActiveRecord::StatementInvalid (Mysql2::Error: Unknown column 'password_digest' in 'field list'):

bcryptでパスワードの暗号化を行なった際に、'password_digest'カラムをデータベースのusersテーブルに追加していました。mysqlにログインしたところ、確かに'password_digest'カラムは存在して値が保存されていました。


###ArgumentError
>ArgumentError (wrong number of arguments (given 0, expected 1)):
app/controllers/users_controller.rb:41:in `login'

値が入るはずの場所に値が入っていないことが確認できました。
該当の場所には下記コードがありました。

```ruby:app/controllers/users_controller.rb
    if @user && @user.authenticate(params[:password])
```

どうやら入力したパスワードが正常に認識されていないことがわかりました。

###NoMethodError
>NoMethodError (undefined method `encrypted_password=' for #<User:0x000000000572e6d8>):

'encrypted_password'は何のことかとわからなかったので、検索するとDeviseを導入した時に追加されるという情報がありました。確認すると、確かにGemfileに「gem 'devise'」を追加していました。そこで、encrypted_passwordを利用したパスワードの暗号化に切り替えることにしました。

##password_digestからencrypted_passwordへの切り替え

まずmysqlでusersテーブルのカラム名を変更します。

```
mysql> alter table users change column password_digest encrypted_password char(255);
```

user.rbを編集します。

```ruby:app/models/user.rb
class User < ApplicationRecord
  has_secure_password #ここを削除します
  validates :name, {presence: true}
  validates :email, {presence: true, uniqueness: true}
  validates :image_name, {presence: true}
  validates :password, {presence: true} #ここも不要になるので削除します

  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable, :omniauthable
end
```

users_controller.rbを編集します。
エラーが発生していた、ログイン認証の行の認証メソッドを書き換えます。

```ruby:app/controllers/users_controller.rb
    if @user && @user.valid_password?(params[:password]
```

パスワードを変更する部分が少々手こずりました。
params[:password]に保存されている新しいパスワードを手動で暗号化して更新しています。

```ruby:app/controllers/users_controller.rb
    if params[:password]
      hashed_password = BCrypt::Password.create(params[:password])
      update_password_sql = "update users set encrypted_password = '#{hashed_password}' where id =#{@current_user.id};"
      ActiveRecord::Base.connection.execute(update_password_sql)
    end
```

コードの編集が終わったらサーバーを再起動します。
nginxを再起動します。

```
$ sudo service nginx restart
```

続いてunicornを再起動します。unicornで走っているスレッドを確認します。

```
$ ps -ef | grep unicorn | grep -v grep
```

3行の走っているスレッド番号が表示されます。
下は一行目の一例です。

```
ユーザー名    番号     1  0 11:16 ?        00:00:00 unicorn_rails master -c /var/www/アプリのディレクトリ/config/unicorn.conf.rb -D -E production
…
```

スレッドを停止します。

```
$ kill 番号
```

もう一度上記の確認コマンドを打ち込んで何も表示されなければ、スレッドが終了しています。
unicornを起動します。

```
$ bundle exec unicorn_rails -c /var/www/アプリのディレクトリ/config/unicorn.conf.rb -D -E production
```

確認コマンドを打ち込んでスレッド番号が表示されれば、再起動できています。

##まとめ

丸一日かかってしまったので、思い出せる限り全部の工程をまとめました。

某プログラミング学習サイトでパスワードの暗号化を習い、簡単にできるものだと思っていたら思わぬ落とし穴がありました。Twitter連携などに使用するDeviseを導入することで、パスワード暗号化の機能が重複してエラーを発生するようです。簡単に習っただけでは実践には足りない、実際にエラーに遭遇することで腕を磨くものだと、今後の良い戒めになりました。