I am building a Flask-based REST API and using Flask-JWT to handle JWT auth. I also want to use the built in roles management with Flask-Security. However, Flask-Security's @roles_required() decorator assumes I am showing a Flask view when it fails.
Here is my token endpoint (which is working as I want):
$ http POST localhost:5000/auth/token username='test' password='test' HTTP/1.0 200 OK Content-Length: 192 Content-Type: application/json Date: Sun, 08 Nov 2015 17:45:46 GMT Server: Werkzeug/0.10.4 Python/3.5.0 { "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE0NDcwMDQ3NDYsIm5iZiI6MTQ0NzAwNDc0NiwiZXhwIjoxNDQ3MDA1MDQ2LCJpZGVudGl0eSI6MX0.RFIeaLuvJNM9fDjFYFQ7sh_WaDVU-_aM7e46tVJzlBQ" }
Here is a successful response to a resource that does not have any role requirement (using only @jwt_required) This is also working as I want:
$http GET localhost:5000/protected Authorization:'JWT eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE0NDcwMDQ3NDYsIm5iZiI6MTQ0NzAwNDc0NiwiZXhwIjoxNDQ3MDA1MDQ2LCJpZGVudGl0eSI6MX0.RFIeaLuvJNM9fDjFYFQ7sh_WaDVU-_aM7e46tVJzlBQ' HTTP/1.0 200 OK Content-Length: 25 Content-Type: text/html; charset=utf-8 Date: Sun, 08 Nov 2015 17:46:24 GMT Server: Werkzeug/0.10.4 Python/3.5.0 <models.User[email=test]>
When I do the same for a resource that has roles required (such as admin in this example), it seems to assume I have a page to display such as /login which I do not since it is a headless REST API:
$ http GET localhost:5000/admin Authorization:'JWT eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE0NDcwMDQ3NDYsIm5iZiI6MTQ0NzAwNDc0NiwiZXhwIjoxNDQ3MDA1MDQ2LCJpZGVudGl0eSI6MX0.RFIeaLuvJNM9fDjFYFQ7sh_WaDVU-_aM7e46tVJzlBQ' HTTP/1.0 302 FOUND Content-Length: 209 Content-Type: text/html; charset=utf-8 Date: Sun, 08 Nov 2015 17:46:43 GMT Location: http://localhost:5000/ Server: Werkzeug/0.10.4 Python/3.5.0 Set-Cookie: session=eyJfZmxhc2hlcyI6W3siIHQiOlsiZXJyb3IiLCJZb3UgZG8gbm90IGhhdmUgcGVybWlzc2lvbiB0byB2aWV3IHRoaXMgcmVzb3VyY2UuIl19XX0.CSEcAw.pjwXLeSWUsORXR-OU5AfFvq6ESg; HttpOnly; Path=/ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> <title>Redirecting...</title> <h1>Redirecting...</h1> <p>You should be redirected automatically to target URL: <a href="/">/</a>. If not click the link.
I know Flask-Security uses Flask-Principal behind the scene for its roles management (@roles_required, etc.) and it ties into the RoleMixin and UserMixin for the datastore which is very nice. However, if there is no way to get Flask-Security to just allow the resource through without using my JWT header, then maybe the best bet is to build my own decorators which uses Flask-Principal to manage the roles.
Does anyone have any experience with this? The idea is that the entire front end can and will be built in whatever language we need and that means it may not be Flask's templates/views which is what Flask-Security appears to be doing.
Thank you for any insight anyone can provide!
To do that, change the endpoint to /user and then in the headers section, add a field as x-access-token and add the JWT token in the value and click on Send. You will get the list of users as JSON. So, this is how you can perform authentication with JWT in Flask.
Integrate the Security Library The code for a working OAuth secured Python Flask API is provided below: The OAuth filter is configured to run before API requests. The filter verifies the token signature and the expected issuer / audience claims. API routes can then access JWT claims in the request object.
Instead of a redirect, you would want to respond with an HTTP status code 403.
Your best bet is indeed to create your own decorator to manage the roles, and move away from using Flask-Security
entirely.
The author of Flask-Security
has mentioned that there are better ways to secure APIs, and it makes even more sense as the library is not maintained.
Flask-JWT
or Flask-JWT-Extended
are perfect candidates for this task. The former would require a bit more boilerplate to get things going. There is a stale PR suggesting an API to support roles, that you could use to create your own decorator if you decide to go with Flask-JWT
.
The Flask-JWT-Extended
docs suggest a simpler solution that might fit your case. You should follow the custom decorators section of the documentation for the full example, but here's the decorator in the nutshell:
from functools import wraps from flask import jsonify from flask_jwt_extended import ( verify_jwt_in_request, get_jwt_claims ) def admin_required(fn): @wraps(fn) def wrapper(*args, **kwargs): verify_jwt_in_request() claims = get_jwt_claims() if claims['roles'] != 'admin': return jsonify(msg='Admins only!'), 403 else: return fn(*args, **kwargs) return wrapper
This code looks for a roles
claim in the JWT, and returns a 403 response if it is not admin
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With