You have to change the order of the decorators. Quoting the Flask documentation:
Answer from syntonym on Stack OverflowSo how would you use that decorator now? Apply it as innermost decorator to a view function. When applying further decorators, always remember that the route() decorator is the outermost:
@app.route('/secret_page') @login_required def secret_page(): pass
You have to change the order of the decorators. Quoting the Flask documentation:
So how would you use that decorator now? Apply it as innermost decorator to a view function. When applying further decorators, always remember that the route() decorator is the outermost:
@app.route('/secret_page') @login_required def secret_page(): pass
When we want the user not to access the private page or the page which requires login for that case flask provides decorators.
@app.route("/welcome")
@login_required # If the user is not logged in then it will redirected to unauthorized_handler
def welcome_page():
return """<h1> welcome user</h1>"""
@login_manager.unauthorized_handler # In unauthorized_handler we have a callback URL
def unauthorized_callback(): # In call back url we can specify where we want to
return redirect(url_for('login')) # redirect the user in my case it is login page!
I hope your problem is solved !!!
Videos
Flask-login doesn't actually have a user backend, it just handles the session machinery to help you login and logout users. You have to tell it (by decorating methods), what represents a user and it is also up to you to figure out how to know if a user is "active" or not (since being "active" can mean different things in different applications).
You should read the documentation and be sure what it does and does not do. Here I am only going to concentrate on wiring it up with the db backend.
To start off with, define a user object; which represents properties for your users. This object can then query databases, or LDAP, or whatever and it is the hook that connects the login mechanism with your database backend.
I will be using the login example script for this purpose.
class User(UserMixin):
def __init__(self, name, id, active=True):
self.name = name
self.id = id
self.active = active
def is_active(self):
# Here you should write whatever the code is
# that checks the database if your user is active
return self.active
def is_anonymous(self):
return False
def is_authenticated(self):
return True
Once you have the user object created, you need to write a method that loads the user (basically, creates an instance of the User class from above). This method is called with the user id.
@login_manager.user_loader
def load_user(id):
# 1. Fetch against the database a user by `id`
# 2. Create a new object of `User` class and return it.
u = DBUsers.query.get(id)
return User(u.name,u.id,u.active)
Once you have these steps, your login method does this:
Checks to see if the username and password match (against your database) - you need to write this code yourself.
If authentication was successful you should pass an instance of the user to
login_user()
Flask-login will try and load a user BEFORE every request. So yes, your example code below will be called before every request. It is used to check what userid is in the current session and will load the user object for that id.
@login_manager.user_loader
def load_user(userid):
#print 'this is executed',userid
return user(userid, 'asdf')
If you look at the Flask-login source code on github, there is a line under function init_app which goes:
app.before_request(self._load_user)
So before every request, the _load_user function is called. The _load_user functions actually calls another function "reload_user()" based on conditions. And finally, reload_user() function calls your callback function that you wrote (load_user() in your example).
Also, flask-login only provides the mechanism to login/logout a user. It does not care if you are using mysql database.
The code block below only allows users who a logged in to access the profile route.
However, I want to add an additional check to see if the user has been confirmed via email. This is just a boolean column in the database.
What is the best way to add this additional check? I'm looking for a solution that is as simple as using the login_ required decorator
@app.route("/profile")
@login_required
def profile():
print(current_user)
return render_template('profile.html', name=current_user.email)Have a look at the official flask docs regarding decorators: https://flask.palletsprojects.com/en/1.1.x/patterns/viewdecorators/ or the python docs https://www.python.org/dev/peps/pep-0318/ as well.
Your decorator should look something like:
from functools import wraps
from flask import abort
import jwt
def authorize(f):
@wraps(f)
def decorated_function(*args, **kws):
if not 'Authorization' in request.headers:
abort(401)
user = None
data = request.headers['Authorization'].encode('ascii','ignore')
token = str.replace(str(data), 'Bearer ','')
try:
user = jwt.decode(token, JWT_SECRET, algorithms=['HS256'])['sub']
except:
abort(401)
return f(user, *args, **kws)
return decorated_function
... and then in your app.py you may have:
@app.route('/api/game', methods=['POST'])
@authorize
def create(user):
data = json.loads(request.data)
....
In this particular case I have used JWT as token and your token can be different respectively the decoding of the token can be your custom implementation, but the basic mechanisms are pretty much as on the example above.
I would place the following decorator function in somewhere common
def validate_api_token(validation_func):
def decorator(f):
@wraps(f)
def decorated_function(*args, **kws):
api_token = request.headers.get('Authorization')
is_valid_api_token = validation_func(api_token)
if is_valid_api_token:
return f(*args, **kws)
return 'Invalid API Token', 401
return decorated_function
return decorator
For small POC flask apps, if you're ok with storing the tokens in a non-versioned file, the following can work:
# tokens are read from a non-versioned `.tokens` file and loaded into a set
api_tokens = load_api_tokens()
def simple_api_token_validation(api_token):
return api_token in api_tokens
@app.route("/v1/my/secret/function", methods=['POST'])
@validate_api_token(simple_api_token_validation)
def my_secret_function():
body = request.get_json()
# ...
Another simple option is to query against a database (e.g. redis):
redis_session = Redis(host=REDIS_HOST, password=REDIS_PASSWORD)
def redis_api_token_validation(api_token):
if not api_token:
return False
api_token_hash = hashlib.sha256(api_token.encode()).hexdigest()
return redis_session.exists(f'api:tokens:{api_token_hash}')
@app.route("/v1/my/secret/function", methods=['POST'])
@validate_api_token(redis_api_token_validation)
def my_secret_function():
body = request.get_json()
# ...
Best IMO as @Velin answered is to use jwt to validate the token
» pip install Flask-Login