カレシの元カノの元カレ・・・をFacebookは知ってますよね


「カレシ、カレシの元カノ、カレシの元カノの元カレ・・・」というちょっと怖い広告がかつてありました。

あの広告が警告していたエイズは怖い病気ですが、もし、もっと怖い、たとえば、潜伏期間は人それぞれ、発症前に治療すれば100%治るが、発症すれば3日で死ぬ、そんな性感染症が発見されたら、すぐに検査を受けたくなりますよね。でも、(未だ収束していない)福島原発事故発生当時のような、「パニックを恐れる」エリートパニックによって、その事実が市民に知らされたときにはもう手遅れ、ということは十分あり得ます。

患者にインタビューしながら感染経路を調べて・・・なんてことをしてもやはり手遅れになるでしょう。

そこで、Facebookを使えないかなあ、とか思うわけです。Facebookでは、ユーザが自分で編集できる基本データに「恋愛ステータス」なる項目があって、交際相手を自己申告できるようになっています。自分の友人の登録状況は、次のアプリで一覧表示できます。

Friends Partner

表示させるには、交際ステータスへのアクセス許可が必要です。下のようなページが表示されたら、「インストール」をクリックしてください(http://www.facebook.com/settings/?tab=applicationsでアンインストールできます)。

私の友人たちの登録状況はこんな感じです。あまり登録していませんね(ウェブブラウザでなら友人以外の情報も見られるのですが、このアプリが利用しているAPIでは自分と友人の情報しか見られません)。

この交際相手、もちろん他人から見られるのは現在の相手だけですが、Facebookのサーバに、過去の相手が保存されていたら面白いですね(情報漏洩を恐れてちゃんと消しておくのが正しいポリシーでしょうが)。

最初に述べたような怖い病気が発生したときに、Facebookはその情報を公開するのだろうか、と思うのです。「公開する」と宣言すれば、人は将来の保険と思って登録するようになるでしょうか。「公開しない」と宣言すれば、情報漏洩を恐れる人も安心して登録するようになるでしょうか。あるいは逆の結果に?

「そういう病気が見つかって初めて過去の恋人たちを登録できるようなフォームが作られるだろうから今は何もしない」という人はまったく冷静ですばらしいです。そのペースで対応できる病気ならよいのですが。

OAuth認証でFacebookを利用するWebアプリケーション(PHPの場合)


OAuth認証でTwitterを利用するWebアプリケーションの作り方を以前紹介しました。OAuth認証はさまざまなサイトで使われていますが、使い方がちょっとずつ違っていたりして、プログラミングが苦手な人には敷居が高い気がします。せっかくおもしろいことを思いついても、最初の段階で躓いて挫折するのはがっかりなので、簡単なサンプルがたくさんあるといいと思います。

というわけで、PHPでFacebookのOAuth認証を利用する例を紹介しましょう。

Facebookの開発者サイトでアプリケーションを登録し、アプリケーションIDシークレットキーを取得します。

アプリケーション登録

2つのファイルでOAuth(説明用)

OAuth認証の開始ページを/facebook/oauth-start.php、終了ページを/facebook/oauth-end.phpとします。

oauth-start.phpは次のようになります。おおざっぱなTwitterと比べて、Facebookでは、OAuth認証時に認可する機能をかなり細かく設定できます。ここでは、掲示板への登録を許可するように、「scope=publish_stream」をつけておきます。他の権限については、Extended Permissionsを参照してください。

<?php
session_start();
$_SESSION['application_id'] = アプリケーションID;
$_SESSION['application_secret'] = シークレットキー;
$_SESSION['redirect_uri'] = 'http://localhost/facebook/oauth-end.php';

$url = 'https://www.facebook.com/dialog/oauth'
        . '?client_id=' . $_SESSION['application_id']
        . '&redirect_uri=' . urlencode($_SESSION['redirect_uri'])
        . '&scope=publish_stream'; //投稿する場合
?>
<!doctype html>
<html>
  <head>
    <title>OAuth Start</title>
  </head>
  <body>
    <p><a href="<?php echo $url; ?>">OAuth</a></p>
  </body>
</html>

oauth-start.phpが生成するURLにアクセスすると、認証画面になります。

認証画面

「許可する」をクリックすると、oautu-start.phpで指定したリダイレクトURL(ここではhttp://localhost/facebook/oauth-end.php)にリダイレクトされます。oauth-end.phpでは、リダイレクトが伴うcodeというパラメータを利用して、認証を完了させます。(ちなみに、http://www.facebook.com/settings/?tab=applicationsで許可を取り消せます。)

<?php
session_start();

$cxContext = NULL;
$cxContext = stream_context_create(array(//プロキシサーバを利用する場合
    'http' => array('proxy' => 'tcp://proxy.example.net',
        'request_fulluri' => True)));

$url = 'https://graph.facebook.com/oauth/access_token'
        . '?client_id=' . $_SESSION['application_id']
        . '&client_secret=' . $_SESSION['application_secret']
        . '&redirect_uri=' . $_SESSION['redirect_uri']
        . '&code=' . $_GET['code'];
$result = file_get_contents($url, False, $cxContext);
$output = null;
parse_str($result, $output);
if (!isset($output['access_token'])) {
  session_destroy();
  die();
}
$_SESSION['access_token'] = $output['access_token'];
?>
<!doctype html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>OAuth End</title>
  </head>
  <body>
    <p>access_tokenをセッションに保存した。</p>
    <p><a href="post.php">送信テスト</a></p>
  </body>
</html>

oauth-end.phpで認証が完了したら、次のようなpost.phpで、メッセージを送信してみましょう。

<?php
session_start();

//セッションからaccess_tokenを復元する。
if(!isset($_SESSION['access_token'])) {
  header('Location: http://localhost/facebook/oauth-start.php');
  die();
}
$access_token=$_SESSION['access_token'];
session_destroy();

//投稿
require_once 'HTTP/Request2.php';
$url = "https://graph.facebook.com/me/feed"
        . "?access_token=$access_token"
        . '&message=' . urlencode("テスト at " . time());
$request = new HTTP_Request2($url, HTTP_Request2::METHOD_POST);
$request->setConfig(array(
    //'proxy_host' => 'proxy.example.net',//プロキシサーバを利用する場合
    //'proxy_port' => 3128,
    'ssl_verify_peer' => false
));
$response = $request->send();
if ($response->getStatus() / 100 != 2) {
  echo $response->getReasonPhrase();
  die();
}
?>
<!doctype html>
<html>
  <head>
    <title>Message Post</title>
  </head>
  <body>
    <p>投稿成功。</p>
    <pre><?php echo $response->getBody(); ?></pre>
    <p><a href="oauth-start.php">start</a></p>
  </body>
</html>

例によって、PHPにはいろんなHTTPクライアントがあって、あまり整理されていないのですが、ここではHTTP_Request2を使っています(HTTP_Requestはプロキシ経由のSSL通信に対応していないため、ここでは使えません)。次のようなコマンドで、HTTP_Request2をインストールしておく必要があります。XAMPP for Windowsの場合、スタートメニューのコマンドプロンプトを右クリックして管理者として実行してください。プロキシが必要な環境では、事前に「set http_proxy=http://proxy.example.net:3128/」(Windows)や「export http_proxy=http://proxy.example.net:3128/」(Windows以外)が必要です。

cd \xampp\php
pear install http_request2

SSL通信のためには、PHPでOpen SSLが有効になっていなければなりません。Open SSLが有効かどうかはphpinfo()で確認できます。「OpenSSL suppor」がenabledになっていれば大丈夫です。有効になっていない場合は、php.iniに「extension=php_openssl.dll」という行を追加してください(XAMPPの場合はc:\xampp\php\php.ini)。「extension=」という記述がたくさんある場所に書いておけばいいでしょう。php.iniを変更したら、Apacheを再起動してください。

1つのファイルでOAuth+投稿

oauth-start.phpとoauth-end.php、post.phpを1つのファイル1click-post.phpにまとめると次のようになります。コールバック処理の最後で一度転送しているのは、ブラウザのアドレス欄にトークンを表示させないためです(セキュリティのためではありません)。

<?php
session_start();

$application_id = アプリケーションID;
$application_secret = シークレットキー;
$redirect_uri = 'http://localhost/facebook/1click-post.php';

if (!isset($_SESSION['access_token'])) {
  if (!isset($_GET['code'])) {//OAuth
    $url = 'https://www.facebook.com/dialog/oauth'
            . "?client_id=$application_id"
            . '&redirect_uri=' . urlencode($redirect_uri)
            . '&scope=publish_stream'; //投稿する場合
    header("Location: $url");
    session_destroy();
  } else {//コールバック
    $cxContext = NULL;
    //$cxContext = stream_context_create(array(//プロキシサーバを利用する場合
    //    'http' => array('proxy' => 'tcp://proxy.example.net:3128',
    //        'request_fulluri' => True,)));
    $url = 'https://graph.facebook.com/oauth/access_token'
            . "?client_id=$application_id"
            . "&client_secret=$application_secret"
            . "&redirect_uri=$redirect_uri"
            . '&code=' . $_GET['code'];
    $result = file_get_contents($url, False, $cxContext);
    $output = null;
    parse_str($result, $output);
    if (!isset($output['access_token'])) {
      session_destroy();
    } else {
      $_SESSION['access_token'] = $output['access_token'];
      header("Location: $redirect_uri"); //トークン隠すための転送(オプショナル)
    }
  }
  die();
} else {//投稿
  $access_token = $_SESSION['access_token'];
  session_destroy();

  require_once 'HTTP/Request2.php';
  $url = "https://graph.facebook.com/me/feed"
          . "?access_token=$access_token"
          . '&message=' . urlencode("テスト at " . time());
  $request = new HTTP_Request2($url, HTTP_Request2::METHOD_POST);
  $request->setConfig(array(
      //'proxy_host' => 'proxy.example.net',//プロキシサーバを利用する場合
      //'proxy_port' => 3128,
      'ssl_verify_peer' => false
  ));
  $response = $request->send();
  if ($response->getStatus() / 100 != 2) {
    echo $response->getReasonPhrase();
    die();
  }
}
?>
<!doctype html>
<html>
  <head>
    <title>Message Post</title>
  </head>
  <body>
    <p>投稿成功。</p>
    <pre><?php echo $response->getBody(); ?></pre>
  </body>
</html>

お約束ですが、こういう話を基本から学びたいという方には、拙著『Webアプリケーション構築入門 実践!Webページ制作からマッシュアップまで 』(森北出版, 2011)がおすすめです。