Skip to content

Commit 67d7777

Browse files
committed
Added first pass at utility functions for OAuth. Updated command-line sample to use the utility functions.
1 parent d69e5e4 commit 67d7777

2 files changed

Lines changed: 140 additions & 79 deletions

File tree

apiclient/oauth.py

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
#!/usr/bin/python2.4
2+
#
3+
# Copyright 2010 Google Inc. All Rights Reserved.
4+
5+
"""Utilities for OAuth.
6+
7+
Utilities for making it easier to work with OAuth.
8+
"""
9+
10+
__author__ = 'jcgregorio@google.com (Joe Gregorio)'
11+
12+
import copy
13+
import urllib
14+
import oauth2 as oauth
15+
16+
try:
17+
from urlparse import parse_qs, parse_qsl
18+
except ImportError:
19+
from cgi import parse_qs, parse_qsl
20+
class MissingParameter(Exception):
21+
pass
22+
23+
def abstract():
24+
raise NotImplementedError("You need to override this function")
25+
26+
27+
class TokenStore(object):
28+
def get(user, service):
29+
"""Returns an oauth.Token based on the (user, service) returning
30+
None if there is no Token for that (user, service).
31+
"""
32+
abstract()
33+
34+
def set(user, service, token):
35+
abstract()
36+
37+
buzz_discovery = {
38+
'required': ['domain', 'scope'],
39+
'request': {
40+
'url': 'https://www.google.com/accounts/OAuthGetRequestToken',
41+
'params': ['xoauth_displayname']
42+
},
43+
'authorize': {
44+
'url': 'https://www.google.com/buzz/api/auth/OAuthAuthorizeToken',
45+
'params': ['iconUrl', 'oauth_token']
46+
},
47+
'access': {
48+
'url': 'https://www.google.com/accounts/OAuthGetAccessToken',
49+
'params': []
50+
},
51+
}
52+
53+
def _oauth_uri(name, discovery, params):
54+
if name not in ['request', 'access', 'authorize']:
55+
raise KeyError(name)
56+
keys = []
57+
keys.extend(discovery['required'])
58+
keys.extend(discovery[name]['params'])
59+
query = {}
60+
for key in keys:
61+
if key in params:
62+
query[key] = params[key]
63+
return discovery[name]['url'] + '?' + urllib.urlencode(query)
64+
65+
class Flow3LO(object):
66+
def __init__(self, discovery, consumer_key, consumer_secret, user_agent,
67+
**kwargs):
68+
self.discovery = discovery
69+
self.consumer_key = consumer_key
70+
self.consumer_secret = consumer_secret
71+
self.user_agent = user_agent
72+
self.params = kwargs
73+
self.request_token = {}
74+
for key in discovery['required']:
75+
if key not in self.params:
76+
raise MissingParameter('Required parameter %s not supplied' % key)
77+
78+
def step1(self, oauth_callback='oob'):
79+
"""Returns a URI to redirect to the provider.
80+
81+
If oauth_callback is 'oob' then the next call
82+
should be to step2_pin, otherwise oauth_callback
83+
is a URI and the next call should be to
84+
step2_callback() with the query parameters
85+
received at that callback.
86+
"""
87+
consumer = oauth.Consumer(self.consumer_key, self.consumer_secret)
88+
client = oauth.Client(consumer)
89+
90+
headers = {
91+
'user-agent': self.user_agent,
92+
'content-type': 'application/x-www-form-urlencoded'
93+
}
94+
body = urllib.urlencode({'oauth_callback': oauth_callback})
95+
uri = _oauth_uri('request', self.discovery, self.params)
96+
resp, content = client.request(uri, 'POST', headers=headers,
97+
body=body)
98+
if resp['status'] != '200':
99+
print content
100+
raise Exception('Invalid response %s.' % resp['status'])
101+
102+
self.request_token = dict(parse_qsl(content))
103+
104+
auth_params = copy.copy(self.params)
105+
auth_params['oauth_token'] = self.request_token['oauth_token']
106+
107+
uri = _oauth_uri('authorize', self.discovery, auth_params)
108+
return uri
109+
110+
def step2_pin(self, pin):
111+
"""Returns an oauth_token and oauth_token_secret in a dictionary"""
112+
113+
token = oauth.Token(self.request_token['oauth_token'],
114+
self.request_token['oauth_token_secret'])
115+
token.set_verifier(pin)
116+
consumer = oauth.Consumer(self.consumer_key, self.consumer_secret)
117+
client = oauth.Client(consumer, token)
118+
119+
headers = {
120+
'user-agent': self.user_agent,
121+
'content-type': 'application/x-www-form-urlencoded'
122+
}
123+
124+
uri = _oauth_uri('access', self.discovery, self.params)
125+
resp, content = client.request(uri, 'POST', headers=headers)
126+
return dict(parse_qsl(content))
127+
128+
def step2_callback(self, query_params):
129+
"""Returns an access token via oauth.Token"""
130+
pass

samples/cmdline/three_legged_dance.py

Lines changed: 10 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -1,97 +1,28 @@
1-
import urlparse
2-
import oauth2 as oauth
3-
import httplib2
4-
import urllib
5-
import simplejson
6-
7-
try:
8-
from urlparse import parse_qs, parse_qsl
9-
except ImportError:
10-
from cgi import parse_qs, parse_qsl
1+
from apiclient.oauth import buzz_discovery, Flow3LO
112

12-
httplib2.debuglevel = 4
13-
headers = {'user-agent': 'google-api-client-python-buzz-cmdline/1.0',
14-
'content-type': 'application/x-www-form-urlencoded'
15-
}
3+
import simplejson
164

5+
user_agent = 'google-api-client-python-buzz-cmdline/1.0',
176
consumer_key = 'anonymous'
187
consumer_secret = 'anonymous'
198

20-
request_token_url = ('https://www.google.com/accounts/OAuthGetRequestToken'
21-
'?domain=anonymous&scope=https://www.googleapis.com/auth/buzz')
22-
23-
access_token_url = ('https://www.google.com/accounts/OAuthGetAccessToken'
24-
'?domain=anonymous&scope=https://www.googleapis.com/auth/buzz')
25-
26-
authorize_url = ('https://www.google.com/buzz/api/auth/OAuthAuthorizeToken'
27-
'?domain=anonymous&scope=https://www.googleapis.com/auth/buzz')
9+
flow = Flow3LO(buzz_discovery, consumer_key, consumer_secret, user_agent,
10+
domain='anonymous',
11+
scope='https://www.googleapis.com/auth/buzz',
12+
xoauth_displayname='Google API Client for Python Example App')
2813

29-
consumer = oauth.Consumer(consumer_key, consumer_secret)
30-
client = oauth.Client(consumer)
31-
32-
# Step 1: Get a request token. This is a temporary token that is used for
33-
# having the user authorize an access token and to sign the request to obtain
34-
# said access token.
35-
36-
resp, content = client.request(request_token_url, 'POST', headers=headers,
37-
body='oauth_callback=oob')
38-
if resp['status'] != '200':
39-
print content
40-
raise Exception('Invalid response %s.' % resp['status'])
41-
42-
request_token = dict(parse_qsl(content))
43-
44-
print 'Request Token:'
45-
print ' - oauth_token = %s' % request_token['oauth_token']
46-
print ' - oauth_token_secret = %s' % request_token['oauth_token_secret']
47-
print
48-
49-
# Step 2: Redirect to the provider. Since this is a CLI script we do not
50-
# redirect. In a web application you would redirect the user to the URL
51-
# below.
52-
53-
base_url = urlparse.urlparse(authorize_url)
54-
query = parse_qs(base_url.query)
55-
query['oauth_token'] = request_token['oauth_token']
56-
57-
print urllib.urlencode(query, True)
58-
59-
url = (base_url.scheme, base_url.netloc, base_url.path, base_url.params,
60-
urllib.urlencode(query, True), base_url.fragment)
61-
authorize_url = urlparse.urlunparse(url)
14+
authorize_url = flow.step1()
6215

6316
print 'Go to the following link in your browser:'
6417
print authorize_url
6518
print
6619

67-
# After the user has granted access to you, the consumer, the provider will
68-
# redirect you to whatever URL you have told them to redirect to. You can
69-
# usually define this in the oauth_callback argument as well.
7020
accepted = 'n'
7121
while accepted.lower() == 'n':
7222
accepted = raw_input('Have you authorized me? (y/n) ')
73-
oauth_verifier = raw_input('What is the PIN? ').strip()
74-
23+
pin = raw_input('What is the PIN? ').strip()
7524

76-
# Step 3: Once the consumer has redirected the user back to the oauth_callback
77-
# URL you can request the access token the user has approved. You use the
78-
# request token to sign this request. After this is done you throw away the
79-
# request token and use the access token returned. You should store this
80-
# access token somewhere safe, like a database, for future use.
81-
token = oauth.Token(request_token['oauth_token'],
82-
request_token['oauth_token_secret'])
83-
token.set_verifier(oauth_verifier)
84-
client = oauth.Client(consumer, token)
85-
86-
resp, content = client.request(access_token_url, 'POST', headers=headers)
87-
access_token = dict(parse_qsl(content))
88-
89-
print 'Access Token:'
90-
print ' - oauth_token = %s' % access_token['oauth_token']
91-
print ' - oauth_token_secret = %s' % access_token['oauth_token_secret']
92-
print
93-
print 'You may now access protected resources using the access tokens above.'
94-
print
25+
access_token = flow.step2_pin(pin)
9526

9627
d = dict(
9728
consumer_key='anonymous',

0 commit comments

Comments
 (0)