#author("2021-12-11T05:44:17+00:00","default:src128","src128")
&tag(Devise/Twitter認証);
*目次 [#qde2babe]
#contents

*参考情報 [#x85e96d6]
-[[[*Rails*] deviseの使い方(rails5版) - Qiita:https://qiita.com/cigalecigales/items/f4274088f20832252374]]
-[[deviseを用いたユーザ認証作成 -Rails奮闘記①- - Farma's Study Desk:http://farma-11.hatenablog.com/entry/2018/01/02/120323]]
-[[deviseによるFacebook / Twitter 認証 -Rails奮闘記②- - Farma's Study Desk:http://farma-11.hatenablog.com/entry/2018/01/03/212502]]


*関連ページ [#k9a9b2c6]
*参考情報 [#sf94cf6e]
-[[[*Rails*] deviseの使い方 - Qiita:http://qiita.com/cigalecigales/items/73d7bd7ec59a001ccd74]] - Twitter認証後メールパスワードを入力したあと登録できるパターン。
-[[Railsのログイン認証gemのDeviseとOmniAuth-Twitterの連携(Twitterでログインする) - Rails Webook:http://ruby-rails.hatenadiary.com/entry/20140805/1407200400]] - Twitter認証後メールアドレスだけ入力し登録できるパターン。
-[[deviseでfacebook,twitter認証 - Qiita:http://qiita.com/mosa_siru/items/9f1faa509f4d3653a1b2]] - Twitter認証後即時登録されるパターン。
*概要 [#ifa27315]
-Twitterにログインしその情報をユーザーとして利用する。
-[[Devise]]
-[[../最もシンプルな認証機能]]
-[[./古い内容]]

*Userモデルの生成 [#iab10e2a]
-Userモデルを生成する
 $ bundle exec rails g devise User
-user.rbを以下のように編集する。参考情報と異なりconfirmableは実装せず画面上で登録が完結できるようにする。
*使用準備 [#m352dbd7]
-Gemfileの編集
#pre{{
class User < ActiveRecord::Base
gem 'devise'
gem 'omniauth-twitter'
}}
-サンプルモデルなどを作成
 bundle exec rails g scaffold  book title:string author:string summary:text
-deviseをインストールする
 bundle exec rails g devise:install
-Userモデルの作成
 bundle exec rails  g devise user
-Userモデルの変更。confirmableは実装せず画面上で登録が完結できるようにする。omniauthableを追加。
#pre{{
class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable,
         :lockable, :timeoutable, :omniauthable, omniauth_providers: [:twitter]
         :recoverable, :rememberable, :trackable, :validatable, :omniauthable
end
}}
-マイグレーションファイルの編集
-以下ようにする。
-migrationファイルdevise_create_user.rbの変更。
#pre{{
class DeviseCreateUsers < ActiveRecord::Migration
class DeviseCreateUsers < ActiveRecord::Migration[5.1]
  def change
    create_table(:users) do |t|
    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.string   :current_sign_in_ip
      t.string   :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.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

      ## Twitter認証用
      t.string :provider
      t.string :uid
      t.string :username, default: "anonymous"

      t.timestamps
    end

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


}}
-migration実行
 bundle exec rake db:migrate
-このままだとエラーになってログインできない。
 undefined method omniauth_authorize_path
 bundle exec rails db:migrate

*Twitterアプリケーションの登録 [#a5b66981]
-https://apps.twitter.com/ からアプリケーションを登録する。
-callback_urlは開発環境と本番環境で異なるので2個登録しておくとよい。
-開発環境の場合Callback URLは次のように設定する。localhostにするとエラーになるので注意。
 http://127.0.0.1:3000/users/omniauth_callbacks

*Twitter認証のための準備 [#bf34e74a]
**devise.rbの編集 [#h0f62ea4]
-config/initializers/devise.rbを編集
*Twitter認証のための準備 [#z114b504]

**devise.rbの編集 [#bb5d6965]
-config/initializers/devise.rbを編集(ここで登録した"twitter"がコールバックで呼ばれるメソッドになる?)
 config.omniauth :twitter, "Consumer Key", "Consumer Secret"
-この段階で「http://localhost:3000/users/sign_in」にアクセスし、「Sign in with Twitter」からログインすると一応Twitterのログイン画面が表示される。
-しかしコールバックが設定されていないのでラーになる。
#pre{{
 config.omniauth :twitter, "Consumer Key", "Consumer Secret"
The action 'twitter' could not be found for Devise::OmniauthCallbacksController
}}
-この段階で「Sign in with Twitter」からログインすると一応Twitterのログイン画面が表示される。
-しかしcallbackが設定されていないのでエラーとなる。
 The action 'twitter' could not be found for Devise::OmniauthCallbacksController

**コールバック用コントローラーの生成 [#ob148449]
-Twitterログイン画面から戻ってきたときに呼び出される処理を行うコントローラーを生成。
**コールバック用コントローラーの生成 [#hf94145f]
-Twitterログイン画面から戻ってきたときに呼び出される処理を行うコントローラーを生成する。
 bundle exec rails g controller omniauth_callbacks
-内容を編集。親クラスがDevise::OmniauthCallbacksControllerなので注意。
#pre{{
class OmniauthCallbacksController < Devise::OmniauthCallbacksController
  def twitter
    @user = User.from_omniauth(request.env["omniauth.auth"].except("extra"))
    if @user.persisted?
      flash.notice = "ログインしました!"
      sign_in_and_redirect @user
    else
      session["devise.user_attributes"] = @user.attributes
      redirect_to new_user_registration_url
    end
  end
end
}}
**Userモデルの変更 [#r6adae74]


**Userモデルの変更 [#b9391b71]
-from_omniauthとnew_with_sessionを作成する。
#pre{{
class User < ActiveRecord::Base
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable,
         :lockable, :timeoutable, :omniauthable, omniauth_providers: [:twitter]
#         :confirmable, :lockable, :timeoutable, :omniauthable, omniauth_providers: [:twitter]
          :timeoutable, :omniauthable, omniauth_providers: [:twitter]
  def self.from_omniauth(auth)
    where(provider: auth["provider"], uid: auth["uid"]).first_or_create do |user|
      user.provider = auth["provider"]
      user.uid = auth["uid"]
      user.username = auth["info"]["nickname"]
#      user.email = Devise.friendly_token[0,20]
      user.email =  "#{auth.provider}-#{auth.uid}@example.com"
      user.password = Devise.friendly_token[0,20] #これが必要?
    end
  end

  def remember_me
    # http://stackoverflow.com/questions/14417201/how-to-automatically-keep-user-remembered-in-devise
    true
  end

  def self.new_with_session(params, session)
    if session["devise.user_attributes"]
      new(session["devise.user_attributes"], without_protection: true) do |user|
        user.attributes = params
        user.valid?
      end
    else
      super
    end
  end

  def password_required?
    super && provider.blank?
  end

  def email_required?
    super && provider.blank?
  end
end


}}

**ルートの設定 [#p359fbf7]
**ルートの設定 [#d05478c3]
-routes.rbを編集。omniauth_callbacks_controllerが呼ばれるようにする。
 devise_for :users, controllers: { :omniauth_callbacks => "omniauth_callbacks" }
#pre{{
devise_for :users, controllers: { :omniauth_callbacks => "omniauth_callbacks" }
}}

**処理の流れ [#l9ba643b]
-初回登録時Twitterログイン画面から戻りOmniauthCallbacksControllerの下の分岐から「redirect_to new_user_registration_url」が呼ばれ登録画面が表示される。
-Userのnew_with_sessionはページをまたいでTwitterのアカウントの情報(uidやらproviderやら)を保存するために必要となる。
-Twitterの場合Twitterのアカウント情報からメールアドレスを取得できないので、戻ってきてからメールアドレス、パスワードを入力し始めて登録完了となる。
**画面の修正 [#s1ff7bfd]
-application.html.erbの修正
#pre{{
<!DOCTYPE html>
<html>
  <head>
    <title>Rails5DeviseTwitterDemo</title>
    <%= csrf_meta_tags %>

#ref(sign_up.png)
    <%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track': 'reload' %>
    <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
  </head>

  <body>
  <header>
    <nav>
      <% if user_signed_in? %>
        <strong<%= link_to current_user.username, pages_show_path %></strong>
        <%= link_to 'ログアウト', destroy_user_session_path, method: :delete %>
      <% else %>
        <%= link_to 'ログイン', user_twitter_omniauth_authorize_path %>
      <% end %>
    </nav>
  </header>
  <p class="notice"><%= notice %></p>
  <p class="alert"><%= alert %></p>
  <%= yield %>
  </body>
</html>
}}
-ログインしていない場合は、「ログイン」リンクが表示される。クリックされるとTwitter認証画面にとぶ。
-ログインしている場合はユーザー名と「ログアウト」リンクが表示される。
-初回の登録とそれ以降のログインに差は無い。

*Twitter認証の改良 [#gbd56603]
**目標 [#u7c24ff7]
-直接TwitterログインリンクをクリックしTwitterのログイン画面に遷移、戻ってきてから登録ボタンを押したら登録完了とする。
-メール、パスワードの入力はなし。

**User.from_omniauthの修正 [#e502f738]
-オリジナルではUser.from_omniauthのレコード生成を試み、パスワードが空のため実際は保存されないという流れになっている。ダミーパスワードを設定するとそのまま保存されてしまい、Twitterログイン後、確認してから登録という流れに鳴らない。
-whereで検索する処理とnewでダミーユーザーを生成する処理に分ける
-また登録画面でパスワード未入力のエラーが表示されないようpassword_required?を変更しておく。
*ビューの変更 [#eefbc6a5]
-Deviseのビューをカスタマイズする場合以下のコマンドを実行
 bundle exec rails g devise:views

*Tips [#g6d40a04]

**認証済みユーザーでないと表示できないページ [#dc821969]
-例えば認証済みユーザーでないとログインできないページ。
#pre{{
  def self.from_omniauth(auth)
    user = where(provider: auth["provider"], uid: auth["uid"]).first
    return user if user
    user = User.new
    user.provider = auth["provider"]
    user.uid = auth["uid"]
    user.username = auth["info"]["nickname"]
    user.email =  "#{auth.provider}-#{auth.uid}@example.com"
    user.password = Devise.friendly_token[0,20] #これが必要?
    user
class UsersController < RegularController
  include ApplicationHelper

  def password_required?
    provider.blank? && super
  #http://stackoverflow.com/questions/7458723/using-devise-to-create-private-profiles ユーザー以外がアクセスするとリダイレクト
  before_action :verify_owner, only: [:show, :edit, :destroy]
  def verify_owner
    redirect_to root_url unless current_user.username == params[:id]
  end
}}
*トラブルシューティング [#h3f33ae5]

**急にログインできなくなった [#hc13f9bc]
-2018年6月、Twitter側の仕様変更により設定されたcallback_urlを正しく送信しないとログインできなくなった模様。
-これには以下の変更が必要。
-Twitter Appの変更。本番のほかテスト用のcallbackを追加しておかないと開発環境でログインできなくなる。
 http://本番サイトのURL/users/auth/twitter/callback
 http://localhost:3000/users/auth/twitter/callback
-アプリ側の設定。callback_urlの設定は本番と開発環境で切り替える必要あり。
#pre{{
Devise.setup do |config|
  config.omniauth :twitter, "TWITTER_CONSUMER_KEY", "TWITTER_CONSUMER_SECRET", callback_url: 'http://localhost:3000/users/auth/twitter/callback'
end
}}
-callback urlはroutesでcallbackをgrepするとわかる。
#pre{{
$ bundle exec rake routes   | grep callback
 user_twitter_omniauth_callback GET|POST /users/auth/twitter/callback(.:format)
}}

**ログイン画面の修正 [#l0829a8d]
-registrations/new.html.erbを変更。
-Twitterのログイン画面からもどってきたあと登録しようとしているTwitterユーザー名を確認し、登録ボタンを押すと実際に登録される。
**Twitterでログインボタンを押すと画面に「Not found. Authentication passthru」とだけ表示される [#j7a92eea]
-2021/05/06(木)bundle updateで発生。
-omniauthのバージョン2.0以降と1.9で互換性がないっぽい。以下のようにomniauthのバージョンを指定する
#pre{{
<h2>Sign up</h2>

<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
  <%= devise_error_messages! %>

  <%= @user.username %>を登録します。

  <div class="actions">
    <%= f.submit "Sign up" %>
  </div>
<% end %>

<%= render "devise/shared/links" %>

gem 'devise'
gem 'omniauth', '1.9.1'
gem 'omniauth-twitter'
}}
-omniauthはomniauth-twitter依存関係で?自動インストールされていたものだが上記の設定が有効のようだ。


トップ   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS