34
35

More than 1 year has passed since last update.

deviseとOmniAuthをRailsアプリケーションに導入しテストする

Last updated at Posted at 2020-01-31

Ruby on Rails のアプリケーションに、 devise を利用して認証機能を実装し、更に Google アカウントに連携した認証機能とテストコードを実装します。よくありがちなユーザー認証機能をdeviseを使いサクッと実装し、Googleを使ったSNS認証機能とテストコードを実装する方法について解説していきます。

動作環境

  • PostgreSQL 12.1
  • macOS Catalina
  • Ruby 2.7.0
  • Rails 6.0.7.2

全体の概要

本記事では、3つのステップで進めていきます。

  1. devise でサインイン機能を実装する
  2. OmniAuth で Google アカウント機能を実装する
  3. Omniauth のテストコードを実装する

1. deviseでサインイン機能を実装する

まずは、devise でサインイン機能を実装し、deviseとはどのような機能を持つGem(ライブラリ)なのかを掴みましょう。

本項は下記を参考にしました。

deviseとは

まずはじめに、deviseについて解説します。deviseとはRailsアプリケーションに柔軟な認証機能を追加するためのライブラリです。
アプリケーションコードをほとんど書かなくても、認証に必要な機能を追加でき、とても便利です。

deviseを理解するには、下記の点を抑えておくと良いでしょう。

  • Rails エンジンの仕組みを利用している
  • 認証機能毎にモジュールがある
  • 導入するだけで利用できるメソッドがある

Railsエンジンとは

deviseはRailsエンジンの仕組みを使ったGemだと説明しましたが、Railsエンジンとは何でしょうか?

エンジン (engine) は、ホストとなるRailsアプリケーションに機能を提供するミニチュア版Railsアプリケーションとみなせます。この場合、ホストとなるRailsアプリケーションは、実際にはエンジンに「ターボをかけた」ようなものにすぎず、Rails::ApplicationクラスはRails::Engineから多くの振る舞いを継承します。
すなわち、エンジンとアプリケーションは、細かな違いを除けばほぼ同じものであると考えていただいてよいでしょう。
とりあえずは Rails エンジンが、基本的な構成が Railsのアプリケーションと同様、ということをとりあえずは抑えておけばよいでしょう。

モジュール一覧

deviseは認証機能毎に複数のモジュールに分かれています。認証と言ってもひつような機能はさまざまで、「ユーザーを新規に登録する」や「パスワードを忘れたらリセットする」、「メールアドレスにバリデーションを追加する」などの機能によって、モジュールがそれぞれ用意されているのです。モジュールの一覧は下記なので、ざっと目を通しておく程度で大丈夫です。

  • Database Authenticatable
    • ユーザー情報をハッシュにして、データベースに保存する
  • Omniauthable
    • Omniauth を使い外部サービスと連携したログインさせる
  • Confirmable
    • サインイン時にemailを送信して、アカウントがemailによる認証済みかを検証する
  • Recoverable
    • パスワードをリセットし、リセット用のURLを発行する
  • Registable
    • ユーザーが自身の情報の登録・編集・削除を可能にする
  • Rememberable
    • 保存されたCookie(PCに保存されるログイン情報のこと)から、リメンバーミー(ブラウザを閉じてもログインが有効)を可能にする
  • Trackable
    • サインインの回数やサインインのタイムスタンプ、IPアドレスなどの情報を保存する
  • Timeoutable
    • ログインから一定期間たったユーザーをタイムアウトさせる
  • Validatable
    • メールアドレスとパスワードのバリデーションを追加する
  • Lockable
    • 指定した回数ログインを失敗するとロックする

devise で使えるメソッド

deviseは導入するだけで使えるヘルパーメソッドがいくつか用意されています。
例えば、user_singed_in?メソッドなどは、地道に実装しようとするとなかなかのコード行数になりますので、あらかじめ準備されていてとても便利です。

用意されているメソッドは、下記があります。

メソッド 用途
before_action :authenticate_user! ユーザーが認証(ログイン済み)の場合のフィルター
user_signed_in? ユーザーがサインインしているかをの真偽値を返す
current_user サインインしているユーザーを取得する
user_session ユーザーのセッション情報にアクセスできる

上記は User モデルの場合を記していますが、Memberモデルの場合は、下記のようになります。

before_action :authenticate_member!

member_signed_in?

current_member

member_session

アプリケーションを新規作成する

データベースに PostgreSQL を指定して、アプリケーションを作成します。作成できたら、データベースを作成しましょう。

$ mkdir devise
$ cd devise
$ rails new . -d postgresql
$ rails db:create

devise のセットアップをする

devise を追加する

次に、Gemfileにdeviseを追加します。
devise は開発環境、テスト環境、プロダクション環境すべての環境で利用するので、Gemfileの一番下に追加します。

Gemfile
gem 'devise'

Gemfile に追加したら下記のコマンドを実行しインストールします。

$ bundle install

devise をインストールする

次に、deviseの機能をRailsアプリケーションにインストールします。

この、インストールしに生成された2つのファイルと、セットアップ用のコメントは非常に重要なため、このあと詳しく見ていきます。

$ rails g devise:install

      create  config/initializers/devise.rb
      create  config/locales/devise.en.yml
===============================================================================

Some setup you must do manually if you haven't yet:

  1. Ensure you have defined default url options in your environments files. Here
     is an example of default_url_options appropriate for a development environment
     in config/environments/development.rb:

       config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }

     In production, :host should be set to the actual host of your application.

  2. Ensure you have defined root_url to *something* in your config/routes.rb.
     For example:

       root to: "home#index"

  3. Ensure you have flash messages in app/views/layouts/application.html.erb.
     For example:

       <p class="notice"><%= notice %></p>
       <p class="alert"><%= alert %></p>

  4. You can copy Devise views (for customization) to your app by running:

       rails g devise:views

===============================================================================

設定ファイルを確認する

config/initializers/devise.rbは devise の各機能ごとの設定を記載するためのファイルです

翻訳ファイルを確認する

config/locales/devise.en.yml は devise で用いるアプリケーションのビューのテキスト表示を記載するためのファイルです

ActionMailerの設定を追加する

1. Ensure you have defined default url options in your environments files. Here
   is an example of default_url_options appropriate for a development environment
   in config/environments/development.rb:

     config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }

   In production, :host should be set to the actual host of your application.
config/environments/development.rb
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }

ルーティングを追加する

2. Ensure you have defined root_url to *something* in your config/routes.rb.
   For example:

     root to: "home#index"

ルートのURLを定義するように言われているので、config/routes.rbに定義します。ここでは、Topsコントローラーのindexアクションを紐付けます。

Gemfile
root to: 'top#index'

flashメッセージを追加する

3. Ensure you have flash messages in app/views/layouts/application.html.erb.
   For example:

     <p class="notice"><%= notice %></p>
     <p class="alert"><%= alert %></p>
app/views/layouts/application.html.erb
<body>
  <p class="notice"><%= notice %></p> # 追記
  <p class="alert"><%= alert %></p> # 追記
  <%= yield %>
</body>

ビューファイルを作成する

4. You can copy Devise views (for customization) to your app by running:

     rails g devise:views

devise に用意されたgenerateコマンドを利用して、viewを作成します。

$ bin/rails g devise:views

作成されたviewを見るとおわかりかと思いますが、認証に必要な一通りのviewが一度に作成できてしまいます。素晴らしい。

Running via Spring preloader in process 72079
      invoke  Devise::Generators::SharedViewsGenerator
      create    app/views/devise/shared
      create    app/views/devise/shared/_error_messages.html.erb
      create    app/views/devise/shared/_links.html.erb
      invoke  form_for
      create    app/views/devise/confirmations
      create    app/views/devise/confirmations/new.html.erb
      create    app/views/devise/passwords
      create    app/views/devise/passwords/edit.html.erb
      create    app/views/devise/passwords/new.html.erb
      create    app/views/devise/registrations
      create    app/views/devise/registrations/edit.html.erb
      create    app/views/devise/registrations/new.html.erb
      create    app/views/devise/sessions
      create    app/views/devise/sessions/new.html.erb
      create    app/views/devise/unlocks
      create    app/views/devise/unlocks/new.html.erb
      invoke  erb
      create    app/views/devise/mailer
      create    app/views/devise/mailer/confirmation_instructions.html.erb
      create    app/views/devise/mailer/email_changed.html.erb
      create    app/views/devise/mailer/password_change.html.erb
      create    app/views/devise/mailer/reset_password_instructions.html.erb
      create    app/views/devise/mailer/unlock_instructions.html.erb

User モデルを作成する

$ rails g devise User

マイグレーションファイルを確認、実行する

マイグレーションファイルには、deviseの各機能(module)ごとにカラムが記載されています。デフォルト設定のものはコメントアウトが外されており、デフォルトにない設定のものはコメントアウトされています

# frozen_string_literal: true

class DeviseCreateUsers < ActiveRecord::Migration[6.0]
  def change
    create_table :users do |t|
      ## Database authenticatable
      t.string :email,              null: false, default: ""
      t.string :encrypted_password, null: false, default: ""

      ## Recoverable
      t.string   :reset_password_token
      t.datetime :reset_password_sent_at

      ## Rememberable
      t.datetime :remember_created_at

      ## Trackable
      # t.integer  :sign_in_count, default: 0, null: false
      # t.datetime :current_sign_in_at
      # t.datetime :last_sign_in_at
      # t.inet     :current_sign_in_ip
      # t.inet     :last_sign_in_ip

      ## Confirmable
      # t.string   :confirmation_token
      # t.datetime :confirmed_at
      # t.datetime :confirmation_sent_at
      # t.string   :unconfirmed_email # Only if using reconfirmable

      ## Lockable
      # t.integer  :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts
      # t.string   :unlock_token # Only if unlock strategy is :email or :both
      # t.datetime :locked_at


      t.timestamps null: false
    end

    add_index :users, :email,                unique: true
    add_index :users, :reset_password_token, unique: true
    # add_index :users, :confirmation_token,   unique: true
    # add_index :users, :unlock_token,         unique: true
  end
end

マイグレーションファイルの確認が終わったら、マイグレーションを実行します

$ rails db:migrate

ルーティングを確認する

先程追加したルートのルーティングに加え、deviseのルーティングが追加されています。具体的にどのようなURLが発行されているのか確認しましょう。

config/routes.rb
Rails.application.routes.draw do
  devise_for :users
  root to: 'top#index'
end
$ rails routes
                               Prefix Verb   URI Pattern                                                                              Controller#Action
                     new_user_session GET    /users/sign_in(.:format)                                                                 devise/sessions#new
                         user_session POST   /users/sign_in(.:format)                                                                 devise/sessions#create
                 destroy_user_session DELETE /users/sign_out(.:format)                                                                devise/sessions#destroy
                    new_user_password GET    /users/password/new(.:format)                                                            devise/passwords#new
                   edit_user_password GET    /users/password/edit(.:format)                                                           devise/passwords#edit
                        user_password PATCH  /users/password(.:format)                                                                devise/passwords#update
                                      PUT    /users/password(.:format)                                                                devise/passwords#update
                                      POST   /users/password(.:format)                                                                devise/passwords#create
             cancel_user_registration GET    /users/cancel(.:format)                                                                  devise/registrations#cancel
                new_user_registration GET    /users/sign_up(.:format)                                                                 devise/registrations#new
               edit_user_registration GET    /users/edit(.:format)                                                                    devise/registrations#edit
                    user_registration PATCH  /users(.:format)                                                                         devise/registrations#update
                                      PUT    /users(.:format)                                                                         devise/registrations#update
                                      DELETE /users(.:format)                                                                         devise/registrations#destroy
                                      POST   /users(.:format)                                                                         devise/registrations#create
                                 root GET    /                                                                                        top#index
        rails_mandrill_inbound_emails POST   /rails/action_mailbox/mandrill/inbound_emails(.:format)                                  action_mailbox/ingresses/mandrill/inbound_emails#create
        rails_postmark_inbound_emails POST   /rails/action_mailbox/postmark/inbound_emails(.:format)                                  action_mailbox/ingresses/postmark/inbound_emails#create
           rails_relay_inbound_emails POST   /rails/action_mailbox/relay/inbound_emails(.:format)                                     action_mailbox/ingresses/relay/inbound_emails#create
        rails_sendgrid_inbound_emails POST   /rails/action_mailbox/sendgrid/inbound_emails(.:format)                                  action_mailbox/ingresses/sendgrid/inbound_emails#create
         rails_mailgun_inbound_emails POST   /rails/action_mailbox/mailgun/inbound_emails/mime(.:format)                              action_mailbox/ingresses/mailgun/inbound_emails#create
       rails_conductor_inbound_emails GET    /rails/conductor/action_mailbox/inbound_emails(.:format)                                 rails/conductor/action_mailbox/inbound_emails#index
                                      POST   /rails/conductor/action_mailbox/inbound_emails(.:format)                                 rails/conductor/action_mailbox/inbound_emails#create
    new_rails_conductor_inbound_email GET    /rails/conductor/action_mailbox/inbound_emails/new(.:format)                             rails/conductor/action_mailbox/inbound_emails#new
   edit_rails_conductor_inbound_email GET    /rails/conductor/action_mailbox/inbound_emails/:id/edit(.:format)                        rails/conductor/action_mailbox/inbound_emails#edit
        rails_conductor_inbound_email GET    /rails/conductor/action_mailbox/inbound_emails/:id(.:format)                             rails/conductor/action_mailbox/inbound_emails#show
                                      PATCH  /rails/conductor/action_mailbox/inbound_emails/:id(.:format)                             rails/conductor/action_mailbox/inbound_emails#update
                                      PUT    /rails/conductor/action_mailbox/inbound_emails/:id(.:format)                             rails/conductor/action_mailbox/inbound_emails#update
                                      DELETE /rails/conductor/action_mailbox/inbound_emails/:id(.:format)                             rails/conductor/action_mailbox/inbound_emails#destroy
rails_conductor_inbound_email_reroute POST   /rails/conductor/action_mailbox/:inbound_email_id/reroute(.:format)                      rails/conductor/action_mailbox/reroutes#create
                   rails_service_blob GET    /rails/active_storage/blobs/:signed_id/*filename(.:format)                               active_storage/blobs#show
            rails_blob_representation GET    /rails/active_storage/representations/:signed_blob_id/:variation_key/*filename(.:format) active_storage/representations#show
                   rails_disk_service GET    /rails/active_storage/disk/:encoded_key/*filename(.:format)                              active_storage/disk#show
            update_rails_disk_service PUT    /rails/active_storage/disk/:encoded_token(.:format)                                      active_storage/disk#update
                 rails_direct_uploads POST   /rails/active_storage/direct_uploads(.:format)                                           active_storage/direct_uploads#create

動作確認をする

上記では、URLがたくさん生成されていることを確認しました。ここでは試しに、sign_in にアクセスし、devise の機能が有効になっているかを確認してみましょう。
localhost:3000/sign_inにアクセスしてください

下記のような画面が表示されていれば成功です。

ScreenShot 2020-01-24 12.09.14.jpg

トップページを作成する

トップページ用のコントローラーを追加します。

$ rails g controller top index

早速、before_action :authenticate_user!|を追加しましょう。

app/controllers/top_controller.rb
class TopController < ApplicationController
  before_action :authenticate_user!, only: %i[index]
  def index; end
end

トップページであるlocalhost:3000にアクセスしようとすると、localhost:3000/users/sign_inにリダイレクトされていれば大丈夫です。

Sign_upからユーザー情報を登録し、ログインできるようになっているはずです。

2. OmniAuth で Google アカウント機能を実装する

さて、ここからは SNS認証機能を追加していきます。今回は、Googleアカウントを利用しましょう。

本項は、下記を参考にしました。

OmniAuth とは

OmniAuthとは、外部サービスのアカウントを使った認証機能を提供する仕組みです。各外部サービスとのOauth認証機能をStragetyとして持っています。

Omniauthの仕組みを知る上では、下記の点を抑えておくと良いでしょう。

  • Oauth2 による認証を提供している
  • Rack ミドルウェアを利用している
  • Strategy を管理している

Oauth2とは

アクセストークンの要求方法とそれに対する応答方法を標準化したものが OAuth 2.0 である

Rackとは

Rackは、RubyのWebアプリケーションに対して、モジュール化された最小限のインターフェイスを提供して、インターフェイスを広範囲に使えるようにします。RackはHTTPリクエストとレスポンスを可能なかぎり簡単な方法でラッピングすることで、Webサーバー、Webフレームワーク、その間に位置するソフトウェア (ミドルウェアと呼ばれています) のAPIを1つのメソッド呼び出しの形にまとめます。

Rackとは、HTTP通信に利用されているミドルウェアのことを言います。

Strategyとは

Strategyとは具体的な外部サービス(Probider)を指します。具体的なサービスとの関連付けは下記のようになっており、Strategyと同名のGemが提供されています。

Probider Strategy
Google google-oauth2
Twitter twitter
FaceBook facebook

CLIENT_ID と CLIENT_SECRET の取得

今回は、Google APIを利用します。

Google Cloud Platform にアクセスし、My Project▼をクリックします。

ScreenShot 2020-01-25 10.10.32.jpg

新しいプロジェクトをクリックします。
ScreenShot 2020-01-25 10.12.09.jpg

任意のプロジェクト名ここではdevise-omniauthと入力し、作成をクリック。
ScreenShot 2020-01-25 10.12.44.jpg

認証情報を作成をクリックします。
ScreenShot 2020-01-25 10.13.46.jpg

OauthクライアントID をクリックします。なお、OAuthクライアントIDとは、ユーザーの情報にアクセスするためのサービス提供者に割り当てられた一意のIDのことです。
ScreenShot 2020-01-25 10.16.44.jpg

Userのタイプを選択します。組織内のみで利用する場合は、内部を、Googleアカウントを持つすべてのユーザーが利用できるようにする場合は、外部を選択します。
ScreenShot 2020-01-25 10.17.31.jpg

任意のアプリケーション名を記載し、下までスクロールして保存をクリックします。

ScreenShot 2020-01-25 10.18.42.jpg

ScreenShot 2020-01-25 10.18.55.jpg

OAuth クライアントID作成の画面では、任意の名前を入力し、承認済みリダイレクトURL(コールバックURL)に下記を記載します。間違えないよう、コピペすることをおすすめします。
なお、コールバックURLとは、認証後に外部サービス(ここでは、Google)認証ページからリダイレクトされる自身のアプリケーションのURLを指しています。

http://localhost:3000/users/auth/google_oauth2/callback

ScreenShot 2020-01-25 10.20.19.jpg

すると、クライアントIDとクライアントシークレットが発行されますので、テキストファイルなどにして保存してください。一度だけしか発行されないため、取り扱いには厳重注意をしてください。

ScreenShot 2020-01-25 10.21.38.jpg

なお、GoogleのCLIENT_IDCLIENT_SECRETの取得に関する情報を探すと、ライブラリ画面からGoogle+APIを利用しているものがありますが、Google+APIはすでにサービスの提供が終了しており2020年1月現在だと、ユーザー名やメールアドレスを取得するにあたりは、APIの追加が不要になっていることを確認しています。

omniauth の実装

Gemfile に下記を追加します。

Gemfile
gem 'omniauth'
gem 'omniauth-google-oauth2'
gem 'dotenv-rails'
$ bundle install

設定ファイルを編集する

config/initializers/devise.rbに下記を追加する。

config/initializers/devise.rb
config.omniauth :google_oauth2, ENV['CLIENT_ID'], ENV['CLIENT_SECRET']

.envファイルを作成し、先程取得したクライアントIDとクライアントシークレットを入力します。
=のあとは、クォーテーション''不要です。

.env
CLIENT_ID=クライアントID
CLIENT_SECRET=クライアントシークレット

コントローラーを追加する

devise を用いた users コントローラーを追加します。

$ rails g devise:controllers users
Running via Spring preloader in process 90155
      create  app/controllers/users/confirmations_controller.rb
      create  app/controllers/users/passwords_controller.rb
      create  app/controllers/users/registrations_controller.rb
      create  app/controllers/users/sessions_controller.rb
      create  app/controllers/users/unlocks_controller.rb
      create  app/controllers/users/omniauth_callbacks_controller.rb
===============================================================================

Some setup you must do manually if you haven't yet:

  Ensure you have overridden routes for generated controllers in your routes.rb.
  For example:

    Rails.application.routes.draw do
      devise_for :users, controllers: {
        sessions: 'users/sessions'
      }
    end

===============================================================================

Users テーブルにカラムを追加する

マイグレーションファイルを作成し、カラムを追加します。

$ rails g migration add_columns_to_users provider uid username
db/migrate/20200125021406_add_columns_to_users.rb
class AddColumnsToUsers < ActiveRecord::Migration[6.0]
  def change
    add_column :users, :provider, :string
    add_column :users, :uid, :string
    add_column :users, :username, :string
  end
end

ここで追加したカラムを簡単に説明します。

  • uid ユーザーID
  • provider サービス名称
  • name ユーザーの名前
$ rails db:migrate

ちなみに、emailなどのカラムはすでにdevise 標準のため用意されています。マイグレーション実行後のdb/schema.rbを見てみましょう。

db/schema.rb
create_table "users", force: :cascade do |t|
  t.string "email", default: "", null: false
  t.string "encrypted_password", default: "", null: false
  t.string "reset_password_token"
  t.datetime "reset_password_sent_at"
  t.datetime "remember_created_at"
  t.datetime "created_at", precision: 6, null: false
  t.datetime "updated_at", precision: 6, null: false
  t.string "provider"
  t.string "uid"
  t.string "username"
  t.index ["email"], name: "index_users_on_email", unique: true
  t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end

コントローラーを追加する

コントローラーを追加します。
Devise::OmniauthCallbacksControllerを継承しているので、見慣れないメソッド名があるかと思いますが、providerであるgoogle_oauth2のメソッドが必要になります。
余談ですが、もし追加でproviderを追加したときは、新たなメソッドとして、twitterを定義し、同じくcallbackメソッドを呼び出せると、メソッドの中身を共通化できますね。

request.env['omniauth.auth']に情報が格納されているため、この値を引数として既存のユーザーを探し出すか、新規作成します。この際、フラッシュメッセージは、Successfully authenticated from Google_oauth2 account.と表示されます。

また、ユーザー情報がデータベースに保存されているかどうかで、リダイレクト先を変更します。

app/controllers/users/omniauth_callbacks_controller.rb
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
  # You should configure your model like this:
  # devise :omniauthable, omniauth_providers: [:twitter]

  def google_oauth2
    callback_from(:google_oauth2)
  end

  def callback_from(provider)
    provider = provider.to_s
    @user = User.find_or_create_for_oauth(request.env['omniauth.auth'])

    if @user.persisted?
      flash[:notice] = I18n.t('devise.omniauth_callbacks.success', kind: provider.capitalize)
      sign_in_and_redirect @user
    else
      session["devise.#{provider}_data"] = request.env['omniauth.auth']
      redirect_to new_user_registration_url
    end
  end
end

Userモデルにメソッドを追加する

request.env['omniauth']emailからユーザー情報を取得し、新規ならば新たにレコードを作成します。

パスワードは、devise の特異メソッドであるfriendly_tokenメソッドを使いランダムに生成します。

app/models/user.rb
class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable,
         :omniauthable, omniauth_providers: [:google_oauth2]

  def self.find_or_create_for_oauth(auth)
    find_or_create_by!(email: auth.info.email) do |user|
      user.provider = auth.provider,
      user.uid = auth.uid,
      user.username = auth.info.name,
      user.email = auth.info.email,
      user.password = Devise.friendly_token[0, 20]
    end
  end
end

ルーティングを追加する

config/routes.rbで、Users::OmniauthCallbacksControllerへのルーティングを追加します。

config/routes.rb
Rails.application.routes.draw do
  get 'top/index'
  devise_for :users, controllers: {
    omniauth_callbacks: 'users/omniauth_callbacks'
  }
  root to: 'top#index'
end

動作確認

Sign in with GoogleOauth2のリンクが自動で生成されています。

これは、app/views/devise/shared/_links.html.erbに自動でリンクが追加されているためです。便利ですね。

エラーの対処

外部通信を伴うエラー処理は切り分けが難しいですよね。いくつか対処した記録を残します。

Error: redirect_uri_mismatch

ScreenShot 2020-01-25 11.54.36.jpg

redirect_uri_mismatchと出力されています。

リダイレクト先のURLを間違ってしまった場合は、下記の画面になります。

JWT::InvalidIatError

ScreenShot 2020-01-25 18.59.27.jpg

JWT::InvalidIatErrorと出力されたら、OSのクロックがずれているエラーです。
config/initializers/devise.rb ファイルを見直し、skip_jwt: trueを追記しましょう。

Devise.setup do |config|
  `config.omniauth :google_oauth2, ENV['CLIENT_ID'], ENV['CLIENT_SECRET'], skip_jwt: true` skip_jwt: true # skip_jwt: true を追記
end

The action 'google_oauth2' could not be found for Devise::OmniauthCallbacksController

Devise::OmniauthCallbacksControllerに対応するgoogle_oauth2というアクションはないと言われています。
今回作成したのは、Devise::OmniauthCallbacksController を継承したUsersコントローラーなので、config/routes.rbがあっているか確認しましょう。

3. Omniauth のテストコードを実装する

Omniauthを使った認証機能のテストを追加していきます。

本項は、下記の記事を参考にしました。

Gemを追加する

Gemfilerspec-railsを追加します。

Gemfile
group :test do
  gem 'rspec-rails'
end
$ bundle install
$ bundle exec rails generate rspec:install
Running via Spring preloader in process 4995
      create  .rspec
      create  spec
      create  spec/spec_helper.rb
      create  spec/rails_helper.rb

設定を追加する

spec/rails_helper.rbsupport配下のRubyファイルを読み込む設定を追加します。
```Ruby:spec/rails_helper.rb`
Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }


### モックを追加する
`spec/support/omniauth.rb`を作成し、下記のコードを記載します。

```ruby:spec/support/omniauth.rb
OmniAuth.config.test_mode = true

OmniAuth.config.mock_auth[:google_oauth2] = OmniAuth::AuthHash.new({
    provider: 'google_oauth2',
    uid: '000000',
    info: { email: 'test@example.com' },
    credentials: { token:'google_oauth2_test' }
  })

このコードをざっくり説明すると、下記のようになります。

  • OmniAuth をテストモードを有効にしている。この設定を追加することで、すべてのOmniauthへのリクエストは、モックのAuth情報を用いて簡素化できるようになる
  • google_oauth2のOmniAuthのモックとして疑似のAuth情報のハッシュを作成している

テストコードを実装する

spec/system/user.rbを作成し、テストケースを追加します。
今回は、未サインアップの場合サインアップ済みの場合をテストします。

spec/system/user.rb
require 'rails_helper'

RSpec.describe 'User', type: :system do
  context '未サインアップの場合' do
    it 'ユーザーが増えること' do
      visit root_path
      expect{
        click_link 'Sign in with GoogleOauth2'
        sleep 1
      }.to change(User, :count).by(1)
    end
  end

  context 'サインアップ済みの場合' do
    before do
      User.create!(
      email: 'test@example.com',
      password: 'test12'
      )
    end

    it 'ユーザーは増えないこと' do
      visit root_path
      expect{
        click_link 'Sign in with GoogleOauth2'
        sleep 1
      }.to_not change(User, :count)
    end
  end
end

まとめ

Railsアプリケーションに認証機能追加に関して、知見が深まりました。
拙い点がありましたがご指摘よろしくお願いします。

34
35
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
34
35