Oauth2 in Python with Flask gets 302 from API - python

I am trying to access MeisterTask's API with Python and Flask, and no matter what I do, I seem to always get a 302 code in return from the API, although I can get an access token (or so I think). Here is the code I have so far (I tried reducing it, this is the smallest snippet I could get that replicates the error):
from flask import Flask, redirect, url_for, session, request, jsonify
from flask_oauthlib.client import OAuth
app = Flask(__name__)
app.debug = True
app.secret_key = "development"
oauth = OAuth(app)
meistertask = oauth.remote_app(
'meistertask',
consumer_key= "XXXXXX",
consumer_secret= "XXXXXX",
request_token_params={"scope" : "meistertask"},
base_url='https://www.meistertask.com/api',
request_token_url=None,
access_token_method='GET',
access_token_url='https://www.mindmeister.com/login/oauth2/token',
authorize_url='https://www.mindmeister.com/oauth2/authorize'
)
#app.route('/')
def index():
if 'meistertask_token' in session:
me = meistertask.get('user')
return jsonify(me.data)
return redirect(url_for('login'))
#app.route('/login')
def login():
return meistertask.authorize(callback=url_for('authorized', _external=True))
#app.route('/logout')
def logout():
session.pop('meistertask_token', None)
return redirect(url_for('index'))
#app.route('/login/authorized')
def authorized():
resp = meistertask.authorized_response()
print(resp.get('code'))
if resp is None or resp.get('code') is None:
return 'Access denied: reason=%s error=%s resp=%s' % (
request.args['error'],
request.args['error_description'],
resp
)
session['meistertask_token'] = (resp['code'], '')
return "Hello"
#meistertask.tokengetter
def get_meistertask_oauth_token():
return session.get('meistertask_token')
if __name__ == "__main__":
app.run()
And here is the traceback:
flask_oauthlib.client.OAuthException: Invalid response from meistertask
Traceback (most recent call last):
File "~\AppData\Local\Programs\Python\Python37-32\lib\site-packages\flask\app.py", line 2309, in __call__ return self.wsgi_app(environ, start_response)
File "~\AppData\Local\Programs\Python\Python37-32\lib\site-packages\flask\app.py", line 2295, in wsgi_app response = self.handle_exception(e)
File "~\AppData\Local\Programs\Python\Python37-32\lib\site-packages\flask\app.py", line 1741, in handle_exception reraise(exc_type, exc_value, tb)
File "~\AppData\Local\Programs\Python\Python37-32\lib\site-packages\flask\_compat.py", line 35, in reraise raise value
File "~\AppData\Local\Programs\Python\Python37-32\lib\site-packages\flask\app.py", line 2292, in wsgi_app response = self.full_dispatch_request()
File "~\AppData\Local\Programs\Python\Python37-32\lib\site-packages\flask\app.py", line 1815, in full_dispatch_request rv = self.handle_user_exception(e)
File "~\AppData\Local\Programs\Python\Python37-32\lib\site-packages\flask\app.py", line 1718, in handle_user_exception reraise(exc_type, exc_value, tb)
File "~\AppData\Local\Programs\Python\Python37-32\lib\site-packages\flask\_compat.py", line 35, in reraise raise value
File "~\AppData\Local\Programs\Python\Python37-32\lib\site-packages\flask\app.py", line 1813, in full_dispatch_request rv = self.dispatch_request()
File "~\AppData\Local\Programs\Python\Python37-32\lib\site-packages\flask\app.py", line 1799, in dispatch_request return self.view_functions[rule.endpoint](**req.view_args)
File "~\Documents\MeisterTaskServer\hello.py", line 49, in authorized resp = meistertask.authorized_response()
File "~\AppData\Local\Programs\Python\Python37-32\lib\site-packages\flask_oauthlib\client.py", line 707, in authorized_response data = self.handle_oauth2_response(args)
File "~\AppData\Local\Programs\Python\Python37-32\lib\site-packages\flask_oauthlib\client.py", line 692, in handle_oauth2_response
Things I have tried
Tried to modify the method to get the access token from GET to POST. The API clearly states that I should use GET, yet every other example I have seen on flask_oauthlib's GitHub uses POST (the examples are 3 years old, but some still work, namely the GitHub one). Either give the same result.
Tried doing it barebones, without any library. The resulting code was thrice as long and also had more problems.
Used Django instead of Flask. Never even managed to get the hello world example going, it was too much work, and also I have discovered the library flask_oauthlib.
Things worth mentioning
I derived this code from this here GitHub example
There is also code there I omitted in order to keep the snippet short, that establishes that the server should use SSL (as per the request from the API that the redirect_uri should use HTTPS)
The app manages to redirect me over at MeisterTask and asks for my permission. Once I grant it, it redirects to "https://127.0.0.1:5000/login/authorized?code=some_token" where I get the traceback. If I look with Chrome's debugging tools to the requests made and what I receive, I see that I get an 302 from the API, but I also get an access token.
I run Windows 10 with Python 3.7.0
So what's the deal? What's the next step here? I've run out of things to try. Thank you for taking the time to solve this!

Related

Troubleshooting Zapier Webhook POSTING to Flask Server

I am trying to use my Flask server to receive webhooks in a POST from a Zap, but I'm getting a 500 Internal Server Error when I test.
Traceback (most recent call last):
File "/usr/local/lib/python3.9/dist-packages/flask/app.py", line 2073, in wsgi_app
response = self.full_dispatch_request()
File "/usr/local/lib/python3.9/dist-packages/flask/app.py", line 1518, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/usr/local/lib/python3.9/dist-packages/flask/app.py", line 1516, in full_dispatch_request
rv = self.dispatch_request()
File "/usr/local/lib/python3.9/dist-packages/flask/app.py", line 1502, in dispatch_request
return self.ensure_sync(self.view_functions[rule.endpoint])(**req.view_args)
TypeError: receive_webhook() missing 1 required positional argument: 'request'
Here is my handler for the webhook:
#app.route("/webhook", methods=['POST'])
def receive_webhook(request):
print(request.json)
return request.json
Here's a screenshot of my Zap I am trying to send the POST from
Thanks for any assistance.
It seems like Zapier is calling my handler with no argument. Shouldn’t it be passing the payload as that request argument or am I misunderstanding something here?
Edit: Also, if it's relevant, I'm using Nginx and Gunicorn for hosting. On an Ubuntu Linode.
I figured out the problem. I was using the Flask request module incorrectly. This code made it work -
#app.route("/webhook", methods=['POST'])
def receive_webhook():
print(request.data)
return request.data

Routing inside routes - A setup function was called after the first request was handled

I am trying to create routes inside of another route (info_form()). For awhile this was working. I am not sure what exactly caused it, but I may have changed some code and that is what caused it to stop working. This is my code:
#app.route('/Register', methods=["GET", "POST"])
def register():
FormErrMessage = 'Email already taken, or invalid email.'
error = False
if request.method == 'POST':
inp_email = request.form['email']
inp_password = request.form['password']
if User.objects(email=inp_email):
error = True
else:
#app.route('/Register/customer-form', methods=['GET', 'POST'])
def info_form():
msg = None
if request.method == 'POST':
form = request.form
inp_name = form['name']
inp_lastName = form['lastName']
inp_zipCode = form['zipCode']
inp_address = form['address']
inp_phoneNumber = form['phoneNumber']
if User.objects(name=inp_name, lastName=inp_lastName):
msg = '...'
elif User.objects(phoneNumber=inp_phoneNumber):
msg = '...'
else:
#app.route('/Register/customer-form/verify/<send>', methods=['GET', 'POST'])
def verify_email(send):
msg = None
error_msg = None
r = 8
if send == 't':
...
if request.method == 'POST':
inp_code = request.form['code']
if int(inp_code) == r:
try:
User(
email=inp_email,
password=inp_password,
user_id=get_user_id(),
admin=False,
loggedIn=1,
# ip_addresses=ipAddress,
registered=False,
name=inp_name,
lastName=inp_lastName,
zipCode=inp_zipCode,
address=inp_address,
phoneNumber=inp_phoneNumber
).save()
email(...)
except ValidationError as e:
print(e)
error_msg = '...'
else:
session['email'] = inp_email
session["password"] = inp_password
print(session)
return redirect('/')
else:
msg = 'Code invalid, try again.'
return render_template('verify.html', email=inp_email, unk_err=error_msg,
msg=msg)
return redirect('/Register/customer-form/verify/t')
return render_template('customerform.html', msg=msg)
return redirect('/Register/customer-form')
return render_template('register.html', FormErrMessage=FormErrMessage, error=error)
It seems to be raising this because the request was already handled.
Traceback (most recent call last):
File "C:\Users\William\Nickels\venv\lib\site-packages\flask\app.py", line 2463, in __call__
return self.wsgi_app(environ, start_response)
File "C:\Users\William\Nickels\venv\lib\site-packages\flask\app.py", line 2449, in wsgi_app
response = self.handle_exception(e)
File "C:\Users\William\Nickels\venv\lib\site-packages\flask\app.py", line 1866, in handle_exception
reraise(exc_type, exc_value, tb)
File "C:\Users\William\Nickels\venv\lib\site-packages\flask\_compat.py", line 39, in reraise
raise value
File "C:\Users\William\Nickels\venv\lib\site-packages\flask\app.py", line 2446, in wsgi_app
response = self.full_dispatch_request()
File "C:\Users\William\Nickels\venv\lib\site-packages\flask\app.py", line 1951, in full_dispatch_request
rv = self.handle_user_exception(e)
File "C:\Users\William\Nickels\venv\lib\site-packages\flask\app.py", line 1820, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "C:\Users\William\Nickels\venv\lib\site-packages\flask\_compat.py", line 39, in reraise
raise value
File "C:\Users\William\Nickels\venv\lib\site-packages\flask\app.py", line 1949, in full_dispatch_request
rv = self.dispatch_request()
File "C:\Users\William\Nickels\venv\lib\site-packages\flask\app.py", line 1935, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "C:\Users\William\Nickels\app.py", line 55, in register
#app.route('/Register/customer-form', methods=['GET', 'POST'])
File "C:\Users\William\Nickels\venv\lib\site-packages\flask\app.py", line 1314, in decorator
self.add_url_rule(rule, endpoint, f, **options)
File "C:\Users\William\Nickels\venv\lib\site-packages\flask\app.py", line 90, in wrapper_func
"A setup function was called after the "
AssertionError: A setup function was called after the first request was handled. This usually indicates a bug in the application where a module was not imported and decorators or other functionality was called too late.
To fix this make sure to import all your view modules, database models and everything related at a central place before the application starts serving requests.
Can someone tell me how to fix this or get around it. Thanks.
I don't understand this:
import all your view modules, database models and everything related at a central place before the application starts serving requests.
What that error is trying to say is that you can't do setup-y things (like adding routes) after the Flask app has starting running.
You have a deeper problem, which is assuming that nested route functions will retain visibility an data from enclosing routes. E.g., that the /Register/customer-form route will have access to the inf_email set in the enclosing /Register route. The mechanisms here don't work that way.
Since the intent is to hang on to information from one step of a registration workflow to the next, the common mechanisms is to inject state from one route as hidden form elements so that they'll be carried along to the next route. Here, that would mean passing inf_email et al. to be expanded into the "customerform.html" template (and similar for the "verify.html" template).

Inexplicable TypeError in python that does not contain any of my code in the traceback

This bug is driving me nuts. I am trying to create the backend of a website in python using flask. I am getting the following error and traceback:
Traceback (most recent call last):
File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1836, in __call__
return self.wsgi_app(environ, start_response)
File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1820, in wsgi_app
response = self.make_response(self.handle_exception(e))
File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1403, in handle_exception
reraise(exc_type, exc_value, tb)
File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1817, in wsgi_app
response = self.full_dispatch_request()
File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1478, in full_dispatch_request
response = self.make_response(rv)
File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1577, in make_response
rv = self.response_class.force_type(rv, request.environ)
File "/usr/local/lib/python2.7/dist-packages/werkzeug/wrappers.py", line 827, in force_type
response = BaseResponse(*_run_wsgi_app(response, environ))
File "/usr/local/lib/python2.7/dist-packages/werkzeug/test.py", line 855, in run_wsgi_app
app_iter = app(environ, start_response)
TypeError: 'int' object is not callable
Bizarrely, the traceback does not contain any of my files so I can't figure out what's causing it or how to fix it.
Here are the relevant methods that I wrote:
#app.route('/notes/<filenum>', methods=['GET', 'POST'])
def view(filenum):
if request.method == 'GET':
pass
else:
if 'username' in session:
if request.values['fnct'] == "delete":
asdf = database.delNote(filenum,session['username'])
return asdf
return "invalid command"
else:
return "not logged in"
def delNote(self, noteid,username):
try:
if self.getPost(noteid)[7] == self.usernameToID(username):
for file in glob.glob('uploads/' + str(noteid)+"-*"):
os.remove(file)
self._execute('DELETE FROM Posts where postID=?',(noteid,))
return 1
else:
return -2 #autherror
except IndexError:
return -1 #note does not exist
When I call delNote(), it works flawlessly without any errors. When I run view(filenum) with the line that calls delNote() commented out, it works flawlessly with no errors. When I call delNote from view, then I get the above error.
It seems that in spite of the error, the function does complete it's intended task. Using print statements, I figured out the entirety of view() runs before the error, but the error is thrown before the return asdf line is run. Can someone please explain to me what is going on because I'm resisting the growing temptation of throwing my computer out the window.
Thanks in advance
The short answer is, you're returning an integer from your controller and Flask doesn't support that.
Here's the relevant docs:
http://flask.pocoo.org/docs/0.10/api/#flask.Flask.make_response
Controllers must return a Response object or a string (or a few other specialized things). You can't return integers, they don't work. That error is because Flask can't figure out what to do with it, so it's assuming it's a callable, and ints are not callables.
The hacktaculuar way of solving your problem is just return str(asdf), but don't do that. It looks like you're trying to show error codes, so you almost certainly want to use the Flask abort() function instead and return the appropriate HTTP status.
Looking at your view procedure, I see the following:
asdf = database.delNote(filenum,session['username'])
There is no return value from the delNote method, so you're assigning None to asdf. I'm not familiar with Flask, but it's probably doing something with that value that it ultimately can't handle. The error you're getting about an int not being callable makes sense then, as None can be coerced to the integer 0. There's no need to assign your delNote to anything. Try this and see if it makes a difference:
database.delNote(filenum, session['username'])
return "Note deleted."

Flask-Admin modelview function

I have a flask app with Flask-Admin to manage the users with the /admin/userview modelview.
I wanted it to show the page when a user that is in the DB with the correct group navigates to /admin/userview, and return plain text "not admin" if they are not. Fortunately I got the last part to work, but unfortunately I cannot seem to get the first part (continuing to show the page if they are in the correct group). Here's the relevant code:
class MyView(ModelView):
#expose('/', methods = ['GET', 'POST'])
def index(self):
## grab kerberos username (PROD)
secuser = request.environ.get('REMOTE_USER')
adminresult = User.query.filter_by(usrlevel='admin').all()
adminresult = str(adminresult)
adminresult = adminresult.strip('[]')
managerresult = User.query.filter_by(usrlevel='manager').all()
managerresult = str(managerresult)
managerresult = managerresult.strip('[]')
if secuser in adminresult:
pass <---------------\
elif secuser in managerresult: |- if a user is apart of either, this gives a ValueError
pass <---------------/
else:
return "NOT ADMIN" <--------- This works!
##ADMIN
admin = Admin(flaskapp, index_view=MyHomeView(), name="test APP")
admin.add_view(MyView(User, db.session, "Edit Users"))
Here's the traceback that I get when a user is in adminresult or in managerresult:
Traceback (most recent call last):
File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1836, in __call__
return self.wsgi_app(environ, start_response)
File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1820, in wsgi_app
response = self.make_response(self.handle_exception(e))
File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1403, in handle_exception
reraise(exc_type, exc_value, tb)
File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1817, in wsgi_app
response = self.full_dispatch_request()
File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1478, in full_dispatch_request
response = self.make_response(rv)
File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1566, in make_response
raise ValueError('View function did not return a response')
ValueError: View function did not return a response
How do I get the ModelView "User" to display it's contents if the user is in either group, and to just return "not admin" text if they are not? I think I got half of that done, just continuing seems to be an issue...
Thanks!
You should actually put the check in is_accessible:
class MyView(ModelView):
def is_accessible(self):
# grab kerberos username (PROD)
secuser = request.environ.get('REMOTE_USER')
admins_and_managers = User.query
.filter(User.usrlevel.in_('admin', 'manager'))
# Better yet, filter again on `secuser`
# to avoid loading *every* admin and manager
# on *every* request for this resource
# and then use `.first` or `.one`
.all()
return secuser in admins_and_managers

request.environ['HTTP_REFERER'] is None

i want to get HTTP_REFERER in python flask framework.
My route is this:
#app.route('/login')
def login():
if authenticateForPanel():
return redirect(url_for("panel"))
else:
ref = request.environ['HTTP_REFERER']
return render_template('login.html',blogOptions = g.blogOptions,ref=ref)
When i execute this,i got KeyError: 'HTTP_REFERER' with the traceback:
Traceback (most recent call last):
File "/Users/ozcan/flask/flask/app.py", line 1823, in __call__
return self.wsgi_app(environ, start_response)
File "/Users/ozcan/flask/flask/app.py", line 1811, in wsgi_app
response = self.make_response(self.handle_exception(e))
File "/Users/ozcan/flask/flask/app.py", line 1809, in wsgi_app
response = self.full_dispatch_request()
File "/Users/ozcan/flask/flask/app.py", line 1482, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/Users/ozcan/flask/flask/app.py", line 1480, in full_dispatch_request
rv = self.dispatch_request()
File "/Users/ozcan/flask/flask/app.py", line 1466, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "/Users/ozcan/Documents/python/app.py", line 318, in login
ref = request.environ['HTTP_REFERER']
KeyError: 'HTTP_REFERER'
When i first wrote this code it was working.I do not directly call this url.I call localhost:5000/panel and it redirects me to the login method.So basically there should be a referer,am i wrong?
When i print the request.environ['HTTP_REFERER'] it prints None
I also tried with the
ref = request.referrer but it is None
Why it can be happen?Thanks a lot.
The werkzeug Request wrapper, which flask uses by default, does this work for you: request.referrer
from flask import Flask, request
import unittest
app = Flask(__name__)
#app.route('/')
def index():
return unicode(request.referrer)
class Test(unittest.TestCase):
def test(self):
c = app.test_client()
resp = c.get('/', headers={'Referer': '/somethingelse'})
self.assertEqual('/somethingelse', resp.data)
if __name__ == '__main__':
unittest.main()
But in the more general case, to retrieve the value of a dictionary key specifying a default if the key is not present, use dict.get
When i first wrote this code it was working.I do not directly call
this url.I call localhost:5000/panel and it redirects me to the login
method.So basically there should be a referer,am i wrong?
This is not correct. A redirect does not guarantee a referrer. Whether a browser will provide a referrer is browser-specific. Perhaps you were using a different browser that had this behavior at one point? The code you have now is working as best as it can be expected to.
See Will a 302 redirect maintain the referer string?
The only way I could get request.referrer to not be None is to load the page through . Redirecting and making regular old HTTP requests did not include a referrer attribute.
#dm03514 has the right idea, but if you're insistent upon using the environ dictionary, you should be using the get method. Also, how are you testing that it is None, i.e., are you using the interactive interpreter to print the value or are you adding print statements in the body?
An example of how you would do this reasonably:
# snip
else:
ref = request.environ.get('HTTP_REFERER')
# You can specify a default return value other than None like so:
# ref = request.environ.get('HTTP_REFERER', '/')
# snip

Categories