When the user accesses this URL running on my flask app, I want the web service to be able to handle the parameters specified after the question mark:
http://10.1.1.1:5000/login?username=alex&password=pw1
#I just want to be able to manipulate the parameters
#app.route('/login', methods=['GET', 'POST'])
def login():
username = request.form['username']
print(username)
password = request.form['password']
print(password)
Use request.args to get parsed contents of query string:
from flask import request
#app.route(...)
def login():
username = request.args.get('username')
password = request.args.get('password')
The URL parameters are available in request.args, which is an ImmutableMultiDict that has a get method, with optional parameters for default value (default) and type (type) - which is a callable that converts the input value to the desired format. (See the documentation of the method for more details.)
from flask import request
#app.route('/my-route')
def my_route():
page = request.args.get('page', default = 1, type = int)
filter = request.args.get('filter', default = '*', type = str)
Examples with the code above:
/my-route?page=34 -> page: 34 filter: '*'
/my-route -> page: 1 filter: '*'
/my-route?page=10&filter=test -> page: 10 filter: 'test'
/my-route?page=10&filter=10 -> page: 10 filter: '10'
/my-route?page=*&filter=* -> page: 1 filter: '*'
You can also use brackets <> on the URL of the view definition and this input will go into your view function arguments
#app.route('/<name>')
def my_view_func(name):
return name
If you have a single argument passed in the URL you can do it as follows
from flask import request
#url
http://10.1.1.1:5000/login/alex
from flask import request
#app.route('/login/<username>', methods=['GET'])
def login(username):
print(username)
In case you have multiple parameters:
#url
http://10.1.1.1:5000/login?username=alex&password=pw1
from flask import request
#app.route('/login', methods=['GET'])
def login():
username = request.args.get('username')
print(username)
password= request.args.get('password')
print(password)
What you were trying to do works in case of POST requests where parameters are passed as form parameters and do not appear in the URL. In case you are actually developing a login API, it is advisable you use POST request rather than GET and expose the data to the user.
In case of post request, it would work as follows:
#url
http://10.1.1.1:5000/login
HTML snippet:
<form action="http://10.1.1.1:5000/login" method="POST">
Username : <input type="text" name="username"><br>
Password : <input type="password" name="password"><br>
<input type="submit" value="submit">
</form>
Route:
from flask import request
#app.route('/login', methods=['POST'])
def login():
username = request.form.get('username')
print(username)
password= request.form.get('password')
print(password)
url:
http://0.0.0.0:5000/user/name/
code:
#app.route('/user/<string:name>/', methods=['GET', 'POST'])
def user_view(name):
print(name)
(Edit: removed spaces in format string)
Use request.args.get(param), for example:
http://10.1.1.1:5000/login?username=alex&password=pw1
#app.route('/login', methods=['GET', 'POST'])
def login():
username = request.args.get('username')
print(username)
password = request.args.get('password')
print(password)
Here is the referenced link to the code.
this should work
#app.route('/login$username=<username>$password=<password>', methods=['GET', 'POST'])
def login(username, password):
# you can add stuff
return f"Username: {username}\nPassword: {password}"
It's really simple. Let me divide this process into two simple steps.
On the html template you will declare name attribute for username and password like this:
<form method="POST">
<input type="text" name="user_name"></input>
<input type="text" name="password"></input>
</form>
Then, modify your code like this:
from flask import request
#app.route('/my-route', methods=['POST'])
# you should always parse username and
# password in a POST method not GET
def my_route():
username = request.form.get("user_name")
print(username)
password = request.form.get("password")
print(password)
#now manipulate the username and password variables as you wish
#Tip: define another method instead of methods=['GET','POST'], if you want to
# render the same template with a GET request too
Related
I'm trying to create a logged area in my Flaks app where each user will have its own routes to access their information.
/user/<userid>/dashboard
/user/<userid>/profile
/user/<userid>/operations
/user/<userid>/analytics
What is the best way to handle that? In the example below, I'm passing the userId variable after the login inside the url_for. Once the redirect will come from the html template don't know how to pass the userId to the other routes.
I was reading something about having multiple routes with a single method but couldn't understand if it fits in what I need. Sry for the noob question and thanks in advance.
#app.route('/logon', methods=['POST'])
def logon():
username = request.form['username']
passwd = request.form['password']
user = login_verified(username, passwd)
session['token'] = user['idToken']
return redirect(url_for('dashboard', usrId=user['userId']))
#app.route('/user/<usrId>/dashboard', methods=['GET'])
def dashboard(usrId):
if 'token' in session:
print('User ID = %s' % usrId)
return render_template('dashboard.html')
else:
return redirect(url_for('login'))
Guys just found a way to do what I was looking for (don't know if this is the best one but works). Below the solution I found:
#app.route('/logon', methods=['POST'])
def logon():
username = request.form['username']
passwd = request.form['password']
user = login_verified(username, passwd)
session['token'] = user['idToken']
return redirect(url_for('dashboard', usrId=user['userId']))
#app.route('/user/<usrId>/dashboard', methods=['GET'])
def dashboard(usrId):
if 'token' in session:
print('User ID = %s' % usrId)
return render_template('dashboard.html', user=usrId)
else:
return redirect(url_for('login'))
#app.route('/user/<usrId>/operations', methods=['GET', 'POST'])
def operations(usrId):
if 'token' in session:
return render_template('operations.html', user=usrId)
else:
return redirect(url_for('login'))
and the template...
<body>
<h1>Dashboard</h1>
<div>
Operações
Perfil
</div>
<br />
<input type="button" value="Logout" onclick="location.href='/logout'" />
</body>
I have setup a loopback API, and I plan to use the login as such flask would make requests to loopback and loopback returns an accessToken
For example login to dashboard:
# Login route
#app.route("/login", methods=['GET', 'POST'])
def login():
status = ""
url_login = 'http://localhost:3000/api/Users/login'
try:
if request.method == 'POST':
username = request.form['username']
password = request.form['password']
payload_login = {
"username": str(username),
"password":str(password)
}
print(payload_login)
r = requests.post(url_login, data=payload_login).text
access_token = json.loads(r)
# access_token = r['id']
# access_token = json.loads(access_token)
print("Access Token: " + str(access_token['id']))
return redirect('/') #CHANGE TO 404 PAGE
except Exception as e:
print(e)
return redirect('/') #CHANGE TO 404 PAGE
return render_template('login.html')
#app.route('/dashboard', methods=['GET', 'POST'])
def logged_in_dashboard():
return render_template('index.html')
How do I set it up so that login to dashboard requires accessToken from loopback? In the past I've used app.config['ACCESS_KEY'] ='key' and have set that if it contains a token it would allow the user to login.
But I'm not sure if this is a good practice. Anything you would like to recommend that could handle lots of user logins?
Don't create requests to the API from within the API. To share functionality between endpoints, use functions. You need at least two functions here:
a function returns a token for valid credentials
a function that requires the token being present in the session or in the request Authorization header, for example
Check the approach that chans linked to for more implementation details: How do you implement token authentication in Flask?
Or the official tutorial for how to implement sessions: https://flask.palletsprojects.com/en/1.1.x/quickstart/#sessions
Which has something like this:
#app.route('/')
def index():
# this if is the login requirement
if 'username' in session:
return 'Logged in as %s' % escape(session['username'])
return 'You are not logged in'
#app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
# Add logic for validating username and password here.
# If credentials are ok, set username to session.
session['username'] = request.form['username']
return redirect(url_for('index'))
return '''
<form method="post">
<p><input type=text name=username>
<p><input type=submit value=Login>
</form>
'''
I'm trying to implement the following views in flask:
/index
* shows a form with username and password entries
* upon submission, redirects to 2
/params
* shows another form with more entries
* is only viewable if they provided a username and password for 1
* upon submission, redirects to 3
/upload
* does magic() with the fields entered from 1 & 2
* is only viewable if they filled in 1 & 2
Every tutorial I have seen assumes that you use a database. The username and password from 1 are not checked against any database. They are used for ntlm authentication only...
Can this be done? Or am I misusing flask?
You can use a session to store the username and password and a flag specifying that the user has completed the first two steps:
import flask
app = flask.Flask(__name__)
app.secret_key = 'something'
def validate_upload(f):
def wrapper():
if 'completed' not in flask.session or not flask.session['completed']:
return flask.redirect('/')
return f()
return wrapper
#app.route('/', methods = ['GET'])
def home():
html = """
<form action='/second' method = 'POST'>
<input type='text' name='username'>
<input type='password' name='password'>
<input type='submit' value='Submit'>
</form>
"""
return html
#app.route('/second', methods=['POST'])
def second():
username, password = flask.request.form['username'], flask.request.form['password']
flask.session['username'] = username
flask.session['password'] = password
flask.session['completed'] = True
return flask.redirect('/upload')
#app.route('/upload', methods=['GET'])
#validate_upload
def do_something():
return '<p>Doing something with values</p>'
As you mention NTLM authentication, I assume you're running inside a Windows network. If that is the case, I'm not sure why you'd need a form where users can enter username and password.
NTLM and Kerberos use the principal of the Windows session to authenticate the user with services running inside the same domain. The user logs in once to Windows and that identity is auto-magically used to authenticate the user when calling services.
Flask supports this using the Flask-Kerberos module
When the user accesses this URL running on my flask app, I want the web service to be able to handle the parameters specified after the question mark:
http://10.1.1.1:5000/login?username=alex&password=pw1
#I just want to be able to manipulate the parameters
#app.route('/login', methods=['GET', 'POST'])
def login():
username = request.form['username']
print(username)
password = request.form['password']
print(password)
Use request.args to get parsed contents of query string:
from flask import request
#app.route(...)
def login():
username = request.args.get('username')
password = request.args.get('password')
The URL parameters are available in request.args, which is an ImmutableMultiDict that has a get method, with optional parameters for default value (default) and type (type) - which is a callable that converts the input value to the desired format. (See the documentation of the method for more details.)
from flask import request
#app.route('/my-route')
def my_route():
page = request.args.get('page', default = 1, type = int)
filter = request.args.get('filter', default = '*', type = str)
Examples with the code above:
/my-route?page=34 -> page: 34 filter: '*'
/my-route -> page: 1 filter: '*'
/my-route?page=10&filter=test -> page: 10 filter: 'test'
/my-route?page=10&filter=10 -> page: 10 filter: '10'
/my-route?page=*&filter=* -> page: 1 filter: '*'
You can also use brackets <> on the URL of the view definition and this input will go into your view function arguments
#app.route('/<name>')
def my_view_func(name):
return name
If you have a single argument passed in the URL you can do it as follows
from flask import request
#url
http://10.1.1.1:5000/login/alex
from flask import request
#app.route('/login/<username>', methods=['GET'])
def login(username):
print(username)
In case you have multiple parameters:
#url
http://10.1.1.1:5000/login?username=alex&password=pw1
from flask import request
#app.route('/login', methods=['GET'])
def login():
username = request.args.get('username')
print(username)
password= request.args.get('password')
print(password)
What you were trying to do works in case of POST requests where parameters are passed as form parameters and do not appear in the URL. In case you are actually developing a login API, it is advisable you use POST request rather than GET and expose the data to the user.
In case of post request, it would work as follows:
#url
http://10.1.1.1:5000/login
HTML snippet:
<form action="http://10.1.1.1:5000/login" method="POST">
Username : <input type="text" name="username"><br>
Password : <input type="password" name="password"><br>
<input type="submit" value="submit">
</form>
Route:
from flask import request
#app.route('/login', methods=['POST'])
def login():
username = request.form.get('username')
print(username)
password= request.form.get('password')
print(password)
url:
http://0.0.0.0:5000/user/name/
code:
#app.route('/user/<string:name>/', methods=['GET', 'POST'])
def user_view(name):
print(name)
(Edit: removed spaces in format string)
Use request.args.get(param), for example:
http://10.1.1.1:5000/login?username=alex&password=pw1
#app.route('/login', methods=['GET', 'POST'])
def login():
username = request.args.get('username')
print(username)
password = request.args.get('password')
print(password)
Here is the referenced link to the code.
this should work
#app.route('/login$username=<username>$password=<password>', methods=['GET', 'POST'])
def login(username, password):
# you can add stuff
return f"Username: {username}\nPassword: {password}"
It's really simple. Let me divide this process into two simple steps.
On the html template you will declare name attribute for username and password like this:
<form method="POST">
<input type="text" name="user_name"></input>
<input type="text" name="password"></input>
</form>
Then, modify your code like this:
from flask import request
#app.route('/my-route', methods=['POST'])
# you should always parse username and
# password in a POST method not GET
def my_route():
username = request.form.get("user_name")
print(username)
password = request.form.get("password")
print(password)
#now manipulate the username and password variables as you wish
#Tip: define another method instead of methods=['GET','POST'], if you want to
# render the same template with a GET request too
I am using the Flask-login's #login_required decorator for some routes in my app. When navigating to those routes while not logged in I am redirected to the login page. So far so good.
The redirected url looks like this: /login?next=%2Fusers
Looks like it url-encoded the next parameter, something I haven't seen in the examples I've run across. After logging in the redirect back to next is always failing and falling back to the index page. I think this is because next is url-encoded.
Should I be going about this a different way? I'm just starting to dive into the Flask framework and working off of examples so I don't know much about the best ways to do things.
Here's an example route:
login_manager.login_view = 'login'
#app.route('users')
#login_required
def users():
return 'Test'
And my login route looks like this:
#app.route('/login')
def login():
error = None
next = request.args.get('next')
if request.method == 'POST':
username = request.form['username']
password = request.form['password']
if authenticate_user(username, password):
user = User.query.filter_by(username=username).first()
if login_user(user):
flash("You have logged in")
session['logged_in'] = True
return redirect(next or url_for('index', error=error))
error = "Login failed"
return render_template('login.html', login=True, next=next, error=error)
Thanks in advance for the help.
I figured it out, navigating to a login-protected route (/requests in this example) would cause a redirect to the login page with the next parameter.
/login?next=%2Frequests
In my login template, I had this:
<form action="{{ url_for('login') }}" method='POST'>
This caused the form to be posted to the /login route without any parameters. Removing the action attribute or changing it to action="" causes the form to be posted to its own url, which includes the original query string. Another option would be including next=next in url_for.
<form action="{{ url_for('login', next=next) }}" method='POST'>
Here you go:
import urllib
#app.route('/login')
def login():
error = None
next = urllib.unquote_plus(request.args.get('next'))
...
In case anyone finds this question like I did, here was the implementation that worked for me. The issue was that the login's POST form was only going to /login, causing the next URL is get thrown away. Following Kevan's suggestion, if you add next=request.args.get('next')) to the login form's action, it will pass the next argument with it.
Inside my login function
#app.route('/login', methods = ['GET', 'POST'])
def login():
next = request.args.get('next')
if request.method == 'POST':
...Confirm User...
if next:
return redirect(next)
else:
return redirect('/')
Inside my login form
<form method="post" action="{{ url_for('login', next=request.args.get('next')) }}" enctype="multipart/form-data">
For security reasons, it should be considered the check for the next parameter, as suggested in
https://flask-login.readthedocs.io/en/latest/#login-example
#app.route('/login', methods=['GET', 'POST'])
def login():
# Here we use a class of some kind to represent and validate our
# client-side form data. For example, WTForms is a library that will
# handle this for us, and we use a custom LoginForm to validate.
form = LoginForm()
if form.validate_on_submit():
# Login and validate the user.
# user should be an instance of your `User` class
login_user(user)
flask.flash('Logged in successfully.')
next = flask.request.args.get('next')
# is_safe_url should check if the url is safe for redirects.
# See http://flask.pocoo.org/snippets/62/ for an example.
if not is_safe_url(next):
return flask.abort(400)
return flask.redirect(next or flask.url_for('index'))
return flask.render_template('login.html', form=form)