Hi I am trying to follow the Tweepy App Engine OAuth Example app in my app but am running into trouble.
Here is a link to the tweepy example code: http://github.com/joshthecoder/tweepy-examples
Specifically look at: http://github.com/joshthecoder/tweepy-examples/blob/master/appengine/oauth_example/handlers.py
Here is the relevant snippet of my code [Ignore the spacing problems]:
try:
authurl = auth.get_authorization_url()
request_token = auth.request_token
db_user.token_key = request_token.key
db_user.token_secret = request_token.secret
db_user.put()
except tweepy.TweepError, e:
# Failed to get a request token
self.generate('error.html', {
'error': e,
})
return
self.generate('signup.html', {
'authurl': authurl,
'request_token': request_token,
'request_token.key': request_token.key,
'request_token.secret': request_token.secret,
})
As you can see my code is very similar to the example. However, when I compare the version of the request_token.key and request_token.secret that are rendered on my signup page
I.e. the variables I output to the browser:
request_token.key
request_token.secret
Are not the same as the data stored in the datastore:
db_user.token_key = request_token.key
db_user.token_secret = request_token.secret
db_user.put()
As an example here is what I am seeing when testing:
Printed to the screen:
request_token.key: MocXJxcqzDJu6E0yBeaC5sAMSkEoH9NxrwZDDvlVU
request_token.secret: C7EdohrWVor9Yjmr58jbObFmWj0GdBHMMMrIkU8Fds
Values in the datastore:
token_key: 4mZQc90GXCqcS6u1LuEe60wQN53A0fj7wdXHQrpDo
token_secret: dEgr8cvBg9jmPNhPV55gaCwYw5wcCdDZU4PUrMPVqk
Any guidance on what I am doing wrong here?
Thanks!
Reference Links:
Here is a sample code to get Twitter followers-count for a single user using Tweepy (version 2.0) on Google App Engine (GAE) in Python (version 2.7).
# ----GAE MODULES-----------
import webapp2
from webapp2_extras import jinja2
from google.appengine.api import users
import tweepy
import urlparse
import logging
# ----JINJA2 TEMPLATE----------
class TemplateHandler(webapp2.RequestHandler):
#webapp2.cached_property
def jinja2(self):
return jinja2.get_jinja2(app=self.app)
def render_template(self, filename, **template_args):
logging.info('calling jinja2 render function %s %s', self, filename)
self.response.write(self.jinja2.render_template(filename, **template_args))
# ----CODE--------------------
class TwitterTweepyImplementation(TemplateHandler):
'''
All Tweepy related methods are handled in this class
'''
#All methods that expect HTTP GET
twitter_tweepy_impl_get_methods = {
'/tweepyimpl/oauthRedirect': 'redirect_to_twitter_for_user_to_enter_uname_and_pwd',
'/tweepyimpl/oauthCallback': 'handle_callback_from_twitter_after_user_authentication',
}
def get(self):
'''
All twitter specific get actions are handled here
'''
#identify page to display from the path in the URL
rcvd_url = self.request.path
#to keep the code a little easier to understand, there are no security checks or exception handling coded added in
#this code example, so please add those on your own.
#get destination method using key-value pair
dest_method = self.__class__.twitter_tweepy_impl_get_methods.get(rcvd_url, None)
if dest_method:
func = getattr(self, dest_method, None)
if func:
func()
return
def redirect_to_twitter_for_user_to_enter_uname_and_pwd(self):
"""
Twitter OAuth Redirection: redirects user to Twitter for entering user name and password
"""
logging.info('redirect_to_twitter_for_user_to_enter_uname_and_pwd')
auth = tweepy.OAuthHandler(TWITTER_CONSUMER_KEY, TWITTER_CONSUMER_SECRET, YOUR_OWN_REDIRECT_URL_AFTER_TWITTER_AUTHENTICATION)
'''YOUR_OWN_REDIRECT_URL_AFTER_TWITTER_AUTHENTICATION: you can set this everytime above or once at twitter.com from where
you get your Consumer Key and Consumer Secret. E.g., http://www.yourwebsite.com/tweepyimpl/oauthCallback'''
#get Twitter redirect url where user enters credentials (uname and pwd)
auth_url = auth.get_authorization_url(); #logging.info("auth_url = %s", auth_url);
#store temp credentials as browser cookies (these need to be stored in the browser so that after user completes authentication
#at Twitter.com, when user is redirected to the return URL above by Twitter (= YOUR_OWN_REDIRECT_URL_AFTER_TWITTER_AUTHENTICATION)
#your application server knows for which user this redirect is for).
self.response.set_cookie('token_key', auth.request_token.key)
self.response.set_cookie('token_secret', auth.request_token.secret)
#redirect user's browser to twitter auth URL where user can enter username and pwd
self.redirect(auth_url)
return
def handle_callback_from_twitter_after_user_authentication(self):
"""
Callback from Twitter after user enters user name and pwd at twitter.com URL
"""
logging.info('handle_callback_from_twitter_after_user_authentication')
#Twitter redirected browser here. Now read verifier and determine if user twitter authentication succeeded, failed, or was
#canceled by the user
auth = tweepy.OAuthHandler(TWITTER_CONSUMER_KEY, TWITTER_CONSUMER_SECRET)
verifier = self.request.get('oauth_verifier', None); #logging.info('verifier = %s', verifier)
#user canceled twitter oauth
if not verifier:
self.redirect('your_app_url') #add your own url here.
return
#fetch temp credentials from browser cookies (as set during redirect_to_twitter_for_user_to_enter_uname_and_pwd method).
token_key = self.request.cookies['token_key'];
token_secret = self.request.cookies['token_secret'];
#now exchange temp credentials for user specific access token
auth.set_request_token(token_key, token_secret)
#parse access token string to extract the key and the secret
access_token = auth.get_access_token(verifier=verifier); logging.info('access_token = %s', access_token)
params = urlparse.parse_qs(str(access_token), keep_blank_values=False)
access_key = params['oauth_token'][0]; logging.info('access_key = %s', access_key)
access_secret = params['oauth_token_secret'][0]; logging.info('access_secret = %s', access_secret)
#add access token information to the datastore for periodic fetch of Twitter information later on for this user, e.g., via a cron job.
user_obj = UserTwitterAccessTokenStorageDatabase.get_by_key_name(users.get_current_user().email())
user_obj.access_key = access_key
user_obj.access_secret = access_secret
user_obj.put()
auth.set_access_token(access_key, access_secret) #this statement you can use later on to fetch twitter data for any user whose
#access-key/secret you have stored in your database. For example, via a cron job.
#User does NOT need to be visiting your website for you to fetch twitter data for the user.
#use tweepy api now to get user data from Twitter
api = tweepy.API(auth)
me = api.me()
#display debug information
logging.info("me = %s", me)
logging.info('me.id_str = %s, name = %s, screen_name = %s', me.id_str, me.name, me.screen_name)
#get followers count for this user
user = api.get_user(me.id_str)
logging.info('num_followers = %s', user.followers_count)
#you have the required information - in this code example followers-count. now redirect user to your app determined URL
self.redirect('your_app_url') #add your own url here.
app = webapp2.WSGIApplication([
('/tweepyimpl/.*', TwitterTweepyImplementation)
], debug=const.DEBUG)
It seems you use twice request_token, request_token.key and request_token.secret. The second time ( in self.generate) you should read their values from your database and not request them again.
Related
I am creating the access token using the flask-jwt-extended and storing it into httpcookie only.
My requirement is if user logins to the site using credentials, I will generate one token and store it in httpcookie, so my protected route will be accessible through that access token.
If the same user will login to the site using the different system with the same credentials, then I want to expire the access token which I have created first, so when user will try to access the protected route it will be forbidden, it means, that it will logout the user explicitly from the first system.
Below is the code using which I am creating the access token and storing it in a cookie.
Note: in my case i am using imspin as an identity.
class NewJwtAuthentication(Resourse):
def post(self):
requestData = request.get_json()
LogRecordings().request(requestData)
id = requestData['id']
useremail = requestData['useremail']
if requestData['id'] == '' or requestData['id'] == '':
emptyDataResponse()
getstudentdetails = fetchAllWhere(DB_Queries.SelectAllUsingPinAndEmail, id,useremail)
if getstudentdetails :
#redirect(url_for('loginsuccess'))
access_token = create_access_token(identity=id)
refresh_token = create_refresh_token(identity=id)
d = jwt.decode(access_token, '48e48e4e1796b1de856737ca2418dd43', algorithms='HS256')
expiration_time = d['exp']
expires_on=datetime.fromtimestamp(expiration_time).strftime('%Y-%m-%d %H:%M:%S')
print(expires_on)
print(access_token)
print(refresh_token)
resp = make_response({'message': 'Login Successfull',
'csrf_token':flask_jwt_extended.get_csrf_token(encoded_token=access_token),
'csrf_refresh_token':flask_jwt_extended.get_csrf_token(encoded_token=refresh_token),
'accesstoken_expiry_time':expires_on,
'id':id})
set_access_cookies(resp, access_token)
set_refresh_cookies(resp, refresh_token)
#print(flask_jwt_extended.get_csrf_token(encoded_token=access_token))
#set_refresh_cookies(response, refresh_token)
return resp
else:
return make_response({'message':'could not verify' })
any help would be highly appreciated.
I am currently using Oauth2.0 to log into my Flask app. I have that working as intended. Now I want to use the same creds I got from logging in to be able to send requests to the Google drive api. Im not really sure where to start, the docs are difficult to follow.
note: I have activated the Google Drive api in the developer console.
Here is the code for the login callback.
globally:
GOOGLE_CLIENT_ID = config['google_client_id']
GOOGLE_CLIENT_SECRET = config['google_client_secret']
GOOGLE_DISCOVERY_URL = (
"https://accounts.google.com/.well-known/openid-configuration"
)
client = WebApplicationClient(GOOGLE_CLIENT_ID)
view function:
#bp.route('/login/callback')
def callback():
# Get authorization code Google sent back to you
code = request.args.get("code")
# Find out what URL to hit to get tokens that allow you to ask for
# things on behalf of a user
google_provider_cfg = get_google_provider_cfg()
token_endpoint = google_provider_cfg["token_endpoint"]
token_url, headers, body = client.prepare_token_request(
token_endpoint,
authorization_response=request.url,
redirect_url=request.base_url,
code=code
)
token_response = requests.post(
token_url,
headers=headers,
data=body,
auth=(GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET),
)
# Parse the tokens!
client.parse_request_body_response(json.dumps(token_response.json()))
# Now that you have tokens (yay) let's find and hit the URL
# from Google that gives you the user's profile information,
# including their Google profile image and email
userinfo_endpoint = google_provider_cfg["userinfo_endpoint"]
uri, headers, body = client.add_token(userinfo_endpoint)
userinfo_response = requests.get(uri, headers=headers, data=body)
# You want to make sure their email is verified.
# The user authenticated with Google, authorized your
# app, and now you've verified their email through Google!
if userinfo_response.json().get("email_verified"):
unique_id = userinfo_response.json()["sub"]
users_email = userinfo_response.json()["email"]
picture = userinfo_response.json()["picture"]
username = userinfo_response.json()["given_name"]
else:
return "User email not available or not verified by Google.", 400
user = User.query.filter(User.google_sub==unique_id).first()
login_user(user)
def get_google_provider_cfg():
return requests.get(GOOGLE_DISCOVERY_URL).json()
My thoughts are maybe I have to set up a google drive endpoint to be able to send api requests to with the same token? Im not quite sure.
I am very new to Python and Stackoverflow. I am working on connecting my Google Ads account with Python to automate few standard charts creation and email them to my team members. Please help me resolve this as I had not been able to find an answer to it upon Googling either.
Let me know if I have missed out on any info which might provide more context to the question here.
I have been using the steps as mentioned by #msaniscalchi. Created client ID and client Secret from https://console.developers.google.com and created googleads.yaml file in the same directory as the generate_refresh_token.py. When I run the script with respective client ID and client Secret values, I am getting "invalid syntax" error. I have verified my multiple times my client secret and ID values multiple times.
"""Generates refresh token for AdWords using the Installed Application flow."""
import argparse
import sys
from google_auth_oauthlib.flow import InstalledAppFlow
from oauthlib.oauth2.rfc6749.errors import InvalidGrantError
# Your OAuth2 Client ID and Secret. If you do not have an ID and Secret yet,
# please go to https://console.developers.google.com and create a set.
DEFAULT_CLIENT_ID = 609XXXXXXX22-58mbhXXXXXXXXXXXXXXXXXX6ri.apps.googleusercontent.com
DEFAULT_CLIENT_SECRET = 7uO7XXXXXXXXXXXXXX7dKBAP
# The AdWords API OAuth2 scope.
SCOPE = u'https://www.googleapis.com/auth/adwords'
# The redirect URI set for the given Client ID. The redirect URI for Client ID
# generated for an installed application will always have this value.
_REDIRECT_URI = 'urn:ietf:wg:oauth:2.0:oob'
parser = argparse.ArgumentParser(description='Generates a refresh token with '
'the provided credentials.')
parser.add_argument('--client_id', default=DEFAULT_CLIENT_ID,
help='Client Id retrieved from the Developer\'s Console.')
parser.add_argument('--client_secret', default=DEFAULT_CLIENT_SECRET,
help='Client Secret retrieved from the Developer\'s '
'Console.')
parser.add_argument('--additional_scopes', default=None,
help='Additional scopes to apply when generating the '
'refresh token. Each scope should be separated by a comma.')
class ClientConfigBuilder(object):
"""Helper class used to build a client config dict used in the OAuth 2.0 flow.
"""
_DEFAULT_AUTH_URI = 'https://accounts.google.com/o/oauth2/auth'
_DEFAULT_TOKEN_URI = 'https://accounts.google.com/o/oauth2/token'
CLIENT_TYPE_WEB = 'web'
CLIENT_TYPE_INSTALLED_APP = 'installed'
def __init__(self, client_type=None, client_id=None, client_secret=None,
auth_uri=_DEFAULT_AUTH_URI, token_uri=_DEFAULT_TOKEN_URI):
self.client_type = client_type
self.client_id = client_id
self.client_secret = client_secret
self.auth_uri = auth_uri
self.token_uri = token_uri
def Build(self):
"""Builds a client config dictionary used in the OAuth 2.0 flow."""
if all((self.client_type, self.client_id, self.client_secret,
self.auth_uri, self.token_uri)):
client_config = {
self.client_type: {
'client_id': self.client_id,
'client_secret': self.client_secret,
'auth_uri': self.auth_uri,
'token_uri': self.token_uri
}
}
else:
raise ValueError('Required field is missing.')
return client_config
def main(client_id, client_secret, scopes):
"""Retrieve and display the access and refresh token."""
client_config = ClientConfigBuilder(
client_type=ClientConfigBuilder.CLIENT_TYPE_WEB, client_id=client_id,
client_secret=client_secret)
flow = InstalledAppFlow.from_client_config(
client_config.Build(), scopes=scopes)
# Note that from_client_config will not produce a flow with the
# redirect_uris (if any) set in the client_config. This must be set
# separately.
flow.redirect_uri = _REDIRECT_URI
auth_url, _ = flow.authorization_url(prompt='consent')
print('Log into the Google Account you use to access your AdWords account '
'and go to the following URL: \n%s\n' % auth_url)
print('After approving the token enter the verification code (if specified).')
code = input('Code: ').strip()
try:
flow.fetch_token(code=code)
except InvalidGrantError as ex:
print('Authentication has failed: %s' % ex)
sys.exit(1)
print('Access token: %s' % flow.credentials.token)
print('Refresh token: %s' % flow.credentials.refresh_token)
if __name__ == '__main__':
args = parser.parse_args()
configured_scopes = [SCOPE]
if not (any([args.client_id, DEFAULT_CLIENT_ID]) and
any([args.client_secret, DEFAULT_CLIENT_SECRET])):
raise AttributeError('No client_id or client_secret specified.')
if args.additional_scopes:
configured_scopes.extend(args.additional_scopes.replace(' ', '').split(','))
main(args.client_id, args.client_secret, configured_scopes)
When I run the above code, I am getting the "Invalid Syntax" error highlighting at the numeric part of the Client ID and Secret.
Syntax error screenshot attached here
Editor Highlighter screenshot attached here
I'm trying to develop a web app using Google App Engine.
The language i'm using is Python and i'm using the facebook-sdk (https://facebook-sdk.readthedocs.io/en/latest/).
I'm stuck at making the OAuth Facebook login works correctly.
Every single time I attempt to log in to my app using Facebook login i get the error
URL Blocked: This redirect failed because the redirect URI is not whitelisted in the app’s Client OAuth Settings. Make sure Client and Web OAuth Login are on and add all your app domains as Valid OAuth Redirect URIs.
You can see the screenshot of the error (in the original language) below
That are the settings of my Facebook app
That's the code of my app's RequestHandler which handle the login's requests
import facebook
import os
import webapp2
import urllib
from webapp2_extras import sessions
facebook_ID = 'xxxxxxxxxxx'
facebook_secret = 'xxxxxxxxxxxxxxxxxxxxxx'
facebook_graphUrl = 'https://graph.facebook.com/v2.8'
facebook_redirectUri = 'http://gold-circlet-160109.appspot.com/login/'
facebook_requestCode = 'https://www.facebook.com/v2.8/dialog/oauth?' +
urllib.urlencode(
{'client_id':facebook_ID,
'redirect_uri':facebook_redirectUri,
'scope':'public_profile, email, user_friends'})
def retrieve_access_token(code):
args = {'redirect_uri': facebook_redirectUri,
'client_id': facebook_ID,
'client_secret': facebook_secret,
'code': code}
access_token = urllib.urlopen(facebook_graphUrl + "/oauth/access_token?" + urllib.urlencode(args)).read()
access_token = urlparse.parse_qs(access_token)
return access_token['access_token'][0]
def get_graph_api(token):
if isinstance(token, dict):
return facebook.GraphAPI(token['access_token'])
else:
return facebook.GraphAPI(token)
class BaseHandler(webapp2.RequestHandler):
def dispatch(self):
self.session_store = sessions.get_store(request=self.request)
try:
webapp2.RequestHandler.dispatch(self)
finally:
self.session_store.save_sessions(self.response)
#webapp2.cached_property
def session(self):
return self.session_store.get_session()
class FBLogin(BaseHandler):
def get(self):
code = self.request.get('code')
if self.request.get("code"):
access_tk = retrieve_access_token(code)
facebookObject = get_graph_api(access_tk)
facebookObject = facebookObject.get_object('me')
self.session["ECommerceUser"] = dict(
idSocial = facebookObject['id'],
image='http://graph.facebook.com/'+facebookObject['id']+'/picture?type=square',
username=last_name[0] + " " + last_name[1],
loginMethod=loginMethod
)
self.redirect("/")
else:
self.redirect(facebook_requestCode)
I set the same redirect URI in the URL login request is set in the Facebook Login settings, as you can see from the code above (i followed this guide: https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow)
Any suggestion?
I have the following class and am currently testing it via running a file which calls the methods in this file. I then use a mixture of print statements and checking the blog to make sure the code has worked.
I'd really like to write some pytest unit tests for this and automate it all, but how can I do this? Also, if authentication doesn't exist or had become invalid it opens the browser and prompts the user to input the auth code. Later on this will be handled by a gui form. Pytest doesn't take user input, and rightly so; it wouldn't be automated.
class BloggerInterface(object):
"""Connects to blogger api and authorises client."""
def get_credentials(self):
"""Gets google api credentials, or generates new credentials
if they don't exist or are invalid."""
scope = 'https://www.googleapis.com/auth/blogger'
flow = oauth2client.client.flow_from_clientsecrets(
'client_secret.json', scope,
redirect_uri='urn:ietf:wg:oauth:2.0:oob')
storage = oauth2client.file.Storage('credentials.dat')
credentials = storage.get()
if not credentials or credentials.invalid:
auth_uri = flow.step1_get_authorize_url()
webbrowser.open(auth_uri)
auth_code = input('Enter the auth code: ')
credentials = flow.step2_exchange(auth_code)
storage.put(credentials)
return credentials
def get_service(self):
"""Returns an authorised blogger api service."""
credentials = self.get_credentials()
http = httplib2.Http()
http = credentials.authorize(http)
service = apiclient.discovery.build('blogger', 'v3', http=http)
return service
def get_blog(self, blog_id):
"""Gets the details ofthe blog withthe id blog_id"""
BlogDetails = collections.namedtuple('BlogDetails', 'blog_id, name, desc, url')
conn = self.get_service()
request = conn.blogs().get(blogId=blog_id, view='ADMIN')
response = request.execute()
name = response.get('name')
desc = response.get('description')
url = response.get('url')
blog = BlogDetails(blog_id=blog_id, name=name, desc=desc, url=url)
return blog
def get_posts(self, blog_id, status='live'):
"""Gets all posts from the blog with the id blog_id"""
posts = []
conn = self.get_service()
request = conn.posts().list(blogId=blog_id, view='ADMIN',
status=status)
#Responses are paginated, so a paging loop is required.
while request:
response = request.execute()
for post in response.get('items', []):
post_id = post.get('id')
title = post.get('title')
url = post.get('url')
status = post.get('status')
content = post.get('content')
posts.append({'post_id':post_id, 'title':title, 'url':url,
'status':status, 'content':content})
request = conn.posts().list_next(request, response)
return posts
def add_post(self, blog_id, post, is_draft=True):
"""Adds a new post to the blog with the id blog_id"""
conn = self.get_service()
#post is in the form {title, content, (labels), author_name, author_id.
title, content, author_name, author_id, labels = post
data = {
'kind': 'blogger#post',
'title': title,
'content': content,
'labels': labels,
'author': {'displayName':author_name, 'id':author_id}
}
request = conn.posts().insert(blogId=blog_id, body=data,
isDraft=is_draft)
response = request.execute()
post_id = response.get('id')
return post_id
Don't test the oauth2client or webbrowser projects. Test how your code reacts to input and output from other parts. Those are black boxes, which you replace with your own mocks, so you can see how your code responds to different return values.
Use the unittest.mock module to produce the mocks. If you are using Python < 3.3, install the backport mock project to do so.
For example, for BloggerInterface.get_credentials(), you mock out oauth2client.client.flow_from_clientsecrets(), oauth2client.file.Storage(), webbrowser.open() and input. You can then play with the response from storage.get() to force your code to use webbrowser.open(), and test if your code correctly tried to open a webbrowser, and then called storage.put() to store the credentials:
with mock.patch('oauth2client.client.flow_from_clientsecrets') as mockflow, \
mock.patch('oauth2client.file.Storage') as MockStorage, \
mock.patch('webbrowser.open') as mockwbopen, \
mock.patch('yourmodule.input') as mockinput:
# set the credentials to invalid
storage = MockStorage.return_value
credentials = storage.get.return_value
credentials.invalid = True
# run the method and see if we get what we want
result = BloggerInterface().get_credentials()
# check that the flow was initialised correctly
mockflow.assert_called_with(
'client_secret.json', 'https://www.googleapis.com/auth/blogger',
redirect_uri='urn:ietf:wg:oauth:2.0:oob')
MockStorage.assert_called_with('credentials.dat')
# With invalid credentials, the code should obtain a auth url from the
# flow, pass it to the browser. Then the authentication code should be taken
# from input and passed back to the flow for exchange. Test these
# interactions took place:
flow.step1_get_authorize_url.assert_called_once_with()
mockwbopen.assert_called_once_with(flow.step1_get_authorize_url.return_value)
flow.step2_exchange.assert_called_once_with(mockinput.return_value)
storage.put(flow.step2_exchange.return_value)
assert result == flow.step2_exchange.return_value