Home > Back-end >  login loop with python flask-login
login loop with python flask-login

Time:02-24

I am learning to use flask-login for a class. This is an intro class, and we are not using a db for this excercise, rather we are storing credentials (hashed) on a resing file. I am able to authenticate the user credentials, however when the user 'successfully' logs in, instead of redirecting to the protected URL, I am getting redirected to the login page. The code: user model:

class User(UserMixin):

    def __repr__(self):
        return '<User{}>'.format(self)

    def __init__(self):
        self.username = None
        self.email = None
        self.password = None
        self.user_id = None

    def get_id(self):
        return self.user_id

login manager:

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


@login_manager.user_loader
def load_user():
    return User

signin section:

user = User()
login_user(user)
return redirect(url_for('main'))

In all the tutorials I've been reading, there is use of a sql db. Since we are not using that, and since I don't need the logged on user to be specific at all - just to pass authentication, a lot of what I was reading wasn't workable within the parameters.

CodePudding user response:

You forgot something when defining the user loader. The function takes a user_id parameter and returns an object of class User, not the class itself. (See documentation)

The following simplified example shows you a possibility of implementation without using a database.

Basically, flask-login stores the attribute id of the user object in the session cookie. Based on this id, the respective user is queried by the LoginManager within the database, which has been replaced here by the class variable / list users.
Database implementations usually have an id column which is defined as unique to the entry. In the example, the unique id of the object is used as a substitute. This means that the mixin's getter will be overwritten. So the example keeps a list of created user objects. Within these users can be found by their id or username.

Flask (app.py)
from flask import (
    Flask,
    redirect,
    render_template,
    request,
    url_for
)
from flask_login import (
    LoginManager,
    UserMixin,
    login_required,
    login_user,
    logout_user
)

class User(UserMixin):
    def __init__(self, username, password):
        self.username = username
        self.password = password
        self.__class__.users.append(self)

    def get_id(self):
        return id(self)

    users = []

    @classmethod
    def get(cls, user_id):
        for usr in cls.users:
            if usr.get_id() == user_id:
                return usr
        return None

    @classmethod
    def find_by_username(cls, username):
        for usr in cls.users:
            if usr.username == username:
                return usr
        return None

app = Flask(__name__)
app.secret_key = 'your secret here'

login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = 'login'

@login_manager.user_loader
def load_user(user_id):
    return User.get(user_id)

@app.route('/')
def index():
    return render_template('index.html')

@app.route('/register', methods=['GET', 'POST'])
def register():
    keys = ('username', 'password')
    if request.method == 'POST' and all(k in request.form for k in keys):
        username = request.form['username']
        password = request.form['password']
        if User.find_by_username(username) is None:
            User(username, password)
            return redirect(url_for('.login'))
    return render_template('register.html')

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        username = request.form.get('username')
        password = request.form.get('password')
        user = User.find_by_username(username)
        if user and user.password == password:
            login_user(user)
            return redirect(request.args.get('next', url_for('.index')))
    return render_template('login.html')

@app.route('/logout')
@login_required
def logout():
    logout_user()
    return redirect(url_for('.index'))

@app.route('/secret')
@login_required
def secret():
    return 'You found the secret.'
HTML (templates/index.html)
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Index</title>
  </head>
  <body>
    {% if current_user.is_authenticated -%}
    <a href="{{ url_for('logout') }}">Logout</a>
    {% else -%}
    <a href="{{ url_for('register') }}">Register</a> |
    <a href="{{ url_for('login') }}">Login</a>
    {% endif -%}
    <h1>Index</h1>
    {% if current_user.is_authenticated -%}
    Welcome {{ current_user.username }}!
    {% else -%}
    Welcome Guest!
    {% endif -%}
  </body>
</html>
HTML (templates/login.html)
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Login</title>
  </head>
  <body>
    <a href="{{ url_for('register') }}">Register</a>
    <h1>Login</h1>
    <form method="post">
      <input type="text" name="username" placeholder="username" />
      <input type="password" name="password" placeholder="password" />
      <input type="submit" />
    </form>
  </body>
</html>
HTML (templates/register.html)
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Register</title>
  </head>
  <body>
    <h1>Register</h1>
    <form method="post">
      <input type="text" name="username" placeholder="username" />
      <input type="password" name="password" placeholder="password" />
      <input type="submit" />
    </form>
  </body>
</html>
  • Related