Rails チュートリアル　第9章　まとめ

#全体像の把握
Cookiesはブラウザのみで保存する
new_tokenでランダムな値を生成し、それを仮想的な属性である.rememberと.remember_confirmationに一時的に保存し(合わせてremember_token)、その値をハッシュ化して、DBのremember_digestに保存。
さらに、remember_tokenはuser.idとともにcookiesにも焼き付ける。cookiesに置いておくことで、ユーザー情報を保持することができる。user_idを復号化することで、Userの情報を復元し、そこからDBのremember_digestを取得する。一方で、焼き付けておいたremember_tokenと照合して、認証を行う。

#DBへdigestを保存
1 remember_tokenをハッシュ化させた値を保存しておく場所を作成
`rails generate migration add_remember_digest_to_users remember_digest:string`
`rails db:migrate`
2 ランダムな文字列を作成
`SecureRandom.urlsafe_base64`

3 仮想的な属性にアクセスする部分の実装(setterとgetter)
has_secure_passwordがやってくれていたことを自分で実装する必要がある
u.passwordに代入できるが、データベースに保存しないという機能（仮想的な属性にアクセス）
今はu.rememberとすることはできない


```
def remember= (token)
   @remember = token
end
def remember
   @remember
end
```

よく使うのでRubyで実装されており、この一行に短縮できる

```
attr_accessor :remember
```

4.保存

```ruby:
def remember
# Userがログイン状態を保持したいと思い、チェックボックスにチェック(remember me)し、
# ログインが成功した時に呼び出されるメソッドremember()から
    self.remember_token = User.new_token
# 新しいトークンを発行し、このメソッドを呼び出したオブジェクト(self)のremember_tokenに入れる
# remember_tokenは次のリクエストが来るまでしか有効ではない
    update_attribute(:remember_digest, User.digest(remember_token))
# new_tokenは開発側が作った値なので、これまでのようにvaliをかける必要がないので
# update_attribute()を使う
# update_attribute(name,value) = (属性名・カラム?,値・更新後の)
# remember_tokenがハッシュ化された値が、DBに保存
 end
```


----
#認証
さらに認証のためにuserのid情報を暗号化したものもcookiesに入れておき、そこからuser_idを復号化し、find_byによりユーザーオブジェクトを取得し、athenticateでユーザーの平文(remember_token)とDBのdigestと照合する
authenticateでuser情報が必要な理由は、どのUserのremember_tokenか指定する必要があるから


bcryptの認証
remember_tokenとdigestが一致したらtrue
実際の認証の部分、図での一番最後の部分

```
def authenticated?(remember_token)
    BCrypt::Password.new(remember_digest).is_password?(remember_token)
end
```



このメソッドをsessionコントローラのcreateアクションのloginの後に書くことで、ログインと同時に、remember_tokenをDBへ保存したり、cookiesへ保存が行われる


```rb:sessions_helper
def remember(user)
    user.remember # DBへremember_digestを保存
    cookies.permanent.signed[:user_id] = user.id
# userのidも暗号化してcookiesに入れる
#　文法はあまり考えないでいい
    cookies.permanent[:remember_token] = user.remember_token
# remember_tokenをcookiesに入れる
end
```

----
sessionを使って復元することを試みて、sessionで無理なら、cookiesを使って復元する


```ruby:
def current_user
#sessionを使った復号化
 if user_id = session[:user_id] 
# user_idというローカル変数にsessionの値を代入
# 代入されたローカル変数の値を条件式で評価、比較演算子(==)ではない
# 評価されるので数字かnilが返る、数字ならtrue
      @current_user ||= User.find_by(id: user_id) # ここまでは同じ

#cookiesを使った復号化、cookiesが入ってなかったらnilで終了
 elsif (user_id = cookies.signed[:user_id])
# 復号化されて実際のuser_idになる
      user = User.find_by(id: user_id)
      # 数字が返ってきたら、user_idをfind_byで取得し、Userオブジェクトに引っ掛ける
      if user && user.authenticated?(cookies[:remember_token])
      #Userのcookiesにあるremember_tokenとremember_digestを照合
        log_in user #login状態に
        @current_user = user #User情報の復元
      end
 end
end
```
このメソッドをどこで呼び出しているのか、分かりづらいが
このメソッドは第8章でしたように、ここで呼び出される
使われているのは、_header.htmlで、ログインしている時としていない時の振る舞いを変えるところ

```rb:
def logged_in?
    !current_user.nil?
end
```
第8章ではsessionが切れているかどうかでログインしているか確かめる意味合いでしかなかったが、ここではcookiesを使った復号化を試みる場所という機能も与えられている。
このlogged_inメソッドがheaderで使われているので、ユーザーがこのwebサイトの何らかのページに行く時には必ずcurrent_userメソッドも呼び出されると考えられる。
ログイン機構の理解
まずログイン時にセッションとクッキーでユーザー情報を保持する部分
次に、ユーザーがログアウトせずにページを離れて、またやってきた場合、そのユーザー情報を渡す（復号化）部分の構築がポイントだと思う



Userを忘れる
rememberメソッドと対になる

```ruby:
def forget
    update_attribute(:remember_digest, nil) #remember_digestをnilに
end
```
remember(user)の対

```ruby:
def forget(user)
    user.forget
    cookies.delete(:user_id)
    cookies.delete(:remember_token)
end
```
```ruby:
def log_out
    forget(current_user) #上のメソッドを呼んでいる
    session.delete(:user_id)
    @current_user = nil
end 
```

連続ログアウト問題
二つのデバイスでログインした状態で、片方をログアウトさせると、もう片方がログアウトする時にはlog_outメソッドのforget(current_user)のcurrent_userがいないのでnilが渡されErrorとなる

解決策として、log_outメソッドを呼び出す時、nilじゃないことを確認する
destroyアクションで
`log_out if logged_in?`
ログアウトするできるのはログインしている時だけ
2回目以降のログアウトはログインしていないので、log_outが実行されず、redirectだけ実行

二つのバグとlog_in_asのテストはよくわからない