Mixiのライフサイクルイベント(アプリが追加された、アプリがマイアプリから削除されたといったイベント)の署名付きリクエストは他のよりまた少し違うみたいです。公開鍵を使うところは他にもありますが、ライフサイクルイベントの場合は、OAuth情報がHTTPのAuthorizationヘッダーに入っているため、お使いのOAuthライブラリーによってうまくいかない場合があるかもしれません。Rubyのoauthではほぼ動くのですが、
xoauth_signature_publickey=lc_20131107
が署名に含まれないため失敗します。
signature = OAuth::Signature.build(request, {:parameters => {'xoauth_signature_publickey' => 'lc_20131107'}}) do
のようにOAuth::Signature.build()に渡すと署名に含まれ、署名の検証が通ります。
検証が通ったbase stringの例:
GET&http%3A%2F%2FXXXXXXXXXX%2FXXXXXXXXXX%2FXXXXXXXXXX%2Faddapp&eventtype%3Devent.addapp%26id%3Dmo3XXXXXXX7fr%26mixi_invite_from%3DmgwXXXXXXXnt8%26oauth_consumer_key%3Dmixi.jp%26oauth_nonce%3D719445958eb7ae359824%26oauth_signature_method%3DRSA-SHA1%26oauth_timestamp%3D1468335606%26oauth_version%3D1.0%26opensocial_app_id%3D41345%26xoauth_signature_publickey%3Dlc_20131107
ちなみに、xoauth_signature_publickeyが一生変わらないわけではありません。Mixiの公開鍵と同じタイミングで変わる予定です。現在使われている公開鍵の有効期限は確か2020年だったと思いますので、まだしばらく大丈夫かもしれないですね。
英訳の下のmixi_signed_request?関数の例もご参照ください。
Mixi’s Lifecycle events (add app and remove app) use an OAuth scheme that is slightly different from Mixi’s other OAuth implementations: the OAuth headers are included in the HTTP Authorization header. Depending on your OAuth library, the non-standard xoauth_signature_publickey (passed in the HTTP header) may not be included when calculating the signature. However, Mixi includes this parameter. You’ll have to pass it manually to OAuth::Signature.build(), e.g., like this:
signature = OAuth::Signature.build(request, {:parameters => {'xoauth_signature_publickey' => 'lc_20131107'}}) do
Here’s an example of a base string that could pass validation:
GET&http%3A%2F%2FXXXXXXXXXX%2FXXXXXXXXXX%2FXXXXXXXXXX%2Faddapp&eventtype%3Devent.addapp%26id%3Dmo3XXXXXXX7fr%26mixi_invite_from%3DmgwXXXXXXXnt8%26oauth_consumer_key%3Dmixi.jp%26oauth_nonce%3D719445958eb7ae359824%26oauth_signature_method%3DRSA-SHA1%26oauth_timestamp%3D1468335606%26oauth_version%3D1.0%26opensocial_app_id%3D41345%26xoauth_signature_publickey%3Dlc_20131107
Note that the certificate and the “lc_20131107” are linked. When there is a change, both will be updated. The current certificate is valid until 2020 or so.
The full verification code could look like this:
def mixi_signed_request? mixi_certificate = <<END -----BEGIN CERTIFICATE----- MIIDNzCCAh+gAwIBAgIJAIQ3zDiILtpzMA0GCSqGSIb3DQEBBQUAMDIxCzAJBgNV BAYTAkpQMREwDwYDVQQKDAhtaXhpIEluYzEQMA4GA1UEAwwHbWl4aS5qcDAeFw0x MzExMDcwODQwNTBaFw0yMzExMDUwODQwNTBaMDIxCzAJBgNVBAYTAkpQMREwDwYD VQQKDAhtaXhpIEluYzEQMA4GA1UEAwwHbWl4aS5qcDCCASIwDQYJKoZIhvcNAQEB BQADggEPADCCAQoCggEBAMxzCu9sUctGAzL/X0/sH2MSRmc/+X2Wx87ObZDpEd5P 19mIUQXW6hCXObB3SkE7kMuXiRhtrxwsnB9fjYIUEq/1vsTHkLJoJVUFIumqe6EH c/WZaTmu34WpEUFXNDS4htidXyVqikoDQZF9wdczyH7bLPbekQfRAcyek3E6/7Qi B00yWUqK8FcUOD4ILmtSHXsz4BNqekNgEzfUi5WkBYKtuD5zSunZalbWUPS7xa57 o1auVdclaHBfqe8dC5DTbxIe0szpHckQrJF9fJ/bIQSmvY6ADBRGfoLF7Fgoc5x+ R5my9weytzg4WdDUjYrxmhy5IpjxytipQqrFDqAUxl8CAwEAAaNQME4wHQYDVR0O BBYEFKCQSlssCWLqd0tT7NVtoBUNzCasMB8GA1UdIwQYMBaAFKCQSlssCWLqd0tT 7NVtoBUNzCasMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBALoI7elk ZCv+pUpi6aJepzLnQDYHB2eXpDkpWEUrF1WMvx8ovmWdVeviHqUdFtGL0XZ5tSoV vGE/KQTavag+MfbafKaff4iHXNyNMygeFP7r/FaFQQRafyNfhaXF6sWfwKrOk/Bc jIXFN9tYWN6LEwNgYT0C+OSOppQJzt2y1am15FExAHQcIEFYc+3T+MGGJ7e9H8tn Qz84WmIgNZRUMYQC0PJTsMNVvr+/DTIzjabKz4W8qodXGA7AxXiRYdgC+3RUj/rA lR09PXZ6nRaKiB0KBDIMUlPu/0u0Vw+GBt0ckH0htOKSxsn09jkITFhy3NX7Slbk jlnY4pS9JO+avmM= -----END CERTIFICATE----- END require 'oauth' require 'oauth/signature/rsa/sha1' begin consumer = OAuth::Consumer.new(nil, mixi_certificate, {:signature_method => 'RSA-SHA1'}) signature = OAuth::Signature.build(request, {:parameters => {'xoauth_signature_publickey' => 'lc_20131107'}}) do [nil, consumer.secret] end return signature.verify rescue => e logger.debug(e.inspect) return false end end