In flask, how to route a link generated by OAuth 2? - python

I'm building a web app with flask. I used
#app.route('/')
def home():
.....
return render_template('home.html',url=url)
to generate the index page. There is a link(the second parameter, url) on the index page which leads to weibo (the chinese "twitter") for OAuth 2. After clicking the link and inputing the weibo username and password, I'm bounced back to a link like www.myflaskwebappaddress.com/?code=valueofcode.
My question is how can I catch the value of code and do something with the code in another page.
I tried the following:
#app.route(url_for('/', code=<tokencode>))
def show_data(tokencode):
.....want to use weibo api here using the tokencode.
But it doesn't work.

Everything after the ? is a query parameter; Flask parses those and provides them as part of the request.args object:
#app.route(url_for('/')
def home():
tokencode = request.args.get('code')
if tokencode is not None:
# there was a `?code=...` query parameter.
Also see The Request Object in the Flask quickstart.
Routing is only dependent on the path and the method of the request, the parameters don't play in that, so you cannot create a separate route for requests with that parameter.
You could, instead, use a before_request handler to look out for the code:
#app.before_request
def check_token():
tokencode = request.args.get('code')
if tokencode is not None:
# there was a `?code=...` query parameter.
If this function returns None, the normal routing takes place. But you can also return valid response; in that case that is the response returned to the end user and normal routing is skipped entirely.
This also means you can use any route on your site with a ?code=<tokencode> query parameter and have the before_request hook function handle it, not just your homepage.

Related

How do I redirect from one flask app to another flask app with url parameters

I have a Python application in which for one specific API, I am trying to redirect it to another API present in another Flask application. To achieve this, I am using the below code:
`
#app.route('/hello')
def hello_name(name):
return redirect("http://localhost:8000/hello", 302)
`
Now, if I try to access my API by appending query parameters like http://localhost:6000/hello?name=Sidharth, it should be redirected to http://localhost:8000/hello?name=Sidharth. Can I get an advice on how this can be done?
I looked online and found that most of the posts are advising usage of url_for() but since I don't want to redirect to another view, I don't think url_for() will be beneficial in my case. With the code that I have written now, the query parameters are not being added to the redirected url.
Try to use HTTP status code 307 Internal Redirect instead of 302 like below:-
#app.route('/hello/')
def hello_name(name):
return redirect(url_for('http://localhost:8000/hello', args1=name), code=307)

Dash Cognito Authentication Logout

I have managed to use this package here to authorize login for my users. However, I am having trouble designing a logout system.
So far, what I’ve done is code up the following method in the class defined here. It essentially calls the revoke endpoint documented here. The revoke endpoint returns a 200 response.
def logout_request(self):
if self.is_authorized():
client_id = self.cognito_bp.client_id
client_secret = self.cognito_bp.client_secret
token = session.get("cognito_oauth_token")["refresh_token"]
resp = cognito.post(
"/oauth2/revoke",
params={"token": token},
headers={"Content-Type": "application/x-www-form-urlencoded"},
auth=HTTPBasicAuth(client_id, client_secret)
)
assert resp.ok, resp.text
del self.cognito_bp.token
session.clear()
return render_template("logout.html")
else:
return self.login_request()
Then, in the application.py folder, I have a Flask route defined:
#application.route("/logout", methods=["GET", "POST"])
def logout_user():
return auth.logout_request()
However, for some reason, the system still keeps me logged in. I feel like I need to delete a cookie server side. Any ideas on how to accomplish this? Jumping ahead, how would I be able to design a multi page concept given that I have written an explicit server route for the “logout” endpoint?

Capturing variable returned by API in Flask

I'm trying to capture a variable generated by Flask after visiting a certain URL.
I want to use that variable in further authorization in the API.
Due to the nature of Flask, every time the user visits the web app's URL, a function is called that returns a variable.
I want to capture that variable and use it further, without executing the function (the function should be executed only once).
Here's the code I have:
#app.route("/callback/")
def callback_code():
auth_code = request.args.get('code')
return auth_code
code = callback_code()
tracks_endpoint = "https://api.spotify.com/v1/me/tracks"
tracks_auth = {
"Authorization":f"Bearer {code}"
}
tracks = requests.get(tracks_endpoint, headers=tracks_auth)
The problem is caused by:
code = callback_code()
After visiting http://127.0.0.1:5000/auth, Flask displays the following error:
RuntimeError: Working outside of request context.
This typically means that you attempted to use functionality that needed
an active HTTP request. Consult the documentation on testing for
information about how to avoid this problem.
How can I capture auth_code and store it, without executing the function?
Thanks for all your help!
So first problem is that you're expecting the server to be running and get some callback, but the code that is not intended in a function is going to be immeadiately ran, externally from the Flask server lifecycle.
Best way I can think to handle that would be to have your callback redirect to another endpoint, and save off your auth token somewhere in between (such as a session cookie)
Or using a separate variable, for example
AUTH = {
'spotify': None
}
tracks_endpoint = "https://api.spotify.com/v1/me/tracks"
#app.route("/callback/")
def callback_code():
auth_code = request.args.get('code')
AUTH['spotify'] = auth_code
return redirect(url_for('tracks'))
#app.route("/tracks")
def tracks():
if not AUTH['spotify']:
raise('Auth code not loaded. Login first')
tracks_auth = {
"Authorization":f"Bearer AUTH['spotify']"
}
tracks = requests.get(tracks_endpoint, headers=tracks_auth)

How to handle unexpected url parameter in Flask

I'm new to Flask and web development. I have a question about url parameters.
For example, I have an endpoint '/categories' which expect no url arguments. I experimented adding some random url parameter in curl like
curl localhost:5000/categories?page=1
It works like normal, I'm wondering if that is the expected behavior or should I handle this with some error or warning?
Also, if I expect an url parameter called 'id' and the request contains no url parameter or wrong url parameter. How should I handle this situation?
What status code should I abort in the above situations?
Thank you for any help.
You will need to inspect your query string (the values after ? in the URL) to see if the parameter exists and the value is valid. There are libraries to do this with a decorator, but you can do it manually as well. You will need to import the request object at the top of your module.
from flask import request
#app.route('/categories')
def categories():
id = request.args.get('id') # will return None if key does not exist
if id:
# id exists, validate value here
pass
else:
# no id, return error or something
pass
Related question:
How do you get a query string on Flask?

Issue with url_for mapping variables to URL if multiple routes are used

I've hit an issue with url_for, where it won't automatically remap the variable straight into the URL because there are two routes.
My use case is an API, where creating an object will return the same data as if a GET command was run on it.
Here's an example of the code:
#app.route('/test', methods=['POST'])
#app.route('/test/<string:name>', methods=['GET'])
def test(name=None):
if request.method == 'POST':
return redirect(url_for('test', name='xyz'))
return name
If the first app.route is removed, then url_for('test', name='xyz') will correctly return "test/xyz".
However, with both app.route lines, it instead returns "test?name=xyz". This then causes name to be None, where the variable is actually located at request.args['name'].
I don't want to do a if name is None: name=request.args.get('name'), so is there any way I can force it to only look at routes with a GET method? My case right now is simple enough I could just do url_for('test')+'/xyz', but it seems like there should be better way of doing this.
According to the Flask Docs you can specify which method to map against use the _method argument.
flask.url_for(endpoint, **values)
And the values you can pass are:
endpoint – the endpoint of the URL (name of the function)
values – the variable arguments of the URL rule
_external – if set to True, an absolute URL is generated. Server address can be changed via SERVER_NAME configuration variable which falls back to the Host header, then to the IP and port of the request.
_scheme – a string specifying the desired URL scheme. The _external parameter must be set to True or a ValueError is raised. The default behavior uses the same scheme as the current request, or PREFERRED_URL_SCHEME from the app configuration if no request context is available. As of Werkzeug 0.10, this also can be set to an empty string to build protocol-relative URLs.
_anchor – if provided this is added as anchor to the URL.
_method – if provided this explicitly specifies an HTTP method. <---- This one
Specify the _method argument in url_for like this:
url_for('test', name='xyz', _method='GET')

Categories