I am having trouble connecting to the Evernote API using the OAuth wrapper bundled with Play 2.6.10 WS.
I am currently using sbt 0.13.15, Oracle JDK 1.8, and Scala 2.12.3.
The relevant piece of code from my OAuth Play controller:
import play.api.libs.oauth._
val KEY = ConsumerKey("KEY", "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
)
// 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
}
The application crashes due to the exception thrown from the Either returned by retrieveRequestToken. The exact exception is:
OAuthCommunicationException: Communication with the service provider failed: Service provider responded in error: 411 (Length Required)
After some snooping around, it seems as if this issue is common in OAuth and requires the POST request headers to contain a Content-Length (typically set to 0). Example: Why I get 411 Length required error?. But as far as I can tell, Play WS does not expose this option from Signpost (OAuth library under the hood), so I was not able to try this solution.
Of course, I may be overlooking something here. Has anyone experienced a similar issue? I just want to make sure before creating a new issue on the WS repo.
Thanks.
Evernote requires content-length for the API calls so I think that's the case.
Getting 411 error bad request in Evernote
Related
I am trying to build a simple Http Get request that requires me to submit an api key as authentication (api key as unsername and blank password). I have seen some solutions using the groovyx.net.http.HTTPBuilder library. However, the piece of code will need to be deployed in an evironment that does not allow for libraries. So I tried the following where is the url of the website i am trying to reach:
// GET
def get = new URL("<url>").openConnection();
def getRC = get.getResponseCode();
println(getRC);
if(getRC.equals(200)) {
println(get.getInputStream().getText());
}
As expected this returns error 400 since I do not include any authentication with the api key, so I tried the following where is the api key:
def get = new URL("<url>");
def authString = "<api_key>:".getBytes().encodeBase64().toString();
def conn = get.openConnection();
conn.setRequestProperty("Authorization", "Basic ${authString}");
def getRC = conn.getResponseCode();
println(getRC);
println(conn.getInputStream().getText());
But I still get the 400 error. I tried picking up the request through Fiddler but it doesn't seem to be tracking it (executing Groovy code through GroovyConsole).
The second approach works. My mistake was to not substitute spaces in the URL with % signs.
I'm getting a 407 error using scalajHTTP. I read through the repository and it seems like I should be able to pass the basic auth credentials as a base64 encoded value. I've also tried using the helper method described in the GitHub issues .proxyAuth but that is no longer part of HTTPRequest in ScalaJ according to error messages (as well as it not being in the documentation)
Any ideas? My endpoint URL is HTTPS as well as my proxy (for additional context)
val proxyHost= s"https://$forwardProxy"
val requestForward = Http(url).postData(redactedSecret)
.option(HttpOptions.allowUnsafeSSL)
.headers(("Content-Type", "application/json"), ("Proxy-Authorization", s"Basic $proxyAuth"))
.proxy(proxyHost, 8080).asString
val responseForward: HttpResponse[String] = requestForward
This issued posted in Github but still not resolved, https://github.com/scalaj/scalaj-http/issues/87
I found a solution to this problem. I researched around and after trying http client libraries, I kept getting 407 errors even though they all support proxy auth. Anyway, I ended up having to do the following.
add
import java.net.{Authenticator,PasswordAuthentication}
and the modified code body that I previously above looks like:
val requestForward: HttpRequest = Http(url).postData(data)
.header("Content-Type", "application/json")
.proxy(proxyHost, 8080)
.option(HttpOptions.allowUnsafeSSL)
Authenticator.setDefault(new Authenticator() {
override def getPasswordAuthentication(): PasswordAuthentication = {
new PasswordAuthentication( s"$username", s"$password".toCharArray())
}
})
So as you can see I removed the header from the original request object and instead overrode the credentials. Make sure you do this before you call on the response object.
We are using IdentityServer for authentication and we are validating the access token using JwtSecurityTokenHandler ValidateToken. This used to work fine, but after we upgraded our client application to ASP.NET Core 1.0 RTM (from RC1), the validation fails. The received error is:
IDX10501: Signature validation failed. Unable to match 'kid'
When I look at the KeyID of the used certificate and the kid of the token, I can see that they are different. I checked the IdentityServer jwks-endpoint to check that I had the correct certificate and noticed that the kid and certificate key id are different from that endpoint too. From what I've understood, they are supposed to be the same?
Any ideas why the code broke during the upgrade since the certificate, token and IdentityServer are still the same and only the client app core was upgraded.
EDIT (More information)
I suspect that ValidateIssuerSigningKey is false by default and the key has not even been validated before (thus it was working). Now it seems that the ValidateIssuerSigningKey is being ignored (as bad practice?) and thus the validation fails.
Workaround/Fix
By setting the IssuerSigningKeyResolver manually and giving the key to use in validation explicitly, fixes the issue and validation passes. Not sure how good the workaround is and why the default doesn't work, but at least I can move on for now.
simplified code...
new JwtSecurityTokenHandler().ValidateToken(authTokens.AccessToken,
new TokenValidationParameters()
{
IssuerSigningKeys = keys,
ValidAudience = audience,
ValidIssuer = issuer,
IssuerSigningKeyResolver = (arbitrarily, declaring, these, parameters) => new List<X509SecurityKey> { securityKey }
}, out securityToken);
The Client and API should refer to the same instance of IdentityServer. We are running IdentityServerHost in Azure, which has different slots (main and staging) and two applications inconsistently referred to different slots. The Client received access token issued by IdSrv-main provider and passed it to API, that expected it from different provider IdSrv-staging. API validated it and returned error.
The problem is that the errror doesn't give a hint to the actual cause of the issue. MS should provide much more detailed error message to help debugging.
The current error message is not sufficient to identify the cause.
I need to programmatically log into a backend server, which returns an auth token as a cookie, then use that cookie to make requests going forward. I'm working in Lift 2.4, and everything I read seems to recommend using http-dispatch, but that has been cumbersome to learn! :-/ I'm working in dispatch-classic because of my SBT version (0.1-SNAPSHOT) and scala version 2.9.1. So I'm currently loading the dispatch 0.8.6 libraries.
I found the following at
https://groups.google.com/forum/#!msg/dispatch-scala/m7oWv2YAtjQ/imnkYoCDVUcJ
For retrieving cookies:
To read a cookie from a response, you have to call the Response#getCookies method. For example, you could do something like this:
val res = Http(url("http://www.google.com/ig/api").addQueryParameter("weather", "Bonn, Germany"))
val response = for { r <- res } yield (r.getCookies, r.getResponseBody)
for adding cookies to subsequent requests:
url("http://www.google.com/ig/api").addCookie(cookie)
But I can't get this to work.
My preference is code that works with dispatch 0.8.6, but if you can make it work in another version and don't see what that version won't work with my SBT and scala/Lift versions, I'll try using your recommended library version.
To get a the cookie, you should be able to do something like this:
Http(url("http://www.google.com/ig/api") <<? List("weather" -> "Bonn, Germany") >:> ((h) => h.get("Set-Cookie")))
That will request the URL, append the weather param, and then pass the response headers to a handler function which looks for the Set-Cookie header and returns an Option with the value, or None if it was not present.
To set a cookie, you can do:
Http(url("http://www.google.com/ig/api") <<? List("weather" -> "Bonn, Germany") <:< Map("Set-Cookie" -> "something") >| )
This will add the headers in the Map following the <:< directive, which in the case above includes the cookie. The >| handler simply ignores the response, but you can use any handler you want.
This guide is a pretty good reference for the different functions and handlers available.
I checked this out with 0.8.8, as I don't have the earlier version, and everything seemed to work for me. I can't sure for sure, but I think it should be the same with 0.8.6.
I have used the below code to run from SoapUI, but I still get a missing property exception:
No such property exists for class request
How do I resolve this issue?
def project = com.eviware.soapui.model.support.ModelSupport.getModelItemProject( request )
// initialize OAuth consumer
def consumer = new oauth.signpost.commonshttp.CommonsHttpOAuthConsumer( project.getPropertyValue( "oauth_consumer_key" ), project.getPropertyValue( "oauth_consumer_secret" ));
consumer.setTokenWithSecret( project.getPropertyValue( "oauth_access_token" ), project.getPropertyValue( "oauth_access_token_secret" ));
// sign the request
consumer.sign( context.httpMethod )
EDIT: took a look at the API guide, and it appears that you have the following:
def project = com.eviware.soapui.model.support.ModelSupport.getModelItemProject( request )
Then you call project.getPropertyValue. According to the API guide, there is no such method called getPropertyValue for com.eviware.soapui.model.support.ModelSupport.
There is a interface called com.eviware.soapui.model.project. Unless you're inheriting from interface com.eviware.soapui.model.TestPropertyHolder, you're not going to get getPropertyValue.
To help resolve your issue, you'll need to debug into your code. Depending on the results from 'request' on your first line, you might very well not have the property oauth_consumer_key, oauth_consumer_secret, oauth_access_token or oauth_access_token_secret. Output the contents of request (or just set a break point in SoapUI at the def project and work through validating if you have the property).
Otherwise, there are a couple of alternate ways to tackle your problem. These are solutions geared towards using OAuth with SoapUI.
Try the following:
def gu = new com.eviware.soapui.support.GroovyUtils( context );
def xml = gu.getXmlHolder( 'Authenticate - Default#Response' );
def token = xml.getNodeValue( '/auth/token' );
log.info( 'Got token: ' + token );
def suite = context.testCase.testSuite;
suite.setPropertyValue( 'auth_token', token );
log.info( 'Saved auth_token to suite.' );
The benefit of this code is that "the token sticks around in the TestSuite properties permanently. This has the side-effect/benefit of allowing me to run tests after the authentication test".
If that doesn't work, there's a great article here explaining how to do the OAuth against Vimeo; this should be very similar to the Twitter OAuth.
Neither of these solutions require SoapUI Pro.
The code you have included here goes into the event tab in the PROJECT editor and you use a RequestFilter.filterRequest event to execute. You also have to create custom properties for consumer_key, consumer_secret, oauth_acces_token, and oauth_acccess_token_secret.
Here is the guide on how to implement this on the smartbear website http://www.soapui.org/REST-Testing/twitter-sample-project.html