【Ruby on Rails5】一から会員登録認証(Facebook,Twitter)機能をつくる

Ruby on Railsの環境

・ruby 2.5.0p0
・Rails 5.1.5
・Twitter,FacebookAPIのAPIキーを取得している状態

1. GemFileの設定

$ mkdir hogeapp.com
$ cd hogeapp.com
$ bundle init

hogeappディレクトリを作成し中にbundle initでGemFileを作成します。

gem "rails"
# frozen_string_literal: true
source "https://rubygems.org"
git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
gem "rails"
gem "mysql2"

2. Railsフレームワークを入れる

$ bundle install --path vendor/bundle
$ bundle exec rails new . -d mysql

なにか質問されたら「Y」でRailsフレームワークをインストールします。
Overwrite /Users/hoge/hogeapp.com/Gemfile? (enter "h" for help) [Ynaqdh] Y

「-d」は「–database」を示しています。デフォルトの設定が「sqLite」なので「mysql」に変更します。

3. データベース(MySQL)の設定をする

データベース(MySQL)を作っておきます。(作り方は割愛します)
ぼくはMySQLに「hogeapp」という名前のデータベースを作成しました。

config/database.ymlを修正します。

default: &default
adapter: mysql2
encoding: utf8
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
username: root
password: root
host: localhost
database: hogeapp
socket: /tmp/mysql.sock
development:
<

MySQLのログインするときの「username」「password」を設定し作成したDB(データベース)を指定します。開発環境で作成するため「host」は「localhost」にしています。今回はdevelopment(開発環境)のみしか実行しませんのでtest,productionの設定はとくに触る必要はありません。defaultに共通の設定を書いて「development」「test」「production」に個別の設定を書きます。個別の設定が優先されて上書きされます。今回は面倒なのでほとんどdefaultに書いてます。

$ bundle exec rake db:migrate

を実行しDBに「ar_internal_metadata」「schema_migrations」テーブルが作成できていればデータベースの設定は成功しています。

deviseのインストール

Gemfileに下記を追記してbundle installします。

gem 'devise'
gem 'omniauth'
gem 'omniauth-twitter'
gem 'omniauth-facebook'
$ bundle install

deviseの機能をインストールします。

$ bundle exec rails g devise:install

実行すると次になにをするべきか案内してくれます。

===============================================================================
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:

<%= notice %>

 

<%= alert %>

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

deviseの設定

アクションメーラーの追加・設定

config/environments/development.rbにアクションメーラーの設定を追記します。

【追記】
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }を追記します。
Rails.application.configure doの中にかかないとエラーになるので要注意です。

Rails.application.configure do
#省略
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
#省略
end

ビュー(view)の追加・設定

下記コマンドを実行してviewを作成します。

$ bundle exec rails g devise:views

viewsに認証に必要なビューを作成します。app/views/deviseが作られます。

モデルとマイグレーションの追加・設定

下記コマンドを実行してモデルとマイグレーションを作成します。

$ bundle exec rails g devise user

app/models/user.rbファイルが作成されます。
app/models/user.rbに追記します。

class User < ApplicationRecord
devise :database_authenticatable, :omniauthable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :confirmable
def self.find_for_facebook_oauth(auth, signed_in_resource=nil)
user = User.where(:provider => auth.provider, :uid => auth.uid).first
unless user
user = User.create(name:     auth.extra.raw_info.name,
provider: auth.provider,
uid:      auth.uid,
email:    auth.info.email,
password: Devise.friendly_token[0,20]
)
end
user
end
def self.find_for_twitter_oauth(auth, signed_in_resource=nil)
user = User.where(:provider => auth.provider, :uid => auth.uid).first
unless user
user = User.create(name:     auth.info.nickname,
provider: auth.provider,
uid:      auth.uid,
email:    User.create_unique_email,
password: Devise.friendly_token[0,20]
)
end
user
end
def self.create_unique_string
SecureRandom.uuid
end
def self.create_unique_email
User.create_unique_string + "@example.com"
end
end

db/migrate/にできたマイグレーションを変更します。

下記部分のコメントアウトを外します。

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

下記部分を追加します。

t.string :provider
t.string :uid
t.string :name

マイグレーションを実行します。

$ bundle exec rake db:migrate

認証に必要なusersテーブルが作成されます。

コントローラーの設定

下記コマンドを実行してコントローラーを作成とルートの設定をします。

$ bundle exec rails g devise:controllers users

ルート(routes)の設定

app/config/routes.rbにルートの設定を追記します。

Rails.application.routes.draw do
root to: "home#index"
devise_for :users, :controllers => {
:sessions      => "users/sessions",
:registrations => "users/registrations",
:passwords     => "users/passwords",
:omniauth_callbacks => "users/omniauth_callbacks"
}
end

app/controllers/users/omniauth_callbacks_controller.rbを作成し下記コードを追加します。

class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
def facebook
@user = User.find_for_facebook_oauth(request.env["omniauth.auth"], current_user)
if @user.persisted?
flash[:notice] = I18n.t "devise.omniauth_callbacks.success", :kind => "Facebook"
sign_in_and_redirect @user, :event => :authentication
else
session["devise.facebook_data"] = request.env["omniauth.auth"]
redirect_to new_user_registration_url
end
end
def twitter
@user = User.find_for_twitter_oauth(request.env["omniauth.auth"], current_user)
if @user.persisted?
flash[:notice] = I18n.t "devise.omniauth_callbacks.success", :kind => "Twitter"
sign_in_and_redirect @user, :event => :authentication
else
session["devise.twitter_data"] = request.env["omniauth.auth"]
redirect_to new_user_registration_url
end
end
end

Twitter,FacebookのAPIキー設定

Twitter,Facebook API取得するURL
Twitter https://apps.twitter.com/
Facebook https://developers.facebook.com/

Twitter,Facebookの取得方法は長くなるため割愛しています。Twitter,FacebookのAPIキーの取得方法やTwitter,Facebook側の設定方法は調べておいてください。

1. config/initializers/devise.rbにアクションメーラーの設定を追記します。
Devise.setup do |config|の中にかかないとエラーになるので要注意です。

Devise.setup do |config|
#省略
if Rails.env.production?     
config.omniauth :facebook, "App ID", "App Secret"
config.omniauth :twitter,  "Consumer key", "Consumer secret"
else
config.omniauth :facebook, "App ID", "App Secret"
config.omniauth :twitter,  "Consumer key", "Consumer secret"
end
#省略
end

viewsの設定

ログイン画面を作成します。

下記コマンドでcontrollerを追加します。

$ bundle exec rails g controller home index

app/config/routes.rbが自動的に
get 'home/index'が追加されるため削除します。

app/views/home/index.html.rbを下記のように修正します。

<%= link_to "Sign in with Facebook", user_facebook_omniauth_authorize_path %>
<%= link_to "Sign in with Twitter", user_twitter_omniauth_authorize_path %>
<% if notice %>

<%= notice %>

<% end %> <% if alert %>

<%= alert %>

<% end %> <% if user_signed_in? %> Logged in as <%= current_user.email %>. <%= link_to "Settings", edit_user_registration_path, :class => "navbar-link" %> | <%= link_to "Logout", destroy_user_session_path, method: :delete, :class => "navbar-link" %> <% else %> <%= link_to "Sign up", new_user_registration_path, :class => 'navbar-link' %> | <%= link_to "Login", new_user_session_path, :class => 'navbar-link' %> <% end %>

作成したログインページにアクセスする

$ bundle exec rails s

でrailsを起動してhttp://127.0.0.1:3000/にアクセスすれば表示されます。
TwitterかFacebookのログイン認証ができてusersテーブルに認証されたデータが残ります。

devise設定時の注意点

app/config/routes.rb
devise_for :usersがふたつあるとArgumentError: Invalid route name, already in use: 'new_user_session' エラーが発生するためdeviseの手順に要注意です。