From fb0250e44a9dba0cae82668aafc6b5e9f86c9798 Mon Sep 17 00:00:00 2001 From: Nicolas Lykke Iversen Date: Fri, 10 May 2024 11:11:36 +0200 Subject: [PATCH 1/2] feat(event_handler): add support for persisting authorization data in Swagger UI --- aws_lambda_powertools/event_handler/api_gateway.py | 4 ++++ .../event_handler/openapi/swagger_ui/html.py | 4 ++++ .../functional/event_handler/test_openapi_swagger.py | 12 ++++++++++++ 3 files changed, 20 insertions(+) diff --git a/aws_lambda_powertools/event_handler/api_gateway.py b/aws_lambda_powertools/event_handler/api_gateway.py index 87433b020d5..49f6a3de85e 100644 --- a/aws_lambda_powertools/event_handler/api_gateway.py +++ b/aws_lambda_powertools/event_handler/api_gateway.py @@ -1696,6 +1696,7 @@ def enable_swagger( security_schemes: Optional[Dict[str, "SecurityScheme"]] = None, security: Optional[List[Dict[str, List[str]]]] = None, oauth2_config: Optional["OAuth2Config"] = None, + persist_authorization: bool = False, ): """ Returns the OpenAPI schema as a JSON serializable dict @@ -1736,6 +1737,8 @@ def enable_swagger( A declaration of which security mechanisms are applied globally across the API. oauth2_config: OAuth2Config, optional The OAuth2 configuration for the Swagger UI. + persist_authorization: bool, optional + Whether to persist authorization data on browser close/refresh. """ from aws_lambda_powertools.event_handler.openapi.compat import model_json from aws_lambda_powertools.event_handler.openapi.models import Server @@ -1814,6 +1817,7 @@ def swagger_handler(): swagger_css, swagger_base_url, oauth2_config, + persist_authorization, ) return Response( diff --git a/aws_lambda_powertools/event_handler/openapi/swagger_ui/html.py b/aws_lambda_powertools/event_handler/openapi/swagger_ui/html.py index 8b748d9338a..85e041f2f56 100644 --- a/aws_lambda_powertools/event_handler/openapi/swagger_ui/html.py +++ b/aws_lambda_powertools/event_handler/openapi/swagger_ui/html.py @@ -10,6 +10,7 @@ def generate_swagger_html( swagger_css: str, swagger_base_url: str, oauth2_config: Optional[OAuth2Config], + persist_authorization: bool = False, ) -> str: """ Generate Swagger UI HTML page @@ -28,6 +29,8 @@ def generate_swagger_html( The base URL for Swagger UI oauth2_config: OAuth2Config, optional The OAuth2 configuration. + persist_authorization: bool, optional + Whether to persist authorization data on browser close/refresh. """ # If Swagger base URL is present, generate HTML content with linked CSS and JavaScript files @@ -86,6 +89,7 @@ def generate_swagger_html( SwaggerUIBundle.plugins.DownloadUrl ], withCredentials: true, + persistAuthorization: {str(persist_authorization).lower()}, oauth2RedirectUrl: baseUrl + "?format=oauth2-redirect", }} diff --git a/tests/functional/event_handler/test_openapi_swagger.py b/tests/functional/event_handler/test_openapi_swagger.py index 11ec0cf24da..a8d9326efcf 100644 --- a/tests/functional/event_handler/test_openapi_swagger.py +++ b/tests/functional/event_handler/test_openapi_swagger.py @@ -118,6 +118,18 @@ def test_openapi_swagger_with_rest_api_stage(): assert "ui.specActions.updateUrl('/prod/swagger?format=json')" in result["body"] +def test_openapi_swagger_with_persist_authorization(): + app = APIGatewayRestResolver(enable_validation=True) + app.enable_swagger(persist_authorization=True) + + event = load_event("apiGatewayProxyEvent.json") + event["path"] = "/swagger" + + result = app(event, {}) + assert result["statusCode"] == 200 + assert "persistAuthorization: true" in result["body"] + + def test_openapi_swagger_oauth2_without_powertools_dev(): with pytest.raises(ValueError) as exc: OAuth2Config(app_name="OAuth2 app", client_id="client_id", client_secret="verysecret") From 57fcfc9f3771a499d0233145ba03c00a5a605673 Mon Sep 17 00:00:00 2001 From: Nicolas Lykke Iversen Date: Fri, 10 May 2024 11:26:01 +0200 Subject: [PATCH 2/2] docs(event_handler): update docs for Swagger UI persist authorization feature --- docs/core/event_handler/api_gateway.md | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/docs/core/event_handler/api_gateway.md b/docs/core/event_handler/api_gateway.md index aaf9352ebc0..d224db63f9c 100644 --- a/docs/core/event_handler/api_gateway.md +++ b/docs/core/event_handler/api_gateway.md @@ -524,12 +524,13 @@ Behind the scenes, the [data validation](#data-validation) feature auto-generate There are some important **caveats** that you should know before enabling it: -| Caveat | Description | -| ------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Swagger UI is **publicly accessible by default** | When using `enable_swagger` method, you can [protect sensitive API endpoints by implementing a custom middleware](#customizing-swagger-ui) using your preferred authorization mechanism. | -| **No micro-functions support** yet | Swagger UI is enabled on a per resolver instance which will limit its accuracy here. | -| You need to expose a **new route** | You'll need to expose the following path to Lambda: `/swagger`; ignore if you're routing this path already. | -| JS and CSS files are **embedded within Swagger HTML** | If you are not using an external CDN to serve Swagger UI assets, we embed JS and CSS directly into the HTML. To enhance performance, please consider enabling the `compress` option to minimize the size of HTTP requests. | +| Caveat | Description | +| ------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Swagger UI is **publicly accessible by default** | When using `enable_swagger` method, you can [protect sensitive API endpoints by implementing a custom middleware](#customizing-swagger-ui) using your preferred authorization mechanism. | +| **No micro-functions support** yet | Swagger UI is enabled on a per resolver instance which will limit its accuracy here. | +| You need to expose a **new route** | You'll need to expose the following path to Lambda: `/swagger`; ignore if you're routing this path already. | +| JS and CSS files are **embedded within Swagger HTML** | If you are not using an external CDN to serve Swagger UI assets, we embed JS and CSS directly into the HTML. To enhance performance, please consider enabling the `compress` option to minimize the size of HTTP requests. | +| Authorization data is **lost** on browser close/refresh | Use `enable_swagger(persist_authorization=True)` to persist authorization data, like OAuath 2.0 access tokens. | ```python hl_lines="12-13" title="enabling_swagger.py" --8<-- "examples/event_handler_rest/src/enabling_swagger.py" @@ -835,8 +836,8 @@ As a practical example, let's refactor our correlation ID middleware so it accep These are native middlewares that may become native features depending on customer demand. -| Middleware | Purpose | -| ------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | +| Middleware | Purpose | +| ------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | | [SchemaValidationMiddleware](/lambda/python/latest/api/event_handler/middlewares/schema_validation.html){target="_blank"} | Validates API request body and response against JSON Schema, using [Validation utility](../../utilities/validation.md){target="_blank"} | #### Being a good citizen @@ -1053,7 +1054,7 @@ When you're describing your API, declare security schemes at the top level, and OpenAPI 3 lets you describe APIs protected using the following security schemes: | Security Scheme | Type | Description | -|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [HTTP auth](https://www.iana.org/assignments/http-authschemes/http-authschemes.xhtml){target="_blank"} | `HTTPBase` | HTTP authentication schemes using the Authorization header (e.g: [Basic auth](https://swagger.io/docs/specification/authentication/basic-authentication/){target="_blank"}, [Bearer](https://swagger.io/docs/specification/authentication/bearer-authentication/){target="_blank"}) | | [API keys](https://swagger.io/docs/specification/authentication/api-keys/https://swagger.io/docs/specification/authentication/api-keys/){target="_blank"} (e.g: query strings, cookies) | `APIKey` | API keys in headers, query strings or [cookies](https://swagger.io/docs/specification/authentication/cookie-authentication/){target="_blank"}. | | [OAuth 2](https://swagger.io/docs/specification/authentication/oauth2/){target="_blank"} | `OAuth2` | Authorization protocol that gives an API client limited access to user data on a web server. |