+import webbrowser
+import urllib, urllib2
+from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
+import urlparse, cgi
+
+class OAuth2:
+ """Implementation of OAuth2 protocol, as described by Facebook:
+
+ http://developers.facebook.com/docs/authentication/#web_server_auth
+
+ Uses a local web server to retrieve the code and hence the access token.
+
+ Copyright (c) Andrew Flegg <andrew@bleb.org> 2010.
+ Released under the Artistic Licence."""
+
+
+ # -----------------------------------------------------------------------
+ def __init__(self, client_id, client_secret, access_token = None):
+ '''Create a new OAuth 2.0 client, with the folowing arguments:
+
+ client_id - Identifier of the application.
+ client_secret - Secret API key of the application.
+ access_token - Current access token, if any.'''
+
+ self._client_id = client_id
+ self._client_secret = client_secret
+ self._access_token = access_token
+
+
+ # -----------------------------------------------------------------------
+ def authorise(self, authorise_url, access_token_url, args = None):
+ '''Open a browser window to allow the user to log in and
+ authorise this client.'''
+
+ redirect_uri = 'http://localhost:3435/success'
+ webbrowser.open_new('%s?client_id=%s&redirect_uri=%s&%s' % (authorise_url, self._client_id, redirect_uri, args and urllib.urlencode(args) or ''))
+ handler = OAuthCodeHandler()
+ code = handler.run()
+
+ result = urllib2.urlopen('%s?client_id=%s&redirect_uri=%s&client_secret=%s&code=%s' % (access_token_url, self._client_id, redirect_uri, self._client_secret, code)).read()
+ params = cgi.parse_qs(result)
+ if 'access_token' in params:
+ self._access_token = params['access_token'][0]
+ else:
+ print result, params
+ raise Exception('Unable to retrieve access_token from Facebook')
+
+
+ # -----------------------------------------------------------------------
+ def request(self, url, args = None):
+ '''Make an authenticated request to the given URL. If no
+ access token is currently set, an exception will be thrown.
+
+ An optional dictionary of parameters can be specified.'''
+
+ if not self._access_token:
+ raise Exception("Unauthorised")
+
+ query_url = '%s?access_token=%s&%s' % (url, self._access_token, args and urllib.urlencode(args) or '')
+# print query_url
+ result = urllib2.urlopen(query_url).read()
+ return result
+
+
+ # -----------------------------------------------------------------------
+ def get_access_token(self):
+ """Get the access token in use by this OAuth 2.0 client,
+ so that it can be persisted."""
+
+ return self._access_token
+
+
+# ---------------------------------------------------------------------------
+class OAuthCodeHandler(HTTPServer):
+ """Handles the response from an OAuth2 handler and allows the
+ retrieval of the code."""
+
+ # -----------------------------------------------------------------------
+ def __init__(self, success_response = '<h1>Success</h1><p>Please now close this window.</p>',
+ failure_response = '<h1>Failed</h1><p>%s</p>'):
+ '''Create a new handler, with optional overrides of the
+ success and failure response messages.'''
+
+ HTTPServer.__init__(self, ('127.0.0.1', 3435), OAuthHttpRequestHandler)
+ self._success_response = success_response
+ self._failure_response = failure_response
+ self._code = None
+
+
+ # -----------------------------------------------------------------------
+ def run(self):
+ '''Start a server and wait for a redirect. Return the code
+ if/when successful.'''
+
+ self.handle_request()
+ return self._code
+
+
+ # -----------------------------------------------------------------------
+ def set_code(self, code):
+ '''Called by the handler to feed us back the code.'''
+
+ self._code = code
+
+ def get_success_response(self):
+ return self._success_response
+
+ def get_failure_response(self):
+ return self._failure_response
+
+
+# ---------------------------------------------------------------------------
+class OAuthHttpRequestHandler(BaseHTTPRequestHandler):
+ def do_GET(self):
+ qs = urlparse.urlparse(self.path).query
+ params = cgi.parse_qs(qs)
+
+ if 'code' in params:
+ self.server.set_code(params['code'][0])
+
+ self.send_response(200)
+ self.send_header('Content-type', 'text/html')
+ self.end_headers()
+ self.wfile.write(self.server.get_success_response())
+ else:
+ print qs, params
+ self.send_response(500)
+ self.send_header('Content-type', 'text/html')
+ self.end_headers()
+ self.wfile.write(self.server.get_failure_response())
+
+ return