Python Django: Why Google OAuth2 Token is valid after expiration time? - python

I am using Google OAuth2 flow as described here: https://developers.google.com/identity/protocols/oauth2/web-server.
First I check if user credentials are already in a DB, if not the auth flow is initiated. After exchanging code to an access token, credentials are saved in a DB. There along with an access token and a refresh token a token expiration dateTime is saved, which is 1 hour (credentials.expiry).
Then I wanted to check how the access token would be refreshed. So I explicitly requested API after the token expiration time. The previously saved credentials are in the DB, so I get them from there. Its validity is checked, and the access token should be updated. However, by some reason, the token is valid and by making a test request to the Google API I am kinda can make requests, no error is raised.
I cannot get why token is valid, when it obviously is expired. I also checked the token validity here: https://www.googleapis.com/oauth2/v3/tokeninfo?access_token=AT and got { "error_description": "Invalid Value" }.
My code:
def google_oauth_init_flow(request):
# get user id
user_id = request.GET.get('user_id', None)
print(user_id)
user = User.objects.get(id=user_id)
# set id to a state parameter
request.session['state'] = str(user.id)
flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(
os.path.abspath(os.path.join(os.path.dirname(__file__), KEY_FILE_LOCATION)),
scopes=SCOPES)
flow.redirect_uri = REDIRECT_URI
authorization_url, state = flow.authorization_url(
# Enable offline access so that you can refresh an access token without
# re-prompting the user for permission. Recommended for web server apps.
access_type='offline',
# Enable incremental authorization. Recommended as a best practice.
include_granted_scopes='true',
state=str(user.id)
)
return HttpResponseRedirect(authorization_url)
def google_oauth_exchange_token(request):
state = request.session.get('state', 'No state')
print(state)
flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(
os.path.abspath(os.path.join(os.path.dirname(__file__), KEY_FILE_LOCATION)),
scopes=SCOPES,
state=state)
flow.redirect_uri = REDIRECT_URI
authorization_response = request.build_absolute_uri()
flow.fetch_token(authorization_response=authorization_response)
credentials = flow.credentials
# save creds in DB
try:
save_credentials(credentials, state)
except IntegrityError as e:
if 'unique constraint' in e.message:
# duplicate detected
return HttpResponse('Violation of unique constraint')
def google_oauth_check_token(credentials, user_id):
# convert dict into credentials instance
if type(credentials) == dict:
credentials = init_creds_instance(credentials)
if credentials.expired:
print('Token is expired getting new one...')
# refresh credentials
request = google.auth.transport.requests.Request()
credentials.refresh(request)
# alternative method
# credentials.refresh(httplib2.Http())
# update token in DB
SystemServiceTokens.objects.filter(id=user_id).update(name='token', key=credentials.token)
return credentials
else: # always Token is valid
print('TOKEN', credentials.token)
print('EXPIRY', credentials.expiry)
print('REFRESH TOKEN', credentials.refresh_token)
print('Token is valid')
return credentials
def get_user_info(credentials):
user_info = build(serviceName='oauth2', version='v2', credentials=credentials)
return user_info.userinfo().get().execute()
def save_credentials(credentials, state):
user = User.objects.get(id=state)
google_oauth_check_token(credentials, user.id)
# get email
user_info = get_user_info(credentials)
model = SystemServiceTokens()
# check duplicate values
if not model.objects.filter(user_id=user.id, email=user_info['email'], service_id='ga').exists():
print("Entry contained in queryset")
model.token = credentials.token
model.refresh_token = credentials.refresh_token
model.expires_at = datetime.datetime.strftime(credentials.expiry, '%Y-%m-%d %H:%M:%S')
model.user = user
model.email = user_info['email']
model.service_id = 'ga'
return model.save()
else:
return HttpResponse('Unique constraint violation')
# No errors
def test_request_google(request):
credentials = SystemServiceTokens.objects.filter(user_id=9).first() # DEBUG ID
print(model_to_dict(credentials))
credentials = google_oauth_check_token(model_to_dict(credentials), 9)
# test
user_info = build(serviceName='oauth2', version='v2', credentials=credentials)
print(user_info.userinfo().get().execute())
drive = build('drive', 'v2', credentials=credentials)

If the python client library can detect a refresh token its going to refresh it for you. Assuming your code works correctly you should never have an access token expire the library will refresh it five minutes before it is due to expire.
My Python isn't the best but its probably this in the source code.
http.py#L1559

Related

equivalent of flask.request.url in FastAPI

i am trying to change google oauth2 example code from python flask to FastAPI.
this is the code
def oauth2callback():
# Specify the state when creating the flow in the callback so that it can
# verified in the authorization server response.
state = flask.session['state']
flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(
CLIENT_SECRETS_FILE, scopes=SCOPES, state=state)
flow.redirect_uri = flask.url_for('oauth2callback', _external=True)
# Use the authorization server's response to fetch the OAuth 2.0 tokens.
print("this is the flask request url -----------", flask.request.url)
**authorization_response = flask.request.url**
flow.fetch_token(authorization_response=authorization_response)
# Store credentials in the session.
# ACTION ITEM: In a production app, you likely want to save these
# credentials in a persistent database instead.
credentials = flow.credentials
flask.session['credentials'] = credentials_to_dict(credentials)
creds = google.oauth2.credentials.Credentials(
**flask.session['credentials'])
return flask.redirect(flask.url_for('test_api_request'))
the line is authorization_response = flask.request.url
the value of it is "http://localhost:8080/oauth2callback?state=79aCVleeoxdA4bYgo5YnzuK8vsvM22&code=4%2F0AWtgzh611Sc3dhUo_pqQSa4RVTEZOgX9rJHc328XCJ4UmLvHdg5zz3t1k8VS3ihZwKMEA&scope=email+profile+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile+openid+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fgmail.readonly+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fgmail.modify+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fgmail.labels+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email&authuser=0&prompt=none"
now i just want to change this line to the same in fastapi, i have tried request.url.path but it gives me "/oauth2callback" or sometimes None.
The request.url property is a string-like object, and you can get the full URL by casting it as a string.
i.e. str(request.url)
You can also access properties of the URL individually - more information on this can be found in Starlette's documentation.

How to expire access token using flask-jwt-extend when new login is initiated

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.

Unable to generate refresh token for Google Ads API using generate_refresh_token.py

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

How would I unit test code like this?

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

Tweepy + App Engine Example OAuth Help

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.

Categories