playframework / playframework/play-ws
OAuth remote request failure due to missing content length
Nobody has claimed this yet.
- Dominant language
- Scala
- Stars
- 224
- Forks
- 92
- Avg merge
- 1d 19h
- Merged PRs (30d)
- 28
Description
Hello!
The OAuth implementation/wrapper provided with Play WS fails when a request for a token is made (i.e., the first step in the OAuth handshake). The remote server (Evernote in this case) returns a content length missing error. Details below.
Play WS Version
The version bundled with Play 2.6.10 (looks like 1.1.3 based on this).
API
Scala
Operating System
macOS/OS X 10.11.3 (El Capitan)
JDK
java version "1.8.0_92"
Java(TM) SE Runtime Environment (build 1.8.0_92-b14)
Java HotSpot(TM) 64-Bit Server VM (build 25.92-b14, mixed mode)
Library Dependencies
N/A
Expected Behavior
- Create an
OAuthobject, then callOAuth#retrieveRequestToken(callbackURL: String). - Method performs a request to remote OAuth server and returns an
Either[OAuthException, RequestToken]containing the result or an exception. - Assuming the OAuth parameters are correct, the
Eithershould contain a validRequestToken. - The token itself is extracted from the
RequestTokenobject to be used in subsequent steps of the OAuth handshake.
Actual Behavior
- Create an
OAuthobject, then callOAuth#retrieveRequestToken(callbackURL: String). - Method returns an
Either[OAuthException, RequestToken]. - The OAuth parameters are correct, yet the
Eithercontains anOAuthException:OAuthCommunicationException: Communication with the service provider failed: Service provider responded in error: 411 (Length Required).
After some research, I was able to determine that this issue is quite common with OAuth 1.0 implementations (examples: 1, 2).
The suggested fix is to include a Content-Length header set to 0 with the HTTP POST to the remote OAuth endpoint. An even better approach is to just perform a plain GET request instead.
However, Play WS does not provide a way to pass in arbitrary HTTP headers along with the request. Also, at a glance, it seems like Signpost does not provide this option either.
Reproducible Test Case
package controllers
import javax.inject.Inject
import play.api.libs.oauth._
import play.api.mvc._
class EvernoteAuthController @Inject() (cc: ControllerComponents) extends AbstractController(cc) {
val KEY = ConsumerKey("YOUR_KEY", "YOUR_SECRET")
val EVERNOTE = OAuth(
ServiceInfo(
"https://sandbox.evernote.com/oauth",
"https://sandbox.evernote.com/oauth",
"https://sandbox.evernote.com/OAuth.action",
key = KEY
),
use10a = false
)
val CALLBACK_URL = "http://localhost:9000/auth"
def authenticate = Action { request: RequestHeader =>
// Step 1: Request temporary token
EVERNOTE.retrieveRequestToken(CALLBACK_URL) match {
case Right(t: RequestToken) =>
// Step 2: Request user authorization; pass temporary token from Step 1
// Also, store temporary token and secret for later use
Redirect(EVERNOTE.redirectUrl(t.token)).withSession("token" -> t.token, "secret" -> t.secret)
// TODO: check this out!
case Left(e) => throw e
}
}
}
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start at OAuth#retrieveRequestToken and trace how the token request is constructed and sent through Play WS. Reproduce the Evernote case from the issue's Scala example, then verify the request supplies the required zero Content-Length or otherwise avoids the 411 response and returns a RequestToken.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- scala
- Domain
- authentication
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100