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
Related
I am trying to access Uber API for requesting rides and I am trying to access appropriate scope like
profile
request_rides
request
And I successfully got the token, clientid, clientsecret, access_token and Then I tried to access profile by calling
from uber_rides.auth import AuthorizationCodeGrant
auth_flow = AuthorizationCodeGrant(
<CLIENT_ID>,
<SCOPES>,
<CLIENT_SECRET>,
<REDIRECT_URI>
)
auth_url = auth_flow.get_authorization_url()
session = auth_flow.get_session(redirect_url)
client = UberRidesClient(session, sandbox_mode=True)
credentials = session.oauth2credential
Above commands didn't raise any errors but when I execute
response = client.get_user_profile()
then I showed
uber_rides.errors.ClientError: 401: This endpoint requires at least one of the following scopes: profile, eats.pos_provisioning, profile.internal_uuid
And When I append profile in below <SCOPES> in browser then It showed invalid_scope . I have no idea why it is showing this error every time I call the url to get the session.
In https://login.uber.com/oauth/v2/token?clientid= POST request, it is showing only "scope": "offline_access" in scope.
So How can I get the access of profile and request_rides scope ?
Any help would be much Appreciated.
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/
I have coded an authentication and I get an access token, but when I use it to grab an object I just end up getting 'NoneType' object is not callable.
Exception Location: googleads\adwords.py in GetService, line 365
Exception Type: Type Error
Exception Value: Internal Server Error: /oauth2callback
I get the same result whether calling for Customer or CampaignService. I don't understand what I am doing wrong.
I am following the code in Googleads.
def getAdwordsFlow():
FLOW.redirect_uri = 'http://localhost/oauth2callback'
# Generate URL for request to Google's OAuth 2.0 server.
authorization_url, state = FLOW.authorization_url(
access_type='offline',
include_granted_scopes='true')
return authorization_url
def getAdwordsTokens(request):
auth_code = request.GET.get('code')
FLOW.fetch_token(code=auth_code)
credentials = FLOW.credentials
oauth2_client = oauth2.GoogleAccessTokenClient(
FLOW.credentials.token, FLOW.credentials.expiry)
adwords_client = adwords.AdWordsClient(
"DEVELOPER-TOKEN", oauth2_client, "USER-AGENT", "CLIENT-CUSTOMER-ID")
customer = adwords_client.GetService('CustomerService').getCustomers()[0]
print('You are logged in as customer: %s' % customer['customerId'])
return HttpResponseRedirect("/")
url.py
urlpatterns = [
re_path(r'^oauth2callback', queries.getAdwordsTokens, name='auth_calledback'),] #How
view.py
def index(request):
return redirect(getAdwordsFlow())
Terminal output:
"GET /oauth2callback?state=XXXXXXXXX&code=4/XXXXXXXXXXX&scope=https://www.googleapis.com/auth/adwords+https://www.googleapis.com/auth/userinfo.profile+https://www.googleapis.com/auth/userinfo.email HTTP/1.1" 500 80213
Why is it 500?
I notice my access token has a different value when I call for it. So I am assuming it's working.
According to your question, your service call is -
GET /oauth2callback?state=XXXXXXXXX&code=4/XXXXXXXXXXX&scope=https://www.googleapis.com/auth/adwords+https://www.googleapis.com/auth/userinfo.profile+https://www.googleapis.com/auth/userinfo.email HTTP/1.1
This call is neither get access token call nor having access_token in the request and as per your reference code it will generate access_token from refresh_token.
def main(access_token, token_expiry, client_customer_id, developer_token,
user_agent):
oauth2_client = oauth2.GoogleAccessTokenClient(access_token, token_expiry)
adwords_client = adwords.AdWordsClient(
developer_token, oauth2_client, user_agent,
client_customer_id=client_customer_id)
customer = adwords_client.GetService('CustomerService').getCustomers()[0]
print 'You are logged in as customer: %s' % customer['customerId']
if __name__ == '__main__':
args = parser.parse_args()
# Retrieve a new access token for use in this example. In a production
# application, you may use a credential store to share access tokens for a
# given user across applications.
oauth2credentials = client.OAuth2Credentials(
None, args.client_id, args.client_secret, args.refresh_token,
datetime.datetime(1980, 1, 1, 12), GOOGLE_OAUTH2_ENDPOINT,
USER_AGENT)
oauth2credentials.refresh(httplib2.Http())
main(oauth2credentials.access_token, oauth2credentials.token_expiry,
args.client_customer_id, args.developer_token, USER_AGENT)
So to use your code, first generate refresh_token using this code and then use it in your given code.
I have the next problem with tornado.oauth : when user logs in with Google,Google sends me code to my redirect uri and then I try to get some information about user with the function get_authenticated_user
class GoogleOAuth2CodeHandler(tornado.web.RequestHandler,
tornado.auth.GoogleOAuth2Mixin):
#tornado.gen.coroutine
def get(self):
user =yield self.get_authenticated_user(
redirect_uri='http://localhost:8890/userdata',
code=self.get_argument("code")
)
self.write("hello world!")
but it fails with the next error:
ERROR:tornado.application:Uncaught exception GET
KeyError: 'google_oauth'
Settings:
settings = dict(
cookie_secret="32oETzKXQAGaYdkL5gEmGeJJFuYh7EQnp2XdTP1o/Vo=",
login_url="/auth/login",
redirect_uri="http://localhost:8890/auth",
google_consumer_key="",
google_consumer_secret="",
google_permissions="https://mail.google.com/ https://www.google.com/m8/feeds",
google_permissions2="https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email"
))
consumer_key and consumer_secret are correct
authorize_redirect works correctly and I really get correct google code
google_consumer_key and google_consumer_secret are from the OAuth1 Google mixin. For oauth2, you need a google_oauth setting, which should be a dict containing 'key' and 'secret' fields:
google_oauth={"key": CLIENT_ID, "secret": CLIENT_SECRET},
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.