Pre Hachioji.pmとHachioji.pm #14に参加した。

pre Hachioji.pm #14 : ATND
Hachioji.pm #14 : ATND

Pre Hachioji.pm

Preのほうは、ハッカソン的なこと!ということだったけど、フタを開けるともくもく会的な感じになりました。


みなさんの発表に理解している範囲で感想を書く。

  • @hondallicaさん:が途中で顔出して会場代とパイの実を差し入れしてくれた。ありがとうございます!パイの実おいしかったです!
  • @perl_o_palさん:X Window+SWIG
    • SWIGを知らなかったので感心してるそばからSWIGがDISられてた。
  • @uzullaさん:ファイルアップローダー。仕事で使うので権限管理もしっかり。
  • @yellow844さん:Node.jsの百人一首アプリ。Web Socketで対戦。縦書きがかっこいいと評判。
  • @ichigotakeさん:Google Docsと連携するウェブアプリ。
    • Google Docsは色々と使い道があって楽しそう。
  • @kkotaro0111さん:iPhoneアプリで脱出ゲームを作るために、スライドの実装。1->2->3->4->1とループするスライドを、pushとpopで作った。
    • そういうiPhoneならではのUIを使うっていうのは楽しそう。
  • @maka2_donzokoさん:Web版パルモン
    • コマンド入力形式
      • そういえば、あとのHachioji.pmで、@uzullaさんとコマンド入力式のアドベンチャーゲームについて話してて楽しそうでした。
  • @charsbarさん:CPAN Testers?の表示をモダンに。
  • @mgikenさん:のところがスッポリ記憶から抜け落ちてる。すみません。
    • 思い出した。GANGで作ったBlogのCSSを作ってた。
    • Hachioji.pmでは情弱な私のためにGANC講座をしてくれた。
  • @cj2tszkさん:Googleの日本語入力APIで、コマンドラインで変換候補を取得。
    • ふだんWAFばかり触っているので、スクリプトベースで使うとこうなるんだー、というのが面白かった。
  • @hide_o_55さん:KyTeaという日本語とかの形態素解析をするののNode.js版ラッパー
    • NPM(PerlでいうCPANみたいなもののNode.js版?)に公開したとか。ちょうかっこいい。
    • 今見たらPerlモジュールもあって、Perl自然言語処理するならこういうの使えるんだろうなー。

人とIDの対応が曖昧なので、間違ってたらごめんなさい。理解できてないところが多すぎて非常に残念な感じに。


僕は、Facebook内で動くタイプのFacebookアプリをつくろうとしてて、Amon2::Auth::Site::Facebookで認証通ったよーという、大変レベルの低い発表をしました。
詳しくはこちら。
Facebook内で使うタイプのアプリで認証にAmon2::Authを使う方法 - hsksnote


といっても、個人的には収穫は多くて。


まず、集中できる時間が持続するのはすごい良かった。普段家でやってるときに躓くと、そのうち漫画に逃げたり、諦めてよろしくないと知りつつも別の方法を採っちゃったりする。
でも今回は、そういうことはせず、プラグインのソースを読んで、動きをある程度理解して、原因を特定するところまで持っていけた。


で、解決できたのもよかったんだけど、ソースを読んで動きを理解するというのが、かなり勉強になった。
たとえばAmon2::Authだと、認証用のURLにトリガーをつけて、設定ファイルから設定を読み込んで、Site::Facebookモジュールで認証用URIを組み立てて、そこにリダイレクトして、というようなことをやってる。
Amon2::Plugin::Web::Auth - auth with SNS - metacpan.org
Amon2::Auth::Site::Facebook - Facebook integration for Amon2 - metacpan.org


使うモジュールのソースを読むのは、そのモジュールの使い方がわかるだけじゃなく、人がどういうコードを書いているのかを知ることでもあるので。

Hachioji.pm

去年の #10 以来2度目の参加。
Hachioji.pm #10 に参加したよ - hsksnote
今回は、テーマと無理やり絡めて人生相談的なLTをしてみて、いろいろとためになるフィードバックを頂いた。
その一方で、自分のレベルの低さというのも如実に見えてしまって、それはそれで貴重なことだしありがたいんだけど、バイトから始めるレベルなのかと思うとヘコむものがあります。自分でもそうなのかもなーと思いつつも、やっぱり。
Hachioji.pmというコミュニティに対しても、自分からは何も提供できてないし、もうちょっと自分何とかしなきゃなーと思いました。

Facebook内で使うタイプのアプリで認証にAmon2::Authを使う方法

前回は外部のFacebookアプリの認証をやって、それを内部でやろうとしてうまくいかなかったということを書きました。
Amon2を使って!DotCloudで!Facebook アプリを作りたい!(まだ試し中) - hsksnote
今回はそれがうまくいったので、ソースと一緒に解説します。


前提として、Facebook内で動かすアプリは、Facebookのキャンバスページ内のiframeに表示されます。なので、ローカルでテストできません。これが非常に面倒くさい。
今回はDotCloudにテストアプリを作って動かしました。


Facebook側の設定は、こちらの記事をご参照。キャンバスURLを設定してやる必要があります。
Apps on Facebook.com : Facebook開発者向けドキュメントの日本語訳とTips


まずは認証につかうAmon2プラグインへの依存を書きます。
Makefile.PL

         'Plack::Session'                  => '0.14',
         'Test::WWW::Mechanize::PSGI'      => '0',
         'Time::Piece'                     => '1.20',
+               'Amon2::Plugin::Web::Auth'        => '0',
+               'Amon2::Auth::Site::Facebook'     => '0',
     },


コンフィグファイルに認証用の設定を書きます。callback_uriにキャンバスページのURLを設定するのがポイント。Facebookのアプリ設定画面にURLが出てます。
dotcloud上で動かすので、development.plだけじゃなくdeployment.plにも同じ事を書く。
config/deployment.pl & config/development.pl

             sqlite_unicode => 1,
         }
     ],
+       Auth => {
+               Facebook => {
+                       client_id => 'xxxxxxxxxxxxxxx',
+                       client_secret => 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
+                       callback_uri => 'http://apps.facebook.com/hsksyusk_sample_app/',
+                       scope => 'read_stream',
+               }
+       }
 };


プラグインを読み込みます。redirect先にも、キャンバスページのURLを指定。
lib/fbsample/Web.pm

+__PACKAGE__->load_plugin(
+       'Web::Auth',
+       {
+               module => 'Facebook',
+               on_finished => sub {
+                       my($c, $token, $user) = @_;
+                       my $name = $user->{name} || die;
+                       $c->session->set('name'  => $name );
+                       $c->session->set('site'  => 'facebook');
+                       $c->session->set('token' => $token);
+                       return $c->redirect('http://apps.facebook.com/hsksyusk_sample_app/');
+               },
+               on_error => sub {
+                       my ($c, $error ) = @_;
+                       warn ("auth_error!![$error]");
+                       return $c->redirect('/');
+               },
+       }
+);

ここでURL末尾の/を書いてなくてずっとハマってた。


今回作ってるWebアプリは、FacebookのキャンバスページからはPOSTで呼ばれます。
おなじくWeb.pmで、Web::CSRFDefenderプラグインの読み込みをコメントアウトします。
lib/fbsample/Web.pm

 # load plugins
 __PACKAGE__->load_plugins(
     'Web::FillInFormLite',
-    'Web::CSRFDefender',
+    # 'Web::CSRFDefender',
 );


HTTPレスポンスヘッダーのX-Frame-Optionsの設定を無効にします。
このオプションは、iframeで読み込まれることを許可しない設定で、クリックジャッキング攻撃を防ぐ目的で使われるのだとか。
lib/fbsample/Web.pm

         $res->header( 'X-Content-Type-Options' => 'nosniff' );

         # http://blog.mozilla.com/security/2010/09/08/x-frame-options/
-        $res->header( 'X-Frame-Options' => 'DENY' );
+        # $res->header( 'X-Frame-Options' => 'DENY' );

このへん、セキュリティ的に無防備になってしまうところは、呼び出し元をfacebookからに限るとか、対策を考えないといけないですね。


ここまでで設定が終わったので、処理を書いていきます。
と言っても、こちらの記事のコードそのままでいけます。
Amon2でFacebookAPIを使う その2 - Perl勉強メモ アルパカDiary出張版
lib/fbsample/Web/Dispatcher.pm

+use JSON qw(decode_json);

 any '/' => sub {
     my ($c) = @_;
-    $c->render('index.tt');
+       my $sign = $c->req->param('signed_request');
+       my $data;
+       my $token = $c->session->get('token');
+       my $sign = $c->req->param('signed_request');
+       if( $token ) { #loggedin
+               my $ua = LWP::UserAgent->new();
+               my $res = $ua->get("https://graph.facebook.com/me/home?access_token=${token}");
+               $res->is_success or die $res->status_line;
+               $data = decode_json($res->decoded_content);
+
+               $c->render(
+                       'index.tt',
+                       {
+                               name => $c->session->get('name'),
+                               data => $data->{data},
+                       }
+               );
+       }
+       else {
+               $c->redirect('/auth/facebook/authenticate');
+       }
+};

 post '/account/logout' => sub {
     my ($c) = @_;
     $c->session->expire();

DotCloud特有なのかわかりませんが、リクエストがPOSTで来ると、responseが全部来る前にセッションが完了しちゃうという問題がありまして。それに対応するために、$c->req->param('signed_request')を変数に入れてます。こうやって明示的にパラメータを読んでやることで、この問題を回避することができるみたい。


HTMLテンプレートはこんな感じ。
tmpl/index.tt

[% WRAPPER 'include/layout.tt' %]
<section>
[% IF data %]
        Hi! [% name %]
        <section>
                <ul>
                        [% FOR v IN data %]
                        <li>[% v.created_time %] [% v.from.name %] [% v.message %]</li>
                        [% END %]
                </ul>
        </section>
[% END %]
</section>
[% END %]


cssで幅をiframeに合わせます。
static/css/main.css

+.container {
+       width: 740px;
+}


これで全部です。git addしてgit commitしてdotcloud pushしてFacebookのキャンバスページにアクセスしたら、リダイレクトして認証したあとにキャンバスページに戻ってきて、自分のニュースフィードの中身が表示されるという寸法です。