blog.lukhnos.org

Flickr OAuth Support in ObjectiveFlickr

I've added Flickr OAuth support to ObjectiveFlickr over the weekend.

Flickr announced in June that they started requiring new apps to use the OAuth-based authentication process, and the previous authentication process was deprecated and will be eventually phased out in early 2012. I recommend developers who use ObjectiveFlickr check out the latest version to start the transition.

ObjectiveFlickr is hosted on github.

What's Added to ObjectiveFlickr

The main class, OFFlickrAPIContext, has two new methods:

Two new delegate methods are added to the OFFlickrAPIRequestDelegate protocol, which you need to implement:

The class OFFlickrAPIContext now also support properties OAuthToken and OAuthTokenSecret. It also has a new method -userAuthorizationURLWithRequestToken:requestedPermission: that returns the URL for user authorization, which you need to open in a new browser window.

A new sample app, OAuthTransitionMac, is also added to show how to use the new authentication API along with the old. It also shows you how to exchange the existing auth token (from the old API) for an OAuth-based token. OAuthTransitionMac uses ARC, so it also shows how to set up the project (since ObjectiveFlickr still uses manual retain/release).

The iPhone app, SnapAndRun, has also been updated to show how to use OAuth in your iOS apps. Many people have had difficulties in linking the ObjectiveFlickr library externally, particularly on the iOS Simulator. So I simply put everything in the project. I also realized that SnapAndRun is unnecessarily complicated for its purpose -- this is one thing you may be able to help improve (see the section "ObjectiveFlickr Needs Your Contribution" below).

What You Need to Do

  1. Read Flickr's own document to get an overview of the new authentication process. A glimpse of the flow chart should suffice for knowing what needs to be done.

  2. If your app doesn't have callback URL support, this is the time to add it. Refer to SnapAndRun (which has snapnrun://) and OAuthTransitionMac (oatransdemo://) for how to add URL support. In essence, you need to handle the incoming URL in your app delegate. This applies to both Mac and iOS apps.

  3. When your app requests the user to authenticate, and when the user agrees to proceed, make a call using -fetchOAuthRequestTokenWithCallbackURL: to obtain a request token. Pass your app's callback URL (e.g. oatransdemo://callback) to the method.

  4. Implement -flickrAPIRequest:didObtainOAuthRequestToken:secret:. This delegate method is called after the request token is successfully obtained, and you need to set your Flickr context's OAuth and OAuthSecret properties to the returned token and secret. Setting those two properties is required so that the subsequent access token fetch request will be signed correctly.

  5. If any error occurs, the delegate method -flickrAPIRequest:didFailWithError: will still be called. The NSError object passed will have the error code OFFlickrAPIRequestOAuthError, and the userInfo dictionary will have a key-value pair under OFFlickrAPIRequestOAuthErrorUserInfoKey, which is simply the error message that Flickr returns.

  6. In the same delegate method, call -userAuthorizationURLWithRequestToken:requestedPermission: to obtain the user authorization URL. The permission will be something like OFFlickrWritePermission (see ObjectiveFlickr.h for possible values). Then, open the URL in a separate browser. Use -[UIApplication openURL:] on iOS, -[NSWorkspace openURL:] on Mac.

  7. After the user authorized your app to use their data, your app will be activated again. Parse the URL's query with the OFExtractOAuthCallback function supplied in OFUtilities.h. You should obtain the authorized request token and verifier now.

  8. Obtain the actual access token with -fetchOAuthAccessTokenWithRequestToken:verifier:

  9. Implement the delegate method -flickrAPIRequest:didObtainOAuthAccessToken:secret: userFullName:userName:userName:userName:. This handles the access token fetch, and set your Flickr context's OAuth and OAuthSecret to the final access token/secret pair that is passed back to the delegate method. Again, any error occurred will be passed to the error handling delegate method.

  10. Done! You can start making API calls and upload photos with your existing code base.

Implementation Details

Flickr's OAuth uses HMAC-SHA1 signing, which I decided to implement inside ObjectiveFlickr without depending on any other library. At this point our app projects probably have too many dependencies, and I want the changes to OF and to the way you use it to be minimal.

I've moved the inline functions out of OFUtilities.h to a new OFUtilities.m. This makes projects that use ARC happier, as inline functions cannot make retain/release calls under ARC. A Base64 encoding routine written by Matt Gallagher is also embedded in OFUtilities.m and is not exposed to the outside (i.e. the functions are declared static). Again this is to minimize dependencies.

To minimize implicit state changes, I decided to require you to set your Flickr context's OAuthToken and OAuthTokenSecret in your delegate methods. Having OFFlickrRequest do it for you implicit seems to be a bad idea architecturally.

The current version "burns in" Flickr's auth service endpoints in the code. It may be a good idea to make them properties in OFFlickrContext. Flickr now also has HTTPS endpoints. I'll be glad to merge your pull requests if you have tried them out and made changes to the framework.

The API calling routines in OFFlickrRequest now has two code paths. If both the OAuthToken and OAuthTokenSecret properties are present (i.e. not nil) in your OFFlickrContext object, then the request object uses the OAuth-based signing to make the call. Otherwise it falls back to use the traditional authToken. If you're only interested in public data, there's no need for the authentication.

The OAuth spec allows three different ways of passing the signature. To simplify, I didn't use the Authorization HTTP header, but chose to put the signature as part of the HTTP POST body or HTTP GET query.

ObjectiveFlickr Needs Your Contribution

Many projects use ObjectiveFlickr, and I need your help.

Please let me know if ObjectiveFlickr works or breaks your project. I no longer work on projects that need to support OSes older than Mac OS X 10.6 or iOS 4.3. I tried to be careful, for example I don't use fast enumeration (lest it break OS X 10.4 support), but there may be places I use properties instead of old-school getters and setters in the library (not samples -- they now only target the latest Mac OS X/iOS). You can help expand the test coverage in this area.

Eventually support for older OS or SDKs will and should be dropped. So much happened during the past two years, and more will come! It is important the shed some skin in order to catch up with those new developments in the Mac/iOS development landscape.

OAuth requires the consumer of an API (your app) to sign their calls and it involves a lot of URI string escaping. Handling improperly and you might see strange errors when you set a fancy title to a photo. The chars that need to be escaped, in addition to the default charset, are defined in the kEscapeChars constant in ObjectiveFlickr.m. I tried a number of common cases, but again the test coverage is very small. This is another area that you may be able to help.

ObjectiveFlickr was first developed in 2006 and was rewritten in early 2009 during the iOS 2.2 days. And it shows: the SnapAndRun iPhone sample app is unnecessarily complicated, and many conventions are out of date. A sample app written from ground up should be able to demo how to use the library more succinctly. The library's own Xcode project has also needed to be remade so that app projects can again use cross-project reference to ObjectiveFlickr.

Then there's documentation and code comments. Or synchronous API calls (so that you can make calls in other threads easily) or even NSOperation/block-based request methods. A lot of things can be and need to be done in a framework project like ObjectiveFlickr. Your contribution will benefit the Flickr and Mac/iOS development community a lot.

Conclusion

It's highly recommended that you start migrating to use Flickr's OAuth-based authentication, and ObjectiveFlickr now has enough support for it. The work for the transition shouldn't be too much, as you only need to change your authentication process. Hopefully ObjectiveFlickr continues to serve your apps well. Best of luck with the transition!