Flask Authentication for User


How do you set up Login for User in Flask?

In this post, we will code flask authentication for users with SQLAlchemy and flask-login Library. Lets get it started

In recent times, virtually every website make use of the user authentication systems encrypted into them. Using Google, Facebook, or Apple as a third-party you can set up an account with user authentication or through a direct method.

user authentication is a crucial part of a webpage considering that it secures user data such that only that specific user can gain access to it. There are quite a several methods to authenticate users:

  • Authentication based on cookie
  • Authentication based on token
  • Saml
  • Openld
  • OAuth authentication.

Lets go straight to coding, as we will be coding using flask-login authentication.

Related course: Create Web Apps with Python Flask

Flask User Authentication

When a user/client login with their details, the flask generates a session that entails a user ID and then sends the session ID to the user through the cookie, with these details they can log out and log in at will as required.

Before anything, the flask-login needs to be installed

pip install flask-login

After the installation, lets go straight to coding.

Coding files for models.py

We need to generate a user model where we can save the user details. We can make use of Flask SQLAlchemy and SQLite database to perform that.

It is worth noting that user's password cannot be saved directly on the website, owing to the fact that if hackers gain access to the site, they will have the license to get all info on the database. We dont have that luxury.

But in Flask Werkzeug it is much easier. Werkzeug, has inbuilt functions to tackle this situation.

Create a password Hash

The answer is to make use of a password Hash. To know what a hash is, we go to python shell and launch the command

from werkzeug.security import generate_password_hash
a = generate_password_hash('1234')
print(a)

Password Hash

If a hacker gains access to them, they won't be able to decode. There is also a comparable function with the password Hash, like the check_password_hash. How It works

from werkzeug.security import generate_password_hash, check_password_hash
a = generate_password_hash('1234')
print(a)
 
check_password_hash(a,'1234')

hit the enter button, if matched it returns true and false if otherwise

Flask Password Hashing

including Hashed Passwords to Your Database

If you dont have FlaskSQLAlchemy, install it using the pip command:

pip install flask-sqlalchemy

Next, create a file models.py and include the code:

from flask_sqlalchemy import SQLAlchemy
from werkzeug.security import generate_password_hash, check_password_hash
from flask_login import UserMixin
 
db = SQLAlchemy()
 
class UserModel(UserMixin, db.Model):
    __tablename__ = 'users'
 
    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String(80), unique=True)
    username = db.Column(db.String(100))
    password_hash = db.Column(db.String())
 
    def set_password(self,password):
        self.password_hash = generate_password_hash(password)
     
    def check_password(self,password):
        return check_password_hash(self.password_hash,password)

In this patch: email, username, and password hash are being stacked away. We also get to will define 2 class methods – set_password to generate the password hash and check_password to compare them we need to generate and initialize the Flask_login extension. We use code:

from flask_login import LoginManager
 
#...
login = LoginManager()
#...

Since Flask_Login knows nothing about databases, we need to create a function to link both of them. This is done using user_loader function. The syntax is:

from flask_login import LoginManager
login = LoginManager()
 
@login.user_loader
def load_user(id):
    return UserModel.query.get(int(id))

Related course: Create Web Apps with Python Flask

Complete Code

Done with the models.py aspect. Let’s consider a whole code once:

from flask_sqlalchemy import SQLAlchemy
from flask_login import UserMixin
from werkzeug.security import generate_password_hash, check_password_hash
from flask_login import LoginManager
 
login = LoginManager()
db = SQLAlchemy()
 
class UserModel(UserMixin, db.Model):
    __tablename__ = 'users'
 
    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String(80), unique=True)
    username = db.Column(db.String(100))
    password_hash = db.Column(db.String())
 
    def set_password(self,password):
        self.password_hash = generate_password_hash(password)
     
    def check_password(self,password):
        return check_password_hash(self.password_hash,password)
 
 
@login.user_loader
def load_user(id):
    return UserModel.query.get(int(id))

It's time to code our main Flask Application File.

from flask import Flask

app =Flask(__name__)

app.run(host='localhost', port=5000)

Linking Database to our Flask File

We need to link our SQLite database with SQLALchemy. Doing that we include the code:

from flask import Flask
  
app =Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///<db_name>.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
  
app.run(host='localhost', port=5000)

Exchange  with any name you want. Also, we need to link our SQLAlchemy DB instance (present in models.py file) with the main app. For that, add:

from flask import Flask
from models import db
 
app =Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///<db_name>.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
  
db.init_app(app)
app.run(host='localhost', port=5000)

Next, we must add the code to create the database file before the first user request itself. We do that simply by:

from flask import Flask
from models import db

app =Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///<db_name>.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

db.init_app(app)
@app.before_first_request
def create_table():
    db.create_all()

app.run(host='localhost', port=5000)

The DB part is done. Next, we move to the Flask_login Part. Adding User Authentication to our app Just like it is with the DB instance, we have to link the login instance to our app as well. We do it by:

from flask import Flask
from models import login
 
app =Flask(__name__)
 
login.init_app(app)
 
app.run(host='localhost', port=5000)

Next, we tell Flask_login about the page; the unauthenticated users will get redirected, which will be nothing but the login page itself. Therefore add the code:

from flask import Flask
from models import login
  
app =Flask(__name__)
 
login.init_app(app)
login.login_view = 'login'
 
app.run(host='localhost', port=5000)

When the redirect page is mentioned, we can simply add the @login_required decorator to all the webpage views that will need authentication. Awesome! Now the main thing left to do is the login, register, and logout views. But before doing that, we could code a simple page that the users can see after authentication

Simple View Coding

Thus include a simple view:

from flask import Flask, render_template
from flask_login import login_required
 
@app.route('/blogs')
@login_required
def blog():
    return render_template('blog.html')

Be mindful of how we used the @login_required decorator. The blog.html template would look like this:

<h2>Welcome to the Blog</h2>
 
<h3>Hi {{ current_user.username }}</h3>
 
<a href="{{ url_for('logout')}}">Logout Here</a>

Log-in View Coding

The Login view is relatively simple. It should be able to do the following:

If the user is already authenticated, redirect to the blogs page or else display an HTML Form Retrieve the user information from the DB Compare the details, if correct, redirect to the blogs page So add the code:

from flask import Flask, request, render_template
from flask_login import current_user, login_user
 
@app.route('/login', methods = ['POST', 'GET'])
def login():
    if current_user.is_authenticated:
        return redirect('/blogs')
     
    if request.method == 'POST':
        email = request.form['email']
        user = UserModel.query.filter_by(email = email).first()
        if user is not None and user.check_password(request.form['password']):
            login_user(user)
            return redirect('/blogs')
     
    return render_template('login.html')

Include the login.html template:

<form action="" method = "POST">
    <p>email <input type = "email" name = "email" /></p>
    <p>password <input type = "password" name = "password" /></p>
    <p> submit <input type = "submit" value = "Submit" /></p>
</form>
 
<h3>Dont Have an account??</h3>
<h3><a href = "{{url_for('register') }}">Register Here</a></h3>

Register View Coding

The Register View should be able to do the following:

If the user already authenticated, redirect to the blogs page or else display an HTML Form Add the user data to the DB Redirect to the login page So the code will look like this:

from flask import Flask, request, render_template
from flask_login import current_user
 
@app.route('/register', methods=['POST', 'GET'])
def register():
    if current_user.is_authenticated:
        return redirect('/blogs')
     
    if request.method == 'POST':
        email = request.form['email']
        username = request.form['username']
        password = request.form['password']
 
        if UserModel.query.filter_by(email=email):
            return ('Email already Present')
             
        user = UserModel(email=email, username=username)
        user.set_password(password)
        db.session.add(user)
        db.session.commit()
        return redirect('/login')
    return render_template('register.html')

Therefore the register.html page will be:

<form action="" method = "POST">
    <p>email <input type = "email" name = "email" /></p>
    <p>Username <input type = "text" name = "username" /></p>
    <p>password <input type = "password" name = "password" /></p>
    <p> submit <input type = "submit" value = "Submit" /></p>
</form>

<h3>Already Have an Account?</h3><br>
<h3><a href ="{{url_for('login')}}">Login Here</a></h3>

Coding the Logout View

The Logout View should just log users out. Thus add the code:

from flask import Flask, render_template
from Flask_login import logout_user
 
@app.route('/logout')
def logout():
    logout_user()
    return redirect('/blogs')

Done!! again let us take a look at the full code for this section:

from flask import Flask,render_template,request,redirect
from flask_login import login_required, current_user, login_user, logout_user
from models import UserModel,db,login
 
app = Flask(__name__)
app.secret_key = 'xyz'
 
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///data.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
 
 
db.init_app(app)
login.init_app(app)
login.login_view = 'login'
 
@app.before_first_request
def create_all():
    db.create_all()
     
@app.route('/blogs')
@login_required
def blog():
    return render_template('blog.html')
 
 
@app.route('/login', methods = ['POST', 'GET'])
def login():
    if current_user.is_authenticated:
        return redirect('/blogs')
     
    if request.method == 'POST':
        email = request.form['email']
        user = UserModel.query.filter_by(email = email).first()
        if user is not None and user.check_password(request.form['password']):
            login_user(user)
            return redirect('/blogs')
     
    return render_template('login.html')
 
@app.route('/register', methods=['POST', 'GET'])
def register():
    if current_user.is_authenticated:
        return redirect('/blogs')
     
    if request.method == 'POST':
        email = request.form['email']
        username = request.form['username']
        password = request.form['password']
 
        if UserModel.query.filter_by(email=email).first():
            return ('Email already Present')
             
        user = UserModel(email=email, username=username)
        user.set_password(password)
        db.session.add(user)
        db.session.commit()
        return redirect('/login')
    return render_template('register.html')
 
 
@app.route('/logout')
def logout():
    logout_user()
    return redirect('/blogs')

The UserModel.query.filter_by(email=email).first() will return the first User it gets from the Database or will return None if no user was found.

Implementing Flask User Authentication Application

Finally test our app. launch the Flask file:

python filename.py

try to go to “/blogs“. You will be redirected to a login page. Click on register and input your details.

Hit submit, it takes you back to the login page. Now enter your details and log in. You will see the Blog page!!

Tip

Using regular emails like a@gmail.com would probably give you an error like the kind shown in the Chrome Browsers. You will be directed to the “blogs” footer.

A security message is displayed considering the password used is very weak. Attempt the same with a much stronger password, and a good email too. You'd be granted access to the blog's page as opposed to the security warning like in this situation.

Closing remarks

Done guys! That's basically what you need to know about User Authentication in the flask.