ウェブアプリ開発日誌:Rails5でユーザ登録機能を実装するまでのあれこれ
Railsアプリにユーザ登録・認証を実装するまでの手順を書きます。ここでいうユーザ登録は、よくあるユーザ名とメールアドレスを入力して届いた認証リンクにアクセスするとアクティベートされるタイプのやつです。
環境
rails new
プロジェクトディレクトリを作ります。今回はデータベースにMySQLを使うのと、テストにはRSpecを使いたいのでデフォルトのテストは外しておきました。
rails new myapp -T -B -d mysql
付与したオプションは次の通り。
-T
デフォルトのテストコード生成を外す-B
生成直後のbundleをスキップする-d mysql
データベースをMySQLに変更する(デフォルトはPostgreSQL)
deviseを導入する
deviseはrailsでユーザ登録・認証機能を実装するgemです。正直ユーザ機能つけるならこれ一択みたいなところがあって、他にもないわけではないですがOAuthやらAPI作成やらとの連携も最も進んでいてわざわざ他のを選ぶ理由がないという感じです。
Gemfileに認証系のgemを追加。
gem 'devise' gem 'omniauth' gem 'omniauth-twitter'
bundle install
後、bundle exec rails g devise:install
でdeviseの設定ファイルを生成します。具体的にはconfig/initializers/devise.rb
と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
これを一つ一つ順番に実行していきましょう。
Userモデルの作成
認証に用いるモデルを作成します。名前はなんでも構いませんが、特に理由がなければUser
が無難だと思います。
$ rails g devise User
できたモデルファイルを変更します。
class User < ApplicationRecord # Include default devise modules. Others available are: # :confirmable, :lockable, :timeoutable and :omniauthable devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable, :confirmable, :timeoutable, :omniauthable, omniauth_providers: [:twitter] end
また、マイグレーションファイルからコメントアウトをいくつか外してカラムを追加します。
class DeviseCreateUsers < ActiveRecord::Migration[5.0] def change create_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 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 end
今回はlockableを省きましたが、アカウントのロック機能を利用する場合はこれもコメントアウトを外します。
figaroを導入
omniauthでtwitter認証を導入するのはいいですがAPIのトークンやシークレットをそのままコードに書くのはダメです。あとで消すつもりでも、うっかりコミットしてしまったりしたら最悪です。 運用では環境変数にこの手のものを指定するのが定石ですが、開発環境でいちいち変数をexportするのも面倒なのでfigaroというgemを使います。 これは変数をファイルに書き出しておいて、それを環境変数として読み込んでくれるやつです。したがって、開発環境ではgitignoreでバージョン管理から外れたファイルで、本番では環境変数で指定といった使い分けができます。
gem 'figaro'
bundle install
したらfigaroで必要になるファイルをインストールしましょう。
bundle exec figaro install
これでconfig/application.yml
というファイルが追加され、.gitignore
にそれが追記されたはずです。このapplication.ymlが環境変数を指定するためのファイルになります。
twitter認証を追加する
Twitter Application Managementにアクセスしてアプリケーションの登録を行います。
APIトークンとシークレットを設定する
入手したトークンとシークレットをapplication.ymlに記述し、それを環境変数として読み込めるようにします。
development: TWITTER_API_KEY: '<token>' TWITTER_API_SECRET: '<secret>'
config/initializers/devise.rb
にomniauthの設定を記述します。figaroが設定した環境変数を読み出すための初期化コードです。
Devise.setup do |config| config.omniauth :twitter, ENV['TWITTER_APP_ID'], ENV['TWITTER_APP_SECRET'] end
これでapplication.ymlを別に共有しておけば開発環境でいちいち変数を設定する手間が省けますし、環境別にファイルを分けたりすることもできます。このファイルの共有方法に関しては議論の余地がありますが、少なくともバージョン管理に含めることが間違いなのは明らかなのでそこからの漏洩リスクはこれでどうにかなりました。
OAuth用のマイグレーションを作成する
ここまでやったらあとはbundle exec rake db:migrate
を実行します。これでデータベースに認証に必要なテーブルができました。
callback用のコントローラを作る
twitterアプリ作成時に指定したコールバックURLを処理できるようにコントローラを作ります。
$ rails g controller omniauth_callbacks
omniauth_callbacks_controller.rb
を以下のように変更する。
class OmniauthCallbacksController < Devise::OmniauthCallbacksController 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