Skip to content

fix(event_handler): propagate UnauthorizedException and await async class-based middlewares - #8633

Open
SuellenQwQ wants to merge 5 commits into
aws-powertools:developfrom
SuellenQwQ:fix/appsync-unauthorized-async-middleware
Open

SuellenQwQ wants to merge 5 commits into
aws-powertools:developfrom
SuellenQwQ:fix/appsync-unauthorized-async-middleware

Conversation

@SuellenQwQ

@SuellenQwQ SuellenQwQ commented Oct 19, 2026 •

Copy link
Copy Markdown

Issue number: closes #8582

Summary

Changes

  • AppSyncEventsResolver now re-raises UnauthorizedException raised by a per-event (aggregate=False) publish resolver, in both the sync and the async path, instead of converting it into a per-event error payload.
  • AsyncMiddlewareFrame now awaits class-based middlewares whose handler is a coroutine function, so BaseMiddlewareHandler subclasses keep working with app.resolve_async().
  • wrap_middleware_async returns the response of a middleware that short-circuits without calling the next one, instead of raising IndexError.

User experience

A resolver that denies access with UnauthorizedException makes AppSync deny the whole operation regardless of aggregate, which matches the intent of the exception and what the subscribe path already does. Middlewares written as BaseMiddlewareHandler subclasses with async def handler now run under app.resolve_async() instead of returning an un-awaited coroutine.

This covers items 1 and 2 of the issue. Item 3 (HttpResolverLocal as a Lambda handler) is a change to the ASGI/Lambda dispatch in __call__ and is left out of this PR. If you would rather keep #8582 open for item 3, say so and I will drop the closes keyword and open a separate issue for it.

Checklist

  • Added tests covering the new behaviour
  • Ran pytest tests/functional/event_handler/required_dependencies/appsync tests/functional/event_handler/required_dependencies/test_async_middleware_frame.py

Disclaimer: We value your time and bandwidth. As such, any pull requests created on non-triaged issues might not be successful.

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

…pSync Events resolvers

The per-event resolvers (`aggregate=False`) catch every exception and turn it into a
per-event error payload. `UnauthorizedException` is a control-flow signal rather than a
payload: re-raise it so AppSync denies the whole operation.

Both the sync and the async paths now behave like the aggregate and subscribe resolvers.

Signed-off-by: SuellenQAQ <sulun6693485@163.com>
`AsyncMiddlewareFrame` only awaited middlewares that are themselves coroutine
functions. A `BaseMiddlewareHandler` subclass with an `async def handler` is not a
coroutine function, so the frame returned the coroutine and the response leaked
through un-awaited.

Detect the class-based `handler` too and await whatever the middleware returns.

Also return a short-circuiting middleware's own response in `wrap_middleware_async`
instead of raising IndexError when the next middleware was never called.

Signed-off-by: SuellenQAQ <sulun6693485@163.com>
…rcuiting

Added tests for class-based async middleware and sync short-circuit behavior.

Signed-off-by: SuellenQAQ <sulun6693485@163.com>
…vent resolvers

Add tests for handling unauthorized exceptions in AppSync events.

Signed-off-by: SuellenQAQ <sulun6693485@163.com>

@leandrodamascena leandrodamascena left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @SuellenQwQ, both fixes are right. I deployed this behind a real AppSync Events API with a Lambda data source: a publish handler raising UnauthorizedException with the default aggregate=False now makes AppSync answer 401 with its own generic message, for sync and async handlers, where develop returned 200 with the exception text in a per-event CustomError. The class-based async middleware works under resolve_async, including the short-circuit cases.

Your branch imported two helpers from api_gateway that only reached develop after you branched, so on its own it failed 121 tests and conflicted on async_utils.py. I merged develop into it keeping your version of the file, which already accounted for them, and everything is green now. Nothing for you to do.

Approving. This goes out in 3.39.0, so it will sit here for a while before I merge it. #8634 covers item 3 of the issue, so keeping closes here is fine. You'll be credited in the release notes.

@sonarqubecloud

Copy link
Copy Markdown

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

event_handlers size/L Denotes a PR that changes 100-499 lines, ignoring generated files. tests

Projects

None yet

2 participants