Explore Flask

12.3. Authentication

Now that we've got a user in the database, we can implement authentication. We'll want to let a user submit a form with their username and password (though this might be email and password for some apps), then make sure that they gave us the correct password. If it all checks out, we'll mark them as authenticated by setting a cookie in their browser. The next time they make a request we'll know that they have already logged in by looking for that cookie.

Let's start by defining a UsernamePassword form with WTForms.

# ourapp/forms.py

from flask_wtf import Form
from wtforms import StringField, PasswordField
from wtforms.validators import DataRequired


class UsernamePasswordForm(Form):
    username = StringField('Username', validators=[DataRequired()])
    password = PasswordField('Password', validators=[DataRequired()])

Next we'll add a method to our user model that compares a string with the hashed password stored for that user.

# ourapp/models.py

from . import db

class User(db.Model):

    # [...] columns and properties

    def is_correct_password(self, plaintext)
        return bcrypt.check_password_hash(self._password, plaintext)

12.3.1. Flask-Login

Our next goal is to define a sign-in view that serves and accepts our form. If the user enters the correct credentials, we will authenticate them using the Flask-Login extension. This extension simplifies the process of handling user sessions and authentication.

We need to do a little bit of configuration to get Flask-Login ready to roll.

In init.py we'll define the Flask-Login login_manager.

# ourapp/__init__.py

from flask.ext.login import LoginManager

# Create and configure app
# [...]

from .models import User

login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view =  "signin"

@login_manager.user_loader
def load_user(userid):
    return User.query.filter(User.id==userid).first()

Here we created an instance of the LoginManager, initialized it with our app object, defined the login view and told it how to get a user object with a user's id. This is the baseline configuration we should have for Flask-Login.

Now we can define the signin view that will handle authentication.

# ourapp/views.py

from flask import redirect, url_for

from flask.ext.login import login_user

from . import app
from .forms import UsernamePasswordForm()

@app.route('signin', methods=["GET", "POST"])
def signin():
    form = UsernamePasswordForm()

    if form.validate_on_submit():
        user = User.query.filter_by(username=form.username.data).first_or_404()
        if user.is_correct_password(form.password.data):
            login_user(user)

            return redirect(url_for('index'))
        else:
            return redirect(url_for('signin'))
    return render_template('signin.html', form=form)

We simply import the login_user function from Flask-Login, check a user's login credentials and call login_user(user). You can log the current user out with logout_user().

# ourapp/views.py

from flask import redirect, url_for
from flask.ext.login import logout_user

from . import app

@app.route('/signout')
def signout():
    logout_user()

    return redirect(url_for('index'))