koba::blog

小林聡: プログラマです

OAuth APIのproxyを作ってみた

OAuth APIのproxyを作ってみました。生のリクエストに署名してAPIに転送し、結果を返します。

login.cgi

#!/usr/bin/perl -T

use strict;
use warnings;
use CGI;
use CGI::Session;
use OAuth::Lite::Consumer;

my %oauth_config = (
    consumer_key        => '_____________________',
    consumer_secret     => '_________________________________________',
    request_token_path  => 'https://api.twitter.com/oauth/request_token',
    authorize_path      => 'https://api.twitter.com/oauth/authorize',
    access_token_path   => 'https://api.twitter.com/oauth/access_token',
);

my $cgi = new CGI;
my $ua = new OAuth::Lite::Consumer(%oauth_config);

my $session = new CGI::Session(undef, $cgi, {Directory=>'/tmp/session'});

if ($cgi->request_method eq 'POST') {

    my $request_token = $ua->get_request_token(callback_url => $cgi->url);
    $session->param(request_token=>$request_token);

    my $url = $ua->url_to_authorize(token => $request_token);

    print $session->header(-status=>302, -location=>$url);
}
elsif ($cgi->request_method eq 'GET') {

    my $access_token = $ua->get_access_token(
                            token    => $session->param('request_token'),
                            verifier => $cgi->param('oauth_verifier'));
    $session->param(access_token=>$access_token);

    print $session->header(-status=>302, -location=>'./');
}
else {
    print $session->header(-status=>405);
}

OAuthの認証シーケンスを実行し、アクセストークンをセッションに保存します。セッション管理にはCGI::Sessionを使っています。

Twitter用の設定になってますが、%oauth_config を変更すれば他のサイトでも大丈夫なはずです。*1

proxy.cgi

#!/usr/bin/perl -T

use strict;
use warnings;
use CGI;
use CGI::Session;
use OAuth::Lite::Consumer;

my %oauth_config = (
    consumer_key        => '_____________________',
    consumer_secret     => '_________________________________________',
);
my $api_base_url = 'http://api.twitter.com/1';

my $cgi = new CGI;
my $ua = new OAuth::Lite::Consumer(%oauth_config);

my $session = new CGI::Session(undef, $cgi, {Directory=>'/tmp/session'});
my $access_token = $session->param('access_token');

my %params = map { my @value = $cgi->param($_); $_ => \@value } $cgi->param;

my $response = $ua->request(
            method  => $cgi->request_method,
            url     => $api_base_url.$cgi->path_info,
            params  => \%params,
            token   => $access_token,
        );

print   $session->header(
            -status => $response->code,
            -type   => $response->header('Content-Type'),
        ),
        ($response->decoded_content || $response->content);

login.cgi がセッションに保存したアクセストークンを使ってリクエストに署名します。転送先は $api_base_url + PATH_INFO です。

こちらもTwitter API(1.0)のURLになっていますが、%oauth_config と $api_base_url を修正すれば、他のサイトでも動くと思います。

index.html

<html>
<head>
<title>OAuth Proxy</title>
<script src="http://code.jquery.com/jquery-1.8.2.min.js"></script>
<script>
$(function(){
    $.getJSON("proxy.cgi/account/verify_credentials.json", function(user){
        $("#icon").attr("src", user.profile_image_url);
        $("#name").text(user.screen_name);
        $("#login").hide();
        $("#contents").show();
    });
});
</script>
</head>

<body>
<div id="login">
<form method="POST" action="login.cgi">
    <input type="submit" value="LOGIN" />
</form>
</div>

<div id="contents" style="display: none;">
<img id="icon">
<span id="name"></span>
<ul>
<li><a href="proxy.cgi/account/verify_credentials.xml">
    account/verify_credentials</a></li>
</ul>
<ul>
<li><a href="proxy.cgi/statuses/home_timeline.xml">
    statuses/home_timeline</a></li>
<li><a href="proxy.cgi/statuses/mentions.xml">
    statuses/mentions</a></li>
<li><a href="proxy.cgi/direct_messages.xml">
    direct_messages</a></li>
</ul>
<form method='POST' action="proxy.cgi/statuses/update.xml">
<textarea name="status" cols="40" rows="3"></textarea>
<div><input type="submit" value="Tweet" /></div>
</form>
</div>
</body>
</html>

認証前はLOGINボタンを表示し、認証後はユーザの情報とAPIへのリンクを表示します。

ちょっとだけAjaxを使ってみました。それからAPIのリンクはJSONではなくXMLの方にしました。そうしないと表示を見ても理解不能なので。*2

Cookieのpathとかはかなりいい加減ですが、ちゃんと直してmod_perlで動くようにすればある程度実用になるんじゃないかなあ。Twitterアプリを書いてみようかな。*3

*1:ただし、はてなはscopeパラメータがあるので、もう一工夫必要

*2:Twitter API 1.1 からはXMLインタフェースはなくなったみたいですね

*3:中国にいることが多いので、モバツイが遮断された場合の保険を作っておきたいのです