play2でdispatchを利用して、oauth系のサービスにアクセスし、responseをargonautでdecodeしてaccess_tokenを取得してみよう！

まだ、ちょっと書きかけです。

## やりたいこと

oauth2系のRESTサービスに、自前のplay2で作成したアプリケーションからアクセスして、認証に成功したリクエストのみサービスの継続を許可したい

## 作成したリポジトリ

https://github.com/letusfly85/play2-call-auth-sample

## 1. 利用ライブラリ

| やること |GitHub|version|補足|
|:-----------|:------------|:------------|:---|
| httpアクセス|[dispatch](https://github.com/dispatch/reboot)|"net.databinder.dispatch" %% "dispatch-core" % "0.11.3"|play2.4.xと0.11.2の組み合わせだと、class not found exception が発生します。。|
|jsonパーサー|[argonaut](https://github.com/argonaut-io/argonaut)|"io.argonaut" %% "argonaut" % "6.1"||

```build.sbt
name := """call-auth-sample"""

version := "1.0-SNAPSHOT"

lazy val root = (project in file(".")).enablePlugins(PlayScala)

scalaVersion := "2.11.7"

libraryDependencies ++= Seq(
  jdbc,
  cache,
  specs2 % Test,
  "net.databinder.dispatch" %% "dispatch-core" % "0.11.3",
  "io.argonaut" %% "argonaut" % "6.1"
)

resolvers += "scalaz-bintray" at "http://dl.bintray.com/scalaz/releases"

// Play provides two styles of routers, one expects its actions to be injected, the
// other, legacy style, accesses its actions statically.
routesGenerator := InjectedRoutesGenerator

```

## 2. サンプルで利用したoauthのサービス

[前回の投稿](../../letusfly85/items/abcde91a37dadf8c72b0)で作成したoauthのplay2アプリをdocker上で動かして利用しました。

access_tokenを下記で取得して、

```bash
curl http://localhost:9000/oauth/access_token -X POST -d "client_id=bob_client_id" -d "client_secret=bob_client_secret" -d "grant_type=client_credentials"
```

リソース取得する、をplay2でしたい

```bash
curl --dump-header - -H "Authorization: Bearer ${access_token}" http://localhost:9000/resources
```

と、いうことで、

```bash
docker run -i -p 80:9000 -e "JAVA_OPTS=-Dplay.evolutions.db.default.autoApply=true" letusfly85/play-sample-auth
```

で`docker run`しました。

## 3. アプリケーションの実装

routesに今回実施したい処理用のroutingを用意します。

```conf/routes
GET     /auth/access_token          controllers.AuthController.accessToken
```

AuthControllerを実装します。


```app/controllers/AuthController.scala
package controllers

import dispatch.{url, Http}
import dispatch._, Defaults._

import play.api.mvc.{Action, Controller}

import argonaut._, Argonaut._


class AuthController extends Controller {

  //access tokenを詰めるcase classを用意
  case class UserAuthToken(tokenType: String, accessToken: String, expiresIn: Int, refreshToken: String)

  //codecを用意
  implicit def UserAuthTokenCodecJson =
    casecodec4(UserAuthToken.apply, UserAuthToken.unapply)("token_type", "access_token", "expires_in", "refresh_token")

  def accessToken = Action {implicit request =>
    val authUrl = System.getProperty("auth.url")

    val host = url(authUrl)
    val request = host / "oauth" / "access_token"

    //curl http://localhost:9000/oauth/access_token -X POST -d "client_id=bob_client_id" -d "client_secret=bob_client_secret" -d "grant_type=client_credentials"
    val requestAsJson = request.POST.setContentType("application/json", "UTF-8")
    val postWithBody  = requestAsJson << """{"client_id": "bob_client_id", "client_secret": "bob_client_secret", "grant_type": "client_credentials"}"""

    val result = Http(postWithBody OK as.String).apply()

    val option: Option[UserAuthToken] =
      Parse.decodeOption[UserAuthToken](result)

    option match {
      case Some(opt) =>
        println(opt)

        //curl --dump-header - -H "Authorization: Bearer ${access_token}" http://localhost:9000/resources
        val resourceRequest = host / "resources"
        val resourceRequestWithHeader = resourceRequest.setHeader("Authorization", s"${opt.tokenType} ${opt.accessToken}")

        val resourceResult = Http(resourceRequestWithHeader OK as.String).apply()
        println(resourceResult)

      case None => println("error....")
    }
    Ok("ok")
  }

}
```

## 4. 実行する

アプリケーションスタート

```bash
sbt -Dauth.url=*.*.*.* run
```

ローカルで動いているアプリケーションをcurl経由で実行する

```bash
curl -XGET localhost:9000/auth/access_token
```

結果は以下のようになりました。


```bash
UserAuthToken(Bearer,3YioO5Bf0tGEwj7QB5gVe80b0ubogJOKZD421VC1,2241,zJtszWyIzcfePqeufRcNjxrhu54vIDZ74b02XniM)
{"account":{"email":"bob@example.com"},"clientId":"bob_client_id","redirectUri":null}
```

## 5. 所感

dispatchとargonautを利用することでうまいことplay2からも複数のweb service連携ができそうですね！
dispatchのfailureハンドリングについても、もうちょっと見ていこうと思います。

また、akkaとも組み合わせて利用してみたいですね＾＾


---

本日はここまでとなります。