I am trying to create a quick web app that authenticates into a users Yahoo account, but I am having trouble getting 'user approval'.
Yahoo Auth Page
Personally, every time I go to external website and have to authenticate, I usually log into my account. This seems to be redirecting me to a page and asking for a code. I have 0 idea what code I would need to supply in order to authenticate. And if I dont know, my users certainly wont! I am building a flask app, and I have tried to model my code around this repo.
I have added some code specifically for Yahoo, but cant seem to connect the dots. New YahooSignIn subclass in the oauth.py file below:
class YahooSignIn(OAuthSignIn):
def __init__(self):
super(YahooSignIn, self).__init__('yahoo')
self.service = OAuth2Service(
name='yahoo',
consumer_id=self.consumer_id,
consumer_secret=self.consume_secret,
authorize_url='https://api.login.yahoo.com/oauth/v2/request_auth',
access_token_url='https://api.login.yahoo.com/oauth/v2/get_token',
base_url='http://fantasysports.yahooapis.com/'
)
def authorize(self):
return redirect(self.service.get_authorize_url(
scope='email',
response_type='code',
redirect_uri=self.get_callback_url())
)
def callback(self):
def decode_json(payload):
return json.loads(payload.decode('utf-8'))
if 'code' not in request.args:
return None, None, None
oauth_session = self.service.get_auth_session(
data={'code': request.args['code'],
'grant_type': 'authorization_code',
'redirect_uri': self.get_callback_url()},
decoder=decode_json
)
me = oauth_session.get('me?fields=id,email').json()
return (
'yahoo$' + me['id'],
me.get('email').split('#')[0],
me.get('email')
)
The only other change made was to the index.html page to add an additional link with a 'yahoo' parameter
<p>Login with Yahoo</p>
Any help would be greatly appreciated as this one has stumped me the last two nights and I would love to move past this!
Previous to this year (2018/19) I had been using Yahoo's Oauth 1.0 API. This year I ran into problems using it so I switched to using Oauth 2.0 via the yahoo-oauth library linked below. They have a nice page that describes how to use their library. Here is the code that I used.
from yahoo_oauth import OAuth2
class YahooFantasyAPI:
def fetchGameID(self):
session = self.getSession()
r = session.get(
'https://fantasysports.yahooapis.com/fantasy/v2/game/nfl'
)
print(r.text)
def getSession(self):
oauth = OAuth2(None, None, from_file='oauth2.json')
if not oauth.token_is_valid():
oauth.refresh_access_token()
return oauth.session
api = YahooFantasyAPI()
fetchGameID()
https://yahoo-oauth.readthedocs.io/en/latest/
Related
I am trying to implement oauth manually on my website which is being implemented using tornado. My url (localhost/form) contains a button which when clicked brings up a facebook login and then if the login is successful redirects back to the same site with a token (localhost/form?code=XXX) where I collect the token/code and begins taking requests from facebook.
My issue is that upon redirecting back to localhost/form with a given code, it appears that I reinitialize a brand new oauth2session object which does not match up with the token and I receive a GET request error. How should I correctly pass this oauth2session object or correctly reinitialize it? Is this reinitialization causing my error or something else? My current code which does not work is:
class FormHandler (BaseHandler):
def get(self):
client_id =XXX
client_secret =XXX
authorization_base_url = 'https://www.facebook.com/dialog/oauth'
token_url = 'https://graph.facebook.com/oauth/access_token'
facebook = OAuth2Session(client_id, redirect_uri='http://localhost/form')
facebook = facebook_compliance_fix(facebook)
authorization_url, state = facebook.authorization_url(authorization_base_url)
self.write('<button id="FacebookManual" type="button">Facebook Manual</button><br><script> document.getElementById("FacebookManual").onclick = function () {location.href ="'+authorization_url+'";};</script>')
#Check to see if I get redirected with a code
authorization_code=self.get_argument("code", default=None, strip=False)
if authorization_code is not None:
redirect_response='https://localhost/form/?code='+authorization_code
facebook.fetch_token(token_url, client_secret=client_secret, authorization_response=redirect_response)
r = facebook.get('https://graph.facebook.com/me?')
self.write('Hello'+r.content)
#Roughly how my tornado is set up
def make_app():
return Application(
[
url('/', BaseHandler, { "var":"nothing" }, name="root"), # this is for the root! :)
url('/form', FormHandler, { "var":"initialize this!" }, name = "forlorn"),
],
# settings:
debug = True,
)
Edit: A friend advised me to include the error that I was receiving. The error that I get is a oauthlib.oauth2.rfc6749.errors.MismatchingStateError: (mismatching_state) CSRF Warning! State not equal in request and response.
ERROR:tornado.access:500 GET /form?code=XxX
I'm following this tutorial here and I have the following code with the python library Rauth and Flask:
def callback(self):
if 'code' not in request.args:
return None, None, None
oauth_session = self.service.get_auth_session(
data={'code': request.args['code'],
'grant_type': 'authorization_code',
'redirect_uri': self.get_callback_url()}
)
me = oauth_session.get('me').json()
print me['id'], me['name'], me.get('email')
return ('facebook$' + me['id'], me.get('email').split('#')[0], # Facebook does not provide username, so the email's user is used instead
me.get('email'))
As you can see, I try to print me['id'], me['name'], me.get('email'). The id and name print as planned though the email prints as None causing an issue with the function (as the email is used at the bottom of the function). When testing this with my personal Facebook account, I made sure I provided the email, I checked the app settings and where it says email, there is a marked checkbox stating that I'm sharing an email with this application.
Has Facebook changed the way the email is handled or received or is there an issue with my app or code? Thanks.
Apparently whit the update to version 2.4 of the APIs Facebook removed the email from the list of fields returned.
If your APP uses version 2.4 of the API you should probably exclude the email field from your code.
For reference:
https://developers.facebook.com/ads/blog/post/2015/07/08/marketing-api-v2_4/
Stefano
Ok, so i've googled around, i've found threads here on stackoverflow and i've checked the official Facebook wiki and.. and what not..
I now hope that one of you guys sits on a Facebook API sample code for Python.
This is what i've got so far and all i get is "Invalid Signature" via PyFacebook which appears to be a dead project:
from facebook import Facebook
api_key = '123456789______'
secret = '<proper secret key>'
OTK = 'XXXXX' # <-- You get this from: https://www.facebook.com/code_gen.php?v=1.0&api_key=123456789______
long_term_key = None
fb = Facebook(api_key, secret)
def generate_session_from_onetime_code(fb, code):
fb.auth_token = code
return fb.auth.getSession()
if not long_term_key:
long_term_key = generate_session_from_onetime_code(fb, OTK)['session_key']
print 'Replace None with this in the .py file for long_term_key:'
print long_term_key
fb.session_key = long_term_key
fb.uid = 000000001 # <-- Your user-id
fb.signature = api_key # <-- This doesn't work at all, MD5 of what?
#fb.validate_signature(fb) # <-- doesn't work either, prob need to pass MD5 handle?
print fb.friends.get() # <-- Generates "Invalid Signature"
"all" i want, is to retrieve my friends list for now,
if there's a better API point me in the right direction but Facebook has officially declared their own Python SDK dead and pyfacebook is almost working for me but not quite..
So, please help.
The unofficial fork of the python sdk is still working fine for me.
To retrieve your friends, generate an access token here:
https://developers.facebook.com/tools/access_token/
Limitations:
A user access token with user_friends permission is required to view
the current person's friends.
This will only return any friends who have used (via Facebook Login) the app making the request.
If a friend of the person declines the user_friends permission, that friend will not show up in the friend list for this person.
Code
import facebook
token = 'your token'
graph = facebook.GraphAPI(token)
profile = graph.get_object("me")
friends = graph.get_connections("me", "friends")
friend_list = [friend['name'] for friend in friends['data']]
print friend_list
I am using oAuth2WebServerFlow to get an oAuth access token and then retrieve a list of a user's contacts. I'm using web2py as the web framework.
flow = oauth2client.client.OAuth2WebServerFlow(client_id=CLIENT_ID,
client_secret=CLIENT_SECRET,
scope='https://www.google.com/m8/feeds',
user_agent=USER_AGENT)
callback = 'http://127.0.0.1:8000/Test/searcher/oauth2callback'
authorise_url = flow.step1_get_authorize_url(callback)
session.flow = pickle.dumps(flow)
redirect(authorise_url)
With the redirect then being handled as follows
flow = pickle.loads(session.flow)
credentials = flow.step2_exchange(request.vars)
My question is how to change the OAuth2Credentials object returned above into an OAuth2AccessToken object, that I can then use to authorise a request to the contacts library with something like:
gc = gdata.contacts.client.ContactsClient(source="")
token.authorize(gc)
gc.GetContacts
I've tried various methods with no success, normally getting an oAuth2AccessTokenError message of "Invalid Grant". I'm thinking something like this may work but also think there must be a simpler way!
token = gdata.gauth.OAuth2Token(client_id=CLIENT_ID, client_secret=CLIENT_SECRET, scope='https://www.google.com/m8/feeds', user_agent=USER_AGENT)
token.redirect_uri = 'http://127.0.0.1:8000/Test/searcher/oauth2callback'
token.get_access_token(<<code to pass the access_token out of the Credentials object??>>)
Can anyone help with this?
I managed to get this working. It was pretty straightforward actually, I just stopped using the OAuth2WebServerFlow, which didn't seem to be adding much value anyway. So the new code looks like this:
token = gdata.gauth.OAuth2Token(client_id, client_secret, scope, ua)
session.token = pickle.dumps(token)
redirect(token.generate_authorize_url(redirect_uri='http://127.0.0.1:8000/Test/default/oauth2callback'))
Followed by
def oauth2callback():
token = pickle.loads(session.token)
token.redirect_uri='http://127.0.0.1:8000/Test/default/oauth2callback'
token.get_access_token(request.vars.code)
gc = gdata.contacts.client.ContactsClient(source='')
gc = token.authorize(gc)
feed = gc.GetContacts()
Hope this is helpful to someoone!
Assuming you have code for newer OAuth2.0 APIs setup correctly, you can get this working by creating a Token class that modifies headers that converts Credentials -> Token class.
OAUTH_LABEL='OAuth '
#Transforms OAuth2 credentials to OAuth2 token.
class OAuthCred2Token(object):
def __init__(self, token_string):
self.token_string = token_string
def modify_request(self, http_request):
http_request.headers['Authorization'] = '%s%s' % (OAUTH_LABEL,
self.token_string)
ModifyRequest = modify_request
You can test it as follows:
gc = gdata.contacts.client.ContactsClient(source='')
token = OAuthCred2Token(creds.access_token)
gc.auth_token = token
print gc.GetContacts()
Note that this code will not handle token refreshes, which code using credentials handles.
In my own application, it is acceptable to make a simple call using a service to refresh the credentials before making a call to get contacts.
Here is sample code that I'm working with.
def index(request):
flow = OAuth2WebServerFlow(
client_id='xyz.apps.googleusercontent.com',
client_secret='xyz',
scope='https://www.googleapis.com/auth/plus.me',
user_agent='sample/1.0')
callback = 'http://%s/oauth2callback' % request.META[ 'HTTP_HOST' ]
authorize_url = flow.step1_get_authorize_url(callback)
return HttpResponse(flow)
For some reason 'flow' is always set to " " or empty instead of a request token. I have searched for days on this issue.
Can anyone tell me why I can't get a request token from google using this method?
fyi: I know that I should be redirecting the user to the authorize url, but I want to see if flow is set before I do since Google will provide the authorize url even if a request token wasn't returned.
Before you can use OAuth 2.0, you must register your application using
the Google APIs Console. After you've registered, go to the API Access
tab and copy the "Client ID" and "Client secret" values, which you'll
need later.
http://code.google.com/p/google-api-python-client/wiki/OAuth2#Registering
If this answer actually helps with your problem then I must bid an R.I.P. to S.O.