oAuth & the 37 Signals API
I started what was supposed to be a quick and fun Django project using the Basecamp API. The lack of documentation in their API makes me want to punch a litter of puppies. (This is a joke, puppies are the best)
To help prevent you from punching puppies and rather than trudge through their APIs Google Group, here's what I've figured out. I'm using Python Requests because it's fantastic. If you don't use it stop the insanity, and make your life easier by using it. Additionally, my code examples are within the context of Django, but you should be able to translate them to your language of choice..
First off, read the oAuth spec. Next we can build a simple django view to handle the initial authentication.
Get Auth Code
import requests import urllib from django import http from django.conf import settings def auth(request): BC_CLIENT_ID = getattr(settings, 'BASECAMP_CLIENT_ID', None) BC_CLIENT_SECRET = getattr(settings, 'BASECAMP_CLIENT_SECRET', None) BC_REDIRECT_URI = getattr(settings, 'BASECAMP_REDIRECT_URI', None) launchpad_base_url = 'https://launchpad.37signals.com/' query_args = { 'type': self.auth_type, 'client_id': client_id, 'redirect_uri': redirect_uri } url = '{0}authorization/new?{1}'.format( launchpad_base_url, urllib.urlencode(self.query_args)) return http.HttpResponseRedirect(url)
Sweet so now the user is going to be redirected to the 37 Signals Launchpad to authenticate. They will then be directed back to your website to the BC_REDIRECT_URI that was defined above. We get that code GET parameter, and then submit a POST request to get the auth token.
Get Access Token
import json import requests from django import http def get_auth_token(request): code = request.GET.get('code') if not code: raise http.Http404 query_args = { 'type': self.auth_type, 'client_id': client_id, 'redirect_uri': redirect_uri } url = "https://launchpad.37signals.com/authorization/token" req = requests.post(url, data=self.query_args) if req.status_code != 200: # raise some kind of exception or do something data = json.loads(req.content) # do other things & `render_to_response()`
At this point, data is going to be a Python dictionary consisting of the following:
expires_in: Time until the token expiresaccess_token: The token needed to make authenticated requests on behalf of the userrefresh_token: A token to use to refresh the access token on behalf of the user without their intervention (eg, making them reauthenticate)
You'll want to save these in the user's session, or perhaps to the database. What ever you think is best.
Make Authorized Requests
Sweet, so now you have the users access_token. You can let the user do things with it now. For instance:
Get accounts the user belongs to
Doing this, you can get the id and urls (eg: https://example.basecamphq.com) to the users basecamp services.
import json import requests from django import http def some_view(request): # Get token from the session access_token = request.session.get('access_token') url = 'https://launchpad.37signals.com/authorization.json?access_token={1}.format( access_token) req = reqeusts.get(url) if req.status_code != 200: # do something if it's a bad request data = json.loads(req.content) return http.HttpResponse(data)
Authenticated calls to the Basecamp API
At this point, we can make calls to each projects API urls. example.basecamphq.com/me.xml
You need to set a custom auth header with the access_token. Why it's not easy like the Facebook API, I don't know. But anyhoo, here's what you'd do:
import requests url = "https://foobar.basecamphq.com/me.xml" headers = { "Authorization": 'Bearer "{0}"'.format(access_token) } req = requests.get(url, headers=headers) print req.content
Note You can substitute .json for .xml on some of these calls. Not all of them work though, and they don't officially support json. So rock out like it's 2006 and enjoy that XML!
Conclusion
APIs can be wonderful like GitHub/Twitter/Facebook/Instagram, or be a nightmare like in our case here. I think a lot comes down to documentation. Does lack of documentation mean they don't want you using it? I don't think the fact that the oAuth spec is kind of a moving target is an excuse to roll something out and provide no definitive documentation.
The wonderful APIs I mentioned above have their shit together. I hope 37 Signals decides to not treat theirs as an afterthought and brings it up to snuff with the excellence of their other offerings.