I wonder how to make correct redirect to authorization url. For example:
#app.route(rule="/add_package/<path:package_id>", methods=["POST"])
#doc_manager.doc()
#login_required
def add_package_to_list(package_id):
return "package {p_id} added".format(p_id=package_id)
#app.route(rule="/add_data/<path:data_id>", methods=["POST"])
#doc_manager.doc()
#login_required
def add_data_to_list(data_id)
return redirect(url_for("add_package_to_list", package_id=data_id), code=307)
When I try to call add_data_to_list method with authorization data added into request I receive 401 status code.
#davidism: you marked my questin as duplicate, but how can I use login_manager.login_view = 'login' in my case. I don't have method like login. Sorry, but marked it as duplicated doesnt' help me
My code used for authorization
#login_manager.request_loader
def sign_in(self, user_request):
"""
:type user_request: Request
:rtype: AuthenticationData
"""
token = request.headers.get("Authorization")
if token:
return authentication.sign_in(
username=user_request.authorization.get("username"),
password=user_request.authorization.get("password")
).
Related
This Flask endpoint is what I am trying hit with Insomnia with a jwt token on a POST:
#app.route('/', methods=['POST'])
#token_required
def json_payloader():
try:
some code to do stuff...
Poking around the internet no matter what I try I always get back a:
{
"message": "Token is missing!"
}
Bearer authentication token:
OR with authentication set to None and just trying headers with the token this also fails:
Any tips try greatly appreciated.
EDIT token_required function
from flask import Flask, request, jsonify
import flask
from flask.helpers import make_response
import jwt
from functools import wraps
def token_required(f):
#wraps(f)
def decorated(*args, **kwargs):
token = request.args.get('token')
if not token:
return jsonify({'message': 'Token is missing!'}), 403
try:
data = jwt.decode(token, app.config['SECRET_KEY'])
except:
return jsonify({'message': 'Token is invalid'}), 403
return f(*args, **kwargs)
return decorated
Unless you're using a different version of flask-jwt (or flask-jwt-extended) then I believe the correct function decorator is #jwt_required()
Seems like you're using JWTs. So, the correct decorator to be used is #jwt_required.
Please see the example from https://flask-jwt-extended.readthedocs.io/en/stable/basic_usage/#basic-usage .
#app.route("/protected", methods=["GET"])
#jwt_required()
def protected():
# Access the identity of the current user with get_jwt_identity
current_user = get_jwt_identity()
return jsonify(logged_in_as=current_user), 200
If you want to create your own implementation, you can retrieve it like
auth_header = request.headers.get("Bearer", "").strip().strip(",")
I want to make auth layer in flask app . I was using flask-jwt-extended for jwt auth so for that i have to mentioned #jwt_requied decorator for each protected route . so i dont want to do that for each protected route. I thought to define a function and make that to execute before each request by using #app.before_request. i want auth to happen in this layer.
def check_user_token():
if 'x-access-token' in request.headers:
token = request.headers['x-access-token']
if not token:
return jsonify({'message': 'Token is missing'}), 401
try:
data = jwt.decode(token, app.config['SECRET_KEY'])
current_user = UserDetails.query.filter_by(username=data['username']).first()
except:
return jsonify({'message': 'Token is in-valid'}), 401
return current_user
So this the function i want to call before each request to check auth.
#app.before_request
def before_calback():
#want to call that check_user_token() from here and
#also dont want to call that for 'login' route .
#Once i will get some response from check_user_token()
#based on that want to proceed further
#and here i dont know to do this.
Basically my qns is how to do authentication in #app.before_request def before_calback() ?
Can you access check_user_token()?
It would be like this
#app.before_request
def before_calback():
check_user_token()
If you can post the some code, it would be easier to help you.
Okay, I am struggling with this problem for 2 days now. When I am manually posting data in the form in the browser, everything works fine, and I am getting the flash message that should say 'Thanks...'. While testing my Flask application, this test does not pass because I am getting a 400 Bad Request error while sending a post request on my Flask form. To be clear, I am using Flask-Mail and WTForms for the form, and my application is dockerized and also running redis and celery. I am kinda new to these stuff, so if my question is not clear enough, please be kind and tell me if I should provide more detailed info. Thanks, and here is the relevant code and the error that is shown while testing with py.test. And sorry about the links, I am still not allowed to post pictures on StackOverflow.
The error code:
Pytest Assertion Error
contact/forms.py:
from flask_wtf import FlaskForm
from wtforms import TextAreaField, StringField
from wtforms.validators import DataRequired, Length, Email
class ContactForm(FlaskForm):
email = StringField("What's your e-mail address?",
[Email(), DataRequired(), Length(3, 254)])
message = TextAreaField("What's your question or issue?",
[DataRequired(), Length(1, 8192)])
contact/views.py:
from flask import (
Blueprint,
flash,
redirect,
request,
url_for,
render_template)
from flexio.blueprints.contact.forms import ContactForm
contact = Blueprint('contact', __name__, template_folder='templates')
#contact.route('/contact', methods=['GET', 'POST'])
def index():
form = ContactForm()
if form.validate_on_submit():
# This prevents circular imports.
from flexio.blueprints.contact.tasks import deliver_contact_email
deliver_contact_email(request.form.get('email'),
request.form.get('message'))
flash('Thanks, expect a response shortly.', 'success')
return redirect(url_for('contact.index'))
return render_template('contact/index.html', form=form)
contact/tasks.py:
from lib.flask_mailplus import send_template_message
from flexio.app import create_celery_app
celery = create_celery_app()
#celery.task()
def deliver_contact_email(email, message):
"""
Send a contact e-mail.
:param email: E-mail address of the visitor
:type user_id: str
:param message: E-mail message
:type user_id: str
:return: None
"""
ctx = {'email': email, 'message': message}
send_template_message(subject='[Flexio] Contact',
sender=email,
recipients=[celery.conf.get('MAIL_USERNAME')],
reply_to=email,
template='contact/mail/index', ctx=ctx)
return None
lib/tests.py:
def assert_status_with_message(status_code=200, response=None, message=None):
"""
Check to see if a message is contained within a response.
:param status_code: Status code that defaults to 200
:type status_code: int
:param response: Flask response
:type response: str
:param message: String to check for
:type message: str
:return: None
"""
assert response.status_code == status_code
assert message in str(response.data)
tests/contact/test_views.py:
from flask import url_for
from lib.tests import assert_status_with_message
class TestContact(object):
def test_contact_page(self, client):
""" Contact page should respond with a success 200. """
response = client.get(url_for('contact.index'))
assert response.status_code == 200
def test_contact_form(self, client):
""" Contact form should redirect with a message. """
form = {
'email': 'foo#bar.com',
'message': 'Test message from Flexio.'
}
response = client.post(url_for('contact.index'), data=form,
follow_redirects=True)
assert_status_with_message(200, response, 'Thanks')
Your browser will have requested the form with a GET request first, and thus have been given a CSRF token as a cookie and as a hidden form element in the form. When you then submit the form, the CSRF protection passes.
Your test doesn't make a GET request nor does it use the form fields from the form that such a request makes, so your POST request is missing both the cookie and the hidden field.
In a test, you could just disable CSRF protection by setting the WTF_CSRF_ENABLED parameter to False:
app.config['WTF_CSRF_ENABLED'] = False
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/
Right now, my webapp has a full auth system implemented through Flask-Login. But say that I have this controller:
#app.route("/search_query")
#login_required
def search_query():
query = request.args.get("query")
return jsonify(search(query))
This works great when you login to the page and make searches against it, if you're authenticated it goes through, otherwise it routes you back to the login page (through an unauthorized_handler function).
#lm.unauthorized_handler
def unauthorized():
return redirect(url_for("login"))
The problem is, I want to be able to use some form of simple HTTP authentication for this without having two separate auth systems so that I can make REST API calls against it. In short, I want to avoid having to use both Flask-Login and HTTPBasicAuth, and just use Flask-Login to achieve simple authentication, is this possible through Flask-Login?
Use the flask-login's request_loader to load from request auth header. You don't need to implement the decorator yourself, and current_user will point to the user loaded.
Example:
login_manager = LoginManager()
#login_manager.request_loader
def load_user_from_header():
auth = request.authorization
if not auth:
return None
user = User.verify_auth(auth.username, auth.password):
if not user:
abort(401)
return user
With that, you can mark a view as login required by:
#app.route('/user/info')
#login_required
def user_info():
return render_template('user_info.html', current_user.name)
If you looking for users to authenticate and make REST API calls they are going to have to send their credentials with every api call.
What you could do is a new decorator that checks for the auth header and authenticate the user.
Please refer to the entire app that I've posted on codeshare for more info:
http://codeshare.io/bByyF
Example using basic auth:
def load_user_from_request(f):
#wraps(f)
def decorated(*args, **kwargs):
auth = request.authorization
if current_user.is_authenticated:
# allow user through
return f(*args, **kwargs)
if auth and check_auth(auth.username, auth.password):
return f(*args, **kwargs)
else:
return bad_auth()
return decorated
With that code you can now decorate your views to check for the header.
#app.route("/search_query")
#load_user_from_request
def search_query():
query = request.args.get("query")
return jsonify(search(query))
you can curl my example app like this:
curl --user admin#admin.com:secret http://0.0.0.05000/success