spec-first / connexion

Connexion is a modern Python web framework that makes spec-first and api-first development easy.
https://connexion.readthedocs.io/en/latest/
Apache License 2.0
4.48k stars 761 forks source link

Connexion with Flask-JWT-Extended response 401 not authorized #1023

Closed xarpy closed 2 years ago

xarpy commented 5 years ago

Description

I have a project already in production, being a REST API designed with flask, flask-restful and flask-jwt-extended. The system did not have any documentation yet, but this was necessary, being my first using connexion. I created the documentation by "swagger.editor.io" and created the correct instances in the project.

Initially I was unable to access the application, soon realizing that the need to install the CORS package, solving the first problem. Now I am unable to authenticate to any endpoint of the system, either by generating swagger / ui or POSTMAN. How do I disable connexion security or include the flask-jwt-extended decorator to validate the token?

Expected behaviour

Connexion Accepts Flask-JWT-Extended settings

Actual behaviour

Apart from the token generator endpoint, the others do not allow authentication.

Steps to reproduce

Connexion insertion in my project

n this short excerpt from init you can see that I had a problem adapting connexion to the project. Thanks a lot to the team for answering this #217 .

'''Added Swagger Docs'''
    options = {
        'uri_parsing_class': OpenAPIURIParser
        }
    con_app = connexion.App(
        __name__, specification_dir='templates/docs',
        options=options
        )
    con_app.add_api('swagger.yaml')

    app = con_app.app

    app.config.from_object(config[config_name])

    '''Added LOG in project'''
    gunicorn_logger = logging.getLogger('gunicorn.error')
    app.logger.handlers = gunicorn_logger.handlers
    app.logger.setLevel(gunicorn_logger.level)

    '''Added JWT'''
    configure_jwt(app)

    '''Added CORS'''
    CORS(app)

My endpoints build with Restful

All project endpoints were built using flask-restful package and flask-jwt-extended authentication, below is a simple example of the features that exist within the project. It works correctly without connexion, but now any endpoint I try to access, even generating the token I don't have access to.

class Status(Resource):
    @jwt_required
    def get(self):
        db = session_ws()
        status_schema = StatusSchema(many=True)

        todo = []
          descriptions = {state.id: state.name for state in status}
          for item in obj:
              item.status_description = descriptions.get(item.status_id)
              todo.append(line)
          result = line_schema.dump(todo)

        return send_reponse(pagination(
            result.data, '/status',
            start=request.args.get('start', 1, type=int),
            limit=request.args.get('limit', 100, type=int)
            ), 200, ('Content-Range', len(result.data)))

Response swagger-ui

{
  "detail": "No authorization token provided",
  "status": 401,
  "title": "Unauthorized",
  "type": "about:blank"
}

My swagger.yaml file

openapi: 3.0.2
info:
  title: Microsservices Tests
  description: API Rest
  version: '1.0'
  contact:
    email: none@none.com
servers:
- url: http://127.0.0.1:5000/nlt
  description: Developer server
components:
....
  securitySchemes:
    Auth:
      type: http
      scheme: bearer
      bearerFormat: JWT
....
paths:
...
  "/status":
    get:
      tags:
      - Status
      summary: Access Status of items
      operationId: app.routes.ms_status.Status.get
      description: List of items of client acess this endpoint
      security:
      - Auth: []
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                type: array
                items:
                  example:
                    count: 3
                    message:
                    - item: '1'
                      status_description: Stand by
                    - item: '2'
                      status_description: Out
                    - item: '3'
                      status_description: Off
        '401':
          description: Not Authorized
          content:
            application/json:
              schema:
                example:
                  message: Token Expired
        '500':
          description: Error Internal Server
          content:
            application/json:
              schema:
                example:
                  message: Error Microsservice

Additional info:

Output of the commands:

SBelkaid commented 4 years ago

Hi @xarpy ,

I was looking into this because I wanted to be able to add a bearer token to the swagger UI and try some requests from there. What I realized after a couple of hours of searching is that the error that is returned is from the swagger package. If you remove the security specification. You'll be able to run it.

"/status": get: tags:

I think this fixes your issue with not being able to authenticate with Flask-JWT-Extended. If you want te be sure, create a curl request with the authorization header and your token as bearer

for reference, here's another issue that was opened with regards to your issue (scroll to the bottom)

https://github.com/zalando/connexion/issues/806#issuecomment-448184925

yashcs commented 4 years ago

if you remove the security: <-- remove

people-can-fly commented 3 years ago

This is not a bug.

according to https://connexion.readthedocs.io/en/latest/security.html With Connexion, the API security definition must include a x-tokenInfoFunc or set TOKENINFO_FUNC env var. x-tokenInfoFunc must contain a reference to a function used to obtain the token info. you havent added x-tokenInfoFunc in openapi doc.

For example

components:
  securitySchemes:
    jwt:
      type: http
      scheme: bearer
      bearerFormat: JWT
      x-bearerInfoFunc: routes.decodetoken

where in routes.py for my case

from flask_jwt_extended import decode_token
def decodetoken(token):
    return decode_token(token)