DeviseとOmniAuthを用いると、メールアドレスを使用することなく認証機能を実装できます。 ここでは、メールアドレスを使わない方法での必要最低限の実装方法について解説します。

動作環境

この記事の内容は、以下のバージョンにより動作を確認しています。

手順

ここでは、プロバイダとしてTwitterを利用する方法について解説します。 Facebookといった他のプロバイダも、同じような手順で追加できます。

1. Gemをインストールする

まず、利用するGemをインストールします。 以下を追記後、bundle installしてください。

Gemfile:

gem 'devise'
gem 'omniauth'
gem 'omniauth-twitter'

2. 設定ファイルを生成する

Deviseで提供されているgenerateコマンドで、Devise用の設定ファイルを生成します。

$ rails generate devise:install
      create  config/initializers/devise.rb
      create  config/locales/devise.en.yml

3. Migrationを生成する

こちらもDeviseが提供しているコマンドで、Modelを追加するMigrationを生成します。

ここでは、例としてUserモデルを追加しています。

$ rails generate devise User
      invoke  active_record
      create    db/migrate/20160615201837_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

4. モデルを編集する

Deviseでは、認証に関する様々な機能がモジュールとして提供されています。

コマンドで生成されたモデルには、6つのモジュールの利用が宣言されています。 そのうち、次のモジュールは使わないため削除します。

  • :database_authenticatable
    • ユーザ認証のためにパスワードをハッシュ化してデータベースに保存する
  • :recoverable
    • パスワードのリセット機能を有効化する
  • :registerable
    • フォームによるサインアップや編集・削除機能を有効化する
  • :validatable
    • メールアドレスとパスワードの組み合わせを検証する

また、:omniauthableモジュールを有効にします。 最終的に、Userモデルは次のような定義になります。

app/models/user.rb:

class User < ActiveRecord::Base
  devise :omniauthable, :rememberable, :trackable
end

5. Migrationを編集する

Migrationにも編集を加えます。 メールアドレスやパスワードといった不要なカラム・インデックスを削除し、代わりにprovideruidを追加します。

class DeviseCreateUsers < ActiveRecord::Migration
  def change
    create_table :users do |t|
      ## Omniauthable
      t.string :provider, null: false
      t.string :uid,      null: false

      ## Rememberable
      t.datetime :remember_created_at
      t.string   :remember_token

      ## 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

      t.timestamps null: false
    end

    add_index :users, [:provider, :uid], unique: true
  end
end

1つ注意として、Rememberableモジュールのためにremember_tokenカラムを追加しています。

Rememberableモジュールは、ユーザのサインイン情報を記憶するためのモジュールですが、通常はpasswordカラムを参照することにより実現しています。 しかし今回はこれを削除しますので、Deviseが代替策として提供しているremember_tokenカラムの作成が必要になります。

詳しくは公式Wikiをご確認ください。

6. Migrationを反映させる

以下のコマンドでデータベースにMigrationを反映させます。

$ rake db:migrate
== 20160615201837 DeviseCreateUsers: migrating ================================
-- create_table(:users)
   -> 0.0036s
-- add_index(:users, [:provider, :uid], {:unique=>true})
   -> 0.0013s
== 20160615201837 DeviseCreateUsers: migrated (0.0050s) =======================

7. コールバック用のコントローラを作成する

プロバイダ(この例ではTwitter)からのコールバック時にユーザの作成・認証を行なうコントローラを実装します。 必要最低限の実装は次のようになると思います。

app/controllers/users/omniauth_callbacks_controller.rb:

class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
  include Devise::Controllers::Rememberable

  def twitter
    auth = request.env['omniauth.auth']

    user = User.find_or_create_by(
      provider: auth.provider,
      uid: auth.uid
    )

    remember_me(user)

    sign_in_and_redirect user, event: :authentication
  end
end

8. ルーティングを編集する

上記で作成したコントローラへのルーティングを設定するために、ルーティングを編集します。 また、OmniAuthを利用する場合、サインアウトへのルーティングをあわせて設定する必要があります。

config/routes.rb:

devise_for :users, controllers: {
  omniauth_callbacks: 'users/omniauth_callbacks'
}

devise_scope :user do
  delete :sign_out, to: 'devise/sessions#destroy', as: :destroy_user_session
end

9. APIキーを設定する

Deviseの設定ファイルに各種APIキーを設定します。

config/initializers/devise.rb:

config.omniauth :twitter, ENV['TWITTER_API_KEY'], ENV['TWITTER_API_SECRET']

動作確認

以上で必要最低限の実装は完了です。 以下のようなViewをもったページを作成し、正しく動作することを確認します。

<% if user_signed_in? %>
  <%= link_to 'Sign out', destroy_user_session_path, method: :delete %>
<% else %>
  <%= link_to 'Sign in', user_twitter_omniauth_authorize_path %>
<% end %>

備考

サインイン用のページを作成する

以上の手順の場合、:authenticate_user!などで認証が必要なアクションに認証なしにアクセスしようとすると、root_pathにリダイレクトします。

これを、例えば専用のサインインページなどに振り分けたい場合は、以下のようにnew_user_session_pathを設定します。

config/routes.rb:

devise_scope :user do
  get :sign_in, to: 'users/sessions#new', as: :new_user_session
  delete :sign_out, to: 'devise/sessions#destroy', as: :destroy_user_session
end

この場合、Users::SessionsController#newを作成し、サインイン用のリンクを含んだページを実装することになると思います。

おわりに

ユーザ認証がOAuthのみで行なえると、ユーザとしてもセキュリティ的に安心ですし、運営者側も不要なリスクを負う必要がなくなります。 必要に応じてぜひ参考にしてください。