こんにちは,池田(ゆ)です.
今回は,Ruby on Rails で作っている Web アプリケーションの認証管理をについて備忘録を残したいと思います.
やりたいことは,
- アカウント作成をメールよる招待制にしたい
- Google アカウント (OAuth) で認証したい
です!
内輪の Web サービスの認証情報管理を Google (OAuth) に任せたいのですが,誰でもログインできるようにはしたくないので,アカウント作成自体は,メールによる招待制にします.
招待制にすることによって,管理者ユーザが都度アカウントを作る面倒もないので,小さなユルいグループで運用するには便利そうです.
イメージは,こんな感じです.
- 内輪サービスにログイン済みのユーザAさんが,招待機能を使ってメールを送る
- メールを受け取った Bさんは,メール中のリンクをクリックすることで,Google の認証に飛ばされる
- 自分の好きな Google のアカウントで認証し,内輪サービスに認証結果を渡す許可を出す
- 内輪サービスに戻って来て,認証済みであることを確認する
- 内輪サービスのユーザ名登録に進む
ここで,招待状を送る相手Bさんのメールアドレスは,必ずしも Google アカウントのメールアドレスである必要ありません.
4つの gem を利用してこれを実現したため,その方法をまとめます.
今回は,すでに存在する Rails プロジェクトに認証管理を追加することが目的なため,
Rails プロジェクトの土台は出来上がっていることを想定しています.
利用する gem
今回利用する gem はこの4つです.
-
アプリケーションの認証に必要な機能を提供
-
複数のプロバイダを通してログイン認証ができる機能を提供
-
omniauth にて Google認証 を行うための gem
-
devise を使った招待機能を実現するための gem
Gemfile
に以下を追記し,
gem "devise"
gem 'omniauth'
gem 'omniauth-google-oauth2'
gem 'devise_invitable'
以下のコマンドを実行し,gem をインストールしてください.
$ bundle install
devise の導入と利用
1. devise の設定
次に,以下のコマンドを実行して devise をインストールします.
$ bundle exec rails generate devise:install
さらに,デフォルト URL の設定として,config/environments/development.rb
(production の場合は production.rb
) に下記の行を追加します.
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
この設定で,招待メールに含まれる URL のホスト部分を変更できます.production の場合は,運用サイトのホスト名になります.
これで devise の導入は完了です!
2. モデルの生成
次に,devise を適用するモデル (User Model) を生成します.
User モデルは,既に存在していても構いません.
以下のコマンドを実行し User Model を生成します
$ bundle exec rails generate devise user
$ bundle exec rake db:migrate
生成されたマイグレーションファイルを見るとわかりますが,
devise で利用する用のカラムがたくさん追加されています!
次に,生成されたapp/models/user.rb
を見るとこんな感じになっています.
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
end
devise では10個のモジュールが定義されていて,使いたいものをapp/models/user.rb
に追加することでモジュールを利用できるようになります.
デフォルトでは,database_authenticatable
やregisterable
など6個のモジュールが使えるようになっています.
使えるモジュールとそれぞれの内容については,deviseのGitHub を参照してください.
これで devise の導入は完了です!
3. リンクの設置と view の生成
app/views/layouts/application.html.erb
に「Sign up」,「Sign in」,「Sign out」のリンクを設置します.
<% if user_signed_in? %>
<%= link_to "Sign out", destroy_user_session_path, method: :delete, :class => "navbar-link" %>
<% else %>
<%= link_to "Sign up", new_user_registration_path, :class => 'navbar-link' %> |
<%= link_to "Sign in", new_user_session_path, :class => 'navbar-link' %>
<% end %>
user_signed_in?
は devise の helper メソッドです.これにより,サインインしているか否かを判断できます.
ここで,アプリケーションの root が設定されていることを確認しておいてください.
devise ではサインインした後にはアプリケーションの root にリダイレクトするようになっているので,
root が設定されていないとエラーになってしまいます.
config/routes.rb
に以下のような記述があると OK です.
Rails.application.routes.draw do
root :to => "welcome#index"
...
サインイン画面などの view は devise が提供してくれます.
もし見た目が気に入らない!みたいな場合は, まず以下のコマンドを実行し view を生成します.
$ bundle exec rails generate devise:views
これにより,app/views/users/
以下に以下の view が生成されるので,これを編集します.
- ログイン
app/views/devise/sessions/new.html.erb
- ユーザー登録
app/views/devise/registrations/new.html.erb
- ユーザ情報変更
app/views/devise/registrations/edit.html.erb
- パスワード変更
app/views/devise/passwords/edit.html.erb
- メール認証
app/views/devise/confirmations/new.html.erb
- パスワードリセット
app/views/devise/passwords/new.html.erb
- アカウントアンロック
app/views/devise/unlocks/new.html.erb
4. controller の生成
view と同様に,devise は controller も提供してくれています.
独自のメソッドやロジックを考慮したい場合は,view と同様に controller も生成します.
$ bundle exec rails generate devise:controllers users
これにより,app/controllers/users/
以下に controller が生成されます.
controller を生成するときは,生成した controller を見るように,ルーティングを変更する必要があります.
devise_for :users, controllers: {
registrations: 'users/registrations',
confirmations: 'users/confirmations',
passwords: 'users/passwords',
sessions: 'users/sessions',
unlocks: 'users/unlocks',
omniauth_callbacks: "users/omniauth_callbacks"
}
omniauth の導入
1. omniauth を有効化
以下のコマンドを実行し,
$ bundle exec rails g migration add_omniauth_to_users
生成されたマイグレーションファイルに User Model に omniauth に必要なカラムを追加します.
class AddOmniauthToUsers < ActiveRecord::Migration
def change
add_column :users, :provider, :string
add_column :users, :uid, :string
add_column :users, :name, :string
add_column :users, :token, :string
end
end
その後に以下のコマンドを実行して,マイグレーションを完了させます.
$ bundle exec rake db:migrate
次に,app/models/user.rb
に:omniauthable
を追加し,omniauth を有効にします.
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, and :timeoutable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :omniauthable
end
2. Google Oauthの設定
まず, Google Console にアクセスし,アプリケーションのClient ID
とClient Secret
を取得してください.
次に,config/initializers/devise.rb
に取得したClient ID
とClient Secret
を記述します.
config.omniauth :google_oauth2,
'Client_ID',
'Client Secret'
3. リンクの設置
app/views/layouts/application.html.erb
に「Sign in with Google」のリンクを設置します.
<%= link_to 'Sign in with Google', omniauth_authorize_path(:user, :google_oauth2) %>
4. callback の記述
Google認証をし,アプリケーションに帰ってきたときのメソッドを記述する必要があります.
今回は,Google Console に以下のように callback を記述したことを想定します.
http://localhost:3000/users/auth/google_oauth2/callback
次に,app/controllers/users/omniauth_callbacks_controller.rb
に callback 時に実行するメソッドを定義します.
def google_oauth2
auth = request.env["omniauth.auth"] if invitation_token != nil
user = User.where(email: auth.info.email).first
if user
sign_in_and_redirect user, :event => :authentication
else
flash[:error] = "Your google account has not been registered"
redirect_to root_path
end
end
devise_invitation の導入
1. devise_invitable のインストール
まず,以下のコマンドを実行します.
$ bundle exec rails g devise_invitable:install
次に,以下のコマンドを実行し,User Model に invitation に必要なカラムを追加します.
$ bundle exec rails g devise_invitable User
$ bundle exec rake db:migrate
さらに,app/models/user.rb
に :invitable
を追加し,devise_invitation を有効化します.
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, and :timeoutable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :omniauthable, :invitable
end
2. controller の生成
devise_invitation には controller を自動生成するコマンドはないため,app/controllers/users/invitations_controller.rb
を作ります.
class Users::InvitationsController < Devise::InvitationsController
def new
super
end
def create
super
end
def edit
super
end
def update
super
end
def destroy
super
end
end
3. routing の設定
先ほど作成した controller への routing をconfig/routes.rb
に追加します.
devise_for :users, controllers: {
registrations: 'users/registrations',
confirmations: 'users/confirmations',
passwords: 'users/passwords',
sessions: 'users/sessions',
unlocks: 'users/unlocks',
omniauth_callbacks: "users/omniauth_callbacks"
invitations: 'users/invitations' #追記
}