Skip to content

Export jwt_authorization_decorator for hosting-fastapi package - #471

Closed
Rodrigo Brandão (rodrigobr-msft) wants to merge 6 commits into
mainfrom
users/robrandao/fastapi-jwt
Closed

Export jwt_authorization_decorator for hosting-fastapi package#471
Rodrigo Brandão (rodrigobr-msft) wants to merge 6 commits into
mainfrom
users/robrandao/fastapi-jwt

Conversation

@rodrigobr-msft

Copy link
Copy Markdown
Contributor

This pull request refactors JWT authorization in the FastAPI integration by introducing a new decorator-based approach and deprecating the use of the JwtAuthorizationMiddleware middleware. It updates sample agents to use the new decorator, improves error handling, and adds missing license headers to several files.

JWT Authorization Refactor:

  • Introduced a new jwt_authorization_decorator in jwt_authorization_middleware.py to 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.
  • Updated the public API in __init__.py to export jwt_authorization_decorator and removed references to JwtAuthorizationMiddleware in the test samples. [1] [2] [3] [4]

Sample Agent Updates:

  • Refactored authorization_agent.py and empty_agent.py to use the new jwt_authorization_decorator on route handlers, removed middleware usage, and ensured agent configuration is set on the app state. [1] [2] [3] [4]

Error Handling Improvements:

  • Enhanced error handling in the middleware and decorator to provide clear error messages when authentication configuration is missing or invalid. [1] [2]

Code Quality:

  • Added missing copyright and license headers to several files for compliance. [1] [2] [3]

Minor Improvements:

  • Changed the default host in the sample agent to 127.0.0.1 for local development.

Copilot AI review requested due to automatic review settings July 16, 2026 16:20

Copilot AI 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.

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_decorator to the FastAPI JWT auth module and exported it from the package public API.
  • Updated FastAPI sample agents to configure auth on app.state and to protect the /api/messages handler via the decorator.
  • Added/normalized MIT license headers (and minor formatting tweaks) across several hosting-fastapi modules.

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.

Comment on lines +97 to +101
@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)
Comment on lines +132 to +136
else:
return JSONResponse(
{"error": "Authorization header not found"},
status_code=401,
)
Comment on lines +39 to +44
auth_config: AgentAuthConfiguration | None = getattr(
state, "agent_configuration", None
)

if not auth_config:
response = JSONResponse(
Comment on lines 87 to +88
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)
Copilot AI review requested due to automatic review settings July 22, 2026 22:42

Copilot AI 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.

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.
    """

Copilot AI 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.

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: Request argument. If a decorated handler doesn’t accept Request (or FastAPI passes parameters by keyword in a different order), this will raise a TypeError before your custom 500 response can run. Since this is exported as public API, make the wrapper accept *args, **kwargs and locate the Request instance 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)

Comment on lines 6 to 15
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",
]
Comment on lines +59 to +60
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.
Comment on lines +27 to +29
Returns:
_JwtAuthorizationResult: The result of the authorization attempt.
"""
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants