ユーザ認証機能

<h2>ログイン機能</h2>

railsでは、ライブラリで簡単に実装することができる！！

<h3>○Deviseを導入してユーザ認証機能を実装</h3>

実装するにあたり、deviceというライブラリを使用します👍（gem）
https://github.com/heartcombo/devise

アカウント乗っ取りされクレジットを不正利用されるなど、重要な機能なので、いいねが多く押されているもの（信頼度が高いもの）を使用することが重要です！！！！！！！


自前でログイン機能を実装することもできるが、そこでミスがありバクが起こり、不正に利用されるのは困る💦

「device rails」と調べればQiitaなどで、実装のやり方も出てきます👍

Getting startedと記載があるところを見れば、最初の設定が書いてあります！

    gem 'devise'
Gemfileに記入

    $bundle install
を実施。

    $rails generate devise:install
次にこれを実施。
色々と説明が出てくる。
 create〜と記載があるので、設定はできている🙆

config/environments/development.rb
ここに、config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }とかけと、書いてあります。

ということで　
それ通りやっていきましょう🏃

    config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
どこに記載してもいいが
mailerと他に記載があるところがあるので、その後ろに記載しました。

これは何をしているのかというと、メールの設定をしている。
railsにはメールを送信する機能が、備わっている！
そのメールを送信するには、サーバーの設定が必要👍
→その設定を書いています！

今回の場合、ローカルホストの3000番からメールを送るよと書いてあります。

デバイスもログインするき、よくユーザー登録するときにemailで確認するときがありますよね？
そういった機能もデバイスの中に含まれている。そのため、メールの設定を先にやらなければいけない！！


次に、 root_urlをconfig/routes.rbに記載しなさいと書いてあります。

    root to: "home#index"

次、flash messagesをapp/views/layouts/application.html.erbここに記載しなさいと書いてある。

    <p class="notice"><%= notice %></p>
    <p class="alert"><%= alert %></p>

最後に、Devise viewsをカスタマイズすのであれば、下記を実行。

    ＄rails g devise:views
これをすることで、ログイン画面のhtml,cssをいじることができる！！


ほんとの最後です！笑

    $ rails generate devise MODEL
これを実施。
ログインするときや、ユーザーを作るときには、ユーザーの情報をデータベースに保存しておかなければならない！！SQLを使わないとユーザーの情報を管理できない！

ここでは、アクティブレコードを使って、ユーザを管理できるようなモデルをつくりましょうとかいてある。

    $rails generate devise User
ここでこれを実行するとユーザーテーブルやユーザーモデルが作成される。
app/models/user.rbを確認すると、色々と作成されている。

さらにdb/migrate/20230225093513_devise_create_users.rbここを見ると
DeviseCreateUsersに、create_tableとなり色々記載されている。

ここまでできたら

    $rails db:migrate
を実行。マイグレーションファイルは、データーベースを設計するため命令、設計書
設計書を作っただけでは、データベースに反映することはできない。ので、db:migrateを実行しデータベースに反映してあげる。

一旦サーバーを再起動

    $rails s

お疲れ様でした💦
これで、ログイン機能を実装することができました👏

http://localhost:3000/rails/info/routes
を確認すると、サインインやサインアウト、パスワードなどが作られている。

http://localhost:3000/users/sign_up
ここをみてみると、勝手にホームができている。
ここに、メールとパスワード適当にいれると、ログインできます👏（メールは飛んできません）


<h2>参考情報</h2>
sessionやcookieについて

基本的な考え方で、HTTPはステートレスというものがあります。
ステートレスの意味は、状態をもたない

ブラウザからリクエストを送る。サーバーが、　レスポンスを返す。これだけの関係。
HTTPは、ブラウザがログインしているなど情報をもたないこと。
リクエストが来たらただ捌くというシンプルな関係。

その状態だとログインしても、リロードしてしまうとログイン前になってしまう。

<h5>①ユーザ登録</h5>
ブラウザからEmailとPasswordを打つ。サーバーへ送る。サーバーにて、Passwordを暗号化して保存。再びブラウザに登録できたよと送る。

<h5>②ログイン</h5>
ブラウザからEmailとPasswordを送ってくる。まずそれを暗号化する。DBにも暗号化されたものが保存されているので、それが一緒なのか？検査する。一緒であれば、本当にユーザーだな！と教えてくれる。
ハッシュかされたものが、元々作ったものと同じか。一緒であれば認識する。

<h5>③暗号化</h5>
パスワード　→ ハッシュ関数　→ 適当な文字列
同じパスワードをハッシュ関数に送れば、毎度同じ適当な文字列が出る。
少しでも違うパスワードだと、全然違う文字列が返ってくる。
また、暗号化されたものからは、もともとのパスワードはわからない。

ログイン情報が、流出ても大丈夫なようにしている。

<h5>④パスワードの検証</h5>
ユーザーが、EmailとPasswordを入力。まず、Emailからユーザーを探す。
そこには、暗号化されたパスワード情報も保存されている。フォームから送られてきた値を暗号化して、保存されている暗号化されたパスワードと一致するか検証。一緒であれば、本人で間違いないねと！認証される。

まだ、この時点では、HTTPのステートレスは解決されていない。

<h5>⑤cookie,session1</h5>
ブラウザからEmailとPasswordを送る。これで、暗号化されているパスワードと一致したので、認証しました。今度は、use_idを暗号化して、ブラウザに渡す。
ブラウザのCookieと呼ばれる。データを保管する場所。ここに、暗号化したuser_idを保持する。

<h5>⑥cookie,session2</h5>
Cookieが保存されている状態で、何か投稿したい！暗号化したuser_idを渡す。
サーバーは、Cookieの内容をチェックして、user_id1だな！となり、その投稿を保存する。レスポンスで、保存できたよと返す。

この一連の流れをsessionという。

これを自動的に管理しているのが、デバイス。

<h2>テンプレートをカスタマイズ</h2>
app/views/deviseができている。erbで書かれているので、Hamlに書き換えます。

    $bundle exec rake haml:replace_erbs
app/views/devise/sessions/new.html.haml
ここの頭に、.containerとつける。
class: 'text'を追加。

    .field
      = f.label :email
      %br/
      = f.email_field :email, autofocus: true, autocomplete: "email", class: 'text'
    .field
      = f.label :password, class: 'text'
      %br/
      = f.password_field :password, autocomplete: "current-password", class: 'text'

app/views/devise/registrations/new.html.haml
ここにも、頭に、.containerとつける。
さらに、 class: 'text'と class: 'btn-primary'を追加

    .field
      = f.label :email
      %br/
      = f.email_field :email, autofocus: true, autocomplete: "email", class: 'text'
    .field
      = f.label :password
      - if @minimum_password_length
        %em
          (#{@minimum_password_length} characters minimum)
      %br/
      = f.password_field :password, autocomplete: "new-password", class: 'text'
    .field
      = f.label :password_confirmation
      %br/
      = f.password_field :password_confirmation, autocomplete: "new-password", class: 'text'
.actions
      = f.submit "Sign up", class: 'btn-primary'

この時点で、どう変わったか確認したが、
まだログアウト機能がないので、userを消します。

    $rails c

    $User.first.destroy

これで、user状態がなくなった。

これで、
http://localhost:3000/users/sign_up
みてみると、少しだけ、見た目が良くなりました👏

app/views/devise/passwords
同様にここにある。hamlも変更していきました👍

<h2>Deviseを使いこなそう</h2>
ログイン機能と登録機能をやってきました。

パスワードリセット機能
http://localhost:3000/users/sign_in
をみてもらって、Forgot your password?とあります。

ここを押し、登録したメアドを入力すると、そのメアドにパスワードリセット案内が届く。

サインupしていないと、動作しない！

シークレットモードで、http://localhost:3000/users/sign_in
でForgot your password?を押す。
ここで、登録したメアドを入れると、送られたよというようなメッセージがでる。

※シークレットモードというのは、cookieなどの情報を保持していないChromeの状態のことを表します。ゆえにシークレットモードでブラウザを開くことで、同じブラウザでもログインしていない状態を表示することができます。

だが、メールを受け取ることができないので、ログに出てきている。
そこに、pタグで囲まれている。Change my passwordのURLをコピー
http://localhost:3000/users/password/edit?reset_password_token=_yN1RCrJ8VxTuxRiKcPd
パスワードをリセットするためのurl

ここを開きパスワードを、入力すると、変更することができる。

http://localhost:3000/users/edit
ここを開くと、ここには、ログインしてからパスワードを変更することやアカウントの削除こんな機能も用意されている。

<h3>日本語化</h3>
現在全て、英語なので日本語に変更していきます。

config/localesここを開く
config/locales/en.ymlこの場所で英語化されている
ということは、これの日本語版を作れば良い👍

config/locales/devise.ja.ymlまずこのファイルを作成する。

すごい長いですが、下記を貼ってください🙆
   ja:
  activerecord:
    attributes:
      user:
        confirmation_sent_at: パスワード確認送信時刻
        confirmation_token: パスワード確認用トークン
        confirmed_at: パスワード確認時刻
        created_at: 作成日
        current_password: 現在のパスワード
        current_sign_in_at: 現在のログイン時刻
        current_sign_in_ip: 現在のログインIPアドレス
        email: Eメール
        encrypted_password: 暗号化パスワード
        failed_attempts: 失敗したログイン試行回数
        last_sign_in_at: 最終ログイン時刻
        last_sign_in_ip: 最終ログインIPアドレス
        locked_at: ロック時刻
        password: パスワード
        password_confirmation: パスワード（確認用）
        remember_created_at: ログイン記憶時刻
        remember_me: ログインを記憶する
        reset_password_sent_at: パスワードリセット送信時刻
        reset_password_token: パスワードリセット用トークン
        sign_in_count: ログイン回数
        unconfirmed_email: 未確認Eメール
        unlock_token: ロック解除用トークン
        updated_at: 更新日
    models:
      user: ユーザ
  devise:
    confirmations:
      confirmed: メールアドレスが確認できました。
      new:
        resend_confirmation_instructions: アカウント確認メール再送
      send_instructions: アカウントの有効化について数分以内にメールでご連絡します。
      send_paranoid_instructions: メールアドレスが登録済みの場合、本人確認用のメールが数分以内に送信されます。
    failure:
      already_authenticated: すでにログインしています。
      inactive: アカウントが有効化されていません。メールに記載された手順にしたがって、アカウントを有効化してください。
      invalid: "%{authentication_keys}またはパスワードが違います。"
      last_attempt: もう一回誤るとアカウントがロックされます。
      locked: アカウントは凍結されています。
      not_found_in_database: "%{authentication_keys}またはパスワードが違います。"
      timeout: セッションがタイムアウトしました。もう一度ログインしてください。
      unauthenticated: アカウント登録もしくはログインしてください。
      unconfirmed: メールアドレスの本人確認が必要です。
    mailer:
      confirmation_instructions:
        action: メールアドレスの確認
        greeting: "%{recipient}様"
        instruction: 以下のリンクをクリックし、メールアドレスの確認手続を完了させてください。
        subject: メールアドレス確認メール
      email_changed:
        greeting: こんにちは、%{recipient}様。
        message: あなたのメール変更（%{email}）のお知らせいたします。
        subject: メール変更完了。
      password_change:
        greeting: "%{recipient}様"
        message: パスワードが再設定されたことを通知します。
        subject: パスワードの変更について
      reset_password_instructions:
        action: パスワード変更
        greeting: "%{recipient}様"
        instruction: パスワード再設定の依頼を受けたため、メールを送信しています。下のリンクからパスワードの再設定ができます。
        instruction_2: パスワード再設定の依頼をしていない場合、このメールを無視してください。
        instruction_3: パスワードの再設定は、上のリンクから新しいパスワードを登録するまで完了しません。
        subject: パスワードの再設定について
      unlock_instructions:
        action: アカウントのロック解除
        greeting: "%{recipient}様"
        instruction: アカウントのロックを解除するには下のリンクをクリックしてください。
        message: ログイン失敗が繰り返されたため、アカウントはロックされています。
        subject: アカウントの凍結解除について
    omniauth_callbacks:
      failure: "%{kind} アカウントによる認証に失敗しました。理由：（%{reason}）"
      success: "%{kind} アカウントによる認証に成功しました。"
    passwords:
      edit:
        change_my_password: パスワードを変更する
        change_your_password: パスワードを変更
        confirm_new_password: 確認用新しいパスワード
        new_password: 新しいパスワード
      new:
        forgot_your_password: パスワードを忘れましたか?
        send_me_reset_password_instructions: パスワードの再設定方法を送信する
      no_token: このページにはアクセスできません。パスワード再設定メールのリンクからアクセスされた場合には、URL をご確認ください。
      send_instructions: パスワードの再設定について数分以内にメールでご連絡いたします。
      send_paranoid_instructions: メールアドレスが登録済みの場合、パスワード再設定用のメールが数分以内に送信されます。
      updated: パスワードが正しく変更されました。
      updated_not_active: パスワードが正しく変更されました。
    registrations:
      destroyed: アカウントを削除しました。またのご利用をお待ちしております。
      edit:
        are_you_sure: 本当によろしいですか?
        cancel_my_account: アカウント削除
        currently_waiting_confirmation_for_email: "%{email} の確認待ち"
        leave_blank_if_you_don_t_want_to_change_it: 空欄のままなら変更しません
        title: "%{resource}編集"
        unhappy: 気に入りません
        update: 更新
        we_need_your_current_password_to_confirm_your_changes: 変更を反映するには現在のパスワードを入力してください
      new:
        sign_up: アカウント登録
      signed_up: アカウント登録が完了しました。
      signed_up_but_inactive: ログインするためには、アカウントを有効化してください。
      signed_up_but_locked: アカウントが凍結されているためログインできません。
      signed_up_but_unconfirmed: 本人確認用のメールを送信しました。メール内のリンクからアカウントを有効化させてください。
      update_needs_confirmation: アカウント情報を変更しました。変更されたメールアドレスの本人確認のため、本人確認用メールより確認処理をおこなってください。
      updated: アカウント情報を変更しました。
      updated_but_not_signed_in: あなたのアカウントは正常に更新されましたが、パスワードが変更されたため、再度ログインしてください。
    sessions:
      already_signed_out: 既にログアウト済みです。
      new:
        sign_in: ログイン
      signed_in: ログインしました。
      signed_out: ログアウトしました。
    shared:
      links:
        back: 戻る
        didn_t_receive_confirmation_instructions: アカウント確認のメールを受け取っていませんか?
        didn_t_receive_unlock_instructions: アカウントの凍結解除方法のメールを受け取っていませんか?
        forgot_your_password: パスワードを忘れましたか?
        sign_in: ログイン
        sign_in_with_provider: "%{provider}でログイン"
        sign_up: アカウント登録
      minimum_password_length: "（%{count}字以上）"
    unlocks:
      new:
        resend_unlock_instructions: アカウントの凍結解除方法を再送する
      send_instructions: アカウントの凍結解除方法を数分以内にメールでご連絡します。
      send_paranoid_instructions: アカウントが見つかった場合、アカウントの凍結解除方法を数分以内にメールでご連絡します。
      unlocked: アカウントを凍結解除しました。
  errors:
    messages:
      already_confirmed: は既に登録済みです。ログインしてください。
      confirmation_period_expired: の期限が切れました。%{period} までに確認する必要があります。 新しくリクエストしてください。
      expired: の有効期限が切れました。新しくリクエストしてください。
      not_found: は見つかりませんでした。
      not_locked: は凍結されていません。
      not_saved:
        one: エラーが発生したため %{resource} は保存されませんでした。
        other: "%{count} 件のエラーが発生したため %{resource} は保存されませんでした。"


貼り付け後！
その後念のためサーバーを再起動
ローカルのサーバーは、再起動しないと読み込まれない

確認してみると日本語化されてますね😆
試しにサインインすると、すでにログインしています。と出ますね👏

これも、デバイスが勝手に読み込んで表示してくれています！


<h3>ログイン状態に応じて表示変更</h3>
ログインしている時は、ユーザーマークあり
ログインしていない時は、ログインボタンを作る

app/views/layouts/application.html.haml
ここに
    
    .dropdown
        = image_tag 'default-avatar.png', class: 'header_avatar dropbtn'
        .dropdown-content
          %a{:href => "#"} プロフィール
          %a{:href => "#"} ログアウト
現在はこの状態で表示しています。

ユーザがサインしているかどうかを確かめられるメソッドがある。
user_signed_in?

    - if user_signed_in?
これを頭に追加することで、ログインしていれば表示する状態になる。


<h3>ログアウト機能実装</h3>
ラウツをみると
destroy_user_session_path　というものがあります。
セッションを壊すというパス

    = link_to 'ログアウト', destroy_user_session_path, data: { method: 'delete'}
上記を記載
※link_toは、デフォルトでGETリクエストになるので、メソッドでデリートとする

以上より確認し実施してみると、ログアウト機能が実装できているのが、確認できます👍

<h3>ログインボタンの配置</h3>
サインインのGETを書く

    - else
        = link_to 'ログイン', new_user_session_path, class: 'header_loginBtn'
ログインしていない時は、これを表示

みためをよくしていく
app/assets/stylesheets/header.scss

     &_loginBtn {
    background-color: $base-bg-color;
    height: $avatar-size;
    border-radius: 8px;
    font-size: 12px;
    padding: 12px 16px;
    color: $primary-color;
      }
を追加。

<h3>ログイン必須のアクション</h3>
ログインしていないと記事が投稿できないようにする。

app/controllers/articles_controller.rb

authenticate_user!というメゾットがある。
これを指定している、ログインしていない状態ではそいつは使えませんとなる。

    before_action :authenticate_user!
を追加
こうするとすべてのアクションがログインしていないと使えないとなる。

この状態でトップを表示しようとすると、アカウント登録もしくはログインしてください。と出る。

さらに、使えない機能を制限する時は、
 only: [:new, :create, :edit, :update, :destroy]後ろにこれを追加、制限したい機能を記入していく。

こうすることで、ログインしていなくても、トップの表示や記事の閲覧はすることができる。

app/views/articles/show.html.haml
ログインしていない時は、記事の編集、削除ボタンがでないようにする。

    - if user_signed_in?

をドロップダウンの機能の前に記入