Tutorial: Roll Your Own User Authorisation Management With Flask-Login
• Mitchell Busby
I'll quickly go through how I did user authentication and authorisation within Flask in this project, because it's annoying having to synthesise multiple best practices into one. There are other tools, like flask-bouncer, that do this for you, but I found them far too difficult to configure in my experience.
First, create your app i.e.
Decorate your user loader function, which should take a unique identifier, and return a class that extends flask_login.UserMixin. In this case I load a user from my regular user model class in SQLAlchemy, create a new object that extends from the flask_login class, and assign some values that I need to identify it by.
#This is my flask_login extension class; note the helper methods
classUserLogin(flask_login.UserMixin):def__init__(self):self.user_id=Nonedefget_id(self):returnself.user_iddefget_role(self):returnself.roledefis_volunteer(self):returnTrueifself.role=='volunteer'elseFalsedefis_customer(self):returnTrueifself.role=='customer'elseFalsedefset_role(self,role):self.role=roledefget_role(self):returnself.role#This is the user loader decorator
@login_manager.user_loaderdefuser_loader(id):user=models.User.query.filter_by(id=id).first()ifnotuser:#Return blank and an anonymous user object will be instantiated
returnflask_user=models.UserLogin()flask_user.user_id=idflask_user.role=user.typereturnflask_user#The route name that you will use to redirect users when they need to login, as defined by the route decorator
login_manager.login_view='login'
That's pretty much all there is to the actual configuration of flask_login. It supplies you with a current_user object, which is plugged into jinja2 so you can use it in your templates to render, for example, specific parts of a page depending on if your user is a customer or administrator.
flask-bouncer integrates well with flask-login to provide privileges to users and only allow certain users to access certain routes, but it's too heavy handed, so I used another one I found on the net:
fromflask.ext.loginimportcurrent_userfromflaskimporturl_for,redirectfromfunctoolsimportwrapsdefrequires_roles(*roles):defwrapper(f):@wraps(f)defwrapped(*args,**kwargs):ifcurrent_user.get_role()notinroles:#Redirect the user to an unauthorized notice!
returnredirect(url_for('unauthorized'))returnf(*args,**kwargs)returnwrappedreturnwrapper
This one works really nicely. All you have to do now is add protection to your routes!
@app.route('/winning_bids')@flask_login.login_required@requires_roles('volunteer')defwinning_bids():#This route will only be accessible by Users with role type of 'volunteer'
returnrender_template('winning_bids.html")