[勉強メモ]rails deviseを使った認証

# deviseとは
- ログイン認証機能を提供するgem
- 認証周りとかユーザの編集や削除機能も提供している

## devise使うと何ができる?
- アカウント作成
- ログイン
- ログアウト
- パスワード変更
- アカウントの凍結(一定回数以上ログインの失敗時など)

# deviseのセットアップ
## Gemfileへ記述
Gemfileに下記を追記

```ruby:Gemfile
# Authenticate by devise
gem "devise"
gem "devise-i18n"
```
- devise-i18n
    - バリデーションエラー時のメッセージとかを日本語化するために使う(後述)

## コマンド実行
機能をインストール

```console:コマンド実行
bundle install
```

モデルとかマイグレーション、ビューを作成

```console
rails g devise:install #deviseのインストール
rails g devise user #モデルを作成
rake db:migrate #userテーブルを作成
rails g devise:views #ビューの作成
```

コマンドを実行するとメッセージが出力されるので書いてある通りに付け加える
```console
Some setup you must do manually if you haven't yet:

  1. Ensure you have defined default url options in your environments files. Here
     is an example of default_url_options appropriate for a development environment
     in config/environments/development.rb:

       config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }

     In production, :host should be set to the actual host of your application.

  2. Ensure you have defined root_url to *something* in your config/routes.rb.
     For example:

       root to: "home#index"

  3. Ensure you have flash messages in app/views/layouts/application.html.erb.
     For example:

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

  4. You can copy Devise views (for customization) to your app by running:

       rails g devise:views
```

`config/environments/development.rb:`に追記

```ruby:config/environments/development.rb:
  # Device gem settings
  config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
```

`routes.rb`に追記

```ruby:routes.rb
Rails.application.routes.draw do
  devise_for :users
  # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html  
  root "home#index"
end
```

`app/views/layouts/application.html.erb.`へアラートを表示したい場所に下記追記

```ruby:app/views/layouts/application.html.erb.
<!DOCTYPE html>
<html>
  <head>
    <title><%= yield :title%></title>
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>

    <%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track': 'reload' %>
    <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
  </head>

  <body>
    <%= render partial: "shared/header"%>   
    <div class="container-fluid">
      <% flash.each do |message_type, message| %>
        <%= tag.div message,class:"alert alert-#{message_type}" %>
      <% end %>
      #追記箇所#
      <p class="notice"><%= notice %></p>
      <p class="alert"><%= alert %></p>
      #追記終わり#
      <div class="row" id="all-body">
        <div class="col-sm-12 col-md-12 content">
          <%= yield %>
        </div>
      </div>
    </div>
    <%= render partial: "shared/footer"%>
    <%= debug(params) if Rails.env.development? %>
  </body>
</html>

```

## マイグレーション
先ほどのコマンドでマイグレーションファイルが作成されている

```ruby:マイグレーションファイル
# frozen_string_literal: true

class AddDeviseToUsers < ActiveRecord::Migration[5.2]
  #drop_table :users
  create_table :users
  def self.up
    change_table :users do |t|
      ## Database authenticatable
      t.string :email,              null: false, default: ""
      t.string :encrypted_password, null: false, default: ""

      ## Recoverable
      t.string   :reset_password_token
      t.datetime :reset_password_sent_at

      ## Rememberable
      t.datetime :remember_created_at

      ## Trackable
       t.integer  :sign_in_count, default: 0, null: false
       t.datetime :current_sign_in_at
       t.datetime :last_sign_in_at
       t.string   :current_sign_in_ip
       t.string   :last_sign_in_ip

      ## Confirmable
       t.string   :confirmation_token
       t.datetime :confirmed_at
       t.datetime :confirmation_sent_at
       t.string   :unconfirmed_email # Only if using reconfirmable

      ## Lockable
       t.integer  :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts
       t.string   :unlock_token # Only if unlock strategy is :email or :both
       t.datetime :locked_at


      # Uncomment below if timestamps were not included in your original model.
       #t.timestamps null: false
    end

    add_index :users, :email,                unique: true
    add_index :users, :reset_password_token, unique: true
    add_index :users, :confirmation_token,   unique: true
    add_index :users, :unlock_token,         unique: true
  end

  def self.down
    # By default, we don't want to make any assumption about how to roll back a migration when your
    # model already existed. Please edit below which fields you would like to remove in this migration.
    raise ActiveRecord::IrreversibleMigration
  end
end
```

今回ユーザ登録画面でユーザ名を使いたかったので、`rails g migrate`でnameカラムを追加

```console
rails g migration AddNameToUser name:string
rails db:migrate
```

ユーザ名を保存できるように`controllers/application_controller.rb`に追記
-(ToDo)どのような処理をしているのか調べておく
```ruby::controllers/application_controller.rb
class ApplicationController < ActionController::Base
    include SessionsHelper
    before_action :configure_permitted_parameters, if: :devise_controller?

    protected
    def configure_permitted_parameters
        devise_parameter_sanitizer.permit(:sign_up, keys: [:name])
        devise_parameter_sanitizer.permit(:account_update, keys: [:name])
    end
end
```

# Viewの作成
コマンドを実行してviewを作成する

```console
rails g devise:view User
```

作成されたViewを変更できるように`config/initializers/devise.rb`を下記に変更
```ruby:config/initializers/devise.rb
config.scoped_views = true
```

#アラートの日本語化
## config/application.rbに下記追記

```ruby:config/application.rb
config.i18n.default_locale = :ja
```
## viewをもう一回作り直す
```console
rails g devise:i18n:locale ja
rails g devise:i18n:locale en
rm config/locales/devise.en.yml
```

#試してみた
(注意)画像は下手くそなりにcssいじった結果の画面です。
初期表示画面
![2019-04-02 0.54のイメージ.jpg](https://qiita-image-store.s3.amazonaws.com/0/179132/4bf1e441-087f-b756-c3dc-3595a1ea6a16.jpeg)

バリデーション引っかかると
![2019-04-02 0.55のイメージ.jpg](https://qiita-image-store.s3.amazonaws.com/0/179132/45932971-c4b2-56f7-f0ca-49415c8dda35.jpeg)

# deviseで作成したViewに任意のパスでアクセスしたい
- deviseで生成したviewに任意のパスを指定できるようにしたい時は下記を記述
- devise_scopeブロック内でrouteを宣言することで任意のルートを設定可能

```ruby:routes.rb
Rails.application.routes.draw do
  devise_for :users
  devise_scope :user do
    get     'signup' => 'devise/registrations#new' 
    get     'login'  => 'devise/sessions#new'     
    post    'login'  => 'devise/sessions#create'  
    delete 'logout'  => 'devise/sessions#destroy' 
  end
  # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html  
  root "muscle#index"
end
```

# deviseが提供するヘルパーメソッド
| メソッド名 | 機能 |
|:-:|:-:|
| before_action :authenticate_user!  | ログイン済みのユーザに対してアクセスを許可する  |
| user_signed_in?  | ログイン済みユーザーか?  |
| current_user  | ログインしているユーザの情報を取得  |
| user_session  | ユーザーのセッション情報にアクセス  |

# まとめ
- railsチュートリアルでは認証周りは自作しているが、deviseを使うことでより簡単に実装ができる
- css周りは既存のviewに対してclass指定したりしてアレンジする
- sessionの保持時間をいじれるか調べて実装したい