Question

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.

Was it helpful?

Solution

Okay, so it turns out that this is possible provided a few things are in place first. You will need to register an app with pocket first in order to get a consumer key. You can do that here:

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

Then, manually go through the steps 2 and 3 at the link below to get your new app approved for your pocket account one time. This is a one time manual step after which things can be automated. I used curl for step 2 and my browser (chrome) for step 3:

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

Then, you will need to find your pocket user id value. For me, it was in a cookie tied to the domain "getpocket.com" and had the name sess_user_id. Armed with your consumer key and user if, you can then use the following code to obtain an auth token for making calls to pocket. Note that I am using dispatch 0.10.0 and spray-json 1.2.3 as 3rd party libs:

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)
  }

}

All you need to do is call the authenticate function on the PocketAuth object and that will return the String auth token.

Is it a little kludgy? Yes, but that's because they don't really want you automating this process, but it is possible as I got it to work repeatedly.

OTHER TIPS

Last time I had to do that (Facebook Login), I have used dispatch and quickly implemented the required parts of OAuth 2.0 myself which was fairly easy and fast thanks to dispatch.

With JSON parsing now integrated in the Scala standard library, the implementation should not be much more than hacking down what they write under the link you provided.

There is a gantry type called password it have to be enabled then your Token URL will be like

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

and header contains client_id and secret as basic authentication check this link

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top