前回の続き。おかげさまで、Iteration 周りは自力でも進められそうな感じになってきたので、Twitter でユーザーがログインする機能を作りたいとおもった。 omniauth-twitter を使えば楽にできるけど、楽に出来すぎてよくないので OAuthからちゃんとやった方がいいですよというアドバイスを頂き、どっちがいいのかなあと思っていたのだが、あまり時間もないので omniauth 使うことに。今回、@katton さんに教えていただいたのですが、 OAuth とは?というところからわかりやすく教えていただき、だいぶカバーされた感じなので、下記おすすめ書籍を読んで理解深めたい。
また、@katton さんは、リアルタイムでエディタでお題を書きながら、そのメモをエディタ上で見せながら進めてくださり、さらにそのメモを帰宅後送って下さるなど、大変ありがたく、またこの方法は教えるのに大変よい方法だと思った。頂いたメモをベースに追記する感じで、復習が進められた(下記は、その記録)。ありがとうございました!
0. OAuthとは
リソース保有者が、リソースプロバイダー(Twitterとか)に対して「アプリに自分の情報を教えても良い」と許可すること。
※ 今回はサーバサイドWebアプリケーションフローを採用。
おすすめ書籍
1. twitterにアプリケーション登録
本番用と開発用両方を登録しておく
dev.twitter.com/apps
- 本番用 Callback URL:
domain/auth/twitter/callback
- 開発用 Callback URL:(pow.cx を使った場合):
APPNAME.dev/auth/twitter/callback
omniauth の callback URL は、/auth/[プロバイダ名]/callback
callback
get '/auth/:provider/callback', to: 'sessions#create'
2. Figaro使う
laserlemon/figaro · GitHub
なぜ?:アプリ固有の設定をapplication.ymlというカタチで保存、Figaro.env.xxxで読み込みができる。
config/application.yml
に先ほどdev.twitter.com で登録したときの、key と secret を書いておく。
こうすることで、各プロバイダにアクセスする用の key 達が一元管理できる。
config/application.yml はインストール時に自動的に.gitignore に追加されるので、ソースコードを公開してもヒミツにしておけるのもナイスだ。
development: twitter_key: xxxxxxxxxxxxxxxxxxxxx twitter_secret: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx production: twitter_key: xxxxxxxxxxxxxxxxxxxxx twitter_secret: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Heroku
Herokuに デプロイするときは、この1行コマンド打てばOK
$ bundle exec rake figaro:heroku
3. Omniauth を使う
intridea/omniauth ・ GitHub
arunagw/omniauth-twitter ・ GitHub
※ omniauth-twitter のように、付属機能的な意味合いの場合はGem名の接続子が "-" になる
インストール
Gemfile
gem 'omniauth' gem 'omniauth-twitter'
$ bundle install
設定ファイルをじぶんで作る
複数プロバイダを使用する場合は、別ファイルに設定を書いたりしないといけなくてめんどい。今回はTwitterだけなので1つのファイルでOK。
config/initializers/omniauth.rb
公式のUsageではこんな感じに、直接KEY と SECRET を書けとなっているが…
Rails.application.config.middleware.use OmniAuth::Builder do provider :twitter, "CONSUMER_KEY", "CONSUMER_SECRET" end
先ほど、Figaro を入れたおかげで、config/application.yml に書いたKEY 達をこのように設定から呼び出せるようになってる。ナイスだ。
Rails.application.config.middleware.use OmniAuth::Builder do provider :developer unless Rails.env.production? provider :twitter, Figaro.env.twitter_key, Figaro.env.twitter_secret end
4. Userモデルをつくる
必要なカラムを確認
omniauth-twitter の Authentication Hashを見ると、Twitterから取れる情報が一覧できる。いっぱい取れる情報があるんですねー。
今回は、ログインに使うくらいなので、名前とアイコンくらいがあればいいかなー。
uid, secret, token は必ず必要だよ。ってなわけでとりあえず必要なのは以下の5つとなる。
- uid
- name
- nickname
- image
- token
- secret
全部 string でいいので、並べるだけでOKだった。
$ rails g model user uid name nickname image token secret
uid は必須かつユニークにしておこう。
/db/migrate/20130815115856_create_users.rb
class CreateUsers < ActiveRecord::Migration def change create_table :users do |t| t.string :uid, null: false t.string :name t.string :nickname t.string :image t.string :token t.string :secret t.timestamps end add_index :users, :uid, unique: true end end
$ rake db:migrate
5. OAuth認証をする
5-2. powインストール
Pow: Zero-configuration Rack server for Mac OS X
開発ディレクトリのシンボリックリンクを~/.pow に追加しておくだけで、http://APPNAME.dev
というURLでアクセスできるというイカしたもの。みんな大好き37signals 製。CoffeeScript と Node.js でできてるらしい。シャレオツだね。
実際のPowの導入は、P4D時間内に間に合わず、帰宅後になったんだけど、少しハマった。
$ curl get.pow.cx | sh
だけで、
$ cd ~/.pow
$ ln -s /path/to/myapp
http://APPNAME.dev
でアクセスできるはずなんだけど、
っていうエラーが出てる。私の環境は、RVMでなくてrbenv(brew インストールでない)なのでおそらく Troubleshootingに書いてある
LoadError: no such file to load -- bundler/setup
~/.powconfig
に下記パスを追加するというのをやれば大丈夫なはず…あれ、動かない。。上記対処後アプリの再起動的なものも色々試みてだめだったので、一回powをUninstallしたのち再インストールしたら動いた。
export PATH="$HOME/.rbenv/shims:$HOME/.rbenv/bin:$PATH"
5-3. Callbackを受け取って必要な情報をUserへ
logger.info で確認
app/controllers/session_controller.rb
def create puts '===========' logger.info(request.env['omniauth.auth']) redirect_to '/' end
ログにTwitterからの情報っぽいものが出力されたら成功(puts '=========' は、ログの目印的に付加してる)
5-4. User.find_or_create_from_auth_hash の実装
この辺の半ばでP4Dはタイムアップになったので、@katton さんが書いて下さったメモを参考に、家で続きやる
app/models/users.rb
に uidからuserを見つける -> なかったら新規にuser を作る という機能に必要なクラスメソッドを作る
# find_or_create_by_oauth 既存のoauthを見つける -> 無かったら新しく作る def self.find_or_create_by_oauth(auth) if user = User.find_by_oauth(auth) user else User.create_by_oauth(auth) end end # uid を使って 既存の user を見つける def self.find_by_oauth(auth) user = User.find_by_uid(auth['uid']) # user = User.find_by(uid: auth['uid']) # Rails4ではこう書ける if user user else nil end end # 新しく user を作る def self.create_by_oauth(auth) user = User.new user.uid = auth['uid'] user.name = auth.info['name'] user.nickname = auth.info['nickname'] user.image = auth.info['image'] user.token = auth.credentials['token'] user.secret = auth.credentials['secret'] user.save user end
同じく app/controllers/sessions_controller.rb
にも authから ユーザーを見つけるクラスメソッドを追加
https://github.com/intridea/omniauth#integrating-omniauth-into-your-application
def create @user = User.find_or_create_by_auth(auth_hash) #self.current_user = @user redirect_to '/' end protected def auth_hash request.env['omniauth.auth'] end
current_user は まだないのでコメントアウトしておく。さきほど作った、Twitterへのリンクをクリックして確認してみる -> 認証ボタン押して戻ってきたら
$ rails c
User.find(:all)
[#
おー、ユーザー情報入ってるーヾ(*'ω'*)ノ゙
5-5. sessionにuser_idを保存して、「ログイン」をさせる
参考)https://speakerdeck.com/takai/login-form-from-scratch
app/controllers/sessions_controller.rb に
session[:user_id] = @user.id を追加
def create @user = User.find_or_create_by_oauth(auth_hash) #self.current_user = @user session[:user_id] = @user.id redirect_to '/' end
5-6. ログインの確認
app/controllers/application_controller.rb
に current_user の メソッドを追加
def current_user if session[:user_id] User.find(session[:user_id]) else nil end
確認してみようー
app/controllers/sessions_controller.rb
で loggers.info を書いて確認
def create ・ ・ ・ logger.info("========current_user.name: #{current_user.name}") #self.current_user = @user end
※ 結局 この self.current_user の行は、別のところで current_user を定義したのでいらなさそげだ
ログに、Twitterの名前が出てきたら成功っぽい!
と、そんなわけで、一連のログイン機能のベースができたっぽいので、ここから、user と task や iteration を紐付けたり、ログインページを作ったりする。公開に一歩近づいた感じが嬉しい。
感想とか
- omniauth 使うと簡単だと聞いたけど、私のレベルだとそれでもかなり苦労したので、結果 omniauth 使ってよかったのではないかと思う・・・
- 今回も、@katton さんに大感謝だ・・・本当に、ありがとうございましたm(__)m
- Pow便利だなー。Powいかすー。
- 毎回、違うプログラマさんに教えていただくが、プログラマさんによってやり方も色々だったりするので、結果、色んな方法やアイデア、色んなツールが知れることになり、これはかなり素晴らしいのではないかと思った!(ありがたい・・・
- find_or_create_by_auth はできたけど、これだけだとユーザーネームやアイコンを変えた時とかに更新されないので、それ用のメソッドがまた必要なんだろうなー
- また、時間いっぱい教わってしまい、こちらからデザインの話とかすることができずに申し訳なす・・・
- 今回、半分の時間で交代で区切っていただいたのだけど、やっぱり教わるとなると時間が足りずにフルに教わることになってしまう。
- ので、前回教わった人は次回教えればいいんじゃないか、ってことにもなったので、次回はなにか教える側に回ります。