How to use oauth2 to access StackExchange API? - python

I'm following the instructions mentioned here: https://api.stackexchange.com/docs/authentication
But since there is no code provided, I'm not able to understand the flow correctly.
I've been trying to get the authentication part done using two methods below but I have hit a deadend.
1)
import requests
from pprint import pprint
resp = requests.get('https://stackexchange.com/oauth/dialog?client_id=6667&scope=private_info&redirect_uri=https://stackexchange.com/oauth/login_success/')
pprint(vars(resp))
2)
import oauth2 as oauth
from pprint import pprint
url = 'https://www.stackexchange.com'
request_token_url = '%s/oauth/' % url
access_token_url = '%s/' % url
consumer = oauth.Consumer(key='mykey',
secret='mysecret')
client = oauth.Client(consumer)
response, content = client.request(request_token_url, 'GET')
print(response, content)
I'm not sure how to go forward from here? I need to use the access token that is returned and use it to query the API. A sample code would really really help! Thanks.
EDIT: This is the code I'm using currently:
from requests_oauthlib import OAuth2Session
from pprint import pprint
client_id = 'x'
client_secret = 'x'
redirect_uri = 'https://stackexchange.com/oauth/login_success'
scope = 'no_expiry'
oauth = OAuth2Session(client_id, redirect_uri=redirect_uri, scope=scope)
pprint(vars(oauth))
authorization_url, state = oauth.authorization_url('https://stackexchange.com/oauth/dialog')
print(authorization_url)
Instead of having to click on the authorization_url and get the token, is there a way I can directly fetch the token within the script itself?

Of the two methods you used, the first is the recommended method for desktop applications. It is probably correct.
OAuth is intended to force the user to go to a specific webpage and acknowledge that they are giving permission (usually through clicking a button) for an application to access their data. The HTTP responses you print are merely the webpage where a user needs to click accept.
To get a feeling for the flow, put the first address (https://stackexchange.com/oauth/dialog?client_id=6667&scope=&redirect_uri=https://stackexchange.com/oauth/login_success/) in the address bar and click accept on the loaded page. The access_token will be in the URL right after that.
If you are making the application only for yourself, the access_token can be copied into your Python script. The token expires in one day; if that is too short add no_expiry to scope to make it last forever. DO NOT share the token with anyone else, since it gives them access to details of your account! Each user of the script must generate their own token.
Test the access_token by inserting in your app's key and the access_token you just obtained into the url: https://api.stackexchange.com/2.2/me?key=key&site=stackoverflow&order=desc&sort=reputation&access_token=&filter=default
If you need a more automated, integrated, user-friendly solution, I would look at selenium webdriver to open a browser window and get the resulting credentials.

Just one minor correction on Marc's answer. If you want the access token to last forever, you should add no_expiry instead of no_expire.

Related

Python Requests / HTTPX - Authenticate All Redirected URL - How To

I wonder if there is a way to authenticate each redirected URL when working with Python modules such as httpx or requests?
Problem Statement
I am trying to connect to an API endpoint under the company network. Due to the company's cyber security measures, the API endpoint will be randomly masked with a company proxy, which causes the 307 Redirect status code.
my current code snippet looks like the below:
import httpx
api_url = 'https://demo.vizionapi.com/carriers'
head = {
'X-API-Key':'API KEY'
}
response = httpx.get(url=api_url, verify='supporting_files/cacert.pem',
headers=head, auth=('my username', 'my password'),
follow_redirects=True)
With above code, I received the 401 authentication needed error (But auth has been passed). This error will only happen when redirection occurs due to the company proxy.
Question:
My assumption is the authentication is only being passed into the first URL not the redirected URL. Therefore, I wonder if anyone know how I can use the same auth parameter for all URLs (direct & redirect)?
Any suggestion will be deeply appracaited.
I don't know what requests behavior with regards to auth during redirect is, but the first solution to come to mind is to manually follow the redirects yourself. Put your request in a loop that checks for the 3xx response codes, and handle auth however you want to.

How to access the hash fragment in the final url after request

Whenever I copy and paste the following into a web browser
Original URL
http://accounts.spotify.com/authorize?client_id=XXX2d0ff4186b517daa4e1c577e4&redirect_uri=http:%2F%2Flocalhost:5000&scope=playlist-modify-public%20playlist-modify-private&response_type=token
I get this for the final url
Final URL
http://localhost:5000/#access_token=XXX-LQY7xCQXTqpmsfroChl5yeUoiIDmoBfybqc9psLE2WFwP2UNQ26nVuiLGQkIWA-4occeXbrScaO5mqmMHZpgDPjTcSEfN9VZCLKKyfE46FUsucmCetP9owY_bRWoraZ8P2wwq0osZEMit0jmOrThvPTyKqp7O_rAbwT1BUsLeB9ux7xdLnTreocpmtZl3wqMXz24B2mwRMEieb_Dq2PUgIaK3zE7X-RvnzqQ&token_type=Bearer&expires_in=3600
My question is how to get the access_token in the final url by using the original URL programmatically. Preferably in python. The original URL will be constant so I won't need to make any changes to the values of the parameters that I already declared
I have already tried
Using python requests, and viewing the response.url.(both using POST and GET) But when I do it only shows the original url.
Same concept using urllib
Displaying it with .json, json(), .text, .headers, and pretty much all of the request functions available, but again it only makes references to the original url.
I assume that the grant flow that you are using is Implicit Grant which is used to obtain user's token. However, using this flow requires user's interaction via browser to complete the flow and get the token.
That means Implicit Grant can't be used to obtain user's access token via browserless application without user's interaction.
In case of browserless application, the authentication flow that you might want to use is Client Credential flow that you can get the access token directly in the response. The limitation of this grant flow is that you can't use the endpoint that requires user's identity.
In the FAQ section of the document might have an answer for your scenario that you might consider a refresh token instead.
I guess that the browser contains a valid session with the identity provider (authorize url -> callback url), but your programmatic way does not (authorize url -> authorize url).
Assuming it is implicit grant, and the aim is to retrieve the access token which is in the url hash fragment. The scripting would need to login to the identity provider or have the relevant session cookies/data.
Sample Python Code:
import requests
from urllib.parse import urlparse
singleSession = requests.Session()
# start with original url as https://oauth-abc.def.com/auth?client_id=123&redirect_uri=https://abc.def.com/oauth-callback&response_type=token&nonce=123&scope=something
# since there is no session with identity provider, the following get request should lead to redirect to identity provider / third party login form page.
url_login_authorize = 'https://oauth-abc.def.com/auth?client_id=123&redirect_uri=https://abc.def.com/oauth-callback&response_type=token&nonce=123&scope=something'
result_login_oauth_page = singleSession.get(url_login_authorize, allow_redirects=True)
# result_login_oauth_page.url is the identity provider login form html page url which could be unique
# post credentials to authenticate with identity provider
url_login_oauth_endpoint = result_login_oauth_page.url + '/login'
result_login_oauth = singleSession.post(url_login_oauth_endpoint, credentials_object, allow_redirects=True)
# post request to grant/confirm the requested scopes with authenticated session
url_login_confirm_oauth_endpoint = result_login_oauth_page.url + '/confirm'
result_login_confirm_oauth = singleSession.post(url_login_confirm_oauth_endpoint, {}, allow_redirects=True)
# end with the final url as https://abc.def.com/oauth-callback#access_token=ilovemoney&expires_in=3600&token_type=Bearer&scope=something
result_oauth_callback_parse = urlparse(result_login_confirm_oauth.url)
result_oauth_callback_params = dict()
for callback_param in result_oauth_callback_parse.fragment.split("&"):
callback_param_split = callback_param.split("=")
result_oauth_callback_params[callback_param_split[0]] = callback_param_split[1]
print("result_oauth_callback_params['access_token']: ", result_oauth_callback_params['access_token'])

How to connect with an API that requires username and password

I am trying to connect to the api as explained in http://api.instatfootball.com/ , It is supposed to be something like the following get /[lang]/data/[action].[format]?login=[login]&pass=[pass]. I know the [lang], [action] and [format] I need to use and I also have a login and password but donĀ“t know how to access to the information inside the API.
If I write the following code:
import requests
r = requests.get('http://api.instatfootball.com/en/data/stat_params_players.json', auth=('login', 'pass'))
r.text
with the actual login and pass, I get the following output:
{"status":"error"}
This API requires authentication as parameters over an insecure connection, so be aware that this is highly lacking on the API part.
import requests
username = 'login'
password = 'password'
base_url = 'http://api.instatfootball.com/en/data/{endpoint}.json'
r = requests.get(base_url.format(endpoint='stat_params_players'), params={'login': username, 'pass': password})
data = r.json()
print(r.status_code)
print(r.text)
You will need to make a http-request using the URL. This will return the requested data in the response body. Depending on the [format] parameter, you will need to decode the data from xml / json to a native Python object.
As rdas already commented, you can use the request library for python (https://requests.readthedocs.io/en/master/). You will also find some code samples there. It will also do proper decoding of JSON data.
If you want to play around with the API a bit, you can use a tool like Postman for testing and debugging your requests. (https://www.postman.com/)

Create Facebook Event with python's urllib2 library

I'm trying to create an event with facebook from an external application. I've read this con events, where it states that you can create an event via POST, so i have the following-
data = dict()
data['access_token'] = self.access_token
data['name'] = 'Fb event from Python!'
data['start_time'] = datetime.datetime.now().isoformat()
data = urllib.urlencode(data)
url = 'https://graph.facebook.com/me/events'
request = urllib2.Request(url=url, data=data)
response = urllib2.urlopen(request)
Where I already have my access token and my fb permissions set so my app can create events and so forth. But I get an error 400 = Bad Request, so if anyone could help I'd be more than happy thanks in advance
Well I would say you should think about using facebook-sdk. From your error i.e http 400 you know that what you are doing wrong is how you are sending the http request itself. The request requires certain parameters in a certain form that you are not giving. If you look at
the request function in here
It makes the request in this form where path is id( I am also not sure me is valid here, but perhaps we can use me for the resource whose access-token we are using)
I think args access token .
file = urllib2.urlopen("https://graph.facebook.com/" + path + "?" +
urllib.urlencode(args),
post_data, timeout=self.timeout)
Thus change the form in which you are making the request. You can go through facebook.py's code and docs to figure out the variables mean based on your needs

How do you access an authenticated Google App Engine service from a (non-web) python client?

I have a Google App Engine app - http://mylovelyapp.appspot.com/
It has a page - mylovelypage
For the moment, the page just does self.response.out.write('OK')
If I run the following Python at my computer:
import urllib2
f = urllib2.urlopen("http://mylovelyapp.appspot.com/mylovelypage")
s = f.read()
print s
f.close()
it prints "OK"
the problem is if I add login:required to this page in the app's yaml
then this prints out the HTML of the Google Accounts login page
I've tried "normal" authentication approaches. e.g.
passman = urllib2.HTTPPasswordMgrWithDefaultRealm()
auth_handler = urllib2.HTTPBasicAuthHandler()
auth_handler.add_password(None,
uri='http://mylovelyapp.appspot.com/mylovelypage',
user='billy.bob#gmail.com',
passwd='billybobspasswd')
opener = urllib2.build_opener(auth_handler)
urllib2.install_opener(opener)
But it makes no difference - I still get the login page's HTML back.
I've tried Google's ClientLogin auth API, but I can't get it to work.
h = httplib2.Http()
auth_uri = 'https://www.google.com/accounts/ClientLogin'
headers = {'Content-Type': 'application/x-www-form-urlencoded'}
myrequest = "Email=%s&Passwd=%s&service=ah&source=DALELANE-0.0" % ("billy.bob#gmail.com", "billybobspassword")
response, content = h.request(auth_uri, 'POST', body=myrequest, headers=headers)
if response['status'] == '200':
authtok = re.search('Auth=(\S*)', content).group(1)
headers = {}
headers['Authorization'] = 'GoogleLogin auth=%s' % authtok.strip()
headers['Content-Length'] = '0'
response, content = h.request("http://mylovelyapp.appspot.com/mylovelypage",
'POST',
body="",
headers=headers)
while response['status'] == "302":
response, content = h.request(response['location'], 'POST', body="", headers=headers)
print content
I do seem to be able to get some token correctly, but attempts to use it in the header when I call 'mylovelypage' still just return me the login page's HTML. :-(
Can anyone help, please?
Could I use the GData client library to do this sort of thing? From
what I've read, I think it should be able to access App Engine apps,
but I haven't been any more successful at getting the authentication working for App Engine stuff there either
Any pointers to samples, articles, or even just keywords I should be
searching for to get me started, would be very much appreciated.
Thanks!
appcfg.py, the tool that uploads data to App Engine has to do exactly this to authenticate itself with the App Engine server. The relevant functionality is abstracted into appengine_rpc.py. In a nutshell, the solution is:
Use the Google ClientLogin API to obtain an authentication token. appengine_rpc.py does this in _GetAuthToken
Send the auth token to a special URL on your App Engine app. That page then returns a cookie and a 302 redirect. Ignore the redirect and store the cookie. appcfg.py does this in _GetAuthCookie
Use the returned cookie in all future requests.
You may also want to look at _Authenticate, to see how appcfg handles the various return codes from ClientLogin, and _GetOpener, to see how appcfg creates a urllib2 OpenerDirector that doesn't follow HTTP redirects. Or you could, in fact, just use the AbstractRpcServer and HttpRpcServer classes wholesale, since they do pretty much everything you need.
thanks to Arachnid for the answer - it worked as suggested
here is a simplified copy of the code, in case it is helpful to the next person to try!
import os
import urllib
import urllib2
import cookielib
users_email_address = "billy.bob#gmail.com"
users_password = "billybobspassword"
target_authenticated_google_app_engine_uri = 'http://mylovelyapp.appspot.com/mylovelypage'
my_app_name = "yay-1.0"
# we use a cookie to authenticate with Google App Engine
# by registering a cookie handler here, this will automatically store the
# cookie returned when we use urllib2 to open http://currentcost.appspot.com/_ah/login
cookiejar = cookielib.LWPCookieJar()
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookiejar))
urllib2.install_opener(opener)
#
# get an AuthToken from Google accounts
#
auth_uri = 'https://www.google.com/accounts/ClientLogin'
authreq_data = urllib.urlencode({ "Email": users_email_address,
"Passwd": users_password,
"service": "ah",
"source": my_app_name,
"accountType": "HOSTED_OR_GOOGLE" })
auth_req = urllib2.Request(auth_uri, data=authreq_data)
auth_resp = urllib2.urlopen(auth_req)
auth_resp_body = auth_resp.read()
# auth response includes several fields - we're interested in
# the bit after Auth=
auth_resp_dict = dict(x.split("=")
for x in auth_resp_body.split("\n") if x)
authtoken = auth_resp_dict["Auth"]
#
# get a cookie
#
# the call to request a cookie will also automatically redirect us to the page
# that we want to go to
# the cookie jar will automatically provide the cookie when we reach the
# redirected location
# this is where I actually want to go to
serv_uri = target_authenticated_google_app_engine_uri
serv_args = {}
serv_args['continue'] = serv_uri
serv_args['auth'] = authtoken
full_serv_uri = "http://mylovelyapp.appspot.com/_ah/login?%s" % (urllib.urlencode(serv_args))
serv_req = urllib2.Request(full_serv_uri)
serv_resp = urllib2.urlopen(serv_req)
serv_resp_body = serv_resp.read()
# serv_resp_body should contain the contents of the
# target_authenticated_google_app_engine_uri page - as we will have been
# redirected to that page automatically
#
# to prove this, I'm just gonna print it out
print serv_resp_body
for those who can't get ClientLogin to work, try app engine's OAuth support.
Im not too familiar with AppEngine, or Googles web apis, but for a brute force approach you could write a script with something like mechanize (http://wwwsearch.sourceforge.net/mechanize/) to simply walk through the login process before you begin doing the real work of the client.
I'm not a python expert or a app engine expert. But did you try following the sample appl at http://code.google.com/appengine/docs/gettingstarted/usingusers.html. I created one at http://quizengine.appspot.com, it seemed to work fine with Google authentication and everything.
Just a suggestion, but look in to the getting started guide. Take it easy if the suggestion sounds naive. :)
Thanks.

Categories