4 min read

(For more resources related to this topic, see here.)

Getting ready

We will work from the app.py file from the sched directory and the models.py file.

How to do it…

  1. Flask provides a session object, which behaves like a Python dictionary, and persists automatically across requests. You can, in your Flask application code:

    from flask import session
    # ... in a request ...
    session['spam'] = 'eggs'
    # ... in another request ...
    spam = session.get('spam') # 'eggs'

  2. Flask-Login provides a simple means to track a user in Flask’s session. Update requirements.txt:

    Flask
    Flask-Login
    Flask-Script
    Flask-SQLAlchemy
    WTForms

  3. Then:

    $ pip install -r requirements.txt

  4. We can then load Flask-Login into sched‘s request handling, in app.py:

    from flask.ext.login import LoginManager, current_user
    from flask.ext.login import login_user, logout_user
    from sched.models import User
    # Use Flask-Login to track current user in Flask's session.
    login_manager = LoginManager()
    login_manager.setup_app(app)
    login_manager.login_view = 'login'
    @login_manager.user_loader
    def load_user(user_id):
    """Flask-Login hook to load a User instance from ID."""
    return db.session.query(User).get(user_id)

  5. Flask-Login requires four methods on the User object, inside class User in models.py:

    def get_id(self):
    return str(self.id)
    def is_active(self):
    return True
    def is_anonymous(self):
    return False
    def is_authenticated(self):
    return True

    Flask-Login provides a UserMixin (flask.ext.login.UserMixin) if you prefer to use its default implementation.

  6. We then provide routes to log the user in when authenticated and log out. In app.py:

    @app.route('/login/', methods=['GET', 'POST']) def login(): if current_user.is_authenticated(): return redirect(url_for('appointment_list')) form = LoginForm(request.form) error = None if request.method == 'POST' and form.validate(): email = form.username.data.lower().strip() password = form.password.data.lower().strip() user, authenticated = User.authenticate(db.session.query, email, password) if authenticated: login_user(user) return redirect(url_for('appointment_list')) else: error = 'Incorrect username or password.' return render_template('user/login.html', form=form, error=error) @app.route('/logout/') def logout(): logout_user() return redirect(url_for('login'))

  7. We then decorate every view function that requires a valid user, in app.py:

    from flask.ext.login import login_required
    @app.route('/appointments/')
    @login_required
    def appointment_list():
    # ...

How it works…

On login_user, Flask-Login gets the user object’s ID from User.get_id and stores it in Flask’s session. Flask-Login then sets a before_request handler to load the user instance into the current_user object, using the load_user hook we provide. The logout_user function then removes the relevant bits from the session.

If no user is logged in, then current_user will provide an anonymous user object which results in current_user.is_anonymous() returning True and current_user. is_authenticated() returning False, which allows application and template code to base logic on whether the user is valid. (Flask-Login puts current_user into all template contexts.) You can use User.is_active to make user accounts invalid without actually deleting them, by returning False as appropriate.

View functions decorated with login_required will redirect the user to the login view if the current user is not authenticated, without calling the decorated function.

There’s more…

Flask’s session supports display of messages and protection against request forgery.

Flashing messages

When you want to display a simple message to indicate a successful operation or a failure quickly, you can use Flask’s flash messaging, which loads the message into the session until it is retrieved. In application code, inside request handling code:

from flask import flash
flash('Sucessfully did that thing.', 'success')

In template code, where you can use the ‘success‘ category for conditional display:

{% for cat, m in get_flashed_messages(with_categories=true) %}
<div class="alert">{{ m }}</div>
{% endfor %}

Cross-site request forgery protection

Malicious web code will attempt to forge data-altering requests for other web services. To protect against forgery, you can load a randomized token into the session and into the HTML form, and reject the request when the two do not match. This is provided in the Flask-SeaSurf extension, pythonhosted.org/Flask-SeaSurf/ or the Flask-WTF extension (which integrates WTForms), pythonhosted.org/Flask-ETF/.

Summary

This article explained how to keep users logged in for on-going requests after authentication. It shed light on how Flask provides a session object, which behaves like a Python dictionary, and persists automatically across requests. It also spoke about coding in Flask application. We got acquainted with flashing messages and cross-site request forgery protection.

Resources for Article:


Further resources on this subject:


LEAVE A REPLY

Please enter your comment!
Please enter your name here