3
votes

I am using Flask-Login and trying to understand how it works. If I understand the docs right, I should create a user class which holds four methods, is_authenticated, is_active, is_anonymous and get_id. I don't really understand what I should do after that. There is a function, login_user(user), which I understand would take my instance of a not yet authenticated user and authenticate it, but how does that work when my user class does not have any set methods?

Here is some code:

class User:
    isAuth = False
    isActive = False
    isAnon = True
    id = unicode(0)

    def __init__(self, isAuth, isActive, isAnon, id):
        self.isAuth = isAuth
        self.isActive = isActive
        self.isAnon = isAnon
        self.id = id

    def is_authenticated(self):
        return isAuth

    def is_active(self):
        return isActive

    def is_anonymous(self):
        return isAnon

    def get_id(self):
        return id

And here is my guess about how I should authenticate a user, by using authomatic and Flask-Login:

@app.route('/login/<provider_name>/', methods=['GET', 'POST'])
def login(provider_name):
    response = make_response()
    result = authomatic.login(WerkzeugAdapter(request, response), provider_name)
    user = User()

    if result:
        if result.user:
            result.user.update()
            login_user(user)
        return render_template('login.html', result=result)

    return response

What am I missing?

EDIT, here is the user_loader:

@lm.user_loader
def load_user(userid):
    return User.get(userid)

And I also have this on top:

lm = LoginManager()
lm.init_app(app)
2
Try adding @login_required decorator above your methods that handle responses (like index() or similar).Bo Milanovich
That is done, but not visible in the code example above.rablentain
PEP-### (I don't really remember which one): do not use Camels! isAuth -> is_auth, or even IS_AUTH if that supposed to be constantsakaRem
have you implemented something like @login_manager.user_loader def load_user(user_id): return User.get(User.id == int(user_id)) ? (sorry for formatting)akaRem

2 Answers

1
votes

When defininig the User model, you have to have those four methods implementend (It's how Flask-Login works). However, you don't have to define them yourself, unless you need some custom behaviour. Flask-Login provides you with the UserMixin class. All you have to do is subclass your user model and UserMixin will provide all four methods with default behaviour for you. For example:

from flask.ext.login import UserMixin

class User(UserMixin, Model):
    # your user model definition

Inside your User model you can override any of these four methods to suit your needs. Also, please refer to the bottom of "Your User Class" section for further reference: https://flask-login.readthedocs.org/en/latest/#your-user-class

For fetching user data you should use a form. The way you put it, user would have to enter his/her directly data into the URL (I'm guessing something like /login?username=yourusername&password=youruserpassword) which isn't secure and is generally considered a bad practice.

Instead, you should use forms for your users to enter their login data in. WTForms and Flask-WTF extensions provide you with the way to easily create forms. For example:

from flask_wtf import Form
from wtforms import StringField, PasswordField, SubmitField
from wtforms.validators import Required

class LoginForm(Form):
    username = StringField('Your username', validators=[Required()])
    password = PasswordField('Your password', validators=[Required()])
    submit = SubmitField('Sign In')

For further reference: https://wtforms.readthedocs.org/en/latest/ and https://flask-wtf.readthedocs.org/en/latest/

After that your view should just capture the form data, check if the user with provided data exists in the database and if the provided password matches the one stored in database. This is how your view would like:

@app.route('/login', methods=['GET', 'POST'])
def login():
    form = LoginForm()

    # on POST check if the submitted form is valid
    if form.validate_on_submit():

        # get user from database
        user = User.query.filter_by(username=form.username.data).first()

        #check if the user exists and if he/she provided correct password
        if user is not None and user.password == form.password.data:

            # if everything checks out, call the login function
            login(user)

            # redirecting after form submission is also considered good practice
            redirect(url_for('somewhere'))

        # if user is not found or he/she provided wrong password, inform them
        flash('Invalid username and/or password') 

    # on GET, just render the template with login form
    return render_template('login.html', form=form)

As a side note, this view assumes you are storing passwords in plain-text, which is a bad idea as someone pointed out. I made it this way just to demonstrate login functionality.

Also, if you wonder how to define models and how to query data from database, refer to Flask-SQLAlchemy https://pythonhosted.org/Flask-SQLAlchemy/ and SQLAlchemy http://www.sqlalchemy.org/

0
votes

I found this to be very helpful: https://www.openshift.com/blogs/use-flask-login-to-add-user-authentication-to-your-python-application

Please remember never to store plain-text passwords in a DB for user authentication.

Are you still stuck? I'm here if you need me :)