Comments (22)
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.
from flask-smorest.
Note that by using @lafrech suggestion, you still need to add:
api.spec.options["security"] = [{"bearerAuth": []}]
Otherwise SwaggerUI won't add Authorization header.
from flask-smorest.
Replace decorator._apidoc = deepcopy(func, '_apidoc', {})
with
decorator._apidoc = deepcopy(getattr(func, "_apidoc", {}))
Also, to complete @manfred-kaiser answer, for those who want to use the @jwt_required
decorator from flask_jwt_extended you can upgrade the snippet like this:
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
from flask-smorest.
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.
from flask-smorest.
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 @auth.login_required
decorators throughout the code, so I actually just subclassed the HTTPTokenAuth from flask_httpauth with this method and overwrote their method.
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 auth = HTTPTokenAuth()
, I do auth = DocHTTPTokenAuth()
and can use @auth.login_required
as normal.
Hope this helps.
from flask-smorest.
Based on @cmabastar solution I have written an improved decorator.
I wanted a solution, which adds the correct authentication methods depending on the used decorator.
I also wanted to use a single decorator for restricting the url to a specific authentication and add the information to the docs.
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 @jwt_required
decorator is needed.
@bp.route("/whoami")
class WhoAmI(MethodView):
@jwt_required()
def get(self):
return {"test": "hi"}
from flask-smorest.
Based on @cmabastar solution I have written an improved decorator.
I'm getting:
line 100, in cookie_required
decorator._apidoc = deepcopy(func, '_apidoc', {})
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Program Files\Python311\Lib\copy.py", line 138, in deepcopy
y = memo.get(d, _nil)
^^^^^^^^
AttributeError: 'str' object has no attribute 'get'
from flask-smorest.
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 @jwt_required
, that would append your parameter to func._apidoc
. Check how the arguments
does that (https://github.com/Nobatek/flask-rest-api/blob/master/flask_rest_api/arguments.py#L55).
It shouldn't be complicated. Tell me how it goes, and feel free to ask if this is unclear.
from flask-smorest.
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
from flask-smorest.
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 Blueprint
to add this decorator as a classmethod, so that you can call
@blp.doc_jwt_required
def view_function():
from flask-smorest.
Thanks @sjmh. That's the way to go.
(Hint: when pasting code, add py after the three backquotes (```py) to get syntax highlighting.)
@dels07, please try something along those lines and tell us how it goes.
from flask-smorest.
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 {}
from flask-smorest.
There's been discussions about allowing response
to swallow a Response
object (see #22). I've been busy with other stuff and I'm not done with this yet.
As a general rule, I'd always place authentication/authorization logic on top to drop the request asap if the client is not authorized.
from flask-smorest.
it tries to pass it's Flask Response object to @blp.response, which then bombs.
@sjmh I just sent a PR fixing this (#40). Feedback welcome.
@dels07 did you manage to adapt @sjmh's code to your need?
from flask-smorest.
Closing this for now. @dels07, feel free to comment if you're still stuck with this.
from flask-smorest.
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 TypeError: "O is undefined"
. I think that is because openapi (3) expects a defined security schema or somethings as described here.
I'd really like to do this in my api as I think authentification is an integral part of documenting your api.
from flask-smorest.
Indeed, the decorator proposed above adds an auth parameter to the resource.
To add a security scheme, use the api.spec.components.security_scheme
method (see apispec doc).
No time right now to elaborate. Hope this helps.
from flask-smorest.
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.
from flask-smorest.
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.
@bp.route("/whoami")
class WhoAmI(MethodView):
@bp.doc(authorize=True)
@jwt_required()
def get(self):
return {"test": "hi"}
This way you'll get selective lock button.
from flask-smorest.
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 api.spec.options["security"] = [{"bearerAuth": []}]
it works.
from flask-smorest.
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
and in the config I've added the below configuration
app.config["API_SPEC_OPTIONS"] = {
"components": {
"securitySchemes": {
"bearerAuth": {
"type": "http",
"scheme": "bearer",
"bearerFormat": "JWT",
}
}
},
}
Hope this might help. If there are any issues then please let me know
from flask-smorest.
Solution by @georgesequeira simply worked without blinking!
Thanks @georgesequeira
from flask-smorest.
Related Issues (20)
- Inconsistent order of parameters when using rule.arguments from werkzeug inside FlaskPlugin HOT 10
- Pagination documentation example is incorrect HOT 3
- Are query based blueprint arguments after path arguments formally unsupported? HOT 5
- Support for mixed multipart forms HOT 5
- ImportError: cannot import name '_app_ctx_stack' from 'flask' HOT 4
- flask smorest import error HOT 1
- Swagger submitting incorrect format for Model-based form inputs HOT 4
- Proposal: Allow register_blueprint option to name the Blueprint outside of Flask HOT 1
- Werkzeug Security Issue
- How can i create pagination for method POST HOT 2
- Missing response header in OpenAPI documentation HOT 3
- How to show docs with nginx url prefix set? HOT 1
- Different `unknown` arguments behaviour locally vs on production HOT 2
- What's the best way to handle multiple routes per function? HOT 1
- Swagger isn't obeying required=True for IPv4 field HOT 6
- Schema validation of mandatory fields on a blueprint with a HTTP PATCH method containing only modified fields HOT 5
- TypeError: argument of type 'SchemaMeta' is not iterable when running flask --app '...' openapi print HOT 1
- Improving error handlers to satisfy both template and API server use cases HOT 6
- Endpoint isn't getting data from json_or_form arguments when using the Swagger UI HOT 8
- flask-smorest with subdomains and redoc HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from flask-smorest.