Rails 4.1 + devise + omniauth-google-oauth2 で認証機能を実装する


TwitterやFacebookに関するOmniAuthの記事はよくみかけるのですが、Googleについてはあまりないようなので、記事にしておきます。恒例の(?)チュートリアル形式でご紹介します。

環境

  • Rails 4.1.1
  • devise 3.2.4
  • omniauth 1.2.1
  • omniauth-google-oauth2 0.2.4

セットアップ

まずは必要なgemをインストールします。

📄Gemfile
gem 'devise'
gem 'omniauth'
gem 'omniauth-google-oauth2'
📄db/migrate/add_omniauth_to_users.rb
$ bundle install
$ rails g devise:install
$ rails g devise user
$ rails g migration add_omniauth_to_users

deviseが作成してくれたUserモデルに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

さっそくDBを作成しましょう。

📄app/models/user.rb
$ rake db:migrate

deviseではデフォルでOmniAuthは有効になっていませんので app/models/user.rb 編集します。deviseメソッドの末尾に :omniauthable の記述を追記するだけです。

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

最後に、deviseの起動時の設定です。config/initializers/devise.rb に次の記述を追記します。APP_ID、APP_SECRETはまだ取得していないので、とりあえずこの文字列で定義しておきましょう。

📄config/initializers/devise.rb
  # ==> OmniAuth
  # Add a new OmniAuth provider. Check the wiki for more information on setting
  # up on your models and hooks.
  # config.omniauth :github, 'APP_ID', 'APP_SECRET', scope: 'user,public_repo'
  config.omniauth :google_oauth2, 'APP_ID', 'APP_SECRET'

ひとまず、初期設定はこれで完了です。

ログインしてみる

任意のページに以下のリンクを表示してみましょう。

📄任意のview.html.erb
<%= link_to 'Signin with Google', user_omniauth_authorize_path(:google_oauth2) %>

ここまでの設定が正しくできているなら、次のようなリンクが表示されるはずです。

link

うん、いい感じ。ノリでこのリンクをクリックしてみると・・・

401

英語が読めない人でもエラーだなと分かる素敵な画面が表示されました。”Invalide_client”らしいです。そりゃそうだ、まだclient_idが”APP_ID”のままなのですから。

ClientIDの取得

OmniAuth認証を利用するには、GoogleにてクライアントIDを取得する必要があります。次のURLにアクセスしてください。

Google Developers Console

[CREATE PROJECT]というリンクをクリックします。

Google Developers Console

任意のPROJECT NAME、PROJECT IDを入力します。

create project

しばらく待つと新しいプロジェクトが完成し自動的にプロジェクトのトップページに誘われます。

画面左のメニューから【APIs】を選択し、必要なAPIを選択します。『Contacts API』『Google+ API』の2つをONにしましょう。

APIs

続いてCredentialsの画面に行きます。【CREATE NEW CLIENT ID】ボタンをクリックしましょう。

Credentials

なんだかよく分かんないダイアログが出てきたぞ・・・。APPLICATION TYPEは『Web application』で間違いないですね。しかしあとはよく分かんないので、このまま【Create Client ID】をクリックしてしまいましょう!

ClientID

Client ID for web applicationというセクションが追加されました。どうやら完成したようですな。次の『Client ID』と『Client Secret』をコピーします。

Copy

この値を先程の config/initializers/devise.rb にペースト・・したいところですが、直接ここに指定してしまうとRAILS_ENV環境毎に切り替えられず不便です。Rails 4.1から使用できる config/secrets.yml に定義してみましょう。

📄config/secrets.yml
development:
  secret_key_base: XXXXXXXXXXXXXXXXX
  google_client_id: 取得した Client ID
  google_client_secret: 取得した Client Secret

そして config/initializers/devise.rb を編集します。

📄config/initializers/devise.rb
  config.omniauth :google_oauth2,
                  Rails.application.secrets.google_client_id,
                  Rails.application.secrets.google_client_secret

Railsを再起動してから、先程の『Signin with Google』を再度クリックしてみましょう。

400

チクショー! また怒られた! “redirect_url_mismatch”だそうです。GoogleからリダイレクトするURLを許可してやらねばなりません。再びGoogle Developers Consoleを開き、『Credentials』から【Edit settings】ボタンをクリックします。

Edit_settings

下のボックス『AUTHORIZED REDIRECT URI』に、先程エラーの出ていたURL”http://localhost:3000/users/auth/google_oauth2/callback”を入力します。

AUTHORIZED REDIRECT URI

再びログインにチャレンジすると・・・

Login成功

やった! ついに成功したか?! しかし・・何この『Project Default Service Account』ってダサい名前? ここは自分のアプリ名に変更しましょう。またまたGoogle Developers Consoleに戻り、左のメニューから『Consent screen』を選択します。

Consent_screen

どうやらこの画面でアプリの設定をできるようですね。『PRODUCT NAME』を変更すれば先程の表示が変わるはずです。その他ロゴ画像なんかも指定できるようです。

適当な名前に変更したら再々ログイン。Googleの『許可のリクエストページ』のアプリケーションの名前が変更されていると思います。それでは【承認する】をクリックしてみましょう。

Unknown action

エラーです。しかしこのエラーはRails側のエラーですので、Googleの設定はこれでOKということです。rails側の実装に移りましょう。

Rails側を実装する

まずはルーティングを定義します。devise :users の箇所を次のように変更します。

📄config/routes.rb
  devise_for :users, controllers: {
    omniauth_callbacks: "users/omniauth_callbacks"
  }

続いて、コールバックで呼び出されるコントローラーを定義します。app/controllers/users ディレクトリを作成し、その中にomniauth_callbacks_controller.rb というコントローラーを作成します。

📄app/controllers/users/omniauth_callbacks_controller.rb
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
  def google_oauth2
    @user = User.find_for_google_oauth2(request.env["omniauth.auth"])

    if @user.persisted?
      flash[:notice] = I18n.t "devise.omniauth_callbacks.success", :kind => "Google"
      sign_in_and_redirect @user, :event => :authentication
    else
      session["devise.google_data"] = request.env["omniauth.auth"]
      redirect_to new_user_registration_url
    end
  end
end

ここで一度、試しにログインしてみましょう。 正しく実装できているなら、次のエラー画面が表示されるはずです。

NoMethodError

先程のコールバックから呼び出している User.find_for_google_oauth2 が見つからないというエラーです。まだ実装してないので、当然ですなんですけどね。。。さっそく実装しましょう。

📄app/models/user.rb
class User < ActiveRecord::Base
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, and :timeoutable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable, :omniauthable

  def self.find_for_google_oauth2(auth)
    user = User.where(email: auth.info.email).first

    unless user
      user = User.create(name:     auth.info.name,
                         provider: auth.provider,
                         uid:      auth.uid,
                         email:    auth.info.email,
                         token:    auth.credentials.token,
                         password: Devise.friendly_token[0, 20])
    end
    user
  end
end

self.find_for_google_oauth2 メソッドを追加しました。Googleから送られてくる認証情報のemailをキーに、データベースを検索します。無ければ新規ユーザーということで、ユーザーを追加してから返すという仕様です。認証情報authってどんな内容なんだよ?って方は、https://github.com/zquestz/omniauth-google-oauth2 にAuthHashのサンプルが掲載されていますのでそちらを確認するとよいでしょう。

さて、今一度ログインしてみると・・・

top

おおっと! ルートURLにリダイレクトされたぞ。これはもう成功しているとしか思えない。念のため&後学のため、ログイン成功したときにリダイレクトするパスを変更して、その画面にユーザー情報を表示させて確認してみましょう。config/routes.rb を編集します。

📄config/routes.rb
  devise_for :users, controllers: {
    omniauth_callbacks: "users/omniauth_callbacks"
  }
  get "pages/user_info", as: "user_root"

4行目を追記しました。ユーザーのルートURLを定義しています。pages/user_info は任意のパスで構いません。最後に、viewを作成し、ユーザー情報を表示させてみます。

📄app/views/pages/user_info.html.erb
<p><%= current_user.name %></p>
<p><%= current_user.provider %></p>
<p><%= current_user.uid %></p>
<p><%= current_user.email %></p>

さて、上手く表示できるでしょうか? 最後のログインに向かいましょう!

login成功

(注意:実際の画面にモザイクはかかっていません) ビンゴぉぉ! 無事ログインに成功したといえよう!! 少々の設定のみでOmniAuth認証が実現しました。便利な時代になったもんですねえ。

その他細かいことは次の参考URLなりGoogle先生なりで漁ってみてください。それではナイスなRailsライフを!陰ながらお祈りしております。

参考URL

関連する記事