Ruby on Railsでユーザとパスワードとtwitter認証でログインできるようにする
実行環境
Rails 4.2.6
ruby 2.3.0p0 (2015-12-25 revision 53290) [x86_64-linux]
作りたいもの
今回はdeviseを使いログイン画面を作成していく。
ただ今回はdeviseがデフォルトで持っている機能の"メールアドレス"でログインする機能をなくしユーザー名、パスワードだけでアカウントを作成しログインすることができるようにする。 ついでにtwitterアカウントでもログインできるようにする。
細かい設定とかはここではやらない。最低限できるようにするだけ。
準備
rails new board -TB --skip-turbolinks
Tはテストを作らない Bはbundle installのスキップ
アプリケーション内のgemファイルに以下のように記述
gem 'twitter-bootstrap-rails','3.2.2' gem 'omniauth-twitter','1.2.1' gem 'slim-rails','3.1.0' gem 'devise','4.1.1' gem 'html2slim','0.2.0' #デバッグ用 gem 'pry' gem 'pry-rails'
そしたらbundle install
する
※今回slimの記法に慣れたいと思ったのでinstallしてます。
bootstrapを使うには以下のコマンドを実行しておく
rails generate bootstrap:install static
deviseの設定
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. 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 ===============================================================================
デフォルトURLの指定
config/environments/development.rb
を編集していく。
上のメッセージで書いてあった
config.action_mailer.default_url_options = { host: 'localhost:3000' }
を追加で記述(localhostのところはサーバでやってるならアドレスに変えればできる)
コントローラーの作成
rails g controller home index
モデルの作成
rails g devise User
生成されたマイグレーションファイルは以下の通り
class DeviseCreateUsers < ActiveRecord::Migration 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.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.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
モデルはこんな感じ
class User < ActiveRecord::Base # Include default devise modules. Others available are: # :confirmable, :lockable, :timeoutable and :omniauthable devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable end
この辺の設定は後で変える。
次にroot_urlの設定をしておく
http://localhost:3000/にアクセスした際に表示されるページを指定する。 これをやっとかないと上手く動いてくれない
config/routes.rb
Rails.application.routes.draw do devise_for :users get 'home/index' root 'home#index' end
ビューの再設定
今回ビューはslimを使うので以下のように変更する
app/views/home/index.html.slim
- if user_signed_in? p=current_user.email =link_to "Settings", edit_user_registration_path =link_to "Logout", destroy_user_session_path, method: :delete - else =link_to "Sign up", new_user_registration_path =link_to "Login", new_user_session_path
テンプレートのerbは名前をapp/views/layouts/application.html.slim
に変更して以下のように記述し直す
doctype html html head title board app meta name="viewport" content="width=device-width, initial-scale=1.0" = stylesheet_link_tag "application", media: 'all' = javascript_include_tag "application" = csrf_meta_tags body - if notice p.alert.alert-success = notice - if alert p.alert.alert-danger = alert = yield
一度ここでマイグレートして起動の確認をしておく
rake db:migrate rails s -b 0.0.0.0
確認ができたら外見をいじるためにもDeviseのViewを作成する 以下のコマンドを実行
rails g devise:views
すると以下の様なファイルが生成される
app/views/devise/shared/_links.html.erb (リンク用パーシャル) app/views/devise/confirmations/new.html.erb (認証メールの再送信画面) app/views/devise/passwords/edit.html.erb (パスワード変更画面) app/views/devise/passwords/new.html.erb (パスワードを忘れた際、メールを送る画面) app/views/devise/registrations/edit.html.erb (ユーザー情報変更画面) app/views/devise/registrations/new.html.erb (ユーザー登録画面) app/views/devise/sessions/new.html.erb (ログイン画面) app/views/devise/unlocks/new.html.erb (ロック解除メール再送信画面) app/views/devise/mailer/confirmation_instructions.html.erb (メール用アカウント認証文) app/views/devise/mailer/reset_password_instructions.html.erb (メール用パスワードリセット文) app/views/devise/mailer/unlock_instructions.html.erb (メール用ロック解除文)
生成されたファイルは拡張子が.erbなのでerb2slim
を使ってslimに変更する
erb2slim -d app/views/devise/
メールアドレスで登録できないようにする
dbのマイグレートファイルを以下のように追記と変更を行う
#上の方にある #t.string :email, null: true, default: "" #コメントアウト t.string :username, null: false, default: "" #追記 #下の方にある #add_index :users, :email, unique: true #コメントアウト add_index :users, :username, unique: false #追記
編集が終わったら
rake db:migrate:reset
を行い、テーブルの作りなおしを行う
次にuser.rbにバリデーションの追加を行う
vim app/models/user.rb
validates :username, presence: true, length: { maximum: 20 } #usernameを利用してログインするようにオーバーライド def self.find_first_by_auth_conditions(warden_conditions) conditions = warden_conditions.dup if login = conditions.delete(:login) #認証の条件式を変更する where(conditions).where(["username = :value", { :value => username }]).first else where(conditions).first end end def email_required? false end def email_changed? false end
String Parametersの設定
以下を追記
app/controllers/application_controller.rb
before_action :configure_permitted_parameters, if: :devise_controller? protected def configure_permitted_parameters devise_parameter_sanitizer.for(:sign_up) << :username devise_parameter_sanitizer.for(:sign_in) << :username devise_parameter_sanitizer.for(:account_update) << :username end
config/initializers/devise.rb
#追記 config.authentication_keys = [:username]
補足 configを弄りたくない人はuserモデルのdevise に
:authentication_keys => [:username]
を追記するのでもいける
ビューの再設定
app/views/home/index.html.slim をemailからusernameに変更
p=current_user.email ↓ p=current_user.username
次に
app/views/devise/registrations/new.html.slim と app/views/devise/sessions/new.html.slim
emailのフィールドを全部消してから以下を記述
.field = f.label :username br = f.text_field :username, autofocus: true
rails s -b 0.0.0.0
で起動してからsigin_upとloginをしてみる
Twitter認証機能の追加
Twitter認証を行うにはAPIキーなどが必要になるのでここをみて作っておく
今回はAPIなどの設定ファイルをdirenvで管理するためここで設定しておく
まずrailsでTwitter認証が行えるようにUserモデルを以下のように編集する
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,omniauth_providers: [:twitter] validates :username, presence: true, length: { maximum: 20 } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~中略~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ end
次に環境変数に作ったAPIキーをrailsで使用する準備をする
devise.rbを編集
最後の行付近に記述する
config/initializers/devise.rb
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ config.omniauth :twitter, ENV['TWITTER_KEY'], ENV['TWITTER_SECRET'] end
これで準備ができたのでdirenvを使う
direnv edit .
エディタが開かれるのでexport文を書く
export TWITTER_KEY="xxxxxxxxxxxxxxxxx" export TWITTER_SECRET="xxxxxxxxxxxxxxxxxxxxxx"
入力し終わると以下のように表示される
direnv: loading .envrc direnv: export +TWITTER_KEY +TWITTER_SECRET
一度rails s
で一度twitter認証をしてみる
Unknown actionと表示されればおk
マイグレートファイルに下の二行を追加
db/migrate/20160628063208_devise_create_users.rb
t.string :provider t.string :uid
rake db:migrate:reset
でDBの作り直し
コールバック用のコントローラー作成
rails g controller sns_callbacks
中身は以下のようになっている
class SnsCallbacksController < 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モデルに追記
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"] end 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
これをやっておかないとuidやproviderなどが登録されず毎回 サインアップページに飛ばされてしまう
最後にコールバック用のコントローラーとしてsns_callbacksが呼ばれるようにroutesファイルを編集
config/routes.rb
Rails.application.routes.draw do devise_for :users, controllers: { :omniauth_callbacks => "sns_callbacks" } get 'home/index' root 'home#index'
これでできるはず