-
Notifications
You must be signed in to change notification settings - Fork 73
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add authorization to header? #36
Comments
I didn't try, but I think this syntax should avoid the error. @blp.doc(parameters=[{'name': 'Authorization', 'in': 'header', 'description': 'Authorization: Bearer <access_token>', 'required': 'true'}]) However I think this is wrong, because this list of parameters will replace the list that is generated automatically rather than extend it. I think the proper way to do that would be to create your own decorator, extending It shouldn't be complicated. Tell me how it goes, and feel free to ask if this is unclear. |
Thanks for your suggestion @lafrech I'm still new to python, this is the code I tried def doc_jwt_required(arg):
@wraps(func)
def wrapper(func):
@jwt_required
def inner(*args, **kwargs):
parameters = [{
'name': 'Authorization',
'in': 'header',
'description': 'Authorization: Bearer <access_token>',
'required': 'true'
}]
func._apidoc = getattr(func, '_apidoc', {})
func._apidoc.setdefault('parameters', []).append(parameters)
func(*args, **kwargs)
return inner
return wrapper However I checked that now all my parameters are gone for the route that implementing above decorator, could you help me what's wrong with it |
Maybe something like this would work (I didn't test): from functools import wraps
def doc_jwt_required():
@wraps
def wrapper(func):
parameters = [{
'name': 'Authorization',
'in': 'header',
'description': 'Authorization: Bearer <access_token>',
'required': 'true'
}]
func._apidoc = getattr(func, '_apidoc', {})
func._apidoc.setdefault('parameters', []).append(parameters)
return jwt_required(func)
return wrapper Tell me if it works. You can subclass @blp.doc_jwt_required
def view_function(): |
Here's the decorator I created for use with flask_httpauth @dels07 def doc_login_required(func):
# 'Decorate' the function with the real authentication decorator
auth_required_func = auth.login_required(func)
# Create the wrapped function. This just calls the 'decorated' function
@wraps(func)
def wrapper(*args, **kwargs):
return auth_required_func(*args, **kwargs)
# Update the api docs on the wrapped function and return it to be
# further decorated by other decorators
parameters = {
'name': 'Authorization',
'in': 'header',
'description': 'Authorization: Bearer <access_token>',
'required': 'true'
}
wrapper._apidoc = getattr(func, '_apidoc', {})
wrapper._apidoc.setdefault('parameters', []).append(parameters)
return wrapper We already had lots of class DocHTTPTokenAuth(HTTPTokenAuth):
def login_required(self, func):
# 'Decorate' the function with the real authentication decorator
auth_required_func = super().login_required(func)
# Create the wrapped function. This just calls the 'decorated' function
@wraps(func)
def wrapper(*args, **kwargs):
return auth_required_func(*args, **kwargs)
# Update the api docs on the wrapped function and return it to be
# further decorated by other decorators
parameters = {
'name': 'Authorization',
'in': 'header',
'description': 'Authorization: Bearer <access_token>',
'required': 'true'
}
wrapper._apidoc = getattr(func, '_apidoc', {})
wrapper._apidoc.setdefault('parameters', []).append(parameters)
return wrapper Then instead of Hope this helps. |
Also, just as a heads up, depending on the auth library ( in my case, flask_httpauth ), the @auth.login_required decorator has to go at the top of your decorators, otherwise it tries to pass it's Flask Response object to @blp.response, which then bombs. So you want: @auth.login_required
@blp.response(MySchema)
def do_something():
return {} |
There's been discussions about allowing As a general rule, I'd always place authentication/authorization logic on top to drop the request asap if the client is not authorized. |
Closing this for now. @dels07, feel free to comment if you're still stuck with this. |
Could somebody please further explain how to achieve this like @sjmh did? I think a small section in the documentation would also be very helpful. I tried to do what @sjmh did and it only kind of worked. ReDoc displays the auth token as parameter but SwaggerUi errors with |
Indeed, the decorator proposed above adds an auth parameter to the resource. To add a security scheme, use the No time right now to elaborate. Hope this helps. |
Hoping this helps others in the future but @DavidM42, I created an API where the entire application required the bearer token. In order to do this, I set the API_SPEC_OPTIONS flask configuration variable before instantiating my flask-smorest API to the following: app.config['API_SPEC_OPTIONS'] = {
'security':[{"bearerAuth": []}],
'components':{
"securitySchemes":
{
"bearerAuth": {
"type":"http",
"scheme": "bearer",
"bearerFormat": "JWT"
}
}
}
}
api = flask_smorest.Api(app) This gave me the ability to set the token at the application level in the swagger ui: Since I wanted to lock down all paths I did this for the flask app from flask_jwt_extended import verify_jwt_in_request
@app.before_request
def before_jwt():
verify_jwt_in_request() This would 401 if the jwt is not present and valid. |
Thanks for sharing. Note that unless you really need to configure this in app config, you could use the dedicated components method to pass the security scheme at init (see https://apispec.readthedocs.io/en/stable/special_topics.html#documenting-security-schemes): api.spec.components.security_scheme(
"bearerAuth", {"type":"http", "scheme": "bearer", "bearerFormat": "JWT"}
) Not a huge benefit, I admit. Well, this abstracts OpenAPI version, but one probably doesn't need that. |
Note that by using @lafrech suggestion, you still need to add:
Otherwise SwaggerUI won't add Authorization header. |
Here's a link to a project in which I overload flask-httpauth's login_required to create a decorator that does the auth and documents both the 401 header and the fact that a basic auth is performed. https://github.com/sigopti/pyodhean-server/blob/master/pyodhean_server/api.py I also add the basic auth schema to the components. |
Hello, hoping this helps! In case anyone would stumble on this. I found a neat solution just to mark certains routes with authorized swagger button. This pattern is base on some fastapi workaround in which I am also using. This also decouples your stuff from flask-jwt-extended. In your config to enable the lock button as mentioned in the previous posts. API_SPEC_OPTIONS = {
"components": {
"securitySchemes": {
"Bearer Auth": {
"type": "apiKey",
"in": "header",
"name": "Authorization",
"bearerFormat": "JWT",
"description": "Enter: **'Bearer <JWT>'**, where JWT is the access token",
}
}
},
} And then in your app factory. You can add this fix for the spec. def create_app(config_name: Optional[str] = None):
app = Flask("webapp")
if not config_name:
config_name = os.getenv("FLASK_CONFIG", "default")
app.config.from_object(config[config_name])
register_extensions(app)
register_blueprints(app)
return app
def register_extensions(app):
"""Register Flask extensions."""
jwt.init_app(app)
api.init_app(app)
return None
def register_blueprints(app):
"""Register Flask blueprints."""
api.register_blueprint(users.views.bp, url_prefix="/api/user")
for path, items in api.spec._paths.items():
for method in items.keys():
if api.spec._paths[path][method].get("authorize", False):
api.spec._paths[path][method]["security"] = [{"Bearer Auth": []}] Then configure your routes like this.
This way you'll get selective lock button. |
Based on @cmabastar solution I have written an improved decorator. Add the authentication schemes to your documentation: This is the same definition as explained in previous posts. API_SPEC_OPTIONS = {
"components": {
"securitySchemes": {
"Bearer Auth": {
"type": "apiKey",
"in": "header",
"name": "Authorization",
"bearerFormat": "JWT",
"description": "Enter: **'Bearer <JWT>'**, where JWT is the access token",
}
}
},
} Create a decorator for JWT based authentication: This decorator is a new version, which adds the authentication schema without postprocessing in the app. from copy import deepcopy
from functools import wraps
def jwt_required(func):
@wraps(func)
def decorator(*args, **kwargs):
# validate the request or call some other methods
return func(*args, **kwargs)
decorator._apidoc = deepcopy(func, '_apidoc', {})
decorator._apidoc.setdefault('manual_doc', {})
decorator._apidoc['manual_doc']['security'] = [{"Bearer Auth": []}]
return decorator Add the decorator to the view: Only the created @bp.route("/whoami")
class WhoAmI(MethodView):
@jwt_required()
def get(self):
return {"test": "hi"} |
I'm getting:
|
Replace Also, to complete @manfred-kaiser answer, for those who want to use the from copy import deepcopy
from functools import wraps
from flask_jwt_extended import jwt_required
def jwt_required_with_doc(*args, **kwargs):
def decorator(func):
@wraps(func)
def wrapper(*f_args, **f_kwargs):
return jwt_required(*args, **kwargs)(func)(*f_args, **f_kwargs)
wrapper._apidoc = deepcopy(getattr(func, "_apidoc", {}))
wrapper._apidoc.setdefault('manual_doc', {})
wrapper._apidoc['manual_doc']['security'] = [{"Bearer Auth": []}]
return wrapper
return decorator |
The issue I found while using the decorator provided by @Vyko is that it does not add the BearerAuth token to the requests, but when I add it to all routes using |
Hi if any one is using flask_smorest then this solution might work, since it worked for me @auth_blueprint.route("/user")
class UserDetails(MethodView):
"""Service to get the details of the user based on the id of the user"""
@auth_blueprint.response(200, UserDetailsSchema)
@jwt_required()
@auth_blueprint.doc(
security=[{"bearerAuth": []}],
components={
"securitySchemes": {
"bearerAuth": {
"type": "http",
"scheme": "bearer",
"bearerFormat": "JWT",
}
}
},
)
def get(self):
"""Service to get the details of the user based on the id of the user""" # noqa
try:
jwt = get_jwt()
user_id = jwt["sub"]["id"]
user_email = jwt["sub"]["email"] # noqa
user = User.query.filter(User.id == user_id).first()
if not user:
raise NotFound(404, "User not found")
return user, 200
except CustomError as e:
abort(e.status_code, message=str(e.message))
except SQLAlchemyError as e:
abort(500, message=f"Database error: {e}")
except Exception as e:
abort(500, message=str(e)) in the code above I've added @auth_blueprint.doc(
security=[{"bearerAuth": []}],
components={
"securitySchemes": {
"bearerAuth": {
"type": "http",
"scheme": "bearer",
"bearerFormat": "JWT",
}
}
},
) to show the lock icon and to get the jwt token for the get service in swagger app.config["API_SPEC_OPTIONS"] = {
"components": {
"securitySchemes": {
"bearerAuth": {
"type": "http",
"scheme": "bearer",
"bearerFormat": "JWT",
}
}
},
}
|
Solution by @georgesequeira simply worked without blinking! Thanks @georgesequeira |
Currently I use flask_jwt_extended for API security, how can I define the need of passing Authorization header?
I tried using this:
@blp.doc(parameters={'Authorization': {'name': 'Authorization', 'in': 'header', 'description': 'Authorization: Bearer <access_token>', 'required': 'true'}})
but it throw an error
The text was updated successfully, but these errors were encountered: