From 7081585861d06545bfeba498af3b270baba15089 Mon Sep 17 00:00:00 2001 From: Anthony Shaw Date: Thu, 15 Feb 2018 09:13:27 +1100 Subject: [PATCH 1/3] add a seperate option for premium accounts to use consumer_key and secret values and inherently generate the bearer token on startup --- searchtweets/credentials.py | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/searchtweets/credentials.py b/searchtweets/credentials.py index f049d8e..38cb0bd 100644 --- a/searchtweets/credentials.py +++ b/searchtweets/credentials.py @@ -11,8 +11,12 @@ import os import logging import yaml +import requests +import base64 from .utils import merge_dicts +OAUTH_ENDPOINT = 'https://api.twitter.com/oauth2/token' + __all__ = ["load_credentials"] logger = logging.getLogger(__name__) @@ -76,8 +80,16 @@ def _parse_credentials(search_creds, account_type): try: if account_type == "premium": - search_args = {"bearer_token": search_creds["bearer_token"], - "endpoint": search_creds["endpoint"]} + if "bearer_token" not in search_creds: + if "consumer_key" in search_creds \ + and "consumer_secret" in search_creds: + search_creds["bearer_token"] = _generate_bearer_token( + search_creds["consumer_key"], + search_creds["consumer_secret"]) + + search_args = { + "bearer_token": search_creds["bearer_token"], + "endpoint": search_creds["endpoint"]} if account_type == "enterprise": search_args = {"username": search_creds["username"], "password": search_creds["password"], @@ -183,3 +195,19 @@ def load_credentials(filename=None, account_type=None, else merge_dicts(env_vars, yaml_vars)) parsed_vars = _parse_credentials(merged_vars, account_type=account_type) return parsed_vars + + +def _generate_bearer_token(consumer_key, consumer_secret): + """ + Return the bearer token for a given pair of consumer key and secret values. + """ + consumer_secret = base64.b64encode(consumer_secret) + auth_value = 'Basic {0} {1}'.format(consumer_key, consumer_secret) + data = 'grant_type=client_credentials' + resp = requests.post(OAUTH_ENDPOINT, + data=data, + headers={'Authorization': auth_value}) + + resp.raise_for_status() + + return resp.json()['access_token'] From bad8ffadbc33d0210f084e22a805583fc92b6395 Mon Sep 17 00:00:00 2001 From: Anthony Shaw Date: Thu, 15 Feb 2018 09:17:31 +1100 Subject: [PATCH 2/3] b64 encode expects a byte string --- searchtweets/credentials.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/searchtweets/credentials.py b/searchtweets/credentials.py index 38cb0bd..22fe935 100644 --- a/searchtweets/credentials.py +++ b/searchtweets/credentials.py @@ -201,7 +201,7 @@ def _generate_bearer_token(consumer_key, consumer_secret): """ Return the bearer token for a given pair of consumer key and secret values. """ - consumer_secret = base64.b64encode(consumer_secret) + consumer_secret = base64.b64encode(consumer_secret.encode()) auth_value = 'Basic {0} {1}'.format(consumer_key, consumer_secret) data = 'grant_type=client_credentials' resp = requests.post(OAUTH_ENDPOINT, From aaaab9fa50183d70dd71f915dec351f8e3e55480 Mon Sep 17 00:00:00 2001 From: Anthony Shaw Date: Thu, 15 Feb 2018 09:40:21 +1100 Subject: [PATCH 3/3] after some more testing, better understood the required headers --- searchtweets/credentials.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/searchtweets/credentials.py b/searchtweets/credentials.py index 22fe935..902d96c 100644 --- a/searchtweets/credentials.py +++ b/searchtweets/credentials.py @@ -201,13 +201,19 @@ def _generate_bearer_token(consumer_key, consumer_secret): """ Return the bearer token for a given pair of consumer key and secret values. """ - consumer_secret = base64.b64encode(consumer_secret.encode()) - auth_value = 'Basic {0} {1}'.format(consumer_key, consumer_secret) + auth = base64.b64encode("{0}:{1}".format( + consumer_key, + consumer_secret).encode()).decode() + + headers = { + 'Authorization': 'Basic {0}'.format(auth), + 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'} data = 'grant_type=client_credentials' resp = requests.post(OAUTH_ENDPOINT, data=data, - headers={'Authorization': auth_value}) - - resp.raise_for_status() + headers=headers) + if resp.status_code >= 400: + logger.error(resp.text) + resp.raise_for_status() return resp.json()['access_token']