How to detect url callback request - python

I have a locally-run app that makes API calls to a website (tumblr.com). This involves setting some OAuth credentials; one step along the way requires extracting a key from a callback url that the server directs the browser to. So what I currently have the user do is:
Open an authorization link in a browser, which prompts them to authorize the OAuth application on the website
Click through the authorization page on the website (“Yes, I allow xxxxx app to access certain info associated with my account”)
Clicking Authorize app makes a request to the localhost which includes a parameter in the url. Meaning that tumblr will redirect the browser to the page http://localhost/?oauth_token={TOKEN}&oauth_verifier={VERIFIER}#_=_. I assume that causes a request to be made to the local machine when it does that.
The user is expected to isolate the key parameter in the url from the browser’s navigation bar, and paste it in the application.
So is there any way I can bypass steps 3 and 4 and simply have the app pick up the callback request instead of expecting the user to copy and paste it from the browser? I’m afraid I don’t know much about how to handle network requests in python.
To be clear, what I need to do is get the {VERIFIER} string.

okay first thing first, for http requests, a good python module is requests
http://docs.python-requests.org/en/master/
Then, your app gives a callback address to tumblr so that tumblr can tell to your app client info, or login error.
Now, your point 3 isn't clear.
"Clicking authorize app makes a request to localhost"
Actually clicking "authorize app" for the user makes a request to tumblr saying he accepts.
Then tumblr makes a request to your callback url passing the infos.
The callback url should probably be your server address, there you must have a script listening for tumblr, which will give you your special parameter to call their api...
In addition :
So when the users click "authorize app" there is a request to tumblr, which redirects the user to the callback url (adding oauth token and verifier).
Now, obviously, you can't ask for every user to have an http server running on their computer.
So you must set the callback url to your server.
So if you set it to "myserver.com/tumblr" for instance, the user will get redirected to your webpage, and you'll get on your server, and for that user session, the oauths token and verifier.
and...
Assuming your app is client only I'd say there are two options.
Either have your users enter manually their API keys.
Or either embed a webserver into your app.
In the case of the embedded webserver, I'd suggest flask for its simplicity.
Simply have your webserver listen on a given port and set the callback url to that server:port.
This way you'll get the client tokens directly.

Related

python: how to redirect from desktop app to url, wait user to accept the authorization and get authorization code

I'm working on an app using the Spotify API but I'm a bit new to all of this. I'm trying to get the Authorization Code with Proof Key for Code Exchange (PKCE) (https://developer.spotify.com/documentation/general/guides/authorization-guide/#authorization-code-flow-with-proof-key-for-code-exchange-pkce)
My problem is how do I redirect the user to the query where he has to ACCEPT the authorization and make my app to wait until the user clicks on ACCEPT. When he does this, the user will be redirected and that new URL (as the docs said) will contain the authorization code that I need to then exchange it for an authorization token.
This is my function so far to get that authorization code:
def get_auth_code(self):
code_challenge = self.get_code_challenge_PKCE()
scopes_needed = "user-read-email%20user-read-private%20playlist-read-collaborative%20playlist-modify-public%20playlist-read-private%20playlist-modify-private%20user-library-modify%20user-library-read"
endpoint = "https://accounts.spotify.com/authorize"
query = f"{endpoint}?client_id={self.client_ID}&response_type=code&redirect_uri={self.redirect_uri}&scope={scopes_needed}&code_challenge_method=S256&code_challenge={code_challenge}"
webbrowser.open(query)
Set up a web server.
To programmatially extract the access tokens you need a web server to handle the redirection after the user logs in on Spotify (which you redirected them to). Now this server can be the user pasting the URI to an input field on a terminal, but obviously this isn't ideal for user experience. It leaves room for lots of mistakes.
I've authored a Spotify Web API client, whose internals might be useful for you to examine. For example, you can use Flask to construct the server. The main principle is using one endpoint (i.e. /login) to redirect (code 307 worked for me browsers won't remember it) the user to a callback (i.e. /callback) which recieves the code parameter with which you can request an access token.
OAuth2 can be a bit of a pain to implement locally, I know. In my library I also made a similar function that you are constructing using webbrowser, but it does have the manual copy-pasting quirk. To use functions you can define yourself for brevity, the gist of it is:
verifier = secrets.token_urlsafe(32) # for PKCE, not in my library yet
url = user_authorisation_url(scope, state, verifier)
# Communicate with the user
print('Opening browser for Spotify login...')
webbrowser.open(url)
redirected = input('Please paste redirect URL: ').strip()
code = parse_code_from_url(redirected)
state_back = parse_state_from_url(redirected)
assert state == state_back # For that added security juice
token = request_user_token(code, verifier)

Implementing Google Directory API users watch with Python

I'm having some trouble understanding and implementing the Google Directory API's users watch function and push notification system (https://developers.google.com/admin-sdk/reports/v1/guides/push#creating-notification-channels) in my Python GAE app. What I'm trying to achieve is that any user (admin) who uses my app would be able to watch user changes within his own domain.
I've verified the domain I want to use for notifications and implemented the watch request as follows:
directoryauthdecorator = OAuth2Decorator(
approval_prompt='force',
client_id='my_client_id',
client_secret='my_client_secret',
callback_path='/oauth2callback',
scope=['https://www.googleapis.com/auth/admin.directory.user'])
class PushNotifications(webapp.RequestHandler):
#directoryauthdecorator.oauth_required
def get(self):
auth_http = directoryauthdecorator.http()
service = build("admin", "directory_v1", http=auth_http)
uu_id=str(uuid.uuid4())
param={}
param['customer']='my_customer'
param['event']='add'
param['body']={'type':'web_hook','id':uu_id,'address':'https://my-domain.com/pushNotifications'}
watchUsers = service.users().watch(**param).execute()
application = webapp.WSGIApplication(
[
('/pushNotifications',PushNotifications),
(directoryauthdecorator.callback_path, directoryauthdecorator.callback_handler())],
debug=True)
Now, the receiving part is what I don't understand. When I add a user on my domain and check the app's request logs I see some activity, but there's no usable data. How should I approach this part?
Any help would be appreciated. Thanks.
The problem
It seems like there's been some confusion in implementing the handler. Your handler actually sets up the notifications channel by sending a POST request to the Reports API endpoint. As the docs say:
To set up a notification channel for messages about changes to a particular resource, send a POST request to the watch method for the resource.
source
You should only need to send this request one time to set up the channel, and the "address" parameter should be the URL on your app that will receive the notifications.
Also, it's not clear what is happening with the following code:
param={}
param['customer']='my_customer'
param['event']='add'
Are you just breaking the code in order to post it here? Or is it actually written that way in the file? You should actually preserve, as much as possible, the code that your app is running so that we can reason about it.
The solution
It seems from the docs you linked - in the "Receiving Notifications" section, that you should have code inside the "address" specified to receive notifications that will inspect the POST request body and headers on the notification push request, and then do something with that data (like store it in BigQuery or send an email to the admin, etc.)
Managed to figure it out. In the App Engine logs I noticed that each time I make a change, which is being 'watched', on my domain I get a POST request from Google's API, but with a 302 code. I discovered that this was due to the fact I had login: required configured in my app.yaml for the script, which was handling the requests and the POST request was being redirected to the login page, instead of the processing script.

administrator has not consented to use the application -- Azure AD

I am trying to obtain a token from Azure AD from Python client application. I want users to seamlessly authenticate with just a username and password (client_id / secret will be embedded in the app). I registered my app and given it all permissions and hit the "grant permissions" button in the new portal according to this post:
The user or administrator has not consented to use the application - Send an interactive authorization request for this user and resource
I am sending an http post to:
https://login.microsoftonline.com/{tenant_id}/oauth2/token
with the following data:
headers = {
"Content-Type": "application/x-www-form-urlencoded"
}
body = "resource={0}&grant_type=password&username={1}&password={2}&client_id={3}&client_secret={4}&scope=openid".format(app_id_uri,user,password,client_id,client_secret)
I cannot seem to get past this error no matter what I try:
b'{"error":"invalid_grant","error_description":"AADSTS65001: The user or administrator has not consented to use the application with ID \'078c1175-e384-4ac7-9116-efbebda7ccc2\'. Send an interactive authorization request for this user and resource.
Again, my goal:
User enters user / pass and nothing else. App sends user / pass / client_id / client_secret, obtains token.
According to your comment:
The message I'm receiving says to do an interactive request but that is exactly what I'm trying to avoid because this is a python app with no web browser and I'm trying to avoid complexity.
I think you want to build a daemon app or an app only application integrating with Azure AD. You can refer to https://graph.microsoft.io/en-us/docs/authorization/app_only for the general introduction.
Furthermore, you can leverage the ADAL for Python to implement this functionality with a ease. Also, you can refer to client_credentials_sample.py for a quick start.
You should try logging in as an admin to be able to give consent to use the application on your tenant at all.

how to know the user login fails in python session?

import requests
r=requests.Session()
name="user"
pas="pass123"
url="http://someurl/login.php"
r.get(url)
Response<200>
login_data=dict(username=name,password=pas)
r.post(url,data=login_data)
Response<200>
page=r.get("http://someurl/user") #this takes me to the user page
print(page.content)
Works fine and takes me to the user's home page.
when i give the wrong user credentials it loads again the same page like facebook etc.the user is not taken to the hoempage.In this case how to know the user has entered is wrong password so I can prompt the user.
It really depends on the service you're talking to. For example API endpoints will likely respond with a 40X status to a failed authentication. On the other hand normal websites are likely to respond with a success and a normal page. In that case you need to figure out if you're logged in either by:
the url you're redirected to,
contents of the cookies set by the web app,
if all else fails - the contents of the website you get back

How to write a post to my facebook app's wall from app engine using python?

I have an app engine web app that would like to automatically write a post to the wall of a facebook application I control (i.e. every time a particular event occurs on the website I would like to update the wall of my facebook application).
This code will be called from a deferred task on the server.
I have been unable to find anything addressing this. Your help would be appreciated.
First thing I did was get my access token with the following code:
https://graph.facebook.com/oauth/access_token?client_id=FACEBOOK_APP_ID&client_secret=FACEBOOK_APP_SECRET&grant_type=client_credentials&scope=manage_pages,offline_access
Using the returned access token this is what I'm running on the server:
form_fields = {
"access_token": FACEBOOK_ACCESS_TOKEN,
"message": tgText
};
form_data = urllib.urlencode(form_fields)
result = urlfetch.fetch(url="https://graph.facebook.com/MYAPP_FACEBOOK_ID/feed",
payload=form_data,
method=urlfetch.POST,
validate_certificate=False,
headers={'Content-Type': 'application/x-www-form-urlencoded'})
But calling this results in:
{"error":{"type":"OAuthException","message":"(#200) The user hasn't authorized the application to perform this action"}}
As an administrator you can grant access to third party apps (e.g. your python app) to post onto your App's Profile Page (http://www.facebook.com/apps/application.php?id=YOUR_APP_ID) using OAuth:
http://developers.facebook.com/docs/authentication/ (Section Page Login)
Once you received an access token you should be able to post to App Profile Page as described here:
http://developers.facebook.com/docs/reference/api/post/ (Section Publishing)
I have a similar app. Facebook can change the code that you're meant to submit as part of the process for gaining an access token. I wrote a simple web page that creates a form with hidden input fields that contain the data required to get Facebook to authorise an app with a user (see http://developers.facebook.com/docs/reference/dialogs/oauth/).
When the user clicks the submit button, the browser does an HTTP GET to Facebook, with an appropriate query string, where the Facebook user is prompted to login (if needed) and authorise the app. If authorised Facebook calls back your redirect_url with the code which you can store in the DataStore to retrieve when needed as part of the "give me an access token" flow.

Categories

Resources