Skip to content

Bug: Global apigateway swagger security config not overwritten by specific route configs with empty security #5893

@victorperezpiqueras

Description

@victorperezpiqueras

Expected Behaviour

I have defined an apigateway rest resolver with swagger enabled and a security scheme with oauth.

I have two endpoints, one protected, and one unprotected. I explicitly define the unprotected endpoint with security empty, so I would expect it to override the global config of swagger and be unprotected.

Current Behaviour

Currently, the unprotected security of the route is not overwritten. Instead, the global config seems to be applied over the specific one of the router. This only happens if the specific config is an empty list.

However, if the global config of security is an empty list or not defined at all, defining a specific security config in a route overwrites it.

With the current behaviour, if I want to keep all resources protected except one, I need to remove the global config of security, and put it in each route that i want protected.

Code snippet

import json
from aws_lambda_powertools.event_handler import APIGatewayRestResolver
from aws_lambda_powertools.event_handler.openapi.models import OAuth2, OAuthFlows, OAuthFlowAuthorizationCode

app = APIGatewayRestResolver(enable_validation=True)
app.enable_swagger(
    path="/swagger",
    security_schemes={
        "oauth": OAuth2(
            flows=OAuthFlows(
                authorizationCode=OAuthFlowAuthorizationCode(
                    authorizationUrl="",
                    tokenUrl="",
                    scopes={
                        "email": "email scope",
                        "openid": "openid scope",
                        "profile": "profile scope",
                    },
                ),
            ),
        )
    },
    security=[{"oauth": ["openid", "profile", "email"]}]
)


@app.get("/unprotected", security=[])
def unprotected() -> dict:
    return {}


@app.get("/protected", security=[{"oauth": ["openid", "profile", "email"]}])
def protected() -> dict:
    return {}


def lambda_handler(event, context):
    return app.resolve(event, context)


if __name__ == "__main__":
    test_event = {
        "body": json.dumps({}),
        "httpMethod": "GET",
        "path": "/swagger",
        "headers": {
            "Content-Type": "application/json"
        },
        "requestContext": {
            "requestId": "test-id",
            "stage": "test-stage",
            "path": "/swagger",
        }
    }

    result = lambda_handler(test_event, {})
    # store swagger in file to be able to check it
    with open("swagger.html", "w") as f:
        f.write(result["body"])

Possible Solution

Either clarify the docs of the enable_swagger() or make the specific router security options override the global security even if the security is empty.

Steps to Reproduce

  1. Copy the snippet provided.
  2. Run it and open the generated swagger.html
  3. The unprotected endpoint is protected (I expected it to not be protected)
  4. Remove the global security config from the enable_swagger()
  5. Rerun and reload the swagger.html
  6. The unprotected endpoint is now marked as unprotected as expected

Powertools for AWS Lambda (Python) version

latest

AWS Lambda function runtime

3.12

Packaging format used

Lambda Layers

Debugging logs

Metadata

Metadata

Projects

Status

Closed

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions