mixiページアプリとOAuthの関係

こんばんは、ritouです。
今年もがんばりましょう。

mixiページアプリをご存知でしょうか?

上記リンクを見た限り、こんな感じでしょうか。

このmixiページアプリを作ってみたところ、みんな大好きなOAuthがふんだんに使われていますので今回はしくみとOAuthの使われどころを紹介します。

試しに作ってみた

とりあえず何か作りたいなと思ったので、簡単な拡散用のページアプリを作成してみました。その名も「拡散希望」。

開発中のステータスにあるmixiページアプリは、自分が管理しているページにしか入れられず、しかも管理者しか利用できない(見えない)ので、URLをさらしてもどうにもなりません。
最初にmixiページアプリを選択して起動したときの画面はこちら。

ここでやっていることを紹介します。

起動時 : 署名付きリクエストを処理

起動時にOAuth 1.0系の署名付きのリクエストが送られます。mixiアプリと同じですね。

署名付きリクエストの検証 << mixi Developer Center (ミクシィ デベロッパーセンター)を参考にして、OAuth::Lite::ServerUtilを用いて検証しました。
このOAuth::Lite、便利ですね。

PC用とスマートフォン用でRSAの公開鍵が違うのでxoauth_signature_publickeyパラメータなどで判定するのがよさそうです。

sub validate {
    my $req = shift;

    if ( $req->param('xoauth_signature_publickey') and $req->param('oauth_signature') ) {

        my $method = $req->method;
        my $url = $req->uri;
        my $params = $req->params;

        my $util = OAuth::Lite::ServerUtil->new();
        $util->support_signature_method('RSA_SHA1');

        my $ca = ( substr($req->param('xoauth_signature_publickey'), 0, 7) eq 'page_pc' ) ? $mixi_ca_pc : $mixi_ca_touch;
        $public_key = Crypt::OpenSSL::CA::X509->parse($ca)
                      ->get_public_key()
                      ->to_PEM();

        my $ret = $util->verify_signature(
                      method          => $method,
                      url             => $url,
                      params          => $params,
                      consumer_secret => $public_key,
                  );
        return $ret;
    }else{
        return 0;
    }
}

githubソース : p5_MixiPageApp1_sample/SignedRequest.pm at master · ritou/p5_MixiPageApp1_sample · GitHub

署名検証後 : mixiページの情報を取得

mixiページから送られてたリクエストだということが確認できた後は、ページの情報を取得します。
ユーザーの認可が不要な、いわゆる2leggedな処理になります。

このとき、OAuth 2.0のClient Credentialsの仕様に沿ってAccess Tokenを取得して、Page APIにアクセスする必要があります。

OAuth::Lite2::Client::ClientCredentialsを使うと簡単です。
このOAuth::Lite2、便利ですね。作った人すごいですね!

sub get_from_clientcredentials {
    my $config = shift;

    my $client = OAuth::Lite2::Client::ClientCredentials->new(
        id               => $config->{client_id},
        secret           => $config->{client_secret},
        access_token_uri => q{https://secure.mixi-platform.com/2/token} 
    );

    my $access_token = $client->get_access_token() or return '';

    return $access_token;
}

githubソース : p5_MixiPageApp1_sample/AccessToken.pm at master · ritou/p5_MixiPageApp1_sample · GitHub

あとはリソースアクセスです。
ページ基本情報を取得するAPIの仕様はこちら : Page API << mixi Developer Center (ミクシィ デベロッパーセンター)

sub get_page_info{
    my ($access_token, $page_id) = @_;

    # build request
    my $endpoint = sprintf(q{https://api.mixi-platform.com/2/pages/%s}, $page_id);
    my $req = HTTP::Request->new( GET => $endpoint );
    $req->header( Authorization => sprintf(q{OAuth %s}, $access_token) );

    # get response
    my $res = LWP::UserAgent->new->request($req);
    return decode_json($res->content);
}

githubソース : p5_MixiPageApp1_sample/Resource.pm at master · ritou/p5_MixiPageApp1_sample · GitHub
レスポンスからidとdisplayNameを取り出せば拡散用のURLが作れそうです。あとはテンプレートに渡してFacebookTwitterのJSをごにょごにょすれば外への拡散リンクが作成できますね。

ボイス投稿準備その1 : ユーザーに対してプロフィールデータ取得とボイス投稿の認可を要求

プロフィール情報の取得とボイス投稿をするためには、OAuth 2.0の認可要求が必要です。
これはJavaScriptが用意されているのですが、mixiページを表示したらみんなに強制的にPermisionくれくれするのもあれかなと思い、ボタン押したら同意画面出てくるようにしました。

いい感じで認可画面が表示されます。
JavaScriptの仕様 : JavaScript API << mixi Developer Center (ミクシィ デベロッパーセンター)
githubソース : p5_MixiPageApp1_sample/Root.tmpl at master · ritou/p5_MixiPageApp1_sample · GitHub

ボイス投稿準備その2 : OAuth 2.0 Webserver Flowのcallback処理したらAPIアクセスの準備完了

戻り先を用意しておきます。(/Authorization/callbackとか)
ここではOAuth::Lite2::Client::WebServerを使ってAccess Tokenの取得まで行います。

このOAuth::Lite2、便利ですね。作った人すごいですね!こういう人の隣の席とかで働けたら幸せですね!

sub get_from_authorizationcode {
    my ($config, $code) = @_;

    my $client = OAuth::Lite2::Client::WebServer->new(
        id               => $config->{client_id},
        secret           => $config->{client_secret},
        authorize_uri    => q{https://mixi.jp/connect_authorize.pl},
        access_token_uri => q{https://secure.mixi-platform.com/2/token} 
    );

    my $access_token = $client->get_access_token(
        code         => $code,
        redirect_uri => $config->{redirect_uri}
    ) or return '';

    return $access_token;
}

githubソース : p5_MixiPageApp1_sample/AccessToken.pm at master · ritou/p5_MixiPageApp1_sample · GitHub

あとはボイスへの投稿ですね。

sub post_voice{
    my ($access_token, $message) = @_;

    # build request
    my $endpoint = q{https://api.mixi-platform.com/2/voice/statuses/update};
    my $req = HTTP::Request->new( POST => $endpoint );
    $req->header( Authorization => sprintf(q{OAuth %s}, $access_token) );
    $req->content_type('application/x-www-form-urlencoded');
    $req->content('status='.uri_escape($message));
    	
    # get response
    my $res = LWP::UserAgent->new->request($req);
    return decode_json($res->content);
}

githubソース : p5_MixiPageApp1_sample/Resource.pm at master · ritou/p5_MixiPageApp1_sample · GitHub

こんな感じでOAuthに絡む部分の説明は終わりです。
あとは、1つのmixiページ内に複数のページアプリが使えたりしますし、複数ページでページアプリが使われることもあるので、セッション周りはうまく考えて作りこみ必要がありますね。

まとめ

上記の通り、OAuth 1.0系とOAuth 2.0系の共演になっています。
悪名高きOAuth 1.0系の署名ではありますが、OAuth 2.0系でリソースアクセス以外の署名付きリクエストが定義されていないのでしょうがないですね。
まぁ、1.0系のほうはmixiページ作られたことのある開発者の方は慣れていると思うので問題ないでしょう。

ずっとPHP触ってたので簡単なスクリプト意外、Perl書くの久しぶりというかほぼ初めてでしたが、とりあえず動かしてみたところ、「OAuthの処理さえおさえておけばページアプリ作るのそんなに難しくなさそう」って思いました。
みなさんもmixiページアプリを作られてみてはいかがでしょうか?

ではまた!