結城浩のはてなブログ

ふと思いついたことをパタパタと書いてます。

はてな認証API / ためしに作ってみました

はてな認証APIが公開されましたので、ためしてみました。

懸念事項

なおやさんところ経由ではてな認証APIの公開について(開発者さま向け)を読んで感じたこと。

  • おそらくすぐに「はてな認証APIで○○を作ってみました」的なものが登場するでしょう。おもしろいアプリが登場することを期待。
  • ただし、一般ユーザにきちんと認識させないと、悪意のある第三者が一般ユーザのパスワードを奪うアプリを作ってしまう危険性があります。たとえば「はてな認証APIを利用しています」と偽って、「ログイン名とパスワードを入力させるフォーム」を見せるアプリが出た場合、ユーザは誤解しないか。
  • それから…解説した図がほしいです。→認証部分のシーケンス図は結城が描きました(このエントリの下の方で公開しています)。

結城さんちのはてな認証APIテスト

追記:2006-04-24 21:09: とりあえず、作ってみました。以下をお試しください(ソースはすぐ下で公開)♪

これは、はてな認証サーバから返された文字列をそのまま表示するだけのアプリです。id:hyukiid:rubycoさんでうまくいくことを試しました。
追記:2006-04-24 21:49: catfrogさんへ:上の「結城さんちのはてな認証APIテスト」を試してみるとすぐにアプリのイメージは浮かぶと思います。もっとも単純なのは掲示板アプリで、はてなユーザとして認証された人だけは「名前の部分がその人のはてなダイアリーにリンクされている」というようなもの。たとえばはてなダイアリーのコメント欄がまさにそうなってますよね。
追記:2006-04-24 21:59: 一度「結城さんちのはてな認証APIテスト」用に許可すると、はてなのシステムが覚えるみたいですね。もう一度新たな気持ちでやってみたいときには、はてな認証APIのところで許可を取り消す必要があるようです。なるほど。
追記:2006-04-24 22:04: r_iizukaさんへ:URLをクリックしたときには説明用のページにジャンプさせるべきでした。実装次第というよりも、結城が入力するURLを間違えていただけです(^_^; 現在は直してあります。
追記:2006-04-24 22:07: 実装してみて気になった点。認証APIでの「説明文を用意するURL」と「認証のコールバックURL」が無関係でもよいというのはまずいのではないか。というのは、ユーザが「許可を与える」というボタンを押すときに見えているのは説明文のURLだけだから。

はてな認証APIのURLの作り方(make_api_sig.pl)

秘密鍵api_keyを入手してからapi_sigを作るには、以下のPerlスクリプト(make_api_sig.pl)が使えます。$secretにあなたの秘密鍵、$api_keyにあなたのapi_keyを入れ、ローカルに動かします。

use strict;
use Digest::MD5 qw(md5_hex);

my $secret = "0123456789abcdef";
my $api_key = "abcdef0123456789abcdef0123456789";
my $api_sig = md5_hex("${secret}api_key${api_key}");

print "secret = $secret\n";
print "api_key = $api_key\n";
print "api_sig = $api_sig\n";
print "URL = http://auth.hatena.ne.jp/auth?api_key=${api_key}&api_sig=${api_sig}\n";

結城さんちのはてな認証APIテストのソース(test.cgi)

追記:2006-04-24 23:27: CGIのソース(test.cgi)を公開します。$secretはあなたの秘密鍵、$api_keyはあなたのapi_key、$help_urlは説明文のURLです。

#!/usr/bin/perl
use strict;
use CGI;
use CGI::Carp qw(fatalsToBrowser);
use Digest::MD5 qw(md5_hex);
use LWP::Simple;

my $q = new CGI;
my $cert = $q->param("cert");
my $secret = "0123456789abcdef";
my $api_key = "abcdef0123456789abcdef0123456789";
my $api_sig = md5_hex("${secret}api_key${api_key}cert${cert}");
my $help_url = "http://www.example.com/about_your_application.html";
unless ($cert) {
    print "Location: $help_url\n\n";
    exit;
}
my $content = get("http://auth.hatena.ne.jp/api/auth.json/?api_key=${api_key}&cert=${cert}&api_sig=${api_sig}");
print
    $q->header,
    $q->start_html('Hatena Auth API Test'),
    $q->start_pre,
    $content,
    $q->end_pre,
    $q->end_html;

はてな認証APIシーケンス図(概略)

追記:2006-04-25 06:15: シーケンス図を描きました(概略のみ)。

  • (1) ユーザAは、サービスXに「利用したい」という。
    • たとえばサービスXのどこかのページにアクセスすることを意味します。
  • (2) サービスXは、ユーザAに「api_keyとapi_sig1」を渡す。
    • たとえばユーザAにリンクという形で示します。
    • 便宜上、api_sigをapi_sig1とapi_sig2に分けました。両者は違う値になります。
    • api_sig1は前もって計算しておきます(たとえば上で公開しているmake_api_sig.plを使う)。
    • 秘密鍵を使うので、api_keyからapi_sig1を作るのはサービスXと「はてな」しかできません。
  • (3) ユーザAは、はてなに「id+password+api_key+api_sig1」を渡す。
    • 通常はユーザAがブラウザでアクセスすることによって渡します。
  • (4) はてなは、ユーザAに「cert」を渡す。
    • 実際には(4)と(5)は自動的にリダイレクトされますので、ユーザAは意識しません。
  • (5) ユーザAは、サービスXに「cert」を渡す。
    • ここでアクセスするのがコールバックURLです。
    • サービスXはcertは知り得るが、certからpasswordを逆算することはできません(そのようになっていなければなりません)。
  • (6) サービスXは、はてなに「api_key+cert+api_sig2」を渡す。
    • api_sig2は毎回計算します(たとえば上で公開しているtest.cgiの中で計算しています)。
    • 秘密鍵を使うので、api_keyからapi_sig2を作るのはサービスXと「はてな」しかできません。
  • (7) はてなは、サービスXに認証結果とアイコン情報などを渡す(OKなら)。
  • (8) サービスXは、認証後の画面をユーザAに提示する。

※こうやってみると、改めて一方向ハッシュ関数(メッセージダイジェスト)の重要性がよくわかりますね。
※上記のapi_sig1やapi_sig2の作成では秘密鍵を混ぜ込んでメッセージダイジェスト(MD5)を利用しているので、メッセージ認証コードの一種を作っていることになる。一方向ハッシュ関数(メッセージダイジェスト)やメッセージ認証コードについては『暗号技術入門 ―― 秘密の国のアリス』がわかりやすいですよ、と自著の宣伝:-)。