Fuddled API, Verbose Workaround
I've started writing some Scala applications (including one atop the Lift web framework) to access Unfuddle's API recently. I've mainly been building daily burndown reports for my team at Treehouse Agency. I've run into a few issues with API methods not working as advertised, and Unfuddle's been pretty good about fixing most of them.
The problem I've been experiencing as of January 5th is that Unfuddle has subtly broken authentication for client libraries that (wisely) wait for a 401 error with an accompanying WWW-Authenticate: Basic header before sending credentials. (Namely, Unfuddle's API stopped sending a WWW-Authenticate header altogether.) If need be, you can force most HTTP client libraries to send authentication on every request in one way or another, and that's what I had to do tonight with the excellent Databinder Dispatch library.
The code to prepare a request that will send Basic credentials when the server requests them is concise:
import dispatch.\_val req = :/("%s.unfuddle.com" format subdomain) / "api/v1" as (user, password)
Clean and simple, right?
To force an outgoing HTTP basic auth header with the least fuss, you'll need to create it by hand by Base64 encoding a user's username and password pair. The Apache Commons library includes a Base64 encoder and decoder, so by dipping your toes into Java-land you can construct this header. Once the Base64 value is encoded, the <:< method of the dispatch.Request class accepts a Map[String, String] and will set the corresponding headers on the outgoing web request.
Here's the full workaround I came up with.
import dispatch.\_import org.apache.commons.codec.binary.Base64val authString = "Basic " + new String(Base64.encodeBase64("%s:%s".format(user, password).getBytes))val req = :/("%s.unfuddle.com" format subdomain) / "api/v1" <:< Map("Authorization" -> authString)
So there you have it - a method to construct a Databinder Dispatch web request that always sends an HTTP basic auth header. Hopefully, all the web APIs that you interact with will do the right thing regarding WWW-Authenticate headers, but if not, now you'll know how to cope.
UPDATE: In case you're wondering if this is happening with an API that you're interacting with, the specific error that indicates that Apache HttpClient (the underlying workhorse of Databinder Dispatch) did not receive any WWW-Authenticate headers looks like this:
WARN - Authentication error: Unable to respond to any of these challenges: {}