Help us understand the problem. What is going on with this article?

Rails4.2.1 + Devise + Twitter 認証 (Bootstrapも入れる)

More than 5 years have passed since last update.

Railsで新規アプリ作ってなんかサービス作ろうと思ったら、大体ログイン機能は作るだろうし、とりあえずBootstrap入れておきたいよねということで、Twitter IDでログイン出来るアプリのひな形を作る手順をメモしておきます。

DeviseとOmniAuthを利用します。

Gemfile
gem 'devise'
gem 'omniauth-twitter'
% bundle install
% rails g devise:install

devise:installで色々指示が出てくる。
1はとりあえずメール飛ばしたりしないのでやらない。
2は言うとおりやる。
3は、後述のBootstrap Generatorでapplication.htmlを生成するのでやらない。
4はRails4なのでやらない。
5もtwitter認証でのログインしか作らないのでやらない。

      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. If you are deploying on Heroku with Rails 3.2 only, you may want to set:

       config.assets.initialize_on_precompile = false

     On config/application.rb forcing your application to not access the DB
     or load models when precompiling your assets.

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

       rails g devise:views

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

home#indexを生成する

% rails g controller home index

モデルを生成して、認証に利用するカラムを追加する。

% rails g devise User
% rails g migration AddOmniauthToUser uid:string provider:string name:string

migrateファイルいじって、indexつける

class AddOmniauthToUser < ActiveRecord::Migration
  def change
    add_column :users, :uid, :string
    add_column :users, :provider, :string
    add_column :users, :name, :string

    add_index :users, [:uid, :provider], unique: true
  end
end
% rake db:create
% rake db:migrate

deviseで使うモジュールの選択

user.rb
  devise :database_authenticatable, :trackable, :omniauthable

OmniAuth用のrouting作成

routes.rb
  devise_for :users, controllers: {
                       omniauth_callbacks: "users/omniauth_callbacks"
                   }
% mkdir app/controllers/users
% touch app/controllers/users/omniauth_callbacks_controller.rb
omniauth_callbacks_controller.rb
class Users::OmniauthCallbacksController < ApplicationController

end

twitterのアプリ作成して、API KEYなど取得しておく。

deviseのomniauth設定

initializers/devise.rb
  config.omniauth :twitter, ENV['APP_ID'], ENV['APP_SECRET']

キーをコードに入れたくないので、環境変数から取るようにした。

開発環境の環境変数はdotenv-railsで管理する。

Gemfile
group :development do
  gem 'dotenv-rails'
end
APP_ID=HOGEHOGE
APP_SECRET=HOGEHOGEfu48rueufif

コード管理から外す

.env

Userモデルに、omniauth時に呼ばれるメソッドを作る
Userを返す。(無ければ作る)メソッド作成。
emailとパスワードはとりあえず使わないので、ユニークになる適当な値を突っ込む。

user.rb
  def self.find_for_twitter_oauth(auth)
    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

omniauthのcallback時の処理。Userを見つけるか、作るかして sign_inする。

omniauth_callbacks_controller.rb
  def twitter
    @user = User.find_for_twitter_oauth(request.env["omniauth.auth"])
    if @user.persisted?
      flash[:notice] = I18n.t "devise.omniauth_callbacks.success", :kind => "Twitter"
      sign_in_and_redirect @user, :event => :authentication
    else
      redirect_to root_path
    end
  end

sign_inするためのリンクをViewに作る。
下記の余談にて、Bootstrap railsを当てている前提で、一番上のナビゲーションバーに sign_in, sign_outのリンクを作る。
sign_in時は twitter名を出して sign_in 出来ているのを分かるようにする。

9行目くらいからが追加した部分です。これで画面の右上に、Sign in with Twitter とか出ればOK。

application.html.haml
        #navbar.collapse.navbar-collapse
          %ul.nav.navbar-nav
            %li.active
              %a{href: "#"} Home
            %li
              %a{href: "#about"} About
            %li
              %a{href: "#contact"} Contact
          %ul.nav.navbar-nav.navbar-right
            - if user_signed_in?
              %li
                %a{href: "#"}
                  = current_user.name
              %li
                = link_to "Sign out", destroy_user_session_path, :method => :delete
            - else
              %li
                = link_to "Sign in with Twitter", user_omniauth_authorize_path(:twitter)

Sign inしていないとアクセス出来ないようにするには、コントローラに以下のように書けばいいです。

home_controller.rb
class HomeController < ApplicationController
  before_action :authenticate_user!
end

これで、URL直接叩いてアクセスしようとすると、rootのpathへリダイレクトされます。

Bootstrap railsにあてる(余談)

Rails assetsを使ってbootstrap-sassを入れているので bundlerのバージョンを1.8.4以降にする必要があります。

Gemfile
gem 'bundler', '>= 1.8.4'

source 'https://rails-assets.org' do
  gem 'rails-assets-bootstrap-sass'
end
gem 'bootstrap-generators'

gem 'haml-rails'
% rails g bootstrap:install -e haml
% rm app/views/layouts/application.html.erb
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away