[CouchDB][CouchApp] CouchApp におけるログイン処理の詳細
認証の話は別の媒体で書くとして、実際どうやってログイン処理をやるの?という話。CouchApp を入れれば、とりあえずOKです。
CouchDBの認証はbasic認証で提供されておりますが、認証ダイアログを出す attemptLogin(win, fail) と 現在認証がすんでいるかどうかを判定する loggedInNow(loggedIn, loggedOut) という2つのメソッドが用意されています。
attemptLogin : function(win, fail) { // depends on nasty hack in blog validation function db.saveDoc({"author":"_self"}, { error: function(s, e, r) { var namep = r.split(':'); if (namep[0] == '_self') { login = namep.pop(); $.cookies.set("login", login, '/'+dbname) win && win(login); } else { $.cookies.set("login", "", '/'+dbname) fail && fail(s, e, r); } }}); }, loggedInNow : function(loggedIn, loggedOut) { login = login || $.cookies.get("login"); if (login) { loggedIn && loggedIn(login); } else { loggedOut && loggedOut(); } },
で、attemptLogin が面白くて、コメントにあるとおり、CouchDBのvalidation機能をhackしています。blog validation ってなんやねん?という話があるのですが、これは、CouchDBで実装されたSofaというブログツール(http://github.com/jchris/sofa/tree/master) に答えがあって、validate_doc_update.js を見ます。このJSは、ドキュメントの更新(HTTP PUT|POST|DELETE)が発生したときに呼び出される検証用スクリプトです。
http://github.com/jchris/sofa/blob/ac978f63ee79dee1ea67f27d61197fa4c3cde60e/validate_doc_update.js
function (newDoc, oldDoc, userCtx) { var type = (oldDoc || newDoc)['type']; var author = (oldDoc || newDoc)['author']; function forbidden(message) { throw({forbidden : message}); }; ... // docs with authors can only be saved by their author if (author) { // dirty hack to provide userCtx.name to the client process if (author == '_self') userCtx.name ? forbidden('_self:' + userCtx.name) : unauthorized('Please log in.');
が答えで、PUTされたドキュメントに { "author" : "_self" } が仕込まれていたら、userCtx.name (実際はリクエストヘッダAuthorizationに含まれる認証済みユーザー名) を見て、あれば(認証が成功していれば)、forbiddenを返します。validate_doc_update.js 中にthrowされるとCouchDBは 403 Forbbiden を返してDB自体は更新しませんが、_self:"認証済みユーザー名" という文字列を返すので、その文字列をparseしてクライアント側でクッキーにユーザー名をセットしています。
一方、userCtx.nameがない(認証が成功していない)場合は、401 Unauthorized が返されますので、ブラウザの挙動により、認証用のダイアログが表示され、再度Authorizationヘッダーをつけたリクエストが発行される、ということになります。CookieをセットするのがクライアントのJavaScriptという発想。認証はサーバーでやるべきで、それはサーバーでやるけれど、認証の情報を記憶するのはクライアントが決めればいいよね、とClientでできることはClientでやる姿勢がすばらしい。
というわけで、CouchAppを使ってログインをしたい場合、
- validate_doc_update.js に { author: "_self" } の場合の検証ロジックを記述する(上記、Sofaのコード参照)
- ログインを実装するページ上で、attemptLogin を呼び出す (A)
- ログイン名を表示するページでは、loggedInNow を呼び出す (B)
とまとめらます。attemptLogin とかの使い方は、単なるJavaScriptの話ですが、ページのロード時に呼び出せばよくて、CouchAppの流儀的には以下のようにするようです。$.CouchApp.(function(app){ // ページロード時に実行するコード }) としておけば
<script type="text/javascript"> $.CouchApp(function(app) { app.attemptLogin( function(login){ // ログイン成功時 login に認証済みのユーザー名が入る }, function(){ // ログイン失敗時 } ); }); </script>
<script type="text/javascript"> $.CouchApp(function(app) { app.loggedInNow( function(login){ // ログイン済みの場合 login に認証済みのユーザー名が入る }, function(){ // ログインしていない場合 } ); }); </script>
しかし、これはワクワクする。Rails以来の面白さだ。