Tutorial: Roll Your Own User Authorisation Management With Flask-Login
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.
app = Flask(__name__)
login_manager = flask_login.LoginManager()
login_manager.init_app(app)
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
class UserLogin(flask_login.UserMixin):
def __init__(self):
self.user_id = None
def get_id(self):
return self.user_id
def get_role(self):
return self.role
def is_volunteer(self):
return True if self.role=='volunteer' else False
def is_customer(self):
return True if self.role=='customer' else False
def set_role(self, role):
self.role = role
def get_role(self):
return self.role
#This is the user loader decorator
@login_manager.user_loader
def user_loader(id):
user = models.User.query.filter_by(id=id).first()
if not user:
#Return blank and an anonymous user object will be instantiated
return
flask_user = models.UserLogin()
flask_user.user_id = id
flask_user.role = user.type
return flask_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:
from flask.ext.login import current_user
from flask import url_for, redirect
from functools import wraps
def requires_roles(*roles):
def wrapper(f):
@wraps(f)
def wrapped(*args, **kwargs):
if current_user.get_role() not in roles:
#Redirect the user to an unauthorized notice!
return redirect(url_for('unauthorized'))
return f(*args, **kwargs)
return wrapped
return wrapper
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')
def winning_bids():
#This route will only be accessible by Users with role type of 'volunteer'
return render_template('winning_bids.html")