From: Mike Verdone Date: Thu, 24 Jul 2014 22:39:28 +0000 (+0200) Subject: Improve OAuth2 app-only code and documentation X-Git-Tag: twitter-1.15.0~12 X-Git-Url: https://jfr.im/git/z_archive/twitter.git/commitdiff_plain/c2176d4e0365bb0764ff6962721053acaa1d1e39 Improve OAuth2 app-only code and documentation --- diff --git a/twitter/__init__.py b/twitter/__init__.py index 326c8f2..d0d6507 100644 --- a/twitter/__init__.py +++ b/twitter/__init__.py @@ -10,11 +10,14 @@ from textwrap import dedent from .api import Twitter, TwitterError, TwitterHTTPError, TwitterResponse from .auth import NoAuth, UserPassAuth -from .oauth import (OAuth, read_token_file, write_token_file, - __doc__ as oauth_doc) -from .oauth2 import OAuth2 +from .oauth import ( + OAuth, read_token_file, write_token_file, + __doc__ as oauth_doc) +from .oauth2 import ( + OAuth2, read_bearer_token_file, write_bearer_token_file, + __doc__ as oauth2_doc) from .stream import TwitterStream -from .oauth_dance import oauth_dance +from .oauth_dance import oauth_dance, oauth2_dance __doc__ = __doc__ or "" @@ -43,9 +46,9 @@ Authentication -------------- You can authenticate with Twitter in three ways: NoAuth, OAuth, or -UserPassAuth. Get help() on these classes to learn how to use them. +OAuth2 (app-only). Get help() on these classes to learn how to use them. -OAuth is probably the most useful. +OAuth and OAuth2 are probably the most useful. Working with OAuth @@ -54,6 +57,27 @@ Working with OAuth __doc__ += dedent(oauth_doc or "") -__all__ = ["Twitter", "TwitterStream", "TwitterResponse", "TwitterError", - "TwitterHTTPError", "NoAuth", "OAuth", "UserPassAuth", - "read_token_file", "write_token_file", "oauth_dance", "OAuth2"] +__doc__ += """ +Working with OAuth2 +------------------- +""" + +__doc__ += dedent(oauth2_doc or "") + +__all__ = [ + "NoAuth", + "OAuth", + "OAuth2", + "oauth2_dance", + "oauth_dance", + "read_bearer_token_file", + "read_token_file", + "Twitter", + "TwitterError", + "TwitterHTTPError", + "TwitterResponse", + "TwitterStream", + "UserPassAuth", + "write_bearer_token_file", + "write_token_file", + ] diff --git a/twitter/oauth2.py b/twitter/oauth2.py index a71bab1..9b4d846 100644 --- a/twitter/oauth2.py +++ b/twitter/oauth2.py @@ -1,17 +1,20 @@ """ -Visit the Twitter developer page and create a new application: +Twitter only supports the application-only flow of OAuth2 for certain +API endpoints. This OAuth2 authenticator only supports the application-only +flow right now. + +To authenticate with OAuth2, visit the Twitter developer page and create a new +application: https://dev.twitter.com/apps/new This will get you a CONSUMER_KEY and CONSUMER_SECRET. -Twitter only supports the application-only flow of OAuth2 for certain -API endpoints. This OAuth2 authenticator only supports the application-only -flow right now. If twitter supports OAuth2 for other endpoints, this -authenticator may be modified as needed. +Exchange your CONSUMER_KEY and CONSUMER_SECRET for a bearer token using the +oauth2_dance function. -Finally, you can use the OAuth2 authenticator to connect to Twitter. In -code it all goes like this:: +Finally, you can use the OAuth2 authenticator and your bearer token to connect +to Twitter. In code it goes like this:: twitter = Twitter(auth=OAuth2(bearer_token=BEARER_TOKEN)) @@ -30,6 +33,20 @@ except ImportError: from base64 import b64encode from .auth import Auth +def write_bearer_token_file(filename, oauth2_bearer_token): + """ + Write a token file to hold the oauth2 bearer token. + """ + oauth_file = open(filename, 'w') + print(oauth2_bearer_token, file=oauth_file) + oauth_file.close() + +def read_bearer_token_file(filename): + """ + Read a token file and return the oauth2 bearer token. + """ + f = open(filename) + return f.readline().strip() class OAuth2(Auth): """ @@ -42,22 +59,16 @@ class OAuth2(Auth): consumer_secret if you are requesting a bearer_token. Otherwise you must supply the bearer_token. """ - self.bearer_token = None - self.consumer_key = None - self.consumer_secret = None - - if bearer_token: - self.bearer_token = bearer_token - elif consumer_key and consumer_secret: - self.consumer_key = consumer_key - self.consumer_secret = consumer_secret - else: + self.bearer_token = bearer_token + self.consumer_key = consumer_key + self.consumer_secret = consumer_secret + + if not (bearer_token or (consumer_key and consumer_secret)): raise MissingCredentialsError( 'You must supply either a bearer token, or both a ' 'consumer_key and a consumer_secret.') def encode_params(self, base_url, method, params): - return urlencode(params) def generate_headers(self): @@ -66,25 +77,18 @@ class OAuth2(Auth): b'Authorization': 'Bearer {0}'.format( self.bearer_token).encode('utf8') } - - elif self.consumer_key and self.consumer_secret: - + else: headers = { b'Content-Type': (b'application/x-www-form-urlencoded;' b'charset=UTF-8'), - b'Authorization': 'Basic {0}'.format( - b64encode('{0}:{1}'.format( + b'Authorization': 'Basic {}'.format( + b64encode('{}:{}'.format( quote(self.consumer_key), quote(self.consumer_secret)).encode('utf8') ).decode('utf8') ).encode('utf8') } - - else: - raise MissingCredentialsError( - 'You must supply either a bearer token, or both a ' - 'consumer_key and a consumer_secret.') - + print(headers) return headers diff --git a/twitter/oauth_dance.py b/twitter/oauth_dance.py index d3f0ea8..1e39849 100644 --- a/twitter/oauth_dance.py +++ b/twitter/oauth_dance.py @@ -2,9 +2,11 @@ from __future__ import print_function import webbrowser import time +import json -from .api import Twitter +from .api import Twitter, json from .oauth import OAuth, write_token_file +from .oauth2 import OAuth2, write_bearer_token_file try: _input = raw_input @@ -12,6 +14,23 @@ except NameError: _input = input +def oauth2_dance(consumer_key, consumer_secret, token_filename=None): + """ + Perform the OAuth2 dance to transform a consumer key and secret into a + bearer token. + + If a token_filename is given, the bearer token will be written to + the file. + """ + twitter = Twitter( + auth=OAuth2(consumer_key=consumer_key, consumer_secret=consumer_secret), + format="", + api_version="") + token = json.loads(twitter.oauth2.token(grant_type="client_credentials") + .encode("utf8"))["access_token"] + if token_filename: + write_bearer_token_file(token) + return token def oauth_dance(app_name, consumer_key, consumer_secret, token_filename=None): """ diff --git a/twitter/stream_example.py b/twitter/stream_example.py index 8869c15..4e5184c 100644 --- a/twitter/stream_example.py +++ b/twitter/stream_example.py @@ -9,9 +9,9 @@ import argparse from twitter.stream import TwitterStream, Timeout, HeartbeatTimeout, Hangup from twitter.oauth import OAuth +from twitter.oauth2 import OAuth2, read_bearer_token_file from twitter.util import printNicely - def parse_arguments(): parser = argparse.ArgumentParser(description=__doc__ or "")