読者です 読者をやめる 読者になる 読者になる

ゆーすけべー日記

はてなBlogってどーなの!?

イカ娘でTwitter OAuth認証

Web Essay Perl

Webサービスのログイン方法の一つに「Twitterログイン」が最近多く見られるようになってきました。 サイト上でのユーザー登録無しでTwitterアカウントを引き回すことも工夫によってはできますので、 ユーザーや開発者にとって手間が省けるという利点があるのではないでしょうか。 今回はアニメ「イカ娘」を題材とした簡単なWebアプリを作りつつTwitterのOAuth認証の流れと実装を見ていきましょう。

イカ娘タイムライン

Twitter OAuth認証の流れ

Twitter OAuthでは主にキーと鍵のペアの値がいくつかでてきて混乱しがちなのでイカ、おっと間違えた、以下にまとめておきます。

  • コンシューマトークン、コンシューマシークレット」 アプリケーション固有のキーと鍵。Twitter Developerのページで発行される。アプリケーション開発者以外に知らせてはいけず、通常は設定ファイルなどに記載してアプリケーションで読み込ませる。
  • リクエストトークン、リクエストトークンシークレット」 アプリと連携する際にTwitterのサイトでログインをしてもらうが、そのログイン画面へリダイレクトさせるURLが発行されるタイミングと同じくらいで使うトークン。リダイレクトさせる前に取得し、クッキーなどで保持し後ほど自サイトにユーザーが戻ってきた時に照合させる。
  • アクセストークン、アクセストークンシークレット」 そのアプリにおけるユーザー固有のキーと鍵。これと上記のコンシューマトークン&シークレットがあればTwitterにアプリケーションでログインが可能になりタイムラインの取得などができるようになる。実際にはTwitter上の認証画面から返ってきた際に初めて取得できる。

そして上記の言葉を使った、カタカナばかりでちょっと分かりにくい、開発の流れはこのようなものです。

  1. Twitter Developerのページにてアプリケーションを登録
  2. アプリケーション固有のコンシューマトークンというキーとコンシューマトークンシークレットという鍵をもらう
  3. アプリを実装。設定からコンシューマトークン&シークレットを呼び出せるようにしておく
  4. コンシューマトークン&シークレットを元にリクエストトークンとリクエストトークンシークレットを作り出す
  5. リクエストトークン&シークレットをクッキーで保持しておく
  6. Twitterの認証ページにリダイレクトさせる
  7. コールバックURLにGETパラメータの引数付きで返ってくるのでそれを元にユーザー固有のアクセストークン、アクセストークンシークレットを取得
  8. アクセストークン&シークレットをクッキーで保持しておく
  9. コンシューマキー&シークレット、アクセストークン&シークレットを使ってログイン、タイムラインなどを取得

サンプルアプリの方針

Twitterログインをして自分のタイムラインを見られるような機能をサンプルとして作成したいのですが、 少々それだけだと味気ないです。 Perlのライブラリ置き場CPANを漁っていると「Acme::Ikamusume」なる興味深いモジュールを発見したのでそれを使ったアプリを考えてみます。このAcme::Ikamusume、動かすのにMeCabという形態素解析のソフトが必要なのが多少敷居が高いですが、簡単に使えてかつ面白いです。

my $text = Acme::Ikamusume->geso('変更したいテキスト');

とするとアニメ「イカ娘」でイカちゃんがしゃべるような言葉に変換されます。自分のTwitterのタイムラインがイカ娘変換されるとどうなるか!?おもしろいんじゃなイカ!?ってことでやってみます。ちなみに作者の富田さんが自身でイカ娘変換を簡単に試せるサービスとAPIを公開していますのでそちらもご参考ください。

実装の方針ですが、PerlのMojolicious Liteを使ったものとさせていただきます。上記の流れを最低限の機能で追うので他の言語でも応用できると思います。また、Mojolicious Liteのセッション管理は若干貧弱なためPlack::Sessionを代わりに、Twitter APIの処理にはNet::Twitter::Liteを使います。

アプリケーションの登録とコンシューマキーの取得

まず、Twitter Developer のページでこれから作るアプリケーションの情報を登録します。

Twitter Develper

いくつかの項目は後から変更が効きますが「Name」の欄は変更がきかなかったり早い物勝ちだったりするので慎重に考えて入力しましょう。アプリケーションを登録したらいつでも「My Applications」からそのアプリを選んで、設定情報を確認することができます。コンシューマキー&シークレットである「Consumer key」「Consumer secret」という文字列が表示されていると思うので、それを後ほど使います。

アプリケーションの実装、設定からキーを呼び出す

いよいよアプリケーションの実装に入っていきます。mojoコマンドを使ってアプリケーションの雛形を作ったら「myapp.conf」などの名前で設定ファイルを作り、そこにコンシューマキー&シークレットを書き込みます。またアプリケーション内でConfig Pluginを使い、設定を呼び出します。

myapp.conf

{
    consumer_key    => 'xxxxxxxxxxxxxxxxxxxxxx',
    consumer_secret => 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
};

myapp.pl

my $config = plugin('Config');
my $nt = Net::Twitter::Lite->new(
    consumer_key    => $config->{consumer_key},
    consumer_secret => $config->{consumer_secret},
);

consumer_keyとconsumer_secretをそのまま渡してNet::Twitter::Liteのインスタンスを作ってそれを使い回していきます。

Twitterログイン画面に飛ばす

次に「/login」にアクセスするとTwitterのログイン画面に飛ぶために、URLを取得しリダイレクトさせる実装をします。 その際にリクエストトークン&シークレットをセッションで保持しておくのを忘れないようにします。

get '/login' => sub {
    my $self    = shift;
    my $session = Plack::Session->new( $self->req->env );
    my $url     = $nt->get_authorization_url(
        callback => $self->req->url->base . '/callback' );
    $session->set( 'token', $nt->request_token );
    $session->set( 'token_secret', $nt->request_token_secret );
    $self->redirect_to($url);
};

「$nt->get_authorization_url」のcallbackパラメータで指定している通り、Twitter側でのログインの処理が終わると「/callback」に戻ってくるように指定しています。

Twitterログイン画面から戻ってきた時の処理

ということで次は「/callback」を実装します。GETメソッドのパラメータ「oauth_verifier」の値と照合させてアクセストークン&シークレットを取得できます。それらの値をセッションで保持し、トップページにリダイレクトさせています。また、もしユーザーがTwitterのログイン画面でログインを拒否した場合には「denied=xxxxxxxx」というパラメータ付きで「/callback」に返ってくるのでその際はセッションのセットをしていません。

get '/callback' => sub {
    my $self = shift;
    unless ( $self->req->param('denied') ) {
        my $session = Plack::Session->new( $self->req->env );
        $nt->request_token( $session->get('token') );
        $nt->request_token_secret( $session->get('token_secret') );
        my $verifier = $self->req->param('oauth_verifier');
        my ( $access_token, $access_token_secret, $user_id, $screen_name ) =
          $nt->request_access_token( verifier => $verifier );
        $session->set( 'access_token',        $access_token );
        $session->set( 'access_token_secret', $access_token_secret );
        $session->set( 'screen_name',         $screen_name );
    }
    $self->redirect_to('/');
};

ログイン後のホーム画面の処理

セッションにはアクセストークン&シークレットが保持された状態になるので、これでユーザーのタイムラインを取得することができるようになります。ホーム画面では「$nt->home_timeline」で取得したツイートにイカ娘変換をかけて配列リファレンスに入れ、そのままテンプレートに渡しています。テンプレートではその配列リファレンスやセッションに値が入っていればそのまま表示し、そうでなければログインしていない状態と見なしログインを促すリンクを張っておきます。

myapp.pl

get '/' => sub {
    my $self    = shift;
    my $session = Plack::Session->new( $self->req->env );
    my $tweets;
    if ( $session->get('access_token') ) {
        $nt->access_token( $session->get('access_token') );
        $nt->access_token_secret( $session->get('access_token_secret') );
        for my $tweet ( @{ $nt->home_timeline } ) {
            next if $tweet->{user}{protected};
            $tweet->{text} = Acme::Ikamusume->geso( $tweet->{text} );
            push @$tweets, $tweet;
        }
    }
    $self->stash->{screen_name} = $session->get('screen_name');
    $self->stash->{tweets}      = $tweets;
    $self->render('index');
};

templates/index.html.ep

% if ( $screen_name ) {
<h3><a href="/">@<%= $screen_name %>さんのイカ娘タイムライン</a></h3>
<ul>
  <li><a href="/logout">ログアウト</a></li>
</ul>
% }else{ 
<ul>
  <li><a href="/login">Twitterログイン</a></li>
</ul>
% }

% if($tweets) {
<hr />
% for my $tweet ( @$tweets ) {
<p style="margin-bottom:1em;padding-bottom:1em;border-bottom:1px dotted #ccc;">
  <img src="<%= $tweet->{user}{profile_image_url} %>" alt=""
       width="36" height="36" style="float:left;margin-right:1em;"/>
  <a href="http://twitter.com/<%= $tweet->{user}{screen_name} %>" 
     target="_blank">@<%= $tweet->{user}{screen_name} %></a>: <%= $tweet->{text} %>
  <span style="display:block;clear:both;"></span>
</p>
% }
% }

ログアウトの実装

最後にログアウトの実装をします。保持しているセッションをクリアして、トップページに遷移させます。

get '/logout' => sub {
    my $self    = shift;
    my $session = Plack::Session->new( $self->req->env );
    $session->expire();
    $self->redirect_to('/');
};

デモとコードの在りか

さてこれらのコードを組み合わせれば「イカ娘タイムライン的な何か」が完成です。

イカ娘タイムラインその2

そのうち使えなくなってしまう可能性大ですが、一応今のところ動いている物をイカの、いや以下のURLでデプロイしておきました。機能は少ないですがあなたのイカ娘タイムラインを眺めることができると思います。

また、ソースコード一式をgithubに置いておきます。

こうやって作ってみるとタイムラインを見るだけじゃなくてイカ娘な発言もしたくなってくるでゲソ。 ってな感じでアプリを拡張したりするのもいいと思います!ということでお楽しみください。

お知らせ

メルマガ「ゆーすけべーラジオ」ではこうしたWebサービスにまつわるエッセイ風のテキストやデザイン思考について書いています。よろしければお試し購読を。