dispatch(reboot)でoauthを扱う
rebootと呼ばれる新しいdispatchにはoauth exchangeというoauth 1.0aを簡単に扱うための仕組みがあるのですが、公式ドキュメントに解説が載っていないのでメモ。
メインとなるのはExchange
トレイトで、自分型としてSomeHttp
、SomeConsumer
、SomeCallback
、SomeEndpoints
の各トレイトがミックスインされています。
これらの各トレイトを継承したオブジェクトを作ることでaccess tokenの取得までが簡単に行えます。
oauth exchangeの実装
以下はDropbox Core APIの場合の実装例。
import com.ning.http.client.oauth._ import dispatch._ import dispatch.oauth._ trait DropboxHttp extends SomeHttp { def http: HttpExecutor = Http } trait DropboxConsumer extends SomeConsumer { def consumer: ConsumerKey = new ConsumerKey("ほげ", "ふが") } trait DropboxCallback extends SomeCallback { def callback: String = "oob" } trait DropboxEndpoints extends SomeEndpoints { def requestToken: String = "https://api.dropbox.com/1/oauth/request_token" def accessToken: String = "https://www.dropbox.com/1/oauth/authorize" def authorize: String = "https://api.dropbox.com/1/oauth/access_token" } object DropboxExchange extends Exchange with DropboxHttp with DropboxConsumer with DropboxCallback with DropboxEndpoints
ここで出てくるConsumerKey
はAsync Http Clientのものです。
ちなみにcallback
に指定された指定された値はrequest token取得時に渡される仕様(oauth 1.0a)なので、authorizeへのリクエスト時に渡したい場合(oauth 1.0)はオーバーライドする必要がありそう。とここまで書いて、authorize時にoauth_callback
を指定するdropbox core apiを例に出したのは失敗だなと気づきました。
oauth exchangeの利用
以下がaccess tokenの取得例。
import com.ning.http.client.oauth._ import dispatch._ import dispatch.Defaults._ import scala.concurrent._ val res1 = DropboxExchange.fetchRequestToken val requestToken = res1 map { case Right(t) => t case Left(m) => sys.error(m) } val verifier = requestToken map { t => blocking { val url = DropboxExchange.signedAuthorize(t) println(url) println("please press enter after authorize") readLine() } } val res2 = requestToken flatMap { t => verifier flatMap { v => DropboxExchange.fetchAccessToken(t, v) } } val accessToken = res2 map { case Right(token) => token case Left(message) => sys.error(message) }
val accountInfo = :/("api.dropbox.com").secure / "1" / "account" / "info" val signedAccountInfo = accountInfo <@ (DropboxExchange.consumer, accessToken()) val res3 = Http(signedAccountInfo OK as.String) res3 onSuccess { case a => println(a) }
ちょっとfutureを扱うコードが汚くなっていますが、ちゃんと書けばそれっぽく見えると思います(?)。
oauth 2.0
oauth 2.0を実装したPRが出ているのですが、現時点より6ヶ月前で更新が止まっています。