litestar-org / litestar

Production-ready, Light, Flexible and Extensible ASGI API framework | Effortlessly Build Performant APIs
https://litestar.dev/
MIT License
5.5k stars 375 forks source link

Bug: Openapi failing validation with python-openapi/openapi-spec-validator #3642

Open bmle69 opened 3 months ago

bmle69 commented 3 months ago

Description

Hi, I'm having some issues with validating the openapi specs outputted by Litestar and not sure where to start. I'd like to build in a validator to validate the spec in order to make these apis publicly available as part of my CI/CD flow. Here's a simple example that doesn't pass validation just from adding BearerToken as a security scheme. In this case, the name key in BearerToken is the issue and removing it from the spec allows it to pass validation.

{
   "info":{
      "title":"Test App",
      "version":"1.0.0"
   },
   "openapi":"3.1.0",
   "servers":[
      {
         "url":"/"
      }
   ],
   "paths":{

   },
   "components":{
      "schemas":{

      },
      "securitySchemes":{
         "BearerToken":{
            "type":"http",
            "description":"JWT api-key authentication and authorization.",
            "name":"Authorization",
            "scheme":"Bearer",
            "bearerFormat":"JWT"
         }
      }
   },
   "security":[
      {
         "BearerToken":[

         ]
      }
   ]
}

This is being generated by this Litestar app:

app = Litestar(
    route_handlers=[login,],
    on_app_init=[jwt_auth.on_app_init,],
    logging_config=logging_config,
    openapi_config=OpenAPIConfig(
        title="Test App",
        version="1.0.0",
        path="/docs",
        render_plugins=[YamlRenderPlugin(path="/openapi.yaml"), StoplightRenderPlugin(path="/")],
    )
)

And with using the validator, I'm getting this error:

root@0ef927971748:/api# curl localhost:8000/docs/openapi.json | openapi-spec-validator -
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   334  100   334    0     0   145k      0 --:--:-- --:--:-- --:--:--  163k
stdin: Validation Error: Unevaluated properties are not allowed ('name' was unexpected)

Failed validating 'unevaluatedProperties' in schema['properties']['components']['properties']['securitySchemes']['additionalProperties']['else']:
    {'$comment': 'https://spec.openapis.org/oas/v3.1.0#security-scheme-object',
     'type': 'object',
     'properties': {'type': {'enum': ['apiKey',
                                      'http',
                                      'mutualTLS',
                                      'oauth2',
                                      'openIdConnect']},
                    'description': {'type': 'string'}},
     'required': ['type'],
     'allOf': [{'$ref': '#/$defs/specification-extensions'},
               {'$ref': '#/$defs/security-scheme/$defs/type-apikey'},
               {'$ref': '#/$defs/security-scheme/$defs/type-http'},
               {'$ref': '#/$defs/security-scheme/$defs/type-http-bearer'},
               {'$ref': '#/$defs/security-scheme/$defs/type-oauth2'},
               {'$ref': '#/$defs/security-scheme/$defs/type-oidc'}],
     'unevaluatedProperties': False,
     '$defs': {'type-apikey': {'if': {'properties': {'type': {'const': 'apiKey'}},
                                      'required': ['type']},
                               'then': {'properties': {'name': {'type': 'string'},
                                                       'in': {'enum': ['query',
                                                                       'header',
                                                                       'cookie']}},
                                        'required': ['name', 'in']}},
               'type-http': {'if': {'properties': {'type': {'const': 'http'}},
                                    'required': ['type']},
                             'then': {'properties': {'scheme': {'type': 'string'}},
                                      'required': ['scheme']}},
               'type-http-bearer': {'if': {'properties': {'type': {'const': 'http'},
                                                          'scheme': {'type': 'string',
                                                                     'pattern': '^[Bb][Ee][Aa][Rr][Ee][Rr]$'}},
                                           'required': ['type', 'scheme']},
                                    'then': {'properties': {'bearerFormat': {'type': 'string'}}}},
               'type-oauth2': {'if': {'properties': {'type': {'const': 'oauth2'}},
                                      'required': ['type']},
                               'then': {'properties': {'flows': {'$ref': '#/$defs/oauth-flows'}},
                                        'required': ['flows']}},
               'type-oidc': {'if': {'properties': {'type': {'const': 'openIdConnect'}},
                                    'required': ['type']},
                             'then': {'properties': {'openIdConnectUrl': {'type': 'string',
                                                                          'format': 'uri'}},
                                      'required': ['openIdConnectUrl']}}}}

On instance['components']['securitySchemes']['BearerToken']:
    {'type': 'http',
     'description': 'JWT api-key authentication and authorization.',
     'name': 'Authorization',
     'scheme': 'Bearer',
     'bearerFormat': 'JWT'}

URL to code causing the issue

No response

MCVE

app = Litestar(
    route_handlers=[login,],
    on_app_init=[jwt_auth.on_app_init,],
    logging_config=logging_config,
    openapi_config=OpenAPIConfig(
        title="Test App",
        version="1.0.0",
        path="/docs",
        render_plugins=[YamlRenderPlugin(path="/openapi.yaml"), StoplightRenderPlugin(path="/")],
    )
)

Steps to reproduce

1. Initialize app like above just adding JWT auth according to the Litestar docs
2. Generate the openapi.json file
3. Use python-openapi/openapi-spec-validator to validate
4. Confirm spec does not validate.

Screenshots

"![SCREENSHOT_DESCRIPTION](SCREENSHOT_LINK.png)"

Logs

No response

Litestar Version

2.9.1

Platform


[!NOTE]
While we are open for sponsoring on GitHub Sponsors and OpenCollective, we also utilize Polar.sh to engage in pledge-based sponsorship.

Check out all issues funded or available for funding on our Polar.sh dashboard

  • If you would like to see an issue prioritized, make a pledge towards it!
  • We receive the pledge once the issue is completed & verified
  • This, along with engagement in the community, helps us know which features are a priority to our users.

Fund with Polar

provinzkraut commented 3 months ago

@bmle69 I've checked the spec and that name field should indeed not be there.

What validator are you using to check this? Thinking if we should add it to our CI.

bmle69 commented 3 months ago

Hi @provinzkraut , this is the one I'm using: https://pypi.org/project/openapi-spec-validator/

I'm seeing some other examples of the generated openapi doc from litestar failing to validate. Let me know if you want me to file separate tickets or just keep it in this one.