How to deal with Facebook's Verification requests on flask app - python

Im setting up a flask app on Heroku to set up web hooks for phishing certificates pulling from Facebook's certificate transparency api. I am trying to get pass facebook's verification requests (facebook sending a GET request asking for hub.challenge) however I do not understand how to give them the required information. Before suggesting I use facebook's prebuilt Heroku app, I am doing this to learn.
I tried looking up more information on GET requests however this hasn't helped me solve this problem.
This is facebook's website on this. https://developers.facebook.com/docs/graph-api/webhooks/getting-started#verification-requests
#app.route("/facebook", methods=['GET', 'POST'])
if request.method == 'GET':
def get_facebook(mode, challenge, verify_token):
#not sure what to put in here

After reviewing the docs, a few pointers:
You'll receive the request as a GET, so you won't need the 'POST' value in methods
The values sent from Facebook will be request args, and can be accessed using request.args.get('e.g.')
Facebook is expecting an int to be returned, which is up to you to decide what this is.
The result (disclaimer: this is untested!):
import datetime
from flask import jsonify
#app.route("/facebook", methods=['GET'])
def get_facebook():
my_token = 'abc123' # The token you setup on the App dashboard
if request.args.get('hub.verify_token') == my_token:
# The token is valid, return the (current datetime as an int)
# Assuming facebook is expecting a JSON result value
return jsonify({'hub.challenge': int(datetime.datetime.now().timestamp())})
return 'invalid', 403

Related

Python- flask digest auth with POSTMAN

So, I am trying to understand the digest authentication implementation with Python- Flask.
I did a simple test, I took the below code from the documentation,
from flask import Flask
from flask_httpauth import HTTPDigestAuth
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret key here'
auth = HTTPDigestAuth()
users = {
"john": "hello",
"susan": "bye"
}
#auth.get_password
def get_pw(username):
if username in users:
return users.get(username)
return None
#app.route('/', methods=['HEAD'])
#auth.login_required
def index():
return "Hello, %s!" % auth.username()
if __name__ == '__main__':
app.run()
The code runs.
I paste this URL(host:port) in same in POSTMAN(head method), it always returns 401.
I am lost and am trying to get it work.
What am I doing wrong here?
OK so here is what the problem is, the URL works fine in the browser but not in POSTMAN.
This is happening due to the name resolution in your case, e.g. 9mxlhm2
You need to make sure that it resolved that name, try and see if you can ping that name mentioned or else easiest fix just use **0.0.0.0**, if this is local.
Make sure the authorization info is mentioned when you are doing a HTTP request via postman. Under the request section there will be a Authorization section. See this image as an example.
To reproduce the problem i used localhost but it didn't work and it gives me an error with 401, as soon as i try 0.0.0.0 in the GET request via postman, it starts to work again. Hope this fixes your problem. Although the hostname for me works(as in my own hostname, e.g rajlocal)

Python slackclient oauth.access call returning 'invalid_code' error

I am using ngrok + flask + python slackclient to respond to Slack API's OAuth Flow and I am receiving an invalid_code error.
I am following steps described in slackclient docs and my code is pretty simple so far:
import os
import slack
from flask import Flask, request
app = Flask(__name__)
SLACK_CLIENT_ID = os.environ['SLACK_CLIENT_ID']
SLACK_CLIENT_SECRET = os.environ['SLACK_CLIENT_SECRET']
#app.route('/install', methods=['GET', 'POST'])
def install():
# Retrieve the auth code from the request params
auth_code = request.args['code']
# An empty string is a valid token for this request
client = slack.WebClient(token='')
# Request the auth tokens from Slack
response = client.oauth_access(
client_id=SLACK_CLIENT_ID,
client_secret=SLACK_CLIENT_SECRET,
code=auth_code
)
print(response)
if __name__ == '__main__':
app.run()
I initiate the App installation from the "Add" button in my Slack workspace's Manage Apps page. I can confirm that I am receiving a code as expected once the installation is initiated, and it is being correctly passed through to the slack.BaseClient.api_call() function that eventually sends the request to https://slack.com/api/oauth.access.
I expect the response from the oauth_access call to be a JSON object containing my access tokens, however, I get:
slack.errors.SlackApiError: The request to the Slack API failed.
The server responded with: {'ok': False, 'error': 'invalid_code', 'warning': 'superfluous_charset', 'response_metadata': {'warnings': ['superfluous_charset']}}
I tried to send a POST with curl to Slack's endpoint with the required parameters and it worked as expected. I also tried with requests.post() and that also worked as expected. So I suspect that I am using slackclient incorrectly or have misunderstood something. Can anyone help point me in the right direction?
It seems to be a problem with the Python SDK. I think this pull request fixes this
https://github.com/slackapi/python-slackclient/pull/527
In the meantime it may be easier to revert to version 2.1.0
This issue is solved in v2.2.1 of slackclient
See changelog:
[WebClient] Oauth previously failed to pass along credentials properly. This is fixed now. #527

Can i append a value to my flask request object in the #app.before_request and pass it forward to the endpoint view function?

I am developing some basic REST APIs in python. I am expecting an authorization token in the header of all requests except some unsecured requests like login and register. I am validating the token in #app.before_request and then I want to pass the decoded payload to the corresponding endpoint view function. But, I am not to attach the decoded info to the request object as I get "TypeError: 'Request' object does not support item assignment".
#app.before_request
def before_request():
print(request.endpoint)
if request.endpoint=="register" or request.endpoint=="login":
pass
else:
auth_header = request.headers.get('Authorization')
if auth_header:
auth_token = auth_header.split(" ")[1]
token=decode_auth_token(auth_token)
request["token"]=token
else:
return jsonify({"result":"","error":"No token present in header !"})
I am thinking of this implementation like an auth filter where all requests pass this filter. I can strike off the ill requests at this layer itself and also, I can fetch the user specific info which is needed in the net middleware.
I have a similar usecase (surprisingly similar, actually). I got around it by setting a custom property in the request object, much like your approach, although instead of using direct assignment (i.e. request["token"]=token), I used setattr(request, "token", token).
I got the tip from a bottle plugin which does something very similar:
https://github.com/agile4you/bottle-jwt/blob/master/bottle_jwt/auth.py#L258
You may even wanna try that, or some other plugin to further improve your application.
Flask offers g to propagate data in application context.
from flask import g
#app.before_request
def before_request():
g.token = vaue
#bp.route("/path", methods=["GET"]):
g.token
Reference:
https://flask.palletsprojects.com/en/2.2.x/appcontext/

Moving a file using a python web service [duplicate]

this is a two-part question: I have seen individual pieces discussed, but can't seem to get the recommended suggestions to work together. I want to create a web service to store images and their metadata passed from a caller and run a test call from Postman to make sure it is working. So to pass an image (Drew16.jpg) to the web service via Postman, it appears I need something like this:
For the web service, I have some python/flask code to read the request (one of many variations I have tried):
from flask import Flask, jsonify, request, render_template
from flask_restful import Resource, Api, reqparse
...
def post(self, name):
request_data = request.get_json()
userId = request_data['UserId']
type = request_data['ImageType']
image = request.files['Image']
Had no problem with the data portion and straight JSON but adding the image has been a bugger. Where am I going wrong on my Postman config? What is the actual set of Python commands for reading the metadata and the file from the post? TIA
Pardon the almost blog post. I am posting this because while you can find partial answers in various places, I haven't run across a complete post anywhere, which would have saved me a ton of time. The problem is you need both sides to the story in order to verify either.
So I want to send a request using Postman to a Python/Flask web service. It has to have an image along with some metadata.
Here are the settings for Postman (URL, Headers):
And Body:
Now on to the web service. Here is a bare bones service which will take the request, print the metadata and save the file:
from flask import Flask, request
app = Flask(__name__)
# POST - just get the image and metadata
#app.route('/RequestImageWithMetadata', methods=['POST'])
def post():
request_data = request.form['some_text']
print(request_data)
imagefile = request.files.get('imagefile', '')
imagefile.save('D:/temp/test_image.jpg')
return "OK", 200
app.run(port=5000)
Enjoy!
Make sure `request.files['Image'] contains the image you are sending and follow http://flask.pocoo.org/docs/1.0/patterns/fileuploads/ to save the file to your file system. Something like
file = request.files['Image']
file.save('./test_image.jpg')
might do what you want, while you will have to work out the details of how the file should be named and where it should be placed.

"Invalid Response from Facebook" when Authenticating with Facebook using Flask-Oauthlib

I'm consistently getting an "Invalid response from Facebook" error when authenticating over Facebook with Oauthlib when building off of the sample code here.
I've outlined the sections of relevant code below.
Setup:
Setting up the Oauth request object.
Not pictured: Navigational routes and Flask app initialization.
from flask_oauthlib.client import OAuth, OAuthException
oauth = OAuth()
facebook = oauth.remote_app('facebook',
base_url='https://graph.facebook.com/',
request_token_url=None,
access_token_url='/oauth/access_token',
authorize_url='https://www.facebook.com/dialog/oauth',
consumer_key=config.get("app_id"),
consumer_secret=config.get("app_secret"),
request_token_params={'scope': 'public_profile,email'}
)
#facebook.tokengetter
def get_facebook_token():
if current_user.is_authenticated():
return current_user.get_facebook_token()
else:
return None
Login handler:
Sending users here in order to begin the process, with the url for the facebook callback appended to the root URL.
#app.route('/facebook/login')
def facebook_login():
return facebook.authorize(callback="http://example.com%s" % url_for('facebook_callback'))
Facebook callback, source of the issue:
From here I can garner that a code (presumably the token) is returned but Oauthlib fails to parse it correctly.
#app.route('/facebook/callback')
def facebook_callback(response):
response = facebook.authorized_response()
if response is None:
flash("You denied the request to sign in.", "error")
return redirect(url_for('index'))
if isinstance(response, OAuthException):
flash("Access denied: %s" % response.message, "error")
return redirect(url_for('index'))
# Request fails here, returns the redirect above.
From dumping the request args I can see fairly clearly that after being directed to Facebook and successfully connecting, there is a very long token being returned to the callback along the lines of '?code=1234567890-abcdefghijklmnop', however actually trying to authenticate with this fails with "Invalid response from Facebook".
Here is a sample request dump:
ImmutableMultiDict([('code', 'AQAPedwWavTb_cBx6NVy-qy3AL5YPr780oG5tA1LfITpVwkk-kr_4I0lG6B-vlhcYEubhuKhEM46bPs-1WpWUpJzcWfhkQ64lIkdLm9uSHSqeBHBM_6zw7SDpvVmGK-JKWBpAqRJuBKenl9zslQizthox96104iiul0uYQY67cmZgPXZi9uL-mcgZ5dRj387eKJIjNninBXxwCGgFkg5kLVHYt7t0ktUH58stYlxn2f98AXuSlrIvWsA5NeHsVbM8XY0XQrDrNbCvjDmEwHQGkZ3uZRbyaecN7MAi0bM0TrZzpuQ8j3M34DnQp_v9n4ktM4')])
Having used similar code based off of the Twitter sample that works, I'm thinking this could be a possible library bug due to Facebook API changes, but I would appreciate any pointers!
For anyone who stumbles upon this from Google in the future, I solved this in a solution that can be read here.
Hey there, I solved this issue in a very hacky way which I would not
recommend for production environments, but I eventually found the
issue a few days after my last message.
When you ask Facebook for an access token, it does NOT give you an
access token in the way you might expect. What I assumed to be a
failure on Facebook's side was instead a (perhaps intentional)
formatting error.
What you might expect:
http://example.com/callback?access_token=00000000000
or
http://example.com/callback with the access token passed as a POST
argument in the headers.
What actually happens is that Facebook responds like this:
http://example.com/callback?#access_token=0000000000
Because of this, it is -impossible- for any server side language
to parse it, as the access token will now only be visible to the
browser itself. It is not passed to the backend whatsoever.
Capturing the request:
#app.route('/facebook/translate', methods=['GET'])
def facebook_translate():
# Facebook responds with the access token as ?#access_token,
# rather than ?access_token, which is only accessible to the browser.
# This part is where things get really, really dumb.
return ''' <script type="text/javascript">
var token = window.location.href.split("access_token=")[1];
window.location = "/facebook/callback?access_token=" + token;
</script> '''
Proceeding as usual:
#app.route('/facebook/callback', methods=['GET', 'POST'])
def facebook_callback():
access_token = request.args.get("access_token")
if access_token == "undefined":
flash("You denied the request to sign in.", "error")
return redirect(url_for('index'))
graph = facebooksdk.GraphAPI(access_token)
profile = graph.get_object("me")

Categories

Resources