Check if Flask-HTTPAuth is authenticated inside view - python

I'm using Flask-HTTPAuth for authentication. I want to display different data from a view depending on if the request was authenticated or not. Decorating the view with auth.login_required only shows it to authenticated users. How can I test if the request was authenticated with Flask-HTTPAuth?
auth = HTTPBasicAuth()
#app.route("/clothesInfo")
#auth.login_required
def show_info():
return jsonify(blah blah blah)

What you want is actually very easy to implement. In your verify_password callback, you will get username and password set to '' when the user does not provide credentials. You can still return True from that function, and that will allow the anonymous user to access the endpoint.
The following example demonstrates this technique:
auth = HTTPBasicAuth()
#auth.verify_password
def verify_password(username, password):
if username == '' or password == '':
# anonymous user, we still let them in
g.current_user = None
return True
g.current_user = my_verify_function(username, password)
return g.current_user is not None
#app.route("/clothesInfo")
#auth.login_required
def show_info():
if g.current_user:
# prepare data for authenticated users here
pass
else:
# prepare data for anonymous users here
pass
return jsonify(data)

You can decorate a function that returns None with login_required. Calling it while not authenticated will return an error response, calling it while authenticated will return None.
# a dummy callable to execute the login_required logic
login_required_dummy_view = auth.login_required(lambda: None)
def is_authenticated():
try:
# default implementation returns a string error
return login_required_dummy_view() is None
except HTTPException:
# in case auth_error_callback raises a real error
return False
#app.route('/info')
def info():
if is_authenticated():
# logged in view
else:
# basic view
See also Default login_required rather than adding decorator everywhere.

Things are simpler since April 2020.
Flask-HTTPAuth 4.0.0 added optional argument to login required to do exactly this.
From the docs:
An optional optional argument can be set to True to allow the route to execute also when authentication is not included with the request, in which case auth.current_user() will be set to None. Example:
#app.route('/private')
#auth.login_required(optional=True)
def private_page():
user = auth.current_user()
return "Hello {}!".format(user.name if user is not None else 'anonymous')

Related

get the returned value from decorated function in endpoint (flask)

I did not understand well using decorators in flasks and would like to use it to clean up my code.
Particularly, I don't understand if the decorator applied to an endpoint must return a view, that is passed to the template, or can be an arbitrary object that is then used in the function that will return a response for the endpoint.
Let me show by example.
I want to enable POST APIs and some endpoints only to authenticated users. I am using a oauth authentication library (flask-dance) that handle the login with Google.
I must use the credentials obtained by a successful log in to do some things, before passing the object user to the rest of the function of the endpoint.
I am doing something like this:
#app.route("/")
def index():
# get the response from the google authentications and do something
# if auth is successful, will return a user, if not, will redirect to a view
def work_on_user_auth(resp):
user = myClass.something(resp) // pseudo code
return user
// here I could not find a best approach than this: if auth fails, than redirect
if not google.authorized:
return redirect(url_for("google.login"))
try:
resp = get_user_auth()
except TokenExpiredError as e:
return redirect(url_for("google.login"))
user = work_on_user_auth(resp)
// and here will use by object user, based on the google auth, and eventually ship it to the view
return render_template('index.html', **user)
I am now copy pasting the logic in each endpoint, that check if user is logged via Oauth.
I would like to simplify, and use the work_on_user_auth(respFromGoogle) as a decorator for the endpoints: if successful, it must pass the object user, if not, a redirect will ask user to login.
I tried with something like this:
from functools import wraps
def logged_user_required(f):
#wraps(f)
def work_on_user_auth(self, *args, **kwargs):
// if not authorized or auth fails, redirect
if not google.authorized:
return redirect(url_for("google.login"))
try:
resp = get_user_auth()
except TokenExpiredError as e:
return redirect(url_for("google.login"))
print('user logged in', resp)
// if everything ok, create an object user and return it
return user
return work_on_user_auth
and would like to use it like that:
#app.route("/")
#logged_user_required
def index():
// here I want to access the `user` object from my decorated function, if failes, it will redirect
// how to:
user = logged_user_required // ??
return render_template('index.html', **user)
But getting different types of errors (last one: logged_user_required() missing 1 required positional argument: 'f')
Can you show the logic for getting a value returned by a decorated function, which, if fails, will tell the endpoint to redirect instead ?
Might there be a straight forward approach with flask-dance, good to know it, but I would like to have an answer to elucidate how to use decorators properly also in the future. Thanks!
Let's say you have user that determined somewhere outside view by Google auth Moon phase. And you want to pass this authenticated user into view.
After that you want to do something inside view based on what user is passed.
So your code should be like:
from functools import wraps
from random import choice
users = ("Aragorn", "Gimli", "Legolas", "Balrog")
def determine_user_by_moon_phase():
return choice(users)
def moon_phased_auth(f):
#wraps(f)
def wrapper(*args, **kwargs):
"""A wrapper function"""
kwargs['moon_phased_user'] = determine_user_by_moon_phase()
return f(*args, **kwargs)
return wrapper
#moon_phased_auth
def index(moon_phased_user):
print('the hero is:', moon_phased_user)
if moon_phased_user == 'Balrog':
print("You shall not pass!!!")
return "Abyss"
else:
print("Welcome to Rivendell")
return "Rivendell"
response = index()
print("response is:", response)
This code will print:
the hero is: Aragorn
Welcome to Rivendell
response is: Rivendell
In other way, if you don't need to know what is authenticated user inside index view, you may replace all check logic inside moon_phased_auth decorator:
from functools import wraps
from random import choice
users = ("Aragorn", "Gimli", "Legolas", "Balrog")
def determine_user_by_moon_phase():
return choice(users)
def moon_phased_auth(f):
#wraps(f)
def wrapper(*args, **kwargs):
moon_phased_user = determine_user_by_moon_phase()
print('the hero is:', moon_phased_user)
if moon_phased_user == 'Balrog':
print("You shall not pass!!!")
return "Abyss"
else:
print("Welcome to Rivendell")
return f(*args, **kwargs)
return wrapper
#moon_phased_auth
def index():
return "Rivendell"
response = index()
print("response is:", response)
the hero is: Balrog
You shall not pass!!!
response is: Abyss
In this case index don't care about authentication. Abyss response will be returned to client before index call

Check Flask cookie for user_id and obtain User if it exists or redirect if not

What I'm after is something like what the #login_required decorators accomplish but I'm not sure if a custom decorator would allow me to pass the User option back to my route function. There are several pages in my app that require the user to be logged in to access them so I am looking for the most efficient way/least code to copy into each access-restricted route that will verify they have a user_id in their cookie (logged in), cache get/query for their User object using the user_id, and carry on with the route function, else redirect to the login page if user_id is not present.
What I was hoping to do was something like:
#noteBP.route('/note', methods=['GET', 'POST'])
def new_note():
user_details = return_user_details_if_logged_in_else_redirect_to_login_url(next=request.url)
...
And that function would check for the user_id in the session cookie and send back the User object or redirect to the login page:
def return_user_details_if_logged_in_else_redirect_to_login_url(next=None):
user_id = session.get('user_id')
if user_id:
user_details = user_util.get_user_details_by_id(user_id)
return user_details
else:
return redirect(url_for('usersBP.login', next=next))
Turns out, redirect does not work the same way as Abort where it gets called even if you are inside another function so now I have do do additional processing back in the route function to check:
user_details = return_user_details_if_logged_in_else_redirect_to_login_url(next=request.url)
if not user_details:
return redirect(redirect_url)
I'm looking to avoid having to paste this chunk of code at the top of every access-restricted route. Is there a more efficient way/DRY approach to do this? if with a decorator, how do I get the user_details into the route function?
If you want to redirect in a function called inside a view, raise a RequestRedirect. If you want to redirect in a decorator, check if the user is not logged in and return a redirect rather than the actual view (or use the previous function to raise the redirect).
import functools
from flask import url_for, redirect, g
from werkzeug.routing import RequestRedirect
def require_login():
if g.user is None:
raise RequestRedirect(url_for('login'))
def login_required(view):
#functools.wraps(view)
def wrapped_view(**kwargs):
require_login()
# or
# if g.user is None:
# return redirect(url_for('login'))
return view(**kwargs)
return wrapped_view
#app.route('/secret2')
#login_required
def secret1():
return 'secret 1'
#app.route('/secret2')
def secret2():
require_login()
return 'secret 2'
Populate g.user in a before_request handler.
from flask import session
#app.before_request
def load_user():
g.user = None
if 'user_id' in session:
# use whatever caching logic you want here.
g.user = User.query.get(session['user_id'])
Populate session['user_id'] in your login view.
#app.route('/login')
def login():
if request.method == 'POST':
user = User.query.filter_by(username=request.form['username']).first()
if user and user.check_password(request.form['password']:
session['user_id'] = user.id
return redirect(url_for('index'))
return render_template('login.html')
Now you can access g.user from any route, without passing it explicitly. If you do want to pass it explicitly, modify login_required.
def require_login():
if g.user is None:
raise RequestRedirect(url_for('login'))
return g.user
def login_required(view):
#functools.wraps
def wrapped_view(**kwargs):
user = require_login()
return view(user, **kwargs)
return wrapped_view
#app.route('/secret')
def secret(user):
return 'user {} is logged in'.format(user.id)
Give that all of this except passing the user is part of Flask-Login, you should really reconsider using Flask-Login instead of trying to maintain your own solution.
A decorator is a function that wraps and replaces another function. Since the original function is replaced, you need to remember to copy the original function’s information to the new function. Use functools.wraps() to handle this for you.
This example assumes that the login page is called 'login' and that the current user is stored in g.user and is None if there is no-one logged in.
from functools import wraps
from flask import g, request, redirect, url_for
def login_required(f):
#wraps(f)
def decorated_function(*args, **kwargs):
if g.user is None:
return redirect(url_for('login', next=request.url))
return f(*args, **kwargs)
return decorated_function
To use the decorator, 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
Note:
The next value will exist in request.args after a GET request for the login page. You’ll have to pass it along when sending the POST request from the login form. You can do this with a hidden input tag, then retrieve it from request.form when logging the user in.
<input type="hidden" value="{{ request.args.get('next', '') }}"/>

python/flask find unique ids for each website visitor [duplicate]

I want to build a simple webapp as part of my learning activity. Webapp is supposed to ask for user to input their email_id if it encounters a first time visitor else it remembers the user through cookie and automatically logs him/her in to carry out the functions.
This is my first time with creating a user based web app. I have a blue print in my mind but I am unable to figure out how to implement it. Primarily I am confused with respect to the way of collecting user cookie. I have looked into various tutorials and flask_login but I think what I want to implement is much simpler as compared to what flask_login is implementing.
I also tried using flask.session but it was a bit difficult to understand and I ended up with a flawed implementation.
Here is what I have so far (it is rudimentary and meant to communicate my use case):
from flask import render_template, request, redirect, url_for
#app.route("/", methods= ["GET"])
def first_page():
cookie = response.headers['cookie']
if database.lookup(cookie):
user = database.get(cookie) # it returns user_email related to that cookie id
else:
return redirect_url(url_for('login'))
data = generateSomeData() # some function
return redirect(url_for('do_that'), user_id, data, stats)
#app.route('/do_that', methods =['GET'])
def do_that(user_id):
return render_template('interface.html', user_id, stats,data) # it uses Jinja template
#app.route('/submit', methods =["GET"])
def submit():
# i want to get all the information here
user_id = request.form['user_id']# some data
answer = request.form['answer'] # some response to be recorded
data = request.form['data'] # same data that I passed in do_that to keep
database.update(data,answer,user_id)
return redirect(url_for('/do_that'))
#app.route('/login', methods=['GET'])
def login():
return render_template('login.html')
#app.route('/loggedIn', methods =['GET'])
def loggedIn():
cookie = response.headers['cookie']
user_email = response.form['user_email']
database.insert(cookie, user_email)
return redirect(url_for('first_page'))
You can access request cookies through the request.cookies dictionary and set cookies by using either make_response or just storing the result of calling render_template in a variable and then calling set_cookie on the response object:
#app.route("/")
def home():
user_id = request.cookies.get('YourSessionCookie')
if user_id:
user = database.get(user_id)
if user:
# Success!
return render_template('welcome.html', user=user)
else:
return redirect(url_for('login'))
else:
return redirect(url_for('login'))
#app.route("/login", methods=["GET", "POST"])
def login():
if request.method == "POST":
# You should really validate that these fields
# are provided, rather than displaying an ugly
# error message, but for the sake of a simple
# example we'll just assume they are provided
user_name = request.form["name"]
password = request.form["password"]
user = db.find_by_name_and_password(user_name, password)
if not user:
# Again, throwing an error is not a user-friendly
# way of handling this, but this is just an example
raise ValueError("Invalid username or password supplied")
# Note we don't *return* the response immediately
response = redirect(url_for("do_that"))
response.set_cookie('YourSessionCookie', user.id)
return response
#app.route("/do-that")
def do_that():
user_id = request.cookies.get('YourSessionCookie')
if user_id:
user = database.get(user_id)
if user:
# Success!
return render_template('do_that.html', user=user)
else:
return redirect(url_for('login'))
else:
return redirect(url_for('login'))
DRYing up the code
Now, you'll note there is a lot of boilerplate in the home and do_that methods, all related to login. You can avoid that by writing your own decorator (see What is a decorator if you want to learn more about them):
from functools import wraps
from flask import flash
def login_required(function_to_protect):
#wraps(function_to_protect)
def wrapper(*args, **kwargs):
user_id = request.cookies.get('YourSessionCookie')
if user_id:
user = database.get(user_id)
if user:
# Success!
return function_to_protect(*args, **kwargs)
else:
flash("Session exists, but user does not exist (anymore)")
return redirect(url_for('login'))
else:
flash("Please log in")
return redirect(url_for('login'))
return wrapper
Then your home and do_that methods get much shorter:
# Note that login_required needs to come before app.route
# Because decorators are applied from closest to furthest
# and we don't want to route and then check login status
#app.route("/")
#login_required
def home():
# For bonus points we *could* store the user
# in a thread-local so we don't have to hit
# the database again (and we get rid of *this* boilerplate too).
user = database.get(request.cookies['YourSessionCookie'])
return render_template('welcome.html', user=user)
#app.route("/do-that")
#login_required
def do_that():
user = database.get(request.cookies['YourSessionCookie'])
return render_template('welcome.html', user=user)
Using what's provided
If you don't need your cookie to have a particular name, I would recommend using flask.session as it already has a lot of niceties built into it (it's signed so it can't be tampered with, can be set to be HTTP only, etc.). That DRYs up our login_required decorator even more:
# You have to set the secret key for sessions to work
# Make sure you keep this secret
app.secret_key = 'something simple for now'
from flask import flash, session
def login_required(function_to_protect):
#wraps(function_to_protect)
def wrapper(*args, **kwargs):
user_id = session.get('user_id')
if user_id:
user = database.get(user_id)
if user:
# Success!
return function_to_protect(*args, **kwargs)
else:
flash("Session exists, but user does not exist (anymore)")
return redirect(url_for('login'))
else:
flash("Please log in")
return redirect(url_for('login'))
And then your individual methods can get the user via:
user = database.get(session['user_id'])

How to implement login required decorator in Flask

I have 2 Flask apps (different projects) that work together . One implements some API which uses tokens for auth. The second one consumes the API and makes a web interface for it. Now I have a login function that sends the username and password to the API, and if correct, gets the auth token in return. Once I have the token, I save it to the session of the user and the user should now be considered as logged in/ autheticated. How can I implement the login_required decorator for such a case.
Here is my login function -
def login(self):
response = make_request(BASE_URL + 'login/', clean_data(self.data))
if response.status_code == 200:
session['auth_token'] = response.json().get('auth_token')
return True
return False
How can I make the login_required decorator?
Also I am using Redis to store sessions if that matters.
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
Given that each subsequent request will contain the API token, the decorator should do the following
Accept a generic request. You can use *args and **kargs for that
Extract the token from the header and compare it with the token stored in db (not Redis, but wherever the token generated is stored in the backend)
If authenticated, the *args and **kargs should be passed on to the decorated function
The output of the decorated function should then be returned as is
If the authentication failed, an error message should be returned.
For explanation on decorators, check out this link:
http://thecodeship.com/patterns/guide-to-python-function-decorators/

django custom registration view, redirect fail

I am using Django-registration and I have subclassed the BaseRegistrationView to customised a backend.
However, the HttpResponseRedirect fail to redirect to a new page when the condition is met.
The code is listed below:
class CustomRegistrationView(BaseRegistrationView):
def register(self, request, **cleaned_data):
captchaMatch = False
#check captcha
captchaValue = request.session.get('captcha')
captchaValueSubmitted = cleaned_data['captcha'] + "123"
if captchaValue != captchaValueSubmitted:
return HttpResponseRedirect(reverse('InvitationRequired'))
else:
self.captchaMatch = True
username, email, password = cleaned_data['username'], cleaned_data['email'], cleaned_data['password1']
if Site._meta.installed:
site = Site.objects.get_current()
else:
site = RequestSite(request)
new_user = RegistrationProfile.objects.create_inactive_user(username, email,
password, site)
signals.user_registered.send(sender=self.__class__,
user=new_user,
request=request)
return new_user
def registration_allowed(self, request):
"""
Indicate whether account registration is currently permitted,
based on the value of the setting ``REGISTRATION_OPEN``. This
is determined as follows:
* If ``REGISTRATION_OPEN`` is not specified in settings, or is
set to ``True``, registration is permitted.
* If ``REGISTRATION_OPEN`` is both specified and set to
``False``, registration is not permitted.
"""
return getattr(settings, 'REGISTRATION_OPEN', True)
def get_success_url(self, request, user):
"""
Return the name of the URL to redirect to after successful
user registration.
"""
print "get successful url"
if self.captchaMatch:
return ('registration_complete', (), {})
else:
return HttpResponseRedirect(reverse('InvitationRequired'))
Can anyone help explain why the redirection is executed inside the function register ?
I guess the register method is supposed to return a user or None. Not an HttpRedirect object. Since I'm not familiar with this specific app, try to see how they handle failure on the original method and follow that policy (e.b. return None or raise an Exception).
Also, based on the comments on the get_success_url() method, I would expect the code to be like this:
def get_success_url(self, request, user):
"""
Return the name of the URL to redirect to after successful
user registration.
"""
if self.captchaMatch:
return ('registration_complete', (), {})
else:
return ('InvitationRequired', (), {})
I guess you should not return a HttpResponse instance, as the method is used to get the URL to be redirect to (not the redirection itself).

Categories

Resources