3

I am developing a Flask 0.12 app with Blueprints and I am trying to use Flask-JWT (0.3.2) for authentication.

None of the tutorials that I can see for Flask-JWT use Blueprints.

There is a usually a block of code in each example, that does something like this:

app = Flask(__name__)
jwt = JWT(app, authenticate, identity)

In a blueprinted flask app the structure tends to be more like this:

__init__.py:

from swarm.controllers.main import main
from swarm.controllers.grid import grid
from swarm.controllers.address import address

def create_app(object_name):
    app = Flask(__name__)
    ...
    app.register_blueprint(main)
    app.register_blueprint(grid)
    app.register_blueprint(address)

controllers/main.py:

main = Blueprint('main', __name__)

controllers/grid.py:

grid = Blueprint('grid', __name__)

controllers/address.py:

address = Blueprint('address', __name__)

How would I reference Flask-JWT and use its decorators in the controllers?

It might prove easier to answer this question by showing how to add JWT decorators to a standard example such as blueprintexample in the flask source code o Michał Karzyński's REST API demo.

Nathan
  • 7,853
  • 4
  • 27
  • 50
ChrisGuest
  • 3,398
  • 4
  • 32
  • 53
  • A blueprint just enables you to organize your code around conceptual chunks of your application, it actually is not different really at all from a normal Flask app. The code you are showing above from `main.py, grid.py, address.py` you should put into `__init__.py` files to make them proper python modules. instantiate Flask-JWT in your main `__init__.py` file, then import it from the blueprint you want to use and decorate those routes. I usually have an __auth__ blueprint that manages login, logout, callback, etc. w/ my authentication service (using JWT). – abigperson Feb 24 '17 at 12:20
  • I didn't quite follow why each controller needs its own controllers/mycontroller/__init__.py rather than just controllers/mycontroller.py – ChrisGuest Feb 27 '17 at 04:40
  • I have tried your approach & I seem to get circular references in my code. – ChrisGuest Feb 27 '17 at 04:43
  • For example, tried it with this repo: https://github.com/postrational/rest_api_demo and it fell in a heap using this approach: ```app.py: jwt = JWT(app, authenticate, identify)``` ```api/grid/endpoints/posts.py: from rest_api_demo.app import jwt``` – ChrisGuest Feb 27 '17 at 04:46
  • NOTE for newcomers: use instead, the Flask-JWT-Extended, which is found https://flask-jwt-extended.readthedocs.io/en/stable/, has been updated more recently and has better documentation. – Alexey Nikonov Mar 21 '21 at 14:24

1 Answers1

7

I've built a simple example that enables use of Flask-JWT decorators.

The file structure:

server
├── app
|   ├── admin # blueprint
|   |   ├── __init__.py
|   |   ├── views.py
|   ├── __init__.py
├── config.py
└── run.py

First, create a blueprint called admin and import its views

# file: server/app/admin/init.py
from flask import Blueprint

admin = Blueprint('admin', __name__)

from . import views

The admin blueprint has one view. Here, we import the admin blueprint and some flask_jwt stuff so we can properly access their decorators:

from flask_jwt import jwt_required, current_identity
from . import admin

# file: server/app/admin/views.py
@admin.route('/protected')
@jwt_required()
def protected():
    return '%s' % current_identity

Now create the flask app, create the JWT instance, and register the admin blueprint to the flask application:

# file: server/app/__init__.py
from flask import Flask
from flask_jwt import JWT
from werkzeug.security import safe_str_cmp

from .admin import admin

# skipping over jwt authenticate() and identity() creation
# https://pythonhosted.org/Flask-JWT/
# ...

app = Flask(__name__)

app.config.from_object('config')

jwt = JWT(app, authenticate, identity)

app.register_blueprint(admin, url_prefix='/admin')

And finally, set up your script manager:

from flask_script import Manager, Server

from server.app import app

manager = Manager(app)

# Turn on debugger by default and reloader
manager.add_command("runserver", Server(
    use_debugger = True,
    use_reloader = True,
    host = '0.0.0.0',
    port = 5000)
)

if __name__ == "__main__":
    manager.run()

Now, if we go to our /admin/protected url, we will see that JWT is up and running:

{
  "description": "Request does not contain an access token",
  "error": "Authorization Required",
  "status_code": 401
}

I hope this helps!

Crispin
  • 2,070
  • 1
  • 13
  • 16
  • Thanks for a very detailed & clear answer. I first created your app & got it working and then put the appropriate changes in my app. All is Good. – ChrisGuest Mar 02 '17 at 01:59
  • One other thing that I noticed that I had been doing wrong was putting the @jwt_required decorator around the API class methods rather than around the class itself. This is the correct approach: – ChrisGuest Mar 02 '17 at 02:02
  • 1
    `@jwt_required()` `@api.route('/api/user/')` `class User(Resource):` – ChrisGuest Mar 02 '17 at 02:02
  • @ChrisGuest `api.route` must be the outermost decorator. See https://stackoverflow.com/a/31757446/5819113 – bluesmonk Jun 13 '19 at 14:00
  • how to write a revoke token method in common for all registered blueprints controllers – Ashok Sri Jun 25 '19 at 04:20