3 from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
7 """Implementation of OAuth2 protocol, as described by Facebook:
9 http://developers.facebook.com/docs/authentication/#web_server_auth
11 Uses a local web server to retrieve the code and hence the access token.
13 Copyright (c) Andrew Flegg <andrew@bleb.org> 2010.
14 Released under the Artistic Licence."""
17 # -----------------------------------------------------------------------
18 def __init__(self, client_id, client_secret, access_token = None):
19 '''Create a new OAuth 2.0 client, with the folowing arguments:
21 client_id - Identifier of the application.
22 client_secret - Secret API key of the application.
23 access_token - Current access token, if any.'''
25 self._client_id = client_id
26 self._client_secret = client_secret
27 self._access_token = access_token
30 # -----------------------------------------------------------------------
31 def authorise(self, authorise_url, access_token_url, args = None):
32 '''Open a browser window to allow the user to log in and
33 authorise this client.'''
35 redirect_uri = 'http://localhost:3435/success'
36 webbrowser.open_new('%s?client_id=%s&redirect_uri=%s&%s' % (authorise_url, self._client_id, redirect_uri, args and urllib.urlencode(args) or ''))
37 handler = OAuthCodeHandler()
40 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()
41 params = cgi.parse_qs(result)
42 if 'access_token' in params:
43 self._access_token = params['access_token'][0]
46 raise Exception('Unable to retrieve access_token from Facebook')
49 # -----------------------------------------------------------------------
50 def request(self, url, args = None):
51 '''Make an authenticated request to the given URL. If no
52 access token is currently set, an exception will be thrown.
54 An optional dictionary of parameters can be specified.'''
56 if not self._access_token:
57 raise Exception("Unauthorised")
59 query_url = '%s?access_token=%s&%s' % (url, self._access_token, args and urllib.urlencode(args) or '')
61 result = urllib2.urlopen(query_url).read()
65 # -----------------------------------------------------------------------
66 def get_access_token(self):
67 """Get the access token in use by this OAuth 2.0 client,
68 so that it can be persisted."""
70 return self._access_token
73 # ---------------------------------------------------------------------------
74 class OAuthCodeHandler(HTTPServer):
75 """Handles the response from an OAuth2 handler and allows the
76 retrieval of the code."""
78 # -----------------------------------------------------------------------
79 def __init__(self, success_response = '<h1>Success</h1><p>Please now close this window.</p>',
80 failure_response = '<h1>Failed</h1><p>%s</p>'):
81 '''Create a new handler, with optional overrides of the
82 success and failure response messages.'''
84 HTTPServer.__init__(self, ('127.0.0.1', 3435), OAuthHttpRequestHandler)
85 self._success_response = success_response
86 self._failure_response = failure_response
90 # -----------------------------------------------------------------------
92 '''Start a server and wait for a redirect. Return the code
93 if/when successful.'''
99 # -----------------------------------------------------------------------
100 def set_code(self, code):
101 '''Called by the handler to feed us back the code.'''
105 def get_success_response(self):
106 return self._success_response
108 def get_failure_response(self):
109 return self._failure_response
112 # ---------------------------------------------------------------------------
113 class OAuthHttpRequestHandler(BaseHTTPRequestHandler):
115 qs = urlparse.urlparse(self.path).query
116 params = cgi.parse_qs(qs)
119 self.server.set_code(params['code'][0])
121 self.send_response(200)
122 self.send_header('Content-type', 'text/html')
124 self.wfile.write(self.server.get_success_response())
127 self.send_response(500)
128 self.send_header('Content-type', 'text/html')
130 self.wfile.write(self.server.get_failure_response())