OAuth::Liteを使って、はてなでOAuth認証

OAuth::Liteを使って、はてなのOAuth対応APIを使ってみる。

はてなのOAuth対応APIのドキュメントは以下

何はともあれ、まずは認証から。認証まわりの説明は以下にある。

AP登録

まず最初にAP登録が必要。はてなにログインして、以下のURLから登録する。

OAuth Consumer Key と OAuth Consumer Secret はAPごとに払い出されるので、これを使う。APの名称、説明、URLは後でも変更可能なので、デフォルトのままでもOK。

「新しいアプリケーションの追加」を実行すると、確認なしにAPが追加され、削除できないので要注意。私は無駄に一個作ってしまいました...

OAuth認証のシーケンス

OAuth 1.0 の認証シーケンスは以下の通り。

       User    User-Agent           Consumer         Service Provider
 (resource owner)  |                (client)             (server)
         |         |                    |                    |
         |-------->|                    |                    |
         |         |------------------->*                    |
         |         |                    | get_request_token  |
         |         |                    |------------------->| /initiate
         |         |                    |<-------------------|
         |         |<-------------------|                    |
         |         |---------------------------------------->| /authorize
         |         |<----------------------------------------|
         |<--------|                    |                    |
         |         |                    |                    |
         |-------->|                    |                    |
         |         |---------------------------------------->|
         |         |<----------------------------------------|
         |         |------------------->*                    |
         |         |                    | get_access_token   |
         |         |                    |------------------->| /token
         |         |                    |<-------------------|
         |         |<-------------------|                    |
         |<--------|                    |                    |
         |         |                    |                    |

get_request_token、get_access_token は OAuth::Lite::Consumer のメソッド名。

/initiate、/authorize、/token は、はてなの場合、以下の通り。

Request Token URL https://www.hatena.com/oauth/initiate (/initiate)
User Authorization URL https://www.hatena.ne.jp/oauth/authorize (/authorize)
Access Token URL https://www.hatena.com/oauth/token (/token)

OAuth::Lite::Consumerオブジェクトの生成

いろんなパラメータが指定できるけど、consumer_key と consumer_secret だけ設定すればOK。

my $consumer = new OAuth::Lite::Consumer(
        consumer_key    => $consumer_key,
        consumer_secret => $consumer_secret,
    );

$consumer_key と $consumer_secret は、AP登録のときに払い出された OAuth Consumer Key と OAuth Consumer Secret を使う。この2つはの値は外部に漏らさないよう注意。なりすましクライアントが作れてしまうからね。

Request Token の取得

get_request_token() をコールして Service Provider に Request Token を要求する。

    my $request_token = $consumer->get_request_token(
            url             => 'https://www.hatena.com/oauth/initiate',
            callback_url    => $cgi->url,
            scope           => 'read_public,write_public',
        )   or die $consumer->errstr."\n";

url には Request Token URL を指定する。callback_url には認証後にリダイレクトするコールバックURLも指定する(上の例では自分自身のURLを指定)。また、はてなの場合、scope というパラメータも必要。scope に何が指定できるかは、

を参照のこと。
Request Token が取得できたら、セッションをまたがって使用するために、何らかの方法で保存しておく。Request Token は OAuth::Lite::Token のオブジェクトなので、as_encoded() と from_encoded() でシリアライズできる。保存方法としてCookieを使ってもいいかもしれないけど、oauth_token_secret はネットワークに不用意に流さない方がいいと思う。

認証URLへリダイレクト

url_to_authorize() をコールして User Authorization URL へリダイレクトするためのURLを生成し、ブラウザにリダイレクトを指示する。

    print $cgi->redirect(
            $consumer->url_to_authorize(
                url     => 'https://www.hatena.ne.jp/oauth/authorize',
                token   => $request_token,
            ));

url には User Authorization URL を指定する(はてなではデバイスに応じて選択可能)。token には先ほど取得した Request Token を設定する。
リダイレクト先のページではAPの情報が表示されるので、ユーザがアクセスの許可/拒否を選択できる。

Access Token の取得

ユーザによってアクセスが許可されると、コールバックURLが以下のパラメータを伴ってコールされる。

  • oauth_token
  • oauth_verifier

oauth_token は Request Token に含まれているものと同じなので、これを使ってセッション管理してもいいかも。
先ほど保存した Request Token を何らかの方法で復元し、それを使って get_access_token() をコールすることで Service Provider に Access Token を要求する。

    my $access_token = $consumer->get_access_token(
            url         => 'https://www.hatena.com/oauth/token',
            token       => $request_token,
            verifier    => $oauth_verifier,
        );

url には Access Token URL を指定する。token は復元した Request Token を設定、verifier はコールバック時のパラメータ oauth_verifier を指定する。
Access Token が取得できたら、いよいよAPIを使った操作に入るわけだが、それはまた後日にでも。

全体のコード

全体のコードはこうなりました。

#!/usr/bin/perl -T

use strict;
use CGI;
use CGI::Carp qw(fatalsToBrowser);
use OAuth::Lite::Consumer;
use OAuth::Lite::Token;

my $consumer_key   = '________________';
my $consumer_secret    = '____________________________';

my $title = 'Test Hatena OAuth API';

sub save_request_token {
    my ($request_token) = @_;

    open(TOKEN, ">.session")    or die $!;
    print TOKEN $request_token->as_encoded;
    close(TOKEN);
}

sub load_request_token {
    open(TOKEN, ".session")     or die $!;
    my $request_token = OAuth::Lite::Token->from_encoded(<TOKEN>);
    close(TOKEN);

    return $request_token;
}

my $cgi = new CGI;

my $consumer = new OAuth::Lite::Consumer(
        consumer_key    => $consumer_key,
        consumer_secret => $consumer_secret,
    );

if (! $cgi->param()) {
    my $request_token = $consumer->get_request_token(
            url             => 'https://www.hatena.com/oauth/initiate',
            callback_url    => $cgi->url,
            scope           => 'write_public,read_private',
        )   or die $consumer->errstr."\n";
    save_request_token($request_token);
    print $cgi->redirect(
            $consumer->url_to_authorize(
                url     => 'https://www.hatena.ne.jp/oauth/authorize',
                token   => $request_token,
            ));
}
else {
    my $oauth_verifier  = $cgi->param('oauth_verifier');
    my $request_token   = load_request_token();

    my $access_token = $consumer->get_access_token(
            url         => 'https://www.hatena.com/oauth/token',
            token       => $request_token,
            verifier    => $oauth_verifier,
        );

    print $cgi->header,
          $cgi->start_html(-title=>$title)
        . $cgi->h1($cgi->a({href=>$cgi->url},$title))
        . $cgi->dl(
                $cgi->dt('Access Token:'), $cgi->dd($access_token->token),
                $cgi->dt('Access Secret:'), $cgi->dd($access_token->secret),
            )
        . $cgi->end_html;
}

セッションの保存のし方はとてもいい加減なので、良い子はまねしちゃいけません。