]> jfr.im git - z_archive/twitter.git/blob - twitter/oauth.py
Improve documentation wrt OAuth.
[z_archive/twitter.git] / twitter / oauth.py
1 """
2 Visit the Twitter developer page and create a new application:
3
4 https://dev.twitter.com/apps/new
5
6 This will get you a CONSUMER_KEY and CONSUMER_SECRET.
7
8 When users run your application they have to authenticate your app
9 with their Twitter account. A few HTTP calls to twitter are required
10 to do this. Please see the twitter.oauth_dance module to see how this
11 is done. If you are making a command-line app, you can use the
12 oauth_dance() function directly.
13
14 Performing the "oauth dance" gets you an ouath token and oauth secret
15 that authenticate the user with Twitter. You should save these for
16 later so that the user doesn't have to do the oauth dance again.
17
18 read_token_file and write_token_file are utility methods to read and
19 write OAuth token and secret key values. The values are stored as
20 strings in the file. Not terribly exciting.
21
22 Finally, you can use the OAuth authenticator to connect to Twitter. In
23 code it all goes like this::
24
25 MY_TWITTER_CREDS = os.path.expanduser('~/.my_app_credentials')
26 if not os.path.exists(MY_TWITTER_CREDS):
27 oauth_dance("My App Name", CONSUMER_KEY, CONSUMER_SECRET,
28 MY_TWITTER_CREDS)
29
30 oauth_token, oauth_secret = read_token_file(MY_TWITTER_CREDS)
31
32 twitter = Twitter(auth=OAuth(
33 oauth_token, oauth_token_secret, CONSUMER_KEY, CONSUMER_SECRET))
34
35 # Now work with Twitter
36 twitter.statuses.update('Hello, world!')
37
38 """
39
40 from __future__ import print_function
41
42 from time import time
43 from random import getrandbits
44
45 try:
46 import urllib.parse as urllib_parse
47 from urllib.parse import urlencode
48 PY3 = True
49 except ImportError:
50 import urllib2 as urllib_parse
51 from urllib import urlencode
52 PY3 = False
53
54 import hashlib
55 import hmac
56 import base64
57
58 from .auth import Auth
59
60
61 def write_token_file(filename, oauth_token, oauth_token_secret):
62 """
63 Write a token file to hold the oauth token and oauth token secret.
64 """
65 oauth_file = open(filename, 'w')
66 print(oauth_token, file=oauth_file)
67 print(oauth_token_secret, file=oauth_file)
68 oauth_file.close()
69
70 def read_token_file(filename):
71 """
72 Read a token file and return the oauth token and oauth token secret.
73 """
74 f = open(filename)
75 return f.readline().strip(), f.readline().strip()
76
77
78 class OAuth(Auth):
79 """
80 An OAuth authenticator.
81 """
82 def __init__(self, token, token_secret, consumer_key, consumer_secret):
83 """
84 Create the authenticator. If you are in the initial stages of
85 the OAuth dance and don't yet have a token or token_secret,
86 pass empty strings for these params.
87 """
88 self.token = token
89 self.token_secret = token_secret
90 self.consumer_key = consumer_key
91 self.consumer_secret = consumer_secret
92
93 def encode_params(self, base_url, method, params):
94 params = params.copy()
95
96 if self.token:
97 params['oauth_token'] = self.token
98
99 params['oauth_consumer_key'] = self.consumer_key
100 params['oauth_signature_method'] = 'HMAC-SHA1'
101 params['oauth_version'] = '1.0'
102 params['oauth_timestamp'] = str(int(time()))
103 params['oauth_nonce'] = str(getrandbits(64))
104
105 enc_params = urlencode_noplus(sorted(params.items()))
106
107 key = self.consumer_secret + "&" + urllib_parse.quote(self.token_secret, '')
108
109 message = '&'.join(
110 urllib_parse.quote(i, '') for i in [method.upper(), base_url, enc_params])
111
112 signature = (base64.b64encode(hmac.new(
113 key.encode('ascii'), message.encode('ascii'), hashlib.sha1)
114 .digest()))
115 return enc_params + "&" + "oauth_signature=" + urllib_parse.quote(signature, '')
116
117 def generate_headers(self):
118 return {}
119
120 # apparently contrary to the HTTP RFCs, spaces in arguments must be encoded as
121 # %20 rather than '+' when constructing an OAuth signature (and therefore
122 # also in the request itself.)
123 # So here is a specialized version which does exactly that.
124 def urlencode_noplus(query):
125 if not PY3:
126 new_query = []
127 for k,v in query:
128 if type(k) is unicode: k = k.encode('utf-8')
129 if type(v) is unicode: v = v.encode('utf-8')
130 new_query.append((k, v))
131 query = new_query
132 return urlencode(query).replace("+", "%20")