I am happy to announce that I have released Signpost 1.2 earlier this week and I will briefly go over the changes the library has undergone. Most changes were under the hood, although there are also some minor API changes. I have rewritten much of the core code base, so as to make the library more flexible and easily extensible, something that was often requested by users.
Flexibility, extensibility, and general design improvements
I have flipped quite some bits in signpost-core, and now Signpost is finally more easily extensible and better configurable by developers. AbstractOAuthConsumer now lets subclasses override practically any step in the signing process, or even the entire signing altogether, by following the template method pattern. So if you need special treatment while collecting message parameters for signing, or want to use a different nonce generation algorithm, just override the respective methods in your subclass.
There is also a new class called SigningStrategy. This class defines how a signature is written to an HTTP request. The default behavior is to write OAuth parameters to the HTTP Authorization header (using AuthorizationHeaderSigningStrategy), but you can go ahead and create a custom strategy and configure your OAuthConsumer with it.
Speaking of customization. Should you be implementing your own OAuthConsumer, I have also improved the testability in that case tremendously. Simply inherit from OAuthConsumerTest and implement buildConsumer(). The test will then be executed for your particular consumer configuration.
There are also two API changes by which everyone will be affected. First, I have removed the SignatureMethod enum, since it was not very useful. Instead, every OAuthConsumer uses the HMac-SHA1 signature method by default, since that’s what everyone seems to be using anyway. So constructing a consumer is now simplified to:
OAuthConsumer consumer = new DefaultOAuthConsumer(CONSUMER_KEY, CONSUMER_SECRET);
If you really want to use a different signer, you can do this:
The second API change affects OAuthProvider. In earlier versions, consumer and provider were closely coupled. That led to problems in service oriented environments, where each object should be self-contained when exposed as a service. Thus, the retrieve*Token() methods of OAuthProvider now accept a consumer as the first argument:
String authUrl = provider.retrieveRequestToken(consumer, "http://example.com/callback"); // ... provider.retrieveAccessToken(consumer, pin);
Signpost worked well on Android before, however, a bug in Android’s Java implementation (that would be Apache Harmony) prevented it to work correctly with certain service providers (HttpURLConnection is sending lower-case HTTP header names on Android, which breaks some server side OAuth implementations because they don’t recognize the Authorization header; see issue 20). I have therefore implemented a CommonsHttpOAuthProvider, which uses Apache HttpClient to receive tokens instead of HttpURLConnection.
Signpost can now sign URLs. Just pass the consumer a URL string, and it will treat it as an HTTP GET message on the resource identified by the URL and append all necessary OAuth parameters to that URL. This can be very useful if you want to produce clickable links to protected resources:
String signedUrl = consumer.sign("http://example.com");
Another minor but useful feature is Signpost’s new debug mode. If you run your app with the -Ddebug flag, Signpost will now print the signature base string and generated signature for each request it signs to stdout. This is very useful when trying to figure out what went wrong should a server answer with 401 and you believe you did everything right:
[SIGNPOST] SBS: GET&http%3A%2F%2Ftwitter.com%2Foauth%2Frequ... [SIGNPOST] signature: BVzjTYNjeJJwI4olm5ISHtvZ7Rc=
Of course this release has seen some bug fixes, too. One of the most important ones is that Signpost does not send a blank oauth_token parameter anymore when retrieving a request token. Although permitted by the standard, and although many providers like Twitter or Google accept this, certain other providers failed when doing so (e.g. Netflix). If you still want to get the old pre-1.2 behavior, call setSendEmptyTokens(true) on your consumer.
Another important fix/enhancement corrects the way Signpost used to write to the Authorization header. Previously, it would just overwrite it with the OAuth parameters it generated, which caused problems when client needed to provide a realm paramter in the Auth header alongside the OAuth params. Signpost now remembers what had been in the Authorization header prior to message signing, appends its own params to it, and writes all of them back to the header. This should get rid of 401s where service providers expect a security realm to be set.
Finally, a problem was corrected where x-www-form-encoded body params were ignored if the content type contained parameters like encodings.
Project reports / API docs
I have also finally uploaded the API docs and other project reports generated by the Maven site plugin to GitHub pages, so you can browse the API docs without linking the JavaDoc JAR. You can find all project reports and other generated information here: http://kaeppler.github.com/signpost/project-reports.html
Last but certainly not least: Signpost 1.2 is now available via Maven Central, which means you do not have to add the signpost-releases repository to your POM anymore. It is available straight away in any Maven powered Java project. Om nom!