Allowing users to log in to your app is one of the most common features you will add to your web applications. You can add authentication to your Flask app with the Flask-Login package.
In this tutorial, you will:
User
modelYou will build a sign-up and a login page that allow users to log in and access protected pages. You will use information from the User
model and display it on the protected pages when the user logs in to simulate what a profile would look like.
Note: This tutorial is limited in scope and does not cover advanced persisting of sessions. Furthermore, modifying the data type for the primary key or considerations for migrating to different database systems are also outside of the scope of this introductory tutorial.
The source code for this project is available on GitHub.
To complete this tutorial, you will need the following:
Here is a diagram to provide a sense of what the file structure of the project will look like once you have completed the tutorial:
.
└── flask_auth_app
└── project
├── __init__.py # setup the app
├── auth.py # the auth routes for the app
├── db.sqlite # the database
├── main.py # the non-auth routes for the app
├── models.py # the user model
└── templates
├── base.html # contains common layout and links
├── index.html # show the home page
├── login.html # show the login form
├── profile.html # show the profile page
└── signup.html # show the signup form
As you progress through the tutorial, you will create these directories and files.
This tutorial was verified with sqlite3
v3.36.0, python
v3.9.8, flask
v2.0.2, flask-login
v0.5.0, and flask-sqlachemy
v2.5.1.
There are three main packages you need for your project:
You will be using SQLite to avoid having to install any extra dependencies for the database.
First, start with creating the project directory:
Next, navigate to the project directory:
You will want to create a Python environment if you don’t have one.
Note: You can consult the tutorial relevant to your local environment for setting up venv
.
Depending on how Python was installed on your machine, your command will look similar to:
The -m
flag is for module-name
. This command will execute the module venv
to create a new virtual environment named auth
. This will create a new directory containing bin
, include
, and lib
subdirectories. And a pyvenv.cfg
file.
Next, run the following command:
This command will activate the virtual environment.
Run the following command from your virtual environment to install the needed packages:
Now that you have installed the packages, you are ready to create the main app file.
Let’s start by creating a project
directory:
The first file will be the __init__.py
file for the project:
This app will use the Flask app factory pattern with blueprints. One blueprint handles the regular routes, which include the index and the protected profile page. Another blueprint handles everything auth-related. In a real app, you can break down the functionality in any way you like, but the solution covered here will work well for this tutorial.
This file will have the function to create the app, which will initialize the database and register the blueprints. At the moment, this will not do much, but it will be needed for the rest of the app.
You will need to initialize SQLAlchemy, set some configuration values, and register the blueprints here:
Now that you have the main app file, start adding in the routes.
For the routes, you will use two blueprints.
For the main_blueprint
, you will have a home page (/
) and a profile page (/profile
).
First, create main.py
:
Then add your main_blueprint
:
For the auth_blueprint
, you will have routes to retrieve both the login page (/login
) and the sign-up page (/signup
). Finally, you will have a logout route (/logout
) to log out an active user.
Next, create auth.py
:
Then add your auth_blueprint
:
For the time being, define login
, signup
, and logout
with text returns. You will also have routes for handling the POST requests from login
and signup
. You will revisit this code later and update it with the desired functionality.
In a terminal, you can set the FLASK_APP
and FLASK_DEBUG
values:
The FLASK_APP
environment variable instructs Flask on how to load the app. You would want this to point to where create_app
is located. For this tutorial, you will be pointing to the project
directory.
The FLASK_DEBUG
environment variable is enabled by setting it to 1
. This will enable a debugger that will display application errors in the browser.
Ensure that you are in the flask_auth_app
directory and then run the project:
Now, in a web browser, you can navigate to the five possible URLs and see the text returned that was defined in auth.py
and main.py
.
For example, visiting localhost:5000/profile
displays: Profile
:
Once you have verified that the routes are behaving as expected, you can create the templates.
Next, create the templates that are used in the app. This is the first step before you can implement the actual login functionality.
The app will use four templates:
You will also have a base template that will have code common to each of the pages. In this case, the base template will have navigation links and the general layout of the page.
First, create a templates
directory under the project
directory:
Then create base.html
:
Next, add the following code to the base.html
file:
This code will create a series of menu links to each page of the application. It also establishes a block for content
that can be overwritten by child templates.
Note: This tutorial uses Bulma to handle styling and layout. For a deeper dive into Bulma, consider reading the official Bulma documentation.
Next, create templates/index.html
:
Add the following code to the newly created file to add content to the page:
This code will create a basic index page with a title and subtitle.
Next, create templates/login.html
:
This code generates a login page with fields for Email and Password. There is also a checkbox to “remember” a logged in session.
Next, create templates/signup.html
:
Add the following code to create a sign-up page with fields for email
, name
, and password
:
Next, create templates/profile.html
:
Add this code to create a page with a title that is hardcoded to welcome Anthony:
You will revisit this code later to dynamically greet any user.
Once you have added the templates, you can update the return statements in each of the routes to return the templates instead of the text.
Next, update main.py
by modifying the import line and the routes for index
and profile
:
Now you will update auth.py
by modifying the import line and routes for login
and signup
:
Once you’ve made these changes, here is what the sign-up page looks like if you navigate to /signup
:
You can navigate to the pages for /
, /login
, and /profile
as well.
Leave /logout
alone for now because it will not display a template later.
The user model represents what it means for the app to have a user. This tutorial will require fields for an email
address, password
, and name
. In future applications, you may decide you want much more information to be stored per user. You can add things like birthdays, profile pictures, locations, or any user preferences.
Models created in Flask-SQLAlchemy are represented by classes that then translate to tables in a database. The attributes of those classes then turn into columns for those tables.
Create the User
model:
Define the User
model:
This code defines a User
with columns for an id
, email
, password
, and name
.
Now that you’ve created a User
model, you can move on to configuring your database.
You will be using an SQLite database. You could create an SQLite database on your own, but let’s have Flask-SQLAlchemy do it for you. You already have the path of the database specified in the __init__.py
file, so you will need to tell Flask-SQLAlchemy to create the database in the Python REPL.
Ensure that you are still in the virtual environment and in the flask_auth_app
directory.
If you stop your app and open up a Python REPL, you can create the database using the create_all
method on the db
object:
Note: If using the Python interpreter is new to you, you can consult the official documentation.
You will now see a db.sqlite
file in your project directory. This database will have the user table in it.
For the sign-up function, you will take the data the user submits to the form and add it to the database. You will need to make sure a user with the same email address does not already exist in the database. If it does not exist, then you need to make sure you hash the password before placing it into the database.
Note: Storing passwords in plaintext is considered a poor security practice. You will generally want a complex hashing algorithm and salt to keep passwords secure.
Let’s start by adding a second function to handle the POST form data. Gather the data passed from the user.
Update auth.py
by modifying the import line and implementing signup_post
:
Create the function and add a redirect. This will provide a user experience of a successful sign-up and being directed to the Login Page.
Now, let’s add the rest of the code necessary for signing up a user. Use the request object to get the form data.
Continue to update auth.py
by adding imports and implementing signup_post
:
This code will check to see if a user with the same email address exists in the database.
Now that you have the sign-up method completed, you will be able to create a new user. Let’s test the form to create a user.
There are two ways you can verify if the sign-up was successful:
Let’s add code to let the user know the email already exists and direct them to go to the login page. By calling the flash
function, you can send a message to the next request, which in this case, is the redirect. The page the user is redirected to will then have access to that message in the template.
First, add the flash
before you redirect to the sign-up page.
To get the flashed message in the template, you can add this code before the form.
This code will display the message "Email address already exists. Go to login page."
if the email address is already in the database.
At this point, you can run the application and attempt to sign up with an email address that already exists.
The login method is similar to the sign-up function. In this case, you will compare the email
address entered to see if it is in the database. If so, you will test the password
the user provided by hashing the password
the user passes in and comparing it to the hashed password
in the database. You will know the user has entered the correct password
when both hashed password
s match.
Once the user has passed the password check, you will know that they have the correct credentials and you can log them in using Flask-Login. By calling login_user
, Flask-Login will create a session for that user that will persist as the user stays logged in, which will allow the user to view protected pages.
You can start with a new route for handling the data submitted with POST. And redirect to the profile page when the user successfully logs in:
Now, you need to verify if the user has the correct credentials:
Let’s add in the block in the template so the user can see the flashed message:
You now have the ability to say a user has been logged in successfully, but there is nothing to log the user into.
Flask-Login can manage user sessions. Start by adding the UserMixin
to your User model. The UserMixin
will add Flask-Login attributes to the model so that Flask-Login will be able to work with it.
Then, you need to specify the user loader. A user loader tells Flask-Login how to find a specific user from the ID that is stored in their session cookie. Add this in the create_app
function along with init
code for Flask-Login:
Finally, add the login_user
function before redirecting to the profile page to create the session:
With Flask-Login setup, use the /login
route. When everything is in place, you will see the profile page.
At this point, you can run the application and attempt to log in.
If your name is not Anthony, then you will see that your name is wrong on the profile page. The goal is for the profile to display the name in the database. You will need to protect the page and then access the user’s data to get the name
.
To protect a page when using Flask-Login, add the @login_requried
decorator between the route and the function. This will prevent a user that is not logged in from seeing the route. If the user is not logged in, the user will get redirected to the login page, per the Flask-Login configuration.
With routes that are decorated with the @login_required
decorator, you can use the current_user
object inside of the function. This current_user
represents the user from the database and provides access all of the attributes of that user with dot notation. For example, current_user.email
, current_user.password
, and current_user.name
, and current_user.id
will return the actual values stored in the database for the logged-in user.
Let’s use the name
of the current_user
and send it to the template:
Then in the profile.html
file, update the page to display the name
value:
Once a user visits the profile page, they will be greeted by their name
.
Now to update the logout view, call the logout_user
function in a route for logging out:
Use the @login_required
decorator because it does not make sense to log out a user that is not logged in to begin with.
After a user logs out and tries to view the profile page again, they will be presented with an error message:
This is because Flask-Login flashes a message when the user is not allowed to access a page.
One last thing to do is put if
statements in the templates to display only the links relevant to the user:
Before the user logs in, they will have the option to log in or sign-up. After they have logged in, they can go to their profile or log out.
With that, you have successfully built your app with authentication.
In this tutorial, you used Flask-Login and Flask-SQLAlchemy to build a login system for an app. You covered how to authenticate a user by first creating a user model and storing the user information. Then you had to verify the user’s password was correct by hashing the password from the form and comparing it to the one stored in the database. Finally, you added authorization to the app by using the @login_required
decorator on a profile page so only logged-in users can see that page.
What you created in this tutorial will be sufficient for smaller apps, but if you wish to have more functionality from the beginning, you may want to consider using either the Flask-User or Flask-Security libraries, which are both built on top of the Flask-Login library.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!
Hello,
Very nice tutorial.
However, I cannot figure out how to listen on to another IP than 127.0.0.1
I tried this:
export FLASK_APP=“myAppName”; export FLASK_DEBUG=1; flask run --host=0.0.0.0:8080
and that
if name == “main”: app.run(host=‘0.0.0.0’, port=8080, debug=True)
Unfortunately, I was not successful.
Note that the app is running fine on 127.0.0.1
Any idea?
Thanks.
P.
from command line add --port ‘nnnn’ --host
host name or ip addrtess
Could you provide more details about the db table, name and column parameters? I would like to create the table in postgres db manually.
Please this did not work for me. From the login section, I got this error AttributeError: ‘User’ object has no attribute ‘is_active’
Why? I followed every step but I’m getting that error. Any idea please?
It happened to me because I forgot to pass UserMixin as a parameter of the User model in models.py
Thanks Anthony. How do you pass UserMixin as a parameter? I could not open models.py in your reply.
I have figured it out. class User(UserMixin, db.Model):
Hi @blessedmadukoma
Place the “login_user” function call after “check_password_hash” if block in auth.py.
The reason behind your error is, cannot login the user if the user is none.
Hey, thank you for this tutorial.
I’m running into an issue when every time I’m trying to login the user it won’t pass the hash check and flashes an error. What could be the problem?
Thank you for your tutorial!
I created a repo for it and I’m going to further combine it with my project. It’s fully functioning right now (after fixing some bugs) and I’d like you to check it out :)
Redirect to GitHub
Hi, thank you for the tutorial. However, as I followed through, I encountered this error ImportError: cannot import name ‘LoginManager’ from ‘flask_login’. May I know how to solve it?
I am getting a flask.cli.NoAppException: Could not import “flask_auth.flask-apps”. when I access the signup page, after Adding Routes section.
This is the best article about flask_login I have ever found! Thanks for your sharing.
It is better if you up full code
This tutorial has small issues because of which one cannot follow it exactly as mentioned. Sometimes the author uses
login()
and sometimeslogin_post()
same forsignup()
andsignup_post()
. All references made are likeauth.login
andauth.signup
so it seems it should have beenlogin()
andsignup()
. Could you please fix it or share a finally code in some repository which is a working one? Also, in__init__()
method there is function inside function see below code.where to exactly place this load_user(user_id) is confusing.
Finally understood the tutorial :)
I need to create separate methods for login (GET) & login_post (POST) and signup (GET) and sign_post (POST).
Thanks!
This comment has been deleted
Hello, thank you for the tutorial. I’m hoping someone here might offer some guidance.
I am confused about how exactly to modify the
main.py
andauth.py
files, as the examples that the author shows at the end of Step 4 don’t exactly match the instructions given in the context of the original code those files were created with.Original
project/main.py
:Said to modify to:
Does this mean that the lines
from . import db
andmain = Blueprint('main', __name__)
should be replaced by...
, or left as is?Original
project/auth.py
:The author states:
…and gives this example:
Same question as above, but additionally - the modified example omits the following entirely:
Does this mean the route for ‘Logout’ should be removed?
I guess another way to ask this is: should the example modifications given by the author be copied verbatim to replace the original contents of
main.py
andauth.py
? Also, I’m not clear as to why the modified code wasn’t used as the original code. What purpose did the original code serve in the process up until it needed to be modified?In any case, I tried modifying each file in multiple ways, but came back with errors on all. For example, the
/signup
page shows:flask.cli.NoAppException: Could not import "project.project".
I’d paste the traceback if I didn’t think it would cause this comment to become too cluttered, and it seems the author doesn’t reply much to comments with questions here, anyway, so I’ll stick to one thing at a time, and hope anyone else might point me in the right direction.
You probably worked this out by now, but for any beginners in this respect, … does not mean replace by … or remove any previous code! It is just indication of other code left from previous.
Hey, when trying to run I get werkzeug.routing.BuildError: Could not build url for endpoint ‘auth.login’. Did you mean ‘auth.login_post’ instead?
Please help
For anyone struggling with the database creation. It’s missing an import part for the models. I’ve added models at the end after create_app else won’t create the user table specified.
Hope it helps someone getting stuck.
@holla22 Top notch! This is first actual mistake I saw in above, hope the writers see this and correct the code! I think it would be great if they show how we can view the actual sql database in the console and run sql commands to show what is in it. A bit more explanation of this after the sign up post part would help. I use sql in day job runninng queries all the time, but not sure how to set this up in console yet for flask sqlite?
Hi, not sure if you’re still struggling with this, however, to see what’s inside a sqlite database i usually use DB Browser for SQLite. Example link
Thank you for this! I have been struggling over this since 2 days and today I thought of checking if anyone had reported or posted about this.
Thank you so much! I was EXACTLY stuck on this issue
thanks bro i was stuck for more than 2 hrs before I saw ur comment
with the latest sqlalchemy create_all() doesn’t support app anymore and require a running context, instead of the python interpreter run db.create_all() from flask shell.
Hi, it is a very good sample. I am new to Flask. I have tried other samples (from other websites) but cannot link together (init, db, login). After copy the code, I have these problems:
(venv) C:\Users\user\Desktop\flask_auth_app>flask run
can I repeat the step of “from project import db, create_app”? After db is created, I deleted db and re-do. Then the db cannot be created again. I need to exit the shell and start from beginning
Is there any tutorial of db that I can learn more?
This comment has been deleted
Thanks for the great tutorial Anthony, I have one question. In the init.py you have “from .auth import auth as auth_blueprint” I am wondering about the dot ’.’ in the ’.auth’. Is this saying ‘in the current directory from file auth.py get auth. Or is this referencing something Flask configuration that will remember the location of the auth file to be referenced from the application object attributes? Thanks.
is there a way to change the login and sign up in the navbar to a name after login
Hello - wondering if anyone can help with the database configuration aspect of this tutorial.
I have followed closely but I am receiving the error ‘AttributeError: can’t set attribute’ when initiating the db.create_all command within Python.
I have used the suggestion in the comments to import models but still no joy. Anyone able to explain where I’m going wrong?
thanks!
Thanking for great tutorials Anthony about flask user authentication. I also had a problem about database if you have the solutions for database configuration My problem also would be solves.
I am having an issue after setting up registration where whenever I try to register a user I get the error
sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such table: users
. To solve this I had to open the SQLite database in this program and make the table myself. Super annoying to figure out but afterwards it is pretty easy to make. Just make sure you make the email (and probably name) unique.As holla22 pointed out here, there’s a mistake in the tutorial; you also need to import models.py in step 6.
Hello, thank you for the tutorial is very interesting and cover many aspect like authentication and Sql alchemy as well. Anyway I copied the code to test it but I have troubles in the import and variables. in models.py on the line: from . import db, I have the error unresolved import
someone had same problem ?
Hi Herbert
Thanks a lot for sharing. This is an awesome tutorial and I have learnt quite a bit by successfully implementing the steps.
I created a github repository with code from this tutorial with branches for the end of each step, with minor style modifications to the code. sharing here in case it’s useful for anyone else.
In the signup.html file there is a hard coded URL in the form tag:
This did not work for me because I am experimenting with this module in a non-root location on my webserver. But it works if I replace it with:
Hi! Thank you for this nice tutorial. I’ve tried to add something that updates the user name and mail but faced with difficulties about it. Any idea ? tnx, T.
Thanks, Anthony for sharing this tutorial. This is an awesome tutorial and I have learnt quite a bit by successfully implementing these steps.
Hi Anthony
Nice tutorial! I am beginner with Python and Flask. The tutorial was very helpfull and easy to understand. It helped me to start with my own Python Web-App. Almost everything worked after the first try.
Only one small issue in “Step 6 — Configuring the Database”: The DB was created but unfortunately without table “User” because of missing line in init.py with link to the user model.
from .models import User
This line was added in later steps. So creating the DB in step 6 would lead in errors.
i am having an issue with the database it seems, when i try and do the register after step configuring database this error comes up on my local host. sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such table: user
This is a great tutorial, and I’ve got it all working. However, I tried protecting another page in my application, and the login session doesn’t “persist” between pages in the app. It requires me to login for each page that is protected with the @login_required" decorator.
How can I persist the user session for all pages?
Hi guys,
Thanks by the tutorial, finally I could undertand something about web development.
But, I made an application using this amazing project, and I want to deploy it in Heroku.
How can I do it?
See ya,
how to set up an admin page with this project?
Did you figure out an answer? Maybe not admin page but how to show different content for a user based on it’s “role”/permissions
Minor error: The label on the second code block in part 7 should be changed to read
project/auth.py
instead ofauth.py
, so that it is consistent with the other code blocks for the same file. This is confusing – as it makes it seem like there is anotherauth.py
file outside theproject/
directory.Sorry, but where am I supposed to add the headers for CORS? I keep getting errors… :/
Please update the tutorial.
The db.create_all() function is not working anymore, instead after the
db.init_app
in__init__.py
, put the next two lines.How can I write a function in this backend, wich handle a post request from axios, and I can get the information about the logged in user?
not sure what you mean here axios can handle post or get requests so you can create a standard route in flask with a get method and then return the data from the database for the current user or if you a admin and want to return any users info you can create a route for that and just use a query string to select the id of the user you want details for and then return the data in the format
What you want to do is add a API key that you can use to authenticate your backend so it can query the data without needing to login all the time but there are entire courses on how to do this.
Hi! The best tutorial on this topic I’ve ever seen. Correctly explained and it is clear where, what and in which file changes or additions should be made. Thank you!
instead of method=‘sha256’ I used method=‘scrypt’ but the “password” field should then be larger, in my case: password varchar(300)
always prints >> user_id=‘None’
I got this working and it is running online as I am using pythonanywhere to host it for free. It took me quite some time even though I have PHP dev experience, but new to python.
Anyway I found that a few tweaks were needed, probably because I am using in non-root location like ‘dosh’ said in an earlier comment.
The main tweaks I had to make were:
1 - change any reference to auth.signup to auth.signup_post (same for auth.login)
2 - change the
auth.py
as follows:and same again for signup_post() lower down.