Google認証

[activeadmin](https://github.com/activeadmin/activeadmin)の[Wiki](https://github.com/activeadmin/activeadmin/wiki/Log-in-through-OAuth-providers)に書いてある通りだと、動作しなかったため、**Google認証を導入する手順**をまとめてみました。利用したgemは、[omniauth-google-oauth2](https://github.com/zquestz/omniauth-google-oauth2)となります。

## 環境

- Rails 5.2.3
- activeadmin 1.0.0.pre4
- devise 4.2.0

## 導入手順

※ rails new..activeadminのインストール手順等は省略します。

### 1. `omniauth-google-oauth2` をGemfileに追加して `bundle`

### 2. AdminUserに `:omniauthable` モジュールを有効化

```rb:app/models/admin_user.rb
class AdminUser < ActiveRecord::Base
  # パスワードでの認証を廃止するため :recoverable, :rememberable は削除（共用させる場合は残す）
  devise :database_authenticatable, :omniauthable, :trackable, :validatable
end
```

### 3. devise に omniauth の設定を追加

```rb:config/initializers/devise.rb
config.omniauth :google_oauth2, ENV['GOOGLE_CLIENT_ID'], ENV['GOOGLE_CLIENT_SECRET'], scope: "email"
```

### 4. `dotenv-rails` をGemfileに追加して `bundle`

```rb:Gemfile
gem 'dotenv-rails', require: 'dotenv/rails-now'
```

### 5. `.env` に 必要な環境変数を設定

```sh:.env
GOOGLE_CLIENT_ID=**********YOUR_CLIENT_ID**********
GOOGLE_CLIENT_SECRET=**********GOOGLE_CLIENT_SECRET**********
```

クライアントIDとクライアントシークレットの設定手順は、[こちら](https://github.com/zquestz/omniauth-google-oauth2#google-api-setup)に書いてある通りです。

ただし、**リダイレクトURIは `http://127.0.0.1:3000/admin/auth/google_oauth2/callback`に設定する必要**があります（`localhost`だと正しくリダイレクトされませんのでご注意ください）

### 6. マイグレーションの作成と実行

```sh
$ bundle exec rails g migration change_columns_in_admin_users
```

```rb:db/migrate/YYYYMMDDHHMMSS_change_columns_in_admin_users.rb
class ChangeColumnsInAdminUsers < ActiveRecord::Migration
  def change
    add_column :admin_users, :provider, :string
    add_column :admin_users, :uid,      :string

    add_index :admin_users, [:provider, :uid], unique: true

    # パスワード認証を廃止するため、以下は削除（共存させる場合は残す）
    remove_column :admin_users, :reset_password_token,   :string
    remove_column :admin_users, :reset_password_sent_at, :datetime
    remove_column :admin_users, :remember_sent_at,       :datetime
  end
end
```

```sh
$ bundle exec rake db:migrate
```

### 7. AdminUserにAuthHashに対応するメソッドを追加

```rb:app/models/admin_user.rb
class AdminUser < ActiveRecord::Base

  devise :database_authenticatable, :omniauthable, :trackable, :validatable

  with_options presence: true do
    validates :email # whitelistでフィルタする場合、inclusionやformatのvalidationを追加
    validates :provider
    validates :uid, uniqueness: { scope: :provider }
  end

  # こちらは、ほぼ公式wiki通りに書いているが、パスワード認証しないなら、常時falseでよい
  # Devise override to ignore the password requirement if the user is authenticated
  def password_required?
    return false if provider.present?
    super
  end

  class << self
    def from_omniauth(auth)
      admin_user = where(email: auth.info.email).first || where(auth.slice(:provider, :uid).to_h).first || new
      admin_user.tap { |this| this.update_attributes(provider: auth.provider, uid: auth.uid, email: auth.info.email) }
    end
  end
end
```

### 8. コールバックを受けるコントローラの実装

```rb:app/controllers/admin_users/omniauth_callbacks_controller.rb
class AdminUsers::OmniauthCallbacksController < Devise::OmniauthCallbacksController

  def all
    admin_user = AdminUser.from_omniauth(auth_hash)

    if admin_user.persisted?
      flash.notice = "Signed in!"
      sign_in_and_redirect admin_user
    else
      flash.notice = "We couldn't sign you in because: " + admin_user.errors.full_messages.to_sentence
      redirect_to new_admin_user_session_url
    end
  end

  # providerを追加したら、aliasも追加
  # 追加予定ないなら、#all => #google_oauth2 に変更する感じ
  alias_method :google_oauth2, :all

  private

  def auth_hash
    request.env["omniauth.auth"]
  end
end
```

### 9. ルーティングでomniauth_callbacksの際に、利用するコントローラをassign

```rb:config/routes.rb
Rails.application.routes.draw do
  devise_config = ActiveAdmin::Devise.config
  devise_config[:controllers][:omniauth_callbacks] = 'admin_users/omniauth_callbacks'
  devise_for :admin_users, devise_config
  ActiveAdmin.routes(self)
  #...
```

### 10. ログインフォームからパスワードフォームを撤去

テンプレートは、activeadminの[元ファイル](https://github.com/activeadmin/activeadmin/blob/master/app/views/active_admin/devise/sessions/new.html.erb)から拝借

```erb:app/views/active_admin/devise/sessions/new.html.erb
<div id="login">
  <h2><%= render_or_call_method_or_proc_on(self, active_admin_application.site_title) %> <%= title t('active_admin.devise.login.title') %></h2>
  <div style="text-align: center">
    <%- if devise_mapping.omniauthable? %>
      <%- resource_class.omniauth_providers.each do |provider| %>
        <%= button_to(
              t('active_admin.devise.links.sign_in_with_omniauth_provider', provider: provider.to_s.titleize),
              omniauth_authorize_path(resource_name, provider)) %><br />
      <% end -%>
    <% end -%>
  </div>
</div>
```

:older_man: **おつかれさまでした** :older_man:

これで、Googleアカウントで認証できるようになります。

#### 認証前

![](https://qiita-image-store.s3.amazonaws.com/0/132049/5f433405-be4f-571a-af46-eb72e40d68cc.png)

![](https://qiita-image-store.s3.amazonaws.com/0/132049/4fada40b-8644-2097-a243-2007dedbd9e4.png)

#### 認証後

![](https://qiita-image-store.s3.amazonaws.com/0/132049/70b64df0-eeb0-2e2a-b01b-7427ebebebad.png)