Search code examples
pythonflaskflask-jwt-extended

flask jwt extended protect only specified request methods


I have several general purpose routes in my API:

...
/api/general/user_types
/api/general/dlc_types
...

I want to have the GET method opened for all users without JWT token, but the POST, PUT and DELETE methods only accessible when the JWT token is included in the request.

The problem is that all these methods are allowed on the same route and I serve a different function based on the request method - GET = read, POST = create, PUT = update, DELETE = delete.

Any way to do this without separating the GET method into a separate route?

Here is how my routes are declared:

@general_api.route("/user_types", methods=["GET", "POST", "PUT", "DELETE"])
@jwt_required()
def user_types_crud():
    instance = ConstantsRoutes(UserTypes, "type")
    return crud_routes(request, instance)

The ConstantsRoute builds a CRUD class for the UserTypes table (SQLAlchemy model), and the crud_routes function takes a look at the request.method and returns the proper method (create|read|update|delete).


Solution

  • I don't know of a build in decorator provided by the library. But if using a slightly extended version of the build in decorator is an option for you, you could define one as followed:

    from typing import Any
    from typing import Union
    from typing import Sequence
    
    from functools import wraps
    from flask import request, current_app
    from flask_jwt_extended import verify_jwt_in_request
    
    LocationType = Union[str, Sequence, None]
    
    
    def jwt_required_for_methods(
        protected_methods: list[str] = ["GET", "POST", "PUT", "DELETE"],
        optional: bool = False,
        fresh: bool = False,
        refresh: bool = False,
        locations: LocationType = None,
        verify_type: bool = True
    ) -> Any:
    
        def wrapper(fn):
            @wraps(fn)
            def decorator(*args, **kwargs):
                if request.method in protected_methods:
                    verify_jwt_in_request(
                        optional, fresh, refresh,
                        locations, verify_type
                    )
                return current_app.ensure_sync(fn)(*args, **kwargs)
    
            return decorator
    
        return wrapper
    

    This way the JWT token is verified only if the request method is in the list passed as an argument. Protect your route with this decorator, where needed:

    @general_api.route("/user_types", methods=["GET", "POST", "PUT", "DELETE"])
    @jwt_required_for_methods(['POST', 'PUT', 'DELETE'])
    def user_types_crud():
        instance = ConstantsRoutes(UserTypes, "type")
        return crud_routes(request, instance)
    

    Hope i could help!