deviseとomniauthを使ったGoogle認証の流れ

deviseとOmniAuthを使ったログインを実現する手続きを紹介した記事は多いものの、ログイン後、Google APIを使ってデータを取得する際に必要となるrefresh_tokenについて書かれたページがあまり見つからず、複数の記事の内容をピックアップしながら実装する必要に迫られたので、その時に参照した内容をまとめました。

***
* 環境
 - ruby 2.3.1p112
 - Rails 4.2.6

***

#1.下準備

必要なgemを`Gemfile`に追記してインストールします。

``` ruby:Gemfile
gem 'devise'
gem 'omniauth'
gem 'omniauth-google-oauth2'
```

```
$ bundle install
```

`devise`のセットアップと、`devise`で`User`モデルを作成します。

```
$ rails g devise:install
$ rails g devise user
$ rails g migration add_omniauth_to_users
```

#2.データベースの用意

`devise`が作成した`User`モデルに`OmniAuth`で必要となるカラムを追加します。

```ruby:db/migrate/add_omniauth_to_users.rb
class AddOmniauthToUsers < ActiveRecord::Migration
  def change
    add_column :users, :provider, :string
    add_column :users, :uid, :string
    add_column :users, :name, :string
    add_column :users, :refresh_token, :string
    add_column :users, :access_token, :string
  end
end
```

データベースを作成します。

```
$ rake db:create
$ rake db:migrate
```

#3.OmniAuthの設定など

`devise`では、デフォルトで`OmniAuth`が有効になっていないことから、`app/models/user.rb`を編集して有効にします。

```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,:omniauthable,
         :omniauthable, :omniauth_providers => [:google_oauth2]

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

`devise`の起動時の設定を行うため、`config/initializers/omniauth.rb`ファイルを作成して次のとおり編集します。[^1]

```ruby:config/initializers/omniauth.rb
Rails.application.config.middleware.use OmniAuth::Builder do
  provider :google_oauth2,
    Rails.application.secrets.google_client_id,
    Rails.application.secrets.google_client_secret,
    {
# ログイン後にGoogle Calendarのデータを取得したいので、scopeに
# https://www.googleapis.com/auth/calendarを記述しています。
# また、promptとaccess_typeを以下の設定にするとrefresh_tokenが得られる
# （その他の組み合わせは試していません）。
      scope: "https://www.googleapis.com/auth/userinfo.email,
              https://www.googleapis.com/auth/userinfo.profile,
              https://www.googleapis.com/auth/calendar",
      prompt: "select_account",
      access_type: "offline"
    }
end
```

Google API Console で割り当てられたクライアントIDなどを`config/secret.yml`に記述します。[^2] なお、クライアントIDの割り当て等を含めたGoogle APIの設定については、以下のページが参考になります。
[googleからのログイン実装 - Qiita](http://qiita.com/ttaka66/items/9ea3052a6f17a0b8f5fc)

```ruby:config/secret.yml
google_client_id: #[google api console]で割り当てられたクライアントIDを記入
google_client_secret: #[google api console]で割り当てられたクライアントシークレットを記入
```

`app/controllers/users/omniauth_callbacks_controller.rb`を編集する。

```ruby: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

```

ログイン用のリンクを表示する画面とログイン後に表示する画面を作成するため、以下のコマンドを実行し、作成されたファイルを以下のとおり編集します。

```
$ rails g controller welcome  #コントローラー名は自由
$ rails g controller user
```

```ruby:app/controllers/welcome_controller.rb
class WelcomeController < ApplicationController
  def index
  end
end
```

```ruby:app/controllers/user_controller.rb
class UserController < ApplicationController
  def user_cal
  end
end
```


`app/views/welcome/index.html.erb`と`app/views/user/user_cal.html.erb`を作成して以下のとおり編集します。

```html+erb:app/views/welcome/index.html.erb
<h1>Google OAuth2 Test</h1>

<p>app/views/welcome/index.html.erb</p>

<%= link_to "Sign in with Google", user_google_oauth2_omniauth_authorize_path %>
```

```html+erb:app/views/user/user_cal.html.erb
<h1><%= current_user.name %>のカレンダー</h1>
<hr>
# 実際のカレンダー表示部分は後ほど作成
```

`route.rb`を以下のとおり編集してルーティングを設定します。

```ruby:route.rb
Rails.application.routes.draw do
  devise_for :users, controllers: {
    :omniauth_callbacks => "users/omniauth_callbacks",
  }
  get 'user/cal', as: 'user_root'  #ログイン後に表示する画面の設定
  root to: 'welcome#index'
#以下省略
```

#4.実際にログインしてみる
これで`http://localhost:3000`にアクセスすると、以下の画面が表示されるので、*Sign in with Google*をクリックします。
![Gcaltest_-_top.png](https://qiita-image-store.s3.amazonaws.com/0/136890/44b488e1-9653-d203-f012-5f0dd6e09068.png)

Googleの認証画面が表示されるので、許可をクリックします。
![Gcaltest_-_authorization.png](https://qiita-image-store.s3.amazonaws.com/0/136890/8f9a550f-3478-cec1-d95b-09e13613d5be.png)

この画面が表示されるはず。
![Gcaltest_-_cal.png](https://qiita-image-store.s3.amazonaws.com/0/136890/ddcdaf9a-98f5-0773-bb8c-42955a5fda47.png)

また、作成しておいた`users`テーブルに、`refresh_token`と`access_token`が保存されているはず。
![sequelpro-screenshot_Fotor.jpg](https://qiita-image-store.s3.amazonaws.com/0/136890/ca7531e0-88d3-83fc-d7ab-209d79a8d92c.jpeg)

***
参考にしたウェブサイト
[GitHub - plataformatec/devise: Flexible authentication solution for Rails with Warden.](https://github.com/plataformatec/devise#configuring-views)
[GitHub - zquestz/omniauth-google-oauth2: Oauth2 strategy for Google](
https://github.com/zquestz/omniauth-google-oauth2)
[googleからのログイン実装 - Qiita](
http://qiita.com/ttaka66/items/9ea3052a6f17a0b8f5fc)
[devise に関する routes まとめ - Qiita](
http://qiita.com/masarakki/items/52751a49f9ec487169e4)
[Rails 4.1 + devise + omniauth-google-oauth2 で認証機能を実装する – Oh My Enter!](http://www.ohmyenter.com/rails-4-1-devise-omniauth-google-oauth2-%e3%81%a7%e8%aa%8d%e8%a8%bc%e6%a9%9f%e8%83%bd%e3%82%92%e5%ae%9f%e8%a3%85%e3%81%99%e3%82%8b/)
[omniauth-google-oauth2でハマったメモ - blog.takuyan.com](
http://blog.takuyan.com/posts/2013/06/14/insufficientpermissions-error-in-omniauth-google-oauth2/) [^3]

***
[^1]: なお、`config/initializers/devise.rb`を編集すると紹介するページもあるが、自分の環境では、そちらに記述するとログインはできるが、ログイン後にGoogleから返ってくるjsonデータに`refresh_token`が含まれなかった。
[^2]: Googleから割り当てられたクライアントIDなどを直接`config/initializers/omniauth.rb`に書き込むと、RAILS_ENV環境毎に切り替えられない。
[^3]: 二段階認証しているユーザーは`refresh_token`がもらえないかもしれないとあるが、自分の環境では`refresh_token`が発行された。