Skip to content

Instantly share code, notes, and snippets.

@leongjinqwen
Last active June 25, 2024 08:37
Show Gist options
  • Save leongjinqwen/a205cbe8185d8c83f9d300cc6c8634f1 to your computer and use it in GitHub Desktop.
Save leongjinqwen/a205cbe8185d8c83f9d300cc6c8634f1 to your computer and use it in GitHub Desktop.
Summary on how to use flask-login

How to use Flask-Login in Flask?

  1. Make sure you have SECRET_KEY in your .env file

    • Flask-Login uses sessions for authentication, secret key is used to encrypting the cookies in session, the user could look at the contents of cookie but not modify it, unless they know the secret key used for signing.
  2. Initializing Flask-Login in your app.py or __init__.py

    • A user loader tells Flask-Login how to get a specific user object from the ID that is stored in the session cookie
      from flask_login import LoginManager
      from models.user import User
    
      login_manager = LoginManager()
      login_manager.init_app(app)
      
      @login_manager.user_loader
      def load_user(user_id):
        return User.get_by_id(user_id) # get id from session,then retrieve user object from database with peewee query
  3. Optional (Customizing the Login Process) => https://flask-login.readthedocs.io/en/latest/#customizing-the-login-process

    • if a user try to access route with @login_required decorator without logged in:
      • Line 1 : redirect user to "sessions.new"
      • Line 2 : flash message
      • Line 3 : declare the flash message category
      login_manager.login_view = "sessions.new" 
      login_manager.login_message = "Please log in before proceeding"
      login_manager.login_message_category = "warning"
  4. Add the login_user function to create the session in instagram_web/blueprints/sessions/views.py.

      from flask_login import login_user
      from models.user import User
      from werkzeug.security import check_password_hash
    
      @sessions_blueprint.route('/', methods=['POST'])
      def create():
        user = User.get_or_none(User.email==request.form.get('email'))
        if user:
          if check_password_hash(user.password, request.form.get('password')) :
            login_user(user) # store user id in session
            return redirect(url_for('users.show')) # redirect to profile page
          else:
            # password not matched
        else:
          # no user found in database
  5. Use the logout_user function for logging out in instagram_web/blueprints/sessions/views.py.

    • have login_required decorator because it doesn't make sense to logout a user who isn't logged in to begin with
      from flask_login import logout_user, login_required
    
      @sessions_blueprint.route('/delete')
      @login_required
      def destroy():
          logout_user()
          return redirect(url_for('sessions.new'))
  6. Adding UserMixin class to our User model in models/user.py.

    • The UserMixin will provide is_authenticated, is_active, is_anonymous and get_id() properties and methods to our model so Flask-Login will be able to work with it
      from flask_login import UserMixin
    
      class User(UserMixin, BaseModel):
        username = pw.CharField(unique=False)
        password = pw.CharField()
        email = pw.CharField(unique=True)
  7. Use current_user in jinja template and views.py.

    • Access the logged-in user with the current_user proxy, which is available in every template.
    • For example, you can use it in your templates/_navbar.html as below:
      {% if current_user.is_authenticated %}
        <li>
          <a href="{{ url_for('sessions.destroy')}}">Sign Out</a>
        </li>
      {% else %}
        <li>
          <a href="{{ url_for('sessions.new')}}">Sign In</a>
        </li>
        <li>
          <a href="{{ url_for('users.new')}}">Sign Up</a>
        </li>
      {% endif %}
    • OR, use it in your instagram_web/blueprints/users/views.py as below:
      from flask_login import current_user
    
      @users_blueprint.route('/<id>/edit', methods=["GET"])
      def edit(id):
        user = User.get_or_none(User.username == username)
        if current_user.role == "admin" or current_user.id == user.id:
          return render_template("users/edit.html", user=user)
        else:
          # flash error message
  8. Apply login_required to all routes of specific blueprint:

    @expenses_blueprint.before_request
    @login_required
    def before_request():
      pass

    But if you have many blueprints then you need to repeat same code in each blueprint. Another way is filtering every request at @app level instead.

    @app.before_request
    def before_request():
      public_endpoints = ['users.new', 'users.create', 'sessions.new', 'sessions.create', 'home', 'static']
      if not request.endpoint in public_endpoints and not current_user.is_authenticated:
        flash('Please log in before proceeding.', 'warning')
        return redirect(url_for('sessions.new'))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment