How to Log Out Users From a Flask App and Test Authentication
Get the project source code below, and follow along with the lesson material.
Download Project Source CodeTo set up the project on your local machine, please follow the directions provided in the README.md
file. If you run into any issues with running the project source code, then feel free to reach out to the author in the course's Discord channel.
Requiring Logins
You might want to make certain routes only accessible to logged in users. To do that you'll first need to tell Flask-Login
what to do with requests to those routes that are not from logged in users.
The most common action would be send users who try to access to the login page. In order to send users back to the page they were trying to visit before after the login, we'll store the page the were trying to access in their cookies (through the use Flask's session
. session
is the interface Flask provides for storing and reading writing data in cookies).
from flask import ..., request, session
@login_manager.unauthorized_handler
def unauthorized():
session['after_login'] = request.url
return redirect(url_for('user.login'))
Then on the login (and/or signup) route, we'll want to make the redirect attempt to go to the page we stored in the session before. If there's no value stored in the session, we can redirect Users to the Products home page.
login_user(user)
return redirect(session.get('after_login') or url_for("products.index"))
To mark that a route requires a user to be logged in, Flask-Login
provides a decorator we can add to each route called login_required
. To make the product creation route in blueprints/products.py
require a login,
from flask_login import login_required
...
@products.route('/create', methods=['GET', 'POST'])
@login_required
def create():
form = ProductForm()
...
Logging out
Implementing log outs requires using the logout_user
function from Flask-Login
. To ensure that only logged in users can access it, we can use the @login_required
decorator.
from flask_login import login_user, logout_user, login_required
...
@user_bp.route('/logout', methods=["GET", "POST"])
@login_required
def logout():
logout_user()
return redirect(url_for('products.index'))
# You may want to only allow access through a valid POST request
Testing
We added a few forms as well as a model here. The first part to test, is our User.create
model as well as our password validations.
In tests/test_user.py
, we can define two unit tests.
import pytest
from yumroad.models import db, User
EXAMPLE_EMAIL = "[email protected]"
EXAMPLE_PASSWORD = "test"
# Unit Tests
def test_user_creation(client, init_database):
assert User.query.count() == 0
user = create_user()
assert User.query.count() == 1
assert user.password is not EXAMPLE_PASSWORD
def test_email_password_validation(client, init_database):
assert User.query.count() == 0
with pytest.raises(ValueError):
create_user('', EXAMPLE_PASSWORD)
with pytest.raises(ValueError):
create_user(EXAMPLE_EMAIL, '')
assert User.query.count() == 0
To add functional tests, we can fill out the sign up and login forms with the same credentials. There are many scenarios here, and many of them will depend on having a logged in user, for which we can define a fixture in conftest.py
@pytest.fixture
def authenticated_request(client):
new_user = User.create("[email protected]", "examplepass")
db.session.add(new_user)
db.session.commit()
response = client.post(url_for('user.login'), data={
'email': "[email protected]",
'password': "examplepass"
}, follow_redirects=True)
yield client
This lesson preview is part of the Fullstack Flask: Build a Complete SaaS App with Flask course and can be unlocked immediately with a \newline Pro subscription or a single-time purchase. Already have access to this course? Log in here.
Get unlimited access to Fullstack Flask: Build a Complete SaaS App with Flask, plus 70+ \newline books, guides and courses with the \newline Pro subscription.