Tweepy Oauth2 - missing_code/callback URI - python

I have a basic understanding of auth2 and believe I am using the right protocol version (Oauth2 user context) to allow user login.
But I keep getting errors, probably because I'm missing knowledge and not understand some concepts.
I'm creating a basic web app with Tweepy.
I'm able to print and check the auth_url which when I paste in my browser, directs me as a user to authorize the app on twitter (but I get stuck on the redirect uri and in general get the error logged below):
Codebase:
import tweepy
import config
oauth2_user_handler = tweepy.OAuth2UserHandler(
client_id="",
redirect_uri="http://127.0.0.1:5000",
scope=["tweet.write"],
client_secret=""
)
auth_url = oauth2_user_handler.get_authorization_url()
access_token = oauth2_user_handler.fetch_token(f"{auth_url}")
client = tweepy.Client(f"{auth_url}")
The error message im getting:
"""
in parse_authorization_code_response
raise MissingCodeError("Missing code parameter in response.")
oauthlib.oauth2.rfc6749.errors.MissingCodeError: (missing_code) Missing code parameter in response.
"""
A few side notes I have:
What website URL do I put in my app settings? (Do I need to spin up a server and get the URL from that dashboard?) How is the website URL related to the callback URI and how do they differ? this is my first time working with authentication & Twitter API.

Related

Make OAuth2 token communication behind the scenes in python

I am trying to build a most basic python app that uses OAuth2 to log into GitHub and fetch from there my username - using requests-oauthlib (this one: https://requests-oauthlib.readthedocs.io/en/latest/examples/github.html).
Here is the code from there:
from requests_oauthlib import OAuth2Session
from flask.json import jsonify
client_id = <my GitHub id>
client_secret = <my GitHub secret>
authorization_base_url = 'https://github.com/login/oauth/authorize'
token_url = 'https://github.com/login/oauth/access_token'
github = OAuth2Session(client_id)
authorization_url, state = github.authorization_url(authorization_base_url)
print('Please go here and authorize,', authorization_url)
redirect_response = input('Paste the full redirect URL here:')
github.fetch_token(token_url, client_secret=client_secret,
authorization_response=redirect_response)
r = github.get('https://api.github.com/user')
print(r.content)
If I add the generated link to the browser, press enter, the url will change like a charm into a localhost:8080 (the callback url I provided in GitHub) with a code and state param. If I input it in my python code, I am able to fetch the GitHub data.
My question is, is there any way to automatize this? Like, skipping the user interaction and simply ask for the credentials in GitHub then print my data in the console?
Thanks
You are using the web application flow. For your purpose, it would be better to use the Device Flow.
Basically, the user needs to add a code that you will provide on a GitHub page input. And since you are using this as a simple app you could do that in a lower level and use the simple GET and POSTS requests together with requests

Getting user info with Cloud Endpoints (using other API Endpoints)

I'm trying to setup endpoints api (with google app engine, python), but I'm having some trouble getting user profile info. API is working, I can create entities through API Explorer on my localhost.
My goal is to allow user to register for my app by providing just an email, and authorizing the app to get the reset of the info from their profile. I have this endpoints method:
#User.method(http_method="POST",
auth_level=endpoints.AUTH_LEVEL.REQUIRED,
allowed_client_ids=[
endpoints.API_EXPLORER_CLIENT_ID
],
scopes=[
'https://www.googleapis.com/auth/userinfo.email',
'https://www.googleapis.com/auth/userinfo.profile',
'https://www.googleapis.com/auth/plus.me',
],
user_required=True,
request_fields=('email',),
response_fields=('id',),
name="register",
path="users")
def UserRegister(self, instance):
logging.info(os.getenv( 'HTTP_AUTHORIZATION' ))
# 'Beared __TOKEN__'
logging.info(endpoints.users_id_token._get_token(None))
# '__TOKEN__'
instance.put()
return instance
This works fine, I receive authorization token and user is created in datastore, but I can't figure out how to get the profile info. If I enter the token in OAuth2 API (through API Explorer):
POST https://www.googleapis.com/oauth2/v2/tokeninfo?access_token=__TOKEN__
I get token info with some data I need { "user_id": "__ID__", "verified_email": true, ...}, and if I use user_id in +API:
GET https://www.googleapis.com/plus/v1/people/__ID__
I can get the rest of the data I need (name, image, etc).
What do I need to do to achieve this in my UserRegister() method? I'd prefer to return just entity ID and do the rest of registration asynchronously, but that's another issue, I'll figure it out (; Just need some guidance how to call other endpoints from my code...
EDIT:
I've managed to figure out how to call other APIs (code on Gist), now only have one issue with Plus API:
I did some queries and eventually got anonymous quota error. Then I added key parameter and set it to WEB_CLIENT_ID or SERVICE_ACCOUNT:
WEB_CLIENT_ID is OAuth2 Client ID (type: Web Application) from console.developers.google.com/apis/credentials,
SERVICE_ACCOUNT is default App Engine service account - MY_APP#appspot.gserviceaccount.com...
and now I'm getting following error:
HttpError: <HttpError 400 when requesting https://www.googleapis.com/plus/v1/people/__VALID_USER_ID__?key=__WEB_CLIENT_ID__or__SERVICE_ACCOUNT__&alt=json returned "Bad Request">
When I use +API explorer I get results as expected:
REQUEST:
https://www.googleapis.com/plus/v1/people/__VALID_USER_ID__?key={YOUR_API_KEY}
RESPONSE:
200 OK + json data for user...
Anyone knows why is this happening?
Why am I getting BadRequest response?
Problem with BadRequest was that I didn't send authorization token... I did try to send it as access_token, but seams like +api docs are outdated - it should be oauth_token. When I included this parameter issue was resolved:
build('plus', 'v1').people().get(userId=user_id, key=SERVICE_ACCOUNT, oauth_token=token).execute()
HINT: Use http://localhost:8001/_ah/api/discovery/v1/apis/, and discoveryRestUrl property it has to see real properties of your API - this is where I found the answer.
oauth_token can be obtained like this:
token = os.getenv('HTTP_AUTHORIZATION').split(" ")[1]
# or like in my question:
token = endpoints.users_id_token._get_token(None)
I'd suggest HTTP_AUTHORIZATION variable, because users_id_token docs state that it's a:
Utility library for reading user information from an id_token.
This is an experimental library that can temporarily be used to extract
a user from an id_token. The functionality provided by this library
will be provided elsewhere in the future.
How to call other API Endpoints?
This is also an answer to my first question:
from googleapiclient.discovery import build
service = build('plus', 'v1')
request = service.people().get(userId=user_id, key=SERVICE_ACCOUNT, oauth_token=token)
response = request.execute()
data = dict(self.response.POST)
Code that worked for me is here.
NOTE: WEB_CLIENT_ID obtained from https://console.developers.google.com/apis/credentials (OAuth2 Client ID of type Web Application) will NOT work in this case. I had to use SERVICE_ACCOUNT - I didn't try to generate one through console, default service account I got from App Engine worked fine.
...things are much clearer now that I got this working. Hope it will help someone else (;

Flask CORS - no Access-control-allow-origin header present on a redirect()

I am implementing OAuth Twitter User-sign in (Flask API and Angular)
I keep getting the following error when I click the sign in with twitter button and a pop up window opens:
XMLHttpRequest cannot load https://api.twitter.com/oauth/authenticate?oauth_token=r-euFwAAAAAAgJsmAAABTp8VCiE. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access.
I am using the python-Cors packages to handle CORS, and I already have instagram sign in working correctly.
I believe it has something to do with the response being a redirect but have not been able to correct the problem.
My flask code looks like this:
app = Flask(__name__, static_url_path='', static_folder=client_path)
cors = CORS(app, allow_headers='Content-Type', CORS_SEND_WILDCARD=True)
app.config.from_object('config')
#app.route('/auth/twitter', methods=['POST','OPTIONS'])
#cross_origin(origins='*', send_wildcard=True)
##crossdomain(origin='')
def twitter():
request_token_url = 'https://api.twitter.com/oauth/request_token'
access_token_url = 'https://api.twitter.com/oauth/access_token'
authenticate_url = 'https://api.twitter.com/oauth/authenticate'
# print request.headers
if request.args.get('oauth_token') and request.args.get('oauth_verifier'):
-- omitted for brevity --
else:
oauth = OAuth1(app.config['TWITTER_CONSUMER_KEY'],
client_secret=app.config['TWITTER_CONSUMER_SECRET'],
callback_uri=app.config['TWITTER_CALLBACK_URL'])
r = requests.post(request_token_url, auth=oauth)
oauth_token = dict(parse_qsl(r.text))
qs = urlencode(dict(oauth_token=oauth_token['oauth_token']))
return redirect(authenticate_url + '?' + qs)
The problem is not yours. Your client-side application is sending requests to Twitter, so it isn't you that need to support CORS, it is Twitter. But the Twitter API does not currently support CORS, which effectively means that you cannot talk to it directly from the browser.
A common practice to avoid this problem is to have your client-side app send the authentication requests to a server of your own (such as this same Flask application that you have), and in turn the server connects to the Twitter API. Since the server side isn't bound to the CORS requirements there is no problem.
In case you want some ideas, I have written a blog article on doing this type of authentication flow for Facebook and Twitter: http://blog.miguelgrinberg.com/post/oauth-authentication-with-flask

Google OAuth 2.0 exchanging code form access token takes forver

I have a basic web app that I uses Google OAUTH flow. The web app navigates to /oauth which triggers the oauth flow. the authorization server then redirects the client to /oauth2callback. I am trying it out locally and the first step works fine. The server is able to redirect the client to google's authorization server and receive the code that is meant to be exchanged for an access token. However, when I try to exchange the code using flow.step2_exchange the http requests hangs for a very long time. Usually the request times out. Occasionally I do get a response back with a valid access token, which leads me to believe that the logic is (generally speaking) sound.
Does anybody know what might be causing google to delay the response for 30 seconds or more? Could it be that google is throttling development requests? Has anyone encountered something like this before? Is there something I am doing wrong? I should note that I am currently using google own oauth2 library but I've tried constructing the http requests manually and that didn't help either. It still hangs on https://accounts.google.com/o/oauth2/token.
This is the flask code for the oauth flow:
from oauth2client.client import OAuth2WebServerFlow
#app.route('/oauth', methods=['GET'])
def oauth():
print "oauth called: "
flow = OAuth2WebServerFlow(
client_id=config.GOOGLE_OAUTH_CLIENT_ID,
client_secret=config.GOOGLE_OAUTH_CLIENT_SECRET,
scope='https://www.googleapis.com/auth/userinfo.email',
redirect_uri='http://localhost:6060/oauth2callback')
auth_uri = flow.step1_get_authorize_url()
return redirect(auth_uri)
#app.route('/oauth2callback', methods=["GET"])
def oauth_callback():
code = request.args['code']
flow = OAuth2WebServerFlow(
client_id=config.GOOGLE_OAUTH_CLIENT_ID,
client_secret=config.GOOGLE_OAUTH_CLIENT_SECRET,
scope='https://www.googleapis.com/auth/userinfo.email',
redirect_uri='http://localhost:6060/oauth2callback')
credentials = flow.step2_exchange(code)

Posting to my Facebook page from my web app in production

I installed Django Facebook and I am using this method to post to a Page:
from open_facebook.api import OpenFacebook
graph = OpenFacebook("my_access_token")
graph.set('me/feed', message='hello world')
It's working on my local dev machine when I am logged in to my Facebook account. It stops working as soon as I sign out and I get this message:
OAuthException: Error validating access token: The session is invalid because the user logged out. (error code 190)
I got my access token from Graph API Explorer by passing /me/accounts
So the question, how do I make my code work on production when of course I'll not be logged in?
Please note that I'll only be posting to a Page that I own.
If you want to use 'me/feed' offline;
You can use the APP Access Token, once you've authorized the app and call USER_ID/feed to post.
Note: use user_id instead of me- me is only used when a user is in session, but you want to do the action after logout.
To get the app access token,
GET /oauth/access_token?
client_id={app-id}
&client_secret={app-secret}
&grant_type=client_credentials

Categories