Say you want to redirect a user if they are not logged in. Currently I have this:
def my_account():
if "user" not in session or not session["user"]:
return redirect('/')
...
def my_favourites():
if "user" not in session or not session["user"]:
return redirect('/')
...
But now I would like to reuse the code that checks if a user is logged in. But this requires the session object.
I'm not sure if I could or should pass the request or session object explicitly in a helper function. Or if there is yet another more preferred way?
If you want to write re-usable code to verify if a user is logged in, one recommended way is to use decorators on the view functions. For example,
#login_required
def do_something():
* pass
Then, you can define this decorator below. You can then use your request and session object. Also, this will redirect to the login url if user is not logged in already and then forward to the next page once logged in.
def login_required(f):
* #wraps(f)
* def decorated_function(*args, **kwargs):
* if "user" not in session or not session["user"]:
* return redirect(url_for('login', next=request.url))
* return f(*args, **kwargs)
* return decorated_function
Have you tried http://packages.python.org/Flask-Login/ ? I believe this is a package that will provide what you are looking for easier.
Related
In Flask, one can use the #login_required decorator in order to protect endpoints. However, I am encountering the opposite issue - how can I prevent my login page from being accessed whilst the user is signed in?
There isn't code that I can really attach, since I have no idea where to even start on this. Any help would be much appreciated! I have followed this tutorial so my code is very similar to this:
https://www.digitalocean.com/community/tutorials/how-to-add-authentication-to-your-app-with-flask-login
Import current_user from flask_login and check if the user (current_user) is authenticated in your view (login view). If the user is authenticated, redirect it to the URL you want.
from flask_login import current_user
#app.route('/login', methods=['GET','POST'])
def login():
if current_user.is_authenticated:
return redirect(url_for('home'))
edit the parameters and:
def login_not_required(f):
#wraps(f)
def decorated_function(*args, **kwargs):
if "logged_in" in session:
return redirect(url_for("index"))
else:
return f(*args, **kwargs)
return decorated_function
how can I prevent my login page from being accessed whilst the user is signed in?
You might use flask-login get_id() and then check if what was returned is not None, which will mean user is logged in.
I am a new-ish to Flask, and I am trying to create a login_required decorated function for my web app. I would like to make use of session for a user that logs in, which is then cleared once the user logs out.
The session for the user is created like this:
# Creates a session for the users and remembers them.
session['username'] = request.form.get("username")
flash("Logged in!")
Has anyone got any suggestions to create a good login_required decorated function so that users can only access the web-page when logged in?
Thank you.
P.S I saw (and tried) this as a solution (see below) but I received a "working out of request context" error:
def login_required(f):
"""
Decorate routes to require login.
https://flask.palletsprojects.com/en/1.1.x/patterns/viewdecorators/
"""
#wraps(f)
def decorated_function(*args, **kwargs):
if 'username'not in session:
return redirect("/login")
return f(*args, **kwargs)
return decorated_function
There is a nice package for this called Flask-Login
Example:
from flask_login import login_required
#app.route('/my_posts')
#login_required
def my_posts():
...
I am doing APIs, and trying to use token. I want to achieve:
1, client issues a request to server with token in the header;
2, server verify the token and do something (one time).
Looking into the DRF, If I would like to overwrite the authenticate function, I also need to return User object or our own customized User object. I would not like to return any User object, because the whole process does not involve any User, just token and permission to do something. How to do this?
Thanks
First of all, create modelToken in models.py. Furthermore, you need to create a token_required decorator. Whenever a user logs in, a token is created, and whenever she/he logs out, the token is destroyed.
login:
def login(request):
username=request.payload.get('username')
password=request.payload.get('password')
user,err=Auth.authenticate(username,password)
if err:
raise Exception()
token=Token.generate()
#you can return user
return {'token':token}
decorators:
def token_required(func):
def inner(request, *args, **kwargs):
try:
request.token=Token.objects.get(token=token)
return func(request, *args, **kwargs)
except Token.DoesNotExists:
pass
return inner
logout:
#token_required
def logout(request):
if request.method=='POST':
request.token.delete()
return {'status':'ok'}
I have some django view handler functions which are structured like this
def view1(request):
# Check for authorization
if not isAuthorized(request):
return HttpResponse('Foo error', status=401)
return HttpResponse('view1 data')
def view2(request):
# Check for authorization
if not isAuthorized(request):
return HttpResponse('Foo error', status=401)
return HttpResponse('view2 data')
def view3(request):
# Check for authorization
if not isAuthorized(request):
return HttpResponse('Foo error', status=401)
return HttpResponse('view3 data')
I want to make this part :
# Check for authorization
if not isAuthorized(request):
return HttpResponse('Foo error', status=401)
some sort of one-liner, so that I do not have to repeat it in each view
In C this would have been a macro, but I've no clue how to achieve something similar in python
The check authorization function part is an example, it can be any check which has nothing to do with user authorization in particular
[Edit]
https://stackoverflow.com/users/2337736/peter-deglopper mentions decorators ...
To elaborate
I have a web API that can take either POST or GET
# return either GET or POST dict whichever exists
def getParams(request):
if request.method == 'GET':
return request.GET
return request.POST
The views do this :
def someAPI(request):
dct = getParams(request)
if not isValid(dct):
return HttpResponse('Bad request', status=401)
How could I acheive this with a decorator? I have that getParams() function in between....
Usually you'd use view decorators for this:
https://docs.djangoproject.com/en/dev/topics/http/decorators/
There's a built in one for checking login status:
https://docs.djangoproject.com/en/1.7/topics/auth/default/#the-login-required-decorator
And one for arbitrary tests on the authenticated user:
https://docs.djangoproject.com/en/1.7/topics/auth/default/#django.contrib.auth.decorators.user_passes_test
A full example adapted from http://mrcoles.com/blog/3-decorator-examples-and-awesome-python/:
from functools import wraps
def validate_params(view_func):
def _decorator(request, *args, **kwargs):
dct = getattr(request, request.METHOD)
if not isValid(dct):
return HttpResponse('Bad request', status=401)
else:
response = view_func(request, *args, **kwargs)
return response
return wraps(view_func)(_decorator)
I'm using getattr rather than an if/then on request.METHOD - I think that's cleaner. If you like you could still use your getParams call instead.
As Peter says, decorators are an ideal solution for this. But an alternative is to use class-based views: you can define a base class that does the checking, and subclasses that implement the specific behaviour.
I used a flask snippet for my flask-login that checks that a user is logged in:
from functools import wraps
def logged_in(f):
#wraps(f)
def decorated_function(*args, **kwargs):
if session.get('logged_in') is not None:
return f(*args, **kwargs)
else:
flash('Please log in first.', 'error')
return redirect(url_for('login'))
return decorated_function
And I decorate views like so:
#app.route('/secrets', methods=['GET', 'POST'])
#logged_in
def secrets():
error = None
I'd like to do something similar for authorization, too. Right now, I have many views to check that a user owns a resource, let's say the hotdogs resource.
If the logged_in user is the owner of that particular hotdog, he can edit and manage his hotdogs. If he isn't, I kick him out to the unauthorized screen.
#app.route('/<hotdog>/addmustard/',methods=["GET"])
#logged_in
def addmustard(hotdog):
if not (authorizeowner(hotdog)):
return redirect(url_for('unauthorized'))
do_stuff()
authorizeowner() takes a hotdog as input and checks that the recorded hotdog owner matches the owner name listed in the session variable.
I tried making a owns_hotdog wrapper/decorator function similar to my logged in one, but it complained that it didn't accept arguments. How can I achieve something similar? Something like...
def owns_hotdog(f):
#wraps(f)
def decorated_function(*args, **kwargs):
if not authorizeowner(hotdog):
return f(*args, **kwargs)
else:
flash('Please log in first.', 'error')
return redirect(url_for('login'))
return decorated_function
From the error message, decorator seems not to be receiving the hotdog argument that Flask views have access to from the variable in the route. My hope is for something like...
#app.route('/<hotdog>/addmustard/',methods=["GET"])
#logged_in
#owns_hotdog(hotdog)
def addmustard(hotdog):
do_stuff()
Everything works with my current authorizeowner(hotdog) function, but it just seems cleaner to have this in place as a wrapper on top of my route, rather than as the first line inside the route.
Some other notes:
I know that Flask-Security and Flask-Principal can manage
authorization for me. Unfortunately, I'm using an unsupported
database back-end and am unable to use these extensions. So, I'm
forced to do authentication without them.
If you see any glaring holes in doing authorization this way, please let me know!
Here's how to do it:
from functools import update_wrapper
def owns_hotdog(hotdog):
def decorator(fn):
def wrapped_function(*args, **kwargs):
# First check if user is authenticated.
if not logged_in():
return redirect(url_for('login'))
# For authorization error it is better to return status code 403
# and handle it in errorhandler separately, because the user could
# be already authenticated, but lack the privileges.
if not authorizeowner(hotdog):
abort(403)
return fn(*args, **kwargs)
return update_wrapper(wrapped_function, fn)
return decorator
#app.errorhandler(403)
def forbidden_403(exception):
return 'No hotdogs for you!', 403
When decorator takes arguments, it's not really a decorator, but a factory function which returns the real decorator.
But if I were you, I would use Flask-Login for authentication and augment it with custom decorators and functions as yours to handle authorization.
I looked into Flask-Principal, but found it overly complicated for my tastes. Haven't checked Flask-Security, but I believe it uses Flask-Principal for authorization. Overall I think that Flask-Login with some custom code is enough most of the time.