LoginSignup
20
19

More than 3 years have passed since last update.

Docker使ったRails環境でdeviseを導入する手順

Posted at

Dockerのコマンドが写経になってしまうので、各コマンドの意味を理解するために記事化。
Docker上でGemのインストール、DBマイグレート、ルーティングと、一通りRailsでWebアプリケーションを作成する上で必要になる操作に応用できるということで、deviseの導入までの手順を記述します。
※最近の現場はDockerで開発環境を作っていることが多いので、Dockerで一から開発環境作成まで出来た方が捗るかと。
※途中参加のプロジェクトが多いので、Docker自体は触ったことあるけど、最初から作るのはやったことがないな、という人向け(つまり自分需要です)。

開発環境

  • Docker
  • Rails 5.2.4.1
  • Ruby 2.5.3

こちらの記事を参考にして、Railsプロジェクトが起動するところまでは持っていきます。
DockerでRuby on Railsの開発をしよう

Gemインストール

こちらの記事を参照しながら設定開始。
[Rails] deviseの使い方(rails5版)

1.Gemのインストール

Gemfileにdeviseの記述を追加したら、以下のコマンドを叩く。

$ docker-compose exec web bundle install

コマンドの意味を紐解くと、以下の通り。
docker-compose
各コンテナに対してコマンド発行する。詳しくは以下。
Docker Compose

exec web
対象のコンテナにコマンドを送る。
この場合「webコンテナに対し、コマンドを送る」という意味になる。

bundle install
実行されるコマンド

これでGemが入ります。

$ docker-compose build

2.deviseの設定

まずはdeviseをインストール。

$ docker-compose exec web rails g devise:install

「rails g devise:install」のコマンドをwebコンテナに送る。
以下のようなログが流れたら成功。

unning via Spring preloader in process 222
      create  config/initializers/devise.rb
      create  config/locales/devise.en.yml
===============================================================================

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

===============================================================================

3.Userモデル生成

deviseを使用するための画面を生成。

$ docker-compose exec web rails g devise:views

以下のようにログが流れて、生成されたViewが確認できる。

Running via Spring preloader in process 140
      invoke  Devise::Generators::SharedViewsGenerator
      create    app/views/devise/shared
      create    app/views/devise/shared/_error_messages.html.erb
      create    app/views/devise/shared/_links.html.erb
      invoke  form_for
      create    app/views/devise/confirmations
      create    app/views/devise/confirmations/new.html.erb
      create    app/views/devise/passwords
      create    app/views/devise/passwords/edit.html.erb
      create    app/views/devise/passwords/new.html.erb
      create    app/views/devise/registrations
      create    app/views/devise/registrations/edit.html.erb
      create    app/views/devise/registrations/new.html.erb
      create    app/views/devise/sessions
      create    app/views/devise/sessions/new.html.erb
      create    app/views/devise/unlocks
      create    app/views/devise/unlocks/new.html.erb
      invoke  erb
      create    app/views/devise/mailer
      create    app/views/devise/mailer/confirmation_instructions.html.erb
      create    app/views/devise/mailer/email_changed.html.erb
      create    app/views/devise/mailer/password_change.html.erb
      create    app/views/devise/mailer/reset_password_instructions.html.erb
      create    app/views/devise/mailer/unlock_instructions.html.erb

Deviseの画面は作ったので、rootの画面を生成する。
Authコントローラーを生成し、indexとshow画面を作成するためのコマンドを実行。

$ docker-compose exec web rails g controller Pages index show

画面が生成されたら、ログイン画面に相当するindexをrootに設定。
showはログイン後に遷移する画面となるので、こちらもルーティングに記述。

config/routes.rb
Rails.application.routes.draw do
  root 'pages#index'
  get 'pages/show'
end

余談ですが、rootは「page/index」のように記述すると以下のエラーが出て、Dockerが立ち上がらないです。

roller key on routes definition, please check your routes

また、ここまでで生成したルーティングを確認する場合のコマンドはこちら。

docker-compose exec web rails routes

ログイン画面と、ログイン後の画面を作ったので、application.html.erbに「サインインしていなければログイン画面に飛ばす」という設定を施します。

app/views/layouts/appliation.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title>App</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>
    <header>
      <nav>
        <% if user_signed_in? %>
          <strong><%= link_to current_user.username, auth_show_path %></strong>
          <%= link_to 'ログアウト', destroy_user_session_path, method: :delete %>
        <% else %>
          <%= link_to 'ログイン', new_user_session_path %>
        <% end %>
      </nav>
    </header>

    <p class="notice"><%= notice %></p>
    <p class="alert"><%= alert %></p>
    <%= yield %>
  </body>
</html>

次にユーザー管理をするUserモデルを生成。

$ docker-compose exec web rails g devise User

以下のようにログが流れる。

Running via Spring preloader in process 299
      invoke  active_record
      create    db/migrate/20200130213032_devise_create_users.rb
      create    app/models/user.rb
      invoke    test_unit
      create      test/models/user_test.rb
      create      test/fixtures/users.yml
      insert    app/models/user.rb
       route  devise_for :users

app/models/user.rbが生成されるので、ここを編集。
twitter認証を使いたいので、こんな感じ。

user.rb
class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable,
         :omniauthable, omniauth_providers: [:twitter]
end

※認証はtwitter認証以外を通さない設定なので、omiauth以外のモジュールは消して良いと思うのですが、今回はこのまま。

次にTwitter認証で取得されるuidとusernameを保存するカラム、そして将来、他の認証を追加した時に「なんの認証を利用したのか」を保存するproviderカラムをusersテーブルに追加。

$ docker-compose exec web rails g migration add_columns_to_users provider uid username

マイグレーションファイルができるので確認。

20200130214734_add_columns_to_users
class AddColumnsToUsers < ActiveRecord::Migration[5.2]
  def change
    add_column :users, :provider, :string
    add_column :users, :uid, :string
    add_column :users, :username, :string
  end
end

追加するカラムは全て文字列で良いので、変更なし。
DBを変更するため、マイグレーションを実行。

$ docker-compose exec web rails db:migrate

Twitter認証を通す

TwitterのDeveloperセンターで、開発者の登録をして、アプリ作成を申請。
2017年くらいに一回やったことがあったんですが、登録がめっちゃ大変になってました。
手順についてはこちらの記事を参照。
Twitter API 登録 (アカウント申請方法) から承認されるまでの手順まとめ

トークンが発行されたら、コールバック先の登録を行います。
ルーティングを確認し、Twitterに設定。

$ docker-compose exec web rails routes

                         Prefix Verb     URI Pattern                                                                              Controller#Action
               new_user_session GET      /users/sign_in(.:format)                                                                 devise/sessions#new
                   user_session POST     /users/sign_in(.:format)                                                                 devise/sessions#create
           destroy_user_session DELETE   /users/sign_out(.:format)                                                                devise/sessions#destroy
user_twitter_omniauth_authorize GET|POST /users/auth/twitter(.:format)                                                            omniauth_callbacks#passthru
 user_twitter_omniauth_callback GET|POST /users/auth/twitter/callback(.:format)                                                   omniauth_callbacks#twitter                                                           
(略)

確認したルーティングをTwitterのコールバックに列挙します。

スクリーンショット 2020-02-24 7.23.29.png

deviseのconfigにはAPIキーとコールバックを記述。

config/initializers/devise.rb
Devise.setup do |config|
(略)
config.omniauth :twitter, "API Key", "API secret key", "https://localhost:3000/users/auth/twitter/callback"
end

コールバック先を記述しておかないと、raise OAuth::Unauthorized, response 403と怒られます。

コールバックの設定

Twitterからコールバックがきたら受け取るコントローラーの設定をします。
まずはコールバック用のコントーラー作成。

$ docker-compose exec web rails g controller omniauth_callbacks

作られたコントーラーに対して、コールバックメソッドを記述。

omniauth_callbacks_controller.rb
class OmniauthCallbacksController < Devise::OmniauthCallbacksController
  # Twitter認証
  def twitter
    @user = User.from_omniauth(request.env["omniauth.auth"].except("extra"))

    if @user.persisted?
      sign_in_and_redirect @user
    else
      session["devise.user_attributes"] = @user.attributes
      redirect_to new_user_registration_url
    end
  end
end

ポイントはDevise::OmniauthCallbacksControllerを継承すること。
継承することで、Deviseがコールバックで受け取った諸々を使うことができる。

User.from_omniauth(request.env["omniauth.auth"].except("extra"))

「Userモデルのfrom_omniauthに、request.envに入った値を引数で渡して処理してもらう」ということなので、userモデルに以下を実装。

user.rb
class User < ApplicationRecord
  (略)

  def self.from_omniauth(auth)
    find_or_create_by(provider: auth["provider"], uid: auth["uid"]) do |user|
      user.provider = auth["provider"]
      user.uid = auth["uid"]
      user.username = auth["info"]["nickname"]
    end
  end

  def self.new_with_session(params, session)
    if session["devise.user_attributes"]
      new(session["devise.user_attributes"]) do |user|
        user.attributes = params
      end
    else
      super
    end
  end
end

from_omniauthでは、providerとuidの組み合わせで、同一があればupdate、無ければinsertを実行する。

20
19
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
20
19