あんパン

こしあん派

Twitterと認可

これは Twitter Advent Calendar 24日目の記事です。昨日はharetaさんの「whywaitaの金でピザの件」でした。

昨日の記事にて、僕のブログとしてここ*1を指定されてしまったため、技術的なことを話さなければいけなくなった。ので、Twitterと認可について話す。

はじめに

これはTwitterの認可、つまるところOAuth1.0およびOAuth1.0aの解説である。特に断りのない限り、OAuth1.0/1.0aのことを以下ではOAuthと呼ぶ*2。本稿は厳密にOAuthの仕様*3に沿った解説をしていないかもしれないが、実際に筆者がTwitter APIライブラリ*4を作成する過程で調べた情報を記述したものであり、内容については保証をする。誤字脱字、間違った解説がある場合は@masawadaまでどうぞ。

Twitterの認証と認可

もともとTwitterはBASIC認証を採用しており、認証にHTTPリクエストを用いることもできた。BASIC認証はパスワードが平文で流される、HTTPSにしてもユーザが悪意のあるクライアントアプリケーションにID/パスワードを渡す可能性がある、リクエストできるAPIを制限できない(全てのAPIエンドポイントについて認可される)等の問題を抱えており、2010年8月31日にBASIC認証を廃止しOAuthに移行することとなった。

なお、このときカウントダウンサイトとして使われていたドメインは絶対に失敗しない引越し土日マニュアルになっている*5。死んでくれ。

おことわり

Twitterで実装されるOAuth1.0/1.0aは一部ずさんな点があるため、PINベースの認可について解説する。現在は変わっているかもしれないが、筆者がTwitter APIライブラリを作った時点ではPINベースがもっとも確実とされていた。(参考: http://www.slideshare.net/nekoruri/20130301-twitter-oauthvulnerability)

Twitter OAuthの概要

CK/CSの取得

TwitterでOAuthを使用したアプリケーションを作成するには、Consumer KeyおよびConsumer Secret(以下CK/CS)を取得する必要がある。https://dev.twitter.comからTwitterアカウントでログインし、Appを作成、CK/CSを取得する。

OAuthなので、アプリケーションにどのAPIへのアクセスを認可するかを変更することができる。AppのページからPermissionを開いて、ラジオボタンで選択・保存する。

  • Read only
  • Read and Write
  • Read, Write and Access direct messages

の三点になっており、上から順にアクセスできるAPIエンドポイントが増える。なお、どの権限でもユーザのパスワードを閲覧することはできない。

認可の流れ

以下の流れに沿ってアプリケーションに認可を与える。

  • Authorize URIの取得
  • Authorize
  • Access Tokenの発行

Authorizationヘッダ

OAuthによる認可を必要とするAPIにリクエストを送る際、HTTPリクエストヘッダにAuthorizationヘッダを付加する必要がある。Authorizationヘッダは以下のparamsを含む。

param名 内容
oauth_consumer_key 取得したConsumer Key
oauth_nonce リクエスト毎にユニークな文字列(文字数はいくつでもよい)
oauth_signature 署名文字列(生成方法は後述)
oauth_signature_method "HMAC-SHA1"
oauth_timestamp リクエストの時間(UNIX Epoch)
oauth_token Access Token
oauth_version "1.0"

ただし、OAuth系のエンドポイント(oauth/request_tokenoauth/access_token等)は例外であり、一部のparamsを必要とせず、別のparamsを要求する。

oauth_signatureの生成

Twitter APIへの要求において、最も気を遣う部分のひとつがoauth_signatureの生成である。どのエンドポイントにおいてもこの生成方法は共通である。

  1. リクエストメソッド(GETまたはPOST, 大文字)とリクエスト先のURI&で繋ぐ
    • このとき、リクエスト先のURIはURIエンコードする。
    • JavaScriptの場合encodeURIComponentでは不十分で、RFC 3986に準拠する必要がある。
    • 参考: encodeURIComponent() - JavaScript | MDN
  2. oauth_signatureを除くoauth paramsをキーの辞書順にソートし、キーと値を=で繋ぐ
    • このとき、値はURIエンコードする。
    • JavaScriptの場合1と同様にencodeURIComponentでは不十分で、RFC 3986に準拠する必要がある。
  3. 1で文字列と2で作った文字列を&で繋ぐ
  4. HMACのSecret Keyを生成する。Consumer SecretとAccess Token Secretを&で繋ぐ
    • Access Token Secretが空の場合はConsumer Secretの最後に&を付加する
  5. 2で作った文字列をSHA-1でハッシュする
  6. 4で得られたハッシュを3で作ったSecret Keyを用いてHMACをとる

以上で得られた文字列がoauth_signatureとなる。これを最後にoauth paramsに加えてAuthorizationヘッダに付加する。

Authorize URIの取得

まずはユーザがアプリケーションに認可を与えるかどうかを決定できるページ(Authorizeページ)のURIを取得する。Authorizeページはhttps://api.twitter.com/oauth/authorizeまたはhttps://api.twitter.com/oauth/authenticateのいずれかであり(詳細は後述)、これにGETメソッドでoauth_tokenクエリを付加すると開くことができる。つまり、このoauth_tokenを取得する必要がある。

oauth_tokenを取得するには、oauth/request_tokenにリクエストを送る。この時のoauth paramsは通常とは異なり、以下のparamsを要求する。

param名 内容
oauth_callback "oob"
oauth_consumer_key 取得したConsumer Key
oauth_nonce リクエスト毎にユニークな文字列(文字数はいくつでもよい)
oauth_signature 署名文字列(生成方法は後述)
oauth_signature_method "HMAC-SHA1"
oauth_timestamp リクエストの時間(UNIX Epoch)
oauth_version "1.0"

また、signatureを生成する際に用いるAccess Token Secretは空なので、HMACのSecret KeyはConsumer Secretに&を付加した文字列になる。

これらをAuthorizationヘッダに追加し、https://api.twitter.com/oauth/request_tokenに対してPOSTリクエストを行うとoauth_token及びoauth_token_secretを得ることができる。これら2つは、OAuthで認可を得るまでアプリケーション側で保持しておく必要がある。

Authorize

PINベースのOAuthの場合、https://api.twitter.com/oauth/authorizeを用いる。https://api.twitter.com/oauth/authorize?oauth_token=に続けて前項で得たoauth_tokenの文字列を付加したURIへユーザを誘導する。ユーザはこのページでTwitterにログインし、アプリケーションを認可するとPINコードを得ることができる。

Access Tokenの発行

最後に、Access TokenおよびAccess Token Secretを発行する。これを発行すると、以後認可を得た範囲で自由にAPIにリクエストを送ることができる。

Access Tokenを取得するには、oauth/access_tokenにリクエストを送る。この時のoauth paramsは通常とは異なり、以下のparamsを要求する。

param名 内容
oauth_consumer_key 取得したConsumer Key
oauth_nonce リクエスト毎にユニークな文字列(文字数はいくつでもよい)
oauth_signature 署名文字列(生成方法は後述)
oauth_signature_method "HMAC-SHA1"
oauth_timestamp リクエストの時間(UNIX Epoch)
oauth_verifier 前項で得たPINコード
oauth_version "1.0"

また、signatureを生成する際に用いるAccess Token Secretにはoauth/request_tokenで取得したoauth_tokenを指定する。

これらをAuthorizationヘッダに追加し、https://api.twitter.com/oauth/access_tokenに対してPOSTリクエストを行うとaccess_token及びaccess_token_secretを得ることができる。これでアプリケーションの認可が完了する。

oauth/authorizeとoauth/authenticateの違い

Twitterはユーザに踏ませるページとしてoauth/authorizeoauth/authenticateの二種類を用意している。前者はアプリケーションが認可済の場合でも毎回認可を行うかユーザに許可を得る。後者はアプリケーションが認可済の場合、なにもせずともアプリケーションへのリダイレクトを発生させる。特に理由がない限り、多少の利便性を残っても前者を用いた方がユーザとしては安心できる。

まとめ

というわけでざっと流れを説明することはできたと思う。基本的にはTwitter APIライブラリがこれらをカバーするため意識する必要はないが、内部でどう処理されているのかは理解しておくとライブラリのサポートが打ち切られる等なにか問題が発生した場合でも自分で書き換えるなどして対処することができるだろう。

明日はharetaさんの記事です。

*1:雑談用もある http://masawada.hatenadiary.com/

*2:OAuth2.0というバージョンもあり、Twitter APIにもエンドポイントが存在する

*3:RFC 5849 - The OAuth 1.0 Protocol

*4:GitHub - masawada/violet: A Twitter Library for Firefox OS

*5: