エンジニアのイシマルです。
前回のプランニングポーカーの記事から約2ヶ月、
今回はその間に起きた、とある事件について、記事にしたいと思います。
■事件の種「iPhoneアプリからサーバ(Rails4)で認証してログインする機能」
ログイン、Webアプリでもよくある機能で、iPhoneアプリでもユーザーを認証するのにログイン機能を作ることはよくあります。
今回は、そのログイン認証に、セッション(cookie)を用いて行うことにしました。
今回試した認証は、Facebookログインで、以下の設計で実装することにしました。
- 【アプリ】サーバへログインAPIを叩く(パラメータで、FacebookのAccessTokenを付与し、SSL通信)
- 【サーバ】は、受け取ったAccessTokenを用いて、FacebookAPIで、そのユーザーのFacebook IDを取得
- 【サーバ】usersテーブルで、該当するFacebook IDのユーザーを抽出
- 【サーバ】セッションに、user_idを保存
- 【サーバ】セッションIDを、responseHeader(Set-Cookie)に入れて、アプリにAPIの戻り値を返す(Railsが勝手にやる)
- 【アプリ】responseHeaderで受け取ったcookie情報を以降付与して、サーバと通信
ブラウザで、以下のようなAPIを実行し、正しく認証されることを確認した後に、アプリ側SEのKingさんに iPhoneアプリ側の実装をお願いしました。
https://192.168.100.1/api/login?facebook_access_token=hoge
※APIの通信は、これらのパラメータの他に、独自の暗号化も行っています。
しばらく時間が経ったあと、Kingさんから、
King「セッションCookieの情報を取得できない。」
と連絡が来ます。
私の方は、ブラウザでセッションCookieがresponseHeaderに入っているのを確認していたので、
私「そんなはずはない!ほら、このブラウザで実行した結果からわかるようにセッションCookieがセットされて返ってる!アプリ側の問題じゃないんですか?」
と返しました。
しばらく時間が経ったあと、またKingさんから、
King「やっぱりCookieの情報を取得できない!」
と連絡が来ます。
私は「おかしいな」と思い、Kingさん側のデバッグした情報を見せてもらいましたが、 確かにセッションCookieの情報がresponseHeaderに入っていないんです。
そこで、我らが編集長、キクチさんに相談させていただいたところ、
どうも、サーバ側(Rails側)の問題ということが判明。
私の心の声「ナンテコッタ・・・」
Rails4では、ApplicationController(ベースとなるコントローラ)で、CSRFの対策が施されています。そこに、APIで使うなら、
のように書けば良いですよ、と書かれているので、素直にこの通りに採用していました。protect_from_forgery with: :null_session
しかし、セッションを空にしていたのは、どうやらコイツの仕業だったのです orz。
クラス仕様:
http://api.rubyonrails.org/classes/ActionController/RequestForgeryProtection/ClassMethods.html
ここに、はっきりこう書かれていますね。。。
:null_session - Provides an empty session during request but doesn't reset it completely. Used as default if :with option is not specified.
結局、こう書き換えて、無事解決しました。
class ApiController < ApplicationController skip_before_action :verify_authenticity_token
今回は、自戒を込めて、記事にさせていただきました。
問題がある時は、まず自分を疑うのが良いですね。
では、また次回まで!