Export jwt_authorization_decorator for hosting-fastapi package - #471
Export jwt_authorization_decorator for hosting-fastapi package#471Rodrigo Brandão (rodrigobr-msft) wants to merge 6 commits into
jwt_authorization_decorator for hosting-fastapi package#471Conversation
There was a problem hiding this comment.
Pull request overview
This PR refactors JWT authorization for the FastAPI hosting integration by introducing a decorator-based enforcement mechanism (jwt_authorization_decorator), updating FastAPI samples to use it instead of JwtAuthorizationMiddleware, and adding missing MIT license headers for compliance.
Changes:
- Added
jwt_authorization_decoratorto the FastAPI JWT auth module and exported it from the package public API. - Updated FastAPI sample agents to configure auth on
app.stateand to protect the/api/messageshandler via the decorator. - Added/normalized MIT license headers (and minor formatting tweaks) across several
hosting-fastapimodules.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| test_samples/fastapi/empty_agent.py | Switches from middleware to decorator, moves auth config to app.state, and changes default bind host. |
| test_samples/fastapi/authorization_agent.py | Switches from middleware to decorator and sets app.state.agent_configuration. |
| libraries/microsoft-agents-hosting-fastapi/microsoft_agents/hosting/fastapi/jwt_authorization_middleware.py | Adds decorator-based JWT enforcement and improves missing-config handling in the middleware. |
| libraries/microsoft-agents-hosting-fastapi/microsoft_agents/hosting/fastapi/cloud_adapter.py | License header formatting (blank line). |
| libraries/microsoft-agents-hosting-fastapi/microsoft_agents/hosting/fastapi/_start_agent_process.py | Adds missing MIT license header. |
| libraries/microsoft-agents-hosting-fastapi/microsoft_agents/hosting/fastapi/init.py | Exports jwt_authorization_decorator and adds missing MIT license header. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| @functools.wraps(func) | ||
| async def wrapper(request: Request): | ||
| if request is None: | ||
| return JSONResponse({"error": "Request object not found"}, status_code=500) | ||
|
|
| status_code=401, | ||
| ) | ||
|
|
||
| return await func(request) |
| else: | ||
| return JSONResponse( | ||
| {"error": "Authorization header not found"}, | ||
| status_code=401, | ||
| ) |
| auth_config: AgentAuthConfiguration | None = getattr( | ||
| state, "agent_configuration", None | ||
| ) | ||
|
|
||
| if not auth_config: | ||
| response = JSONResponse( |
| port = int(environ.get("PORT", 3978)) | ||
| uvicorn.run(app, host="0.0.0.0", port=port) | ||
| uvicorn.run(app, host="127.0.0.1", port=port) |
…into users/robrandao/fastapi-jwt
…into users/robrandao/fastapi-jwt
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 13 out of 13 changed files in this pull request and generated 4 comments.
Comments suppressed due to low confidence (3)
libraries/microsoft-agents-hosting-fastapi/microsoft_agents/hosting/fastapi/jwt_authorization_middleware.py:56
- JwtAuthorizationMiddleware returns JSONResponse using an invalid keyword argument ("body"), and it never persists the successful ClaimsIdentity onto request.state. Downstream adapters (e.g., FastApiRequestAdapter.get_claims_identity) will always see None, breaking auth-dependent behavior.
if isinstance(res, HttpResponse):
response = JSONResponse(body=res.body, status_code=res.status_code)
await response(scope, receive, send)
return
libraries/microsoft-agents-hosting-fastapi/microsoft_agents/hosting/fastapi/jwt_authorization_middleware.py:70
- The FastAPI jwt_authorization_decorator also uses JSONResponse(body=...) (invalid), and it doesn't attach the authorized ClaimsIdentity to request.state. Additionally, the wrapper currently requires the Request parameter name to be exactly "request"; if a handler uses a different name (e.g. "req: Request"), FastAPI will pass it via kwargs and the wrapper will fail with a missing positional argument.
@functools.wraps(func)
async def wrapper(request: Request, *args, **kwargs):
if request is None:
return JSONResponse({"error": "Request object not found"}, status_code=500)
libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/authorization/jwt/_authorize_request.py:29
- The _authorize_request docstring return type refers to "_JwtAuthorizationResult", but the function actually returns ClaimsIdentity | HttpResponse. Updating this helps keep the contract clear for host integrations.
Returns:
_JwtAuthorizationResult: The result of the authorization attempt.
"""
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 32 out of 33 changed files in this pull request and generated 3 comments.
Comments suppressed due to low confidence (2)
libraries/microsoft-agents-hosting-aiohttp/microsoft_agents/hosting/aiohttp/jwt_authorization_middleware.py:18
request.app["agent_configuration"]will raise a KeyError when the app isn't configured, which bypasses the new_authorize_request()error handling and yields a generic 500 instead of the intended{"error": "Agent Authentication configuration not found"}response.
auth_config: AgentAuthConfiguration = request.app["agent_configuration"]
libraries/microsoft-agents-hosting-fastapi/microsoft_agents/hosting/fastapi/jwt_authorization_middleware.py:67
- The decorator wrapper requires a positional
request: Requestargument. If a decorated handler doesn’t acceptRequest(or FastAPI passes parameters by keyword in a different order), this will raise aTypeErrorbefore your custom 500 response can run. Since this is exported as public API, make the wrapper accept*args, **kwargsand locate theRequestinstance defensively.
@functools.wraps(func)
async def wrapper(request: Request, *args, **kwargs):
if request is None:
return JSONResponse({"error": "Request object not found"}, status_code=500)
| from ._http_request_protocol import HttpRequestProtocol | ||
| from ._http_response import HttpResponse, HttpResponseFactory | ||
| from ._http_adapter_base import HttpAdapterBase | ||
| from ._channel_service_routes import ChannelServiceRoutes | ||
|
|
||
| __all__ = [ | ||
| "HttpRequestProtocol", | ||
| "HttpResponse", | ||
| "HttpResponseFactory", | ||
| "HttpAdapterBase", | ||
| "ChannelServiceRoutes", | ||
| ] |
| JWT Authorization Decorator for FastAPI endpoints. Until a SDK solution is made available, | ||
| this decorator can be applied to any FastAPI route handler to enforce JWT validation using the Microsoft Agents SDK's JwtTokenValidator. |
| Returns: | ||
| _JwtAuthorizationResult: The result of the authorization attempt. | ||
| """ |
This pull request refactors JWT authorization in the FastAPI integration by introducing a new decorator-based approach and deprecating the use of the
JwtAuthorizationMiddlewaremiddleware. It updates sample agents to use the new decorator, improves error handling, and adds missing license headers to several files.JWT Authorization Refactor:
jwt_authorization_decoratorinjwt_authorization_middleware.pyto enforce JWT validation on FastAPI route handlers, replacing the previous middleware approach. This decorator performs token validation and returns appropriate error responses for missing or invalid tokens.__init__.pyto exportjwt_authorization_decoratorand removed references toJwtAuthorizationMiddlewarein the test samples. [1] [2] [3] [4]Sample Agent Updates:
authorization_agent.pyandempty_agent.pyto use the newjwt_authorization_decoratoron route handlers, removed middleware usage, and ensured agent configuration is set on the app state. [1] [2] [3] [4]Error Handling Improvements:
Code Quality:
Minor Improvements:
127.0.0.1for local development.