2

tl;dr - I want to authenticate against an OAuth 2.0 API with my own username/password from a desktop app (do not want to open browser) from Java/Scala.

Why? I want to authenticate with Pocket's v3 API from Java/Scala using my own credentials and fetch my unread items. This is for a personal command line tool that I do not intend to release for general use. They used to have a nice basic-auth API but they deprecated it and introduced OAuth 2.0 and I am not sure how to do what I want anymore.

4

3 回答 3

5

好的,事实证明这是可能的,前提是先准备好一些东西。您需要先使用 Pocket 注册一个应用程序才能获得消费者密钥。你可以在这里做到这一点:

http://getpocket.com/developer/apps/new

然后,手动执行以下链接中的步骤 2 和 3,以使您的新应用获得一次批准用于您的袖珍帐户。这是一次性的手动步骤,之后事情可以自动化。我在第 2 步中使用 curl,在第 3 步中使用浏览器(chrome):

http://getpocket.com/developer/docs/authentication

然后,您将需要找到您的袖珍用户 ID 值。对我来说,它位于与域“getpocket.com”相关的 cookie 中,名称为 sess_user_id。有了您的消费者密钥和用户,您可以使用以下代码获取身份验证令牌以调用 Pocket。请注意,我使用 dispatch 0.10.0 和 spray-json 1.2.3 作为 3rd 方库:

import dispatch._
import spray.json._
import scala.concurrent.ExecutionContext
import java.util.concurrent.Executors
import scala.concurrent.Await
import scala.concurrent.duration._
import com.ning.http.client.Cookie

case class CodeRequest(consumer_key:String, redirect_uri:String = "fake:uri")
object CodeRequest
case class CodeResponse(code:String)
object CodeResponse

case class AuthRequest(consumer_key:String, code:String)
object AuthRequest
case class AuthResponse(access_token:String, username:String)
object AuthResponse

object PocketJsonProtocol extends DefaultJsonProtocol {
  implicit val codeRequestFormat = jsonFormat2(CodeRequest.apply)
  implicit val codeResponseFormat = jsonFormat1(CodeResponse.apply)
  implicit val authRequestFormat = jsonFormat2(AuthRequest.apply)
  implicit val authResponseFormat = jsonFormat2(AuthResponse.apply)  
}

object PocketAuth {
  import PocketJsonProtocol._
  val JsonHeaders = Map("X-Accept" -> "application/json", "Content-Type" -> "application/json; charset=UTF-8")
  implicit val EC = ExecutionContext.fromExecutor(Executors.newCachedThreadPool())

  def authenticate(consumerKey:String, userId:String) = {
    val fut = for{
      codeResp <- requestCode(consumerKey)
      _ <- activateToken(codeResp, userId)
      authResp <- requestAuth(consumerKey, codeResp)
    } yield{
      JsonParser(authResp).convertTo[AuthResponse]
    }

    val auth = Await.result(fut, 5 seconds)
    auth.access_token
  }

  def requestCode(key:String) = {
    val req = url("https://getpocket.com/v3/oauth/request") <:< JsonHeaders << CodeRequest(key).toJson.toString
    Http(req.POST OK as.String).map(JsonParser(_).convertTo[CodeResponse])
  }

  def activateToken(codeResp:CodeResponse, userId:String) = {
    val req = (url("https://getpocket.com/auth/authorize") <<? Map("request_token" -> codeResp.code, "redirect_uri" -> "foo")).addCookie(
      new Cookie(".getpocket.com", "sess_user_id", userId, "/", 100, false))
    Http(req)
  }

  def requestAuth(key:String, codeResp:CodeResponse) = {
    val req = url("https://getpocket.com/v3/oauth/authorize") <:< JsonHeaders << AuthRequest(key, codeResp.code).toJson.toString
    Http(req.POST OK as.String)
  }

}

您需要做的就是调用对象authenticate上的函数PocketAuth,这将返回String身份验证令牌。

是不是有点笨拙?是的,但那是因为他们真的不希望你使这个过程自动化,但我让它重复工作是有可能的。

于 2013-04-30T14:16:20.377 回答
0

有一种称为密码的龙门类型,必须启用它,然后您的令牌 URL 将像

/oauth/token?grant_type=password&username=&password=

并且标头包含 client_id 和 secret 作为基本身份验证检查此链接

于 2013-04-29T05:19:09.660 回答
0

上次我不得不这样做(Facebook 登录)时,我使用了dispatch并自己快速实现了 OAuth 2.0 的所需部分,这要归功于 dispatch。

随着 JSON 解析现在集成在 Scala 标准库中,实现不应该只是破解他们在您提供的链接下编写的内容。

于 2013-04-30T00:27:20.327 回答