And then I was surprised that both OAuth2Mixin and FacebookGraphMixin code are a bit... messy. Below are my thoughts:
1) In my opinion OAuth2Mixin._oauth_request_token_url() is unnecessary. Different OAuth provider demands different arguments for _OAUTH_ACCESS_TOKEN_URL. Each Oauth client should build the necessary arguments inside get_authenticated_user()
2) OAuth2Mixin.authorize_redirect() expects _OAUTH_AUTHORIZE_URL. Which is sensible, but Foursquare always throws error on that URL. So I had to use _OAUTH_AUTHENTICATE_URL instead.
3) Foursquare.authorize_redirect() also demands yet another custom argument: { "response_type": "code" }
Overall, It was a messy affair trying to extend Oauth2Mixin. Furthermore, FacebookGraphMixin code can be simplified greatly by not extending OAuth2Mixin. Take a look: https://github.com/didip/tornado_api/blob/master/facebook.py
Perhaps tornado/auth.py needs refactoring?
- Didip -
1) Isn't it better if web service APIs are pulled out of tornado and into their own repo/egg?
My reasoning is that companies change their API quite frequently and sometimes on a whim (A certain social-network-with-blue-background-color does this). Tying tornado release to service API updates seems silly.
Also, it seems like there's a new social network company every month. We need to be able to add new services quickly without affecting tornado release.
For backward compatibility, tornado egg can include the tornado-api egg
2) The newly refactored auth module should be explicit about the type of API and what version (I know that the following looks like java but please bear with my contrived example here):
auth.oauth2.v2.Foursquare
3) This one is a bit more subjective, auth.py and database.py felt like blemish to an otherwise stable mini framework.
- Didip -
Much of twitter_request could also be pulled down to an oauth_request
method in the base class. It would be great if the base OAuth classes
were usable as-is, e.g.
twitter_client = OAuthClient(consumer_key, consumer_secret)
resp = yield gen.Task(twitter_client.oauth_fetch,
'http://api.twitter.com/blah.json', ...)
resp.rethrow()
results = json_decode(resp.body)
Service-specific subclasses or wrappers could provide convenience
methods to help construct urls and parse responses, but wouldn't be
strictly necessary.
>
> 2) I don't know I agree. In the end they are all twitter requests, just some
> api end points request POST and accept post_args. The rest of the logic is
> pretty much the same so separating the 2 just seems like more code to
> maintain.
I agree. (Does PUT get its own method, or does it share with POST
because they both take a body? What about DELETE?...) In general I'd
suggest basing the request interface on AsyncHTTPClient wherever it's
relevant (with modifications e.g. to take arguments as a dict instead
of as an encoded body string)
>
> 3) I was thinking leave the access_token to be supplied on request, that way
> you can generate one Twitter object to support multiple client requests for
> different users. Not sure exactly why you'd want to do that, but access
> token just seems to be request oriented not necessary at object creation. I
> do agree on creating a reusable httpclient, will keep that in mind.
I tend to agree, although if each of these oauth clients only has one
entry point and no state beyond saving its constructor arguments,
maybe it's useful to think of the design in terms of partial function
application rather than OO and subclassing. That would make it easier
for applications to decide which parameters they want to globally bind
and which should be per-request (although in practice since
http_client, consumer_key and consumer_secret would nearly always be
globally fixed, the class-based design is appealingly familiar).
>
> 4) Not sure.. I was thinking authenticate and authorize would be in the
> final TwitterMixin, because the way oauth works the user has to be
> redirected to app so keeping that dependent on the request handler just
> makes sense. Unless I'm missing something?
One thing I've thought about here is that instead of a TwitterMixin,
maybe there should be a BaseTwitterLoginHandler which just has an
abstract save_user() method for the application to override. This
would be nicer in the common case than the cut-n-paste blob the
current TwitterMixin uses, although maybe people need more flexibility
to integrate things into their apps.
-Ben
-Ben