Adding resources with jwt_required? - python

I've created an API using flask, where the authentication is all working fine using flask_jwt_extended.
However if I add a resource that has a jwt_required decorator I get this error.
File "/Library/Python/2.7/site-packages/flask_jwt/__init__.py", line 176, in decorator
_jwt_required(realm or current_app.config['JWT_DEFAULT_REALM'])
KeyError: 'JWT_DEFAULT_REALM'
Example resource:
class Endpoint(Resource):
#jwt_required()
def get(self):
return {"State": "Success"}
Initialising the app:
app = Flask(__name__)
api = Api(app)
Adding the resource:
api.add_resource(resource_class, "/myEndpoint")
The only way I can get it to work is to define the Endpoint class in the same file as the API.
I think I need someway to pass the Realm into the endpoint class and have use the optional parameter on jwt_required to set the Realm.

Discovered the issue, in the resource I was importing the jwt_required:
from flask_jwt_extended import jwt_required
However I needed to import jwt_required from the class that where JWT was initalized.

I think you forgot to initialize JWT instance. You can do it in 2 ways. First:
from flask import Flask
from flask_jwt import jwt_required, JWT
from flask_restful import Resource, Api
class Endpoint(Resource):
#jwt_required()
def get(self):
return {"State": "Success"}
app = Flask(__name__)
app.config['SECRET_KEY'] = 'super-secret'
def authenticate(username, password):
# you should find user in db here
# you can see example in docs
user = None
if user:
# do something
return user
def identity(payload):
# custom processing. the same as authenticate. see example in docs
user_id = payload['identity']
return None
# here what you need
jwt = JWT(app, authenticate, identity)
api = Api(app)
api.add_resource(Endpoint, '/myEndpoint')
if __name__ == '__main__':
app.run(debug=True)
app.run(host='0.0.0.0')
The second way is update our configuration of application. Just change:
jwt = JWT(app, authenticate, identity)
To:
app.config.update(
JWT=JWT(app, authenticate, identity)
)
Let's open our route. You will see:
{
"description": "Request does not contain an access token",
"error": "Authorization Required",
"status_code": 401
}
Hope it helps.

Related

Flask jwt POST data to endpoint with a token

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(",")

Flask app doen't register jwt.user_lookup_loader, Flask-JWT-Extended

I have a Flask app with blueprints. It worked just fine, but than I decided to use flask_jwt_extended to handle tokens. It is said in docs that I can decorate method with jwt.user_lookup_loader to have current_user working. But for some reason calling current_user ends up with an error:
You must provide a `#jwt.user_lookup_loader` callback to use this method
But it is there, in the same blueprint. There is also a method, decorated with #jwt.user_identity_loader and it works perfectly well.
Here is a simplified version of my code:
from . import rpc, jwt
from flask_jwt_extended import current_user, jwt_required
bp = Blueprint('login_bp', __name__)
#jwt.user_identity_loader
def _user_identity_lookup(user):
return user.id
#jwt.user_lookup_loader
def _user_lookup_callback(_jwt_header, jwt_data):
identity = jwt_data["sub"]
user = rpc.cache_service.get_user(identity)
if user is None:
return None
return UserSchema().load(user)
#jwt_required()
#bp.route("/logout", methods=['POST'])
def logout():
rpc.cache_service.forget_user(current_user.id)
return {"status": "OK"}
jwt here is JWTManager, initialized with my app:
jwt = JWTManager()
def create_app():
app = Flask(__name__, instance_relative_config=False)
app.config.from_mapping(JWT_SECRET_KEY=os.environ["JWT_SECRET_KEY"])
...
jwt.init_app(app)
...
with app.app_context():
from . import login_bp
app.register_blueprint(login_bp.bp)
This code is basicly from the documentation examples:
https://flask-jwt-extended.readthedocs.io/en/stable/automatic_user_loading/
and I can't see what the problem might be (
Link to the repo:
https://github.com/GreenBlackSky/COIN/blob/master/api_app/app/login_bp.py
Your decorators are in the wrong order. #bp.route() needs to come before #jwt_required(), otherwise it tries to evaluate the logout method when a request comes in before it decodes the JWT in the request.

Flask RESTFUL, creating a cookie from an endpoint (restful.Resource)

I'm currently working on creating a Cookie from an endpoint. As my backend and frontend only interacts via RESTful endpoints, is there anyway I can create a cookie when the frontend calls my backend's endpoint?
flask.make_response.set_cookie() doesn't seem to work for me. Also, I can't use app.route('/') to set my cookie either.
You can do this with Set-Cookie header returning with a response.
from flask import Flask
from flask_restful import Resource, Api
app = Flask(__name__)
api = Api(app)
class HelloWorld(Resource):
def get(self):
return {'task': 'Hello world'}, 200, {'Set-Cookie': 'name=Nicholas'}
api.add_resource(HelloWorld, '/')
if __name__ == '__main__':
app.run(debug=True)
Setting the header in the response tuple is one of the standard approaches. However, keep in mind that the Set-Cookie header can be specified multiple times, which means that a python Dictionary won't be the most effective way to set the cookies in the response.
According to the flask docs the header object can also be initialized with a list of tuples, which might be more convenient in some cases.
Example:
from flask import Flask
from flask_restful import Api, Resource
app = Flask(__name__, static_url_path='')
api = Api(app)
class CookieHeaders(Resource):
def get(self):
# Will only set one cookie "age = 23"
return { 'message' : 'Made with dict'}, 200, { 'Set-Cookie':'name=john', 'Set-Cookie':'age=23' }
def post(self):
# Will set both cookies "name = john" and "age = 23"
headers = [ ('Set-Cookie', 'name=john'), ('Set-Cookie', 'age=23') ]
return { 'message' : ' Made with a list of tuples'}, 200, headers
api.add_resource(CookieHeaders, '/')
if __name__ == '__main__':
app.run(debug=True)
The GET call will only set 1 cookie (due to the lack of multi-key support in python dictionaries), but the POST call will set both.
Flask has a #after_this_request callback decorator. (see: http://flask.pocoo.org/docs/1.0/api/#flask.after_this_request)
so you can set your cookies in it
from flask import after_this_request
from flask_restful import Resource
class FooResource(Resource):
def get(self):
#after_this_request
def set_is_bar_cookie(response):
response.set_cookie('is_bar', 'no', max_age=64800, httponly=True)
return response
return {'data': 'foooo'}
or even
from flask import after_this_request, request
from flask_restful import Resource, abort
class FooResource(Resource):
def get(self):
self._check_is_bar()
return {'data': 'foooo'}
def _check_is_bar(self)
if request.cookies.get('is_bar') == 'yes':
abort(403)
#after_this_request
def set_is_bar_cookie(response):
response.set_cookie('is_bar', 'no', max_age=64800, httponly=True)
return response

How to protect custom endpoints using BasicAuth?

Say I have enabled authentication to the resources using BasicAuth:
class MyBasicAuth(BasicAuth):
def check_auth(self,username,password,allowed_roles,resource,method):
return username == 'secretusername' and password == 'secretpass'
I also have custom routes which are used to manage documents from a HTML view. How do I use the same MyBasicAuth to protect the all the custom routes? I also need to implement logic which authenticates using the above MyBasicAuth.
Please help me with this. It's for personal use, so I preferred hard coding the username and password.
If you are trying to use a custom end-point Authentication you will find it difficult as mentioned here:
https://github.com/pyeve/eve/issues/860
I ended up writing a wrapper to get around the issue of 'resource' not being passed to 'requires_auth':
def auth_resource(resource):
def fdec(f):
#wraps(f)
def wrapped(*args, **kwargs):
return f(resource=resource, *args, **kwargs)
return wrapped
return fdec
This way you can define in your DOMAIN an authentication class:
DOMAIN = {
'testendpoint'= {'authentication':MyCustomAuthetication},
'otherendpoints'=...
And in my app I have wrapped the requires_auth decorator and added this as a authentication resource.
#app.route('/testendpoint/<item>', methods=['GET'])
#auth_resource('testendpoint')
#requires_auth('item')
def my_end_point_function(*args, **kwargs):
dosomthinghere
As long as an authentication class is defined in the settings file for an endpoint, this also allows you to reuse any authentication defined in another endpoint which may be handy if you want to make sure all the endpoints use the same authentication.
You can leverage the requires_auth decorator which is used internally by Eve itself. That way, your auth class will also be used to protect your custom routes:
from eve import Eve
from eve.auth import requires_auth
app = Eve()
#app.route('/hello')
#requires_auth('resource')
def hello_world():
return 'Hello World!'
if __name__ == '__main__':
app.run()
If you are using flask blueprints for your custom routes, you can add a before request function for your blueprint to do that.
First, create a function to check authentication from blueprints. You need to get the Authorization header from the flask request by yourself, like this:
from flask import request, abort, current_app
from werkzeug.http import parse_authorization_header
def check_blueprint_auth():
if 'Authorization' not in request.headers:
print('Authorization header not found for authentication')
return abort(401, 'Authorization header not found for authentication')
header = parse_authorization_header(request.headers['Authorization'])
username = None if header is None else header['username']
password = None if header is None else header['password']
return username == 'secretusername' and password == 'secretpass'
Then, you can set this function to be called before each blueprint's request. Below is an example of a blueprint definition, setting the before_request function:
from flask import Blueprint, current_app as app
# your auth function
from auth import check_blueprint_auth
blueprint = Blueprint('prefix_uri', __name__)
# this sets the auth function to be called
blueprint.before_request(check_blueprint_auth)
#blueprint.route('/custom_route/<some_value>', methods=['POST'])
def post_something(some_value):
# something
Finally, you need to bind the blueprint with your eve app. An example on how to bind blueprints, taken in part from here:
from eve import Eve
# your blueprint
from users import blueprint
from flask import current_app, request
app = Eve()
# register the blueprint to the main Eve application
app.register_blueprint(blueprint)
app.run()
Hope that helps.

flask-restful having a get/<id> and post with json in the same class

The get method on user works if the # api.add_resource(User, '/user/')
line is uncommented, and the other api.add_resource is.
The inverse of that is true to make the post method work.
How can I get both of these paths to work?
from flask import Flask, request
from flask.ext.restful import reqparse, abort, Api, Resource
import os
# set the project root directory as the static folder, you can set others.
app = Flask(__name__)
api = Api(app)
class User(Resource):
def get(self, userid):
print type(userid)
if(userid == '1'):
return {'id':1, 'name':'foo'}
else:
abort(404, message="user not found")
def post(self):
# should just return the json that was posted to it
return request.get_json(force=True)
api.add_resource(User, '/user/')
# api.add_resource(User, '/user/<string:userid>')
if __name__ == "__main__":
app.run(debug=True)
Flask-Restful supports registering multiple URLs for a single resource. Simply provide both URLs when you register the User resource:
api.add_resource(User, '/user/', '/user/<userid>')

Categories

Resources