# Logging in with Flask
TIP
List of all code changes made in this lecture: https://diff-store.com/diff/section13__03_logging_in_users (opens new window)
Logging in is extremely similar to signing up! The main (and only) difference is that we don't add the user data to our users
dictionary.
All we do is populate the session
, since that is what tells us the user is logged in.
This is what we're starting with:
@app.route("/login")
def login():
return render_template("login.html")
Just as we did with the /signup
endpoint, we need to accept POST
requests and handle them:
@app.route("/login", methods=["GET", "POST"])
def login():
if request.method == "POST":
pass
return render_template("login.html")
Then, we need to get the email
and password
, and see if they match what we have in our users
dictionary:
@app.route("/login", methods=["GET", "POST"])
def login():
if request.method == "POST":
email = request.form.get("email")
password = request.form.get("password")
if users.get(email) == password:
session["email"] = email
return redirect(url_for("protected"))
return render_template("login.html")
# Handling incorrect login details
There are two ways to handle incorrect login details:
- Send the users to an error page that says "Unauthorized" or something along those lines; or
- Be a bit more helpful and send the users back to the login page and show them a message to tell them what went wrong.
DANGER
If the e-mail or password are wrong, it's important you don't say which one is wrong. This is so that a malicious attacker can't keep guessing passwords for an e-mail they know exists.
# Send the user to an error page due to incorrect login details
Super easy, all you do is use abort
to raise an exception:
@app.route("/login", methods=["GET", "POST"])
def login():
if request.method == "POST":
email = request.form.get("email")
password = request.form.get("password")
if users.get(email) == password:
session["email"] = email
return redirect(url_for("protected"))
abort(401)
return render_template("login.html")
You can then catch with with @app.errorhandler
as we did in How to add error handling to a Flask app.
# Re-render the form and show a message
A slightly better approach would be to use flash()
to show an informative message.
This is better because the user then doesn't have to go back to the login page from the error page:
@app.route("/login", methods=["GET", "POST"])
def login():
if request.method == "POST":
email = request.form.get("email")
password = request.form.get("password")
if users.get(email) == password:
session["email"] = email
return redirect(url_for("protected"))
flash("Incorrect e-mail or password.")
return render_template("login.html")
You could take this a step further and pass the email the user typed back to the form so it's already populated when the page refreshes:
@app.route("/login", methods=["GET", "POST"])
def login():
email = ""
if request.method == "POST":
email = request.form.get("email")
password = request.form.get("password")
if users.get(email) == password:
session["email"] = email
return redirect(url_for("protected"))
flash("Incorrect e-mail or password.")
return render_template("login.html", email=email)
This requires that we change the HTML form slightly also, adding value="{{ email }}"
. This will put the email
there if it's passed. If nothing is passed, then the field will remain empty.
{% extends "base.html" %} {% block content %}
<form method="POST">
<label>
E-mail
<input type="email" name="email" value="{{ email }}" />
</label>
<label>
Password
<input type="password" name="password" />
</label>
<input type="submit" value="Log in" />
</form>
<p><a href="{{ url_for('signup') }}">Sign up</a> instead</p>
{% endblock %}
Note that this doesn't tell the user the e-mail they typed is correct, but if it is then it makes it easier for them.