Skip to content

JWT auth middleware refactor - #497

Merged
Rodrigo Brandão (rodrigobr-msft) merged 25 commits into
mainfrom
users/robrandao/fastapi-jwt
Jul 24, 2026
Merged

JWT auth middleware refactor#497
Rodrigo Brandão (rodrigobr-msft) merged 25 commits into
mainfrom
users/robrandao/fastapi-jwt

Conversation

@rodrigobr-msft

@rodrigobr-msft Rodrigo Brandão (rodrigobr-msft) commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

This pull request adds integration tests for OAuth continuation and JWT validation, and updates the integration test dependencies and configuration accordingly. The main changes include the addition of new test modules for authentication and JWT validation, updates to test dependencies, and configuration files for test environments.

Integration test additions and improvements:

  • Added comprehensive integration tests for OAuth continuation, including a full test flow with fake token and connector clients in dev/integration/tests/auth/test_oauth_continuation.py.
  • Added integration tests for JWT validation in aiohttp apps, covering anonymous access, invalid tokens, and real service connection tokens in dev/integration/tests/jwt_validation/test_aiohttp_jwt_validation.py.
  • Added test helpers for JWT validation, including functions for generating tokens and manipulating auth config in dev/integration/tests/jwt_validation/_helpers.py.
  • Added MIT license headers to new test files. [1] [2] [3]

Test configuration and dependencies:

  • Updated dev/integration/pyproject.toml to add new dependencies required for the tests, including microsoft-agents-authentication-msal, microsoft-agents-hosting-aiohttp, and microsoft-agents-hosting-fastapi.
  • Added environment files for test configuration: auth.env for OAuth tests, jwt_anonymous.env and jwt_required.env for JWT validation tests. [1] [2] [3]
  • Added a warning filter for a FastAPI/Starlette deprecation and set pythonpath in pytest.ini to improve test discovery.

CI/CD pipeline updates:

  • Updated GitHub Actions and Azure DevOps pipelines to install the new microsoft_agents_hosting_fastapi wheel during test setup. (.github/workflows/python-package.yml, .azdo/ci-pr.yaml) [1] [2]

Copilot AI review requested due to automatic review settings July 22, 2026 23:06

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 centralizes JWT authorization logic into a shared core helper and updates the Aiohttp/FastAPI hosting layers (middleware + decorator usage) to use it, while adding unit and integration tests to validate consistent authorization behavior and an OAuth token-exchange continuation scenario.

Changes:

  • Introduces microsoft_agents.hosting.core.authorization.jwt._authorize_request and refactors Aiohttp/FastAPI JWT authorization middleware to use it.
  • Adds unit tests for the core authorization helper and framework middleware/decorator behavior.
  • Adds integration tests + .env fixtures for JWT validation scenarios and an end-to-end OAuth token exchange continuation test.

Reviewed changes

Copilot reviewed 32 out of 33 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
tests/hosting_fastapi/test_jwt_authorization_middleware.py Adds FastAPI JWT middleware/decorator unit tests.
tests/hosting_fastapi/init.py Adds license header for the test package.
tests/hosting_core/telemetry/test_http_adapter_telemetry.py Updates HttpAdapterBase import path.
tests/hosting_core/authorization/test_jwk_client_manager.py Updates JwtTokenValidator import path.
tests/hosting_core/authorization/test_authorize_request.py Adds unit tests for _authorize_request.
tests/hosting_aiohttp/test_jwt_authorization_middleware.py Adds aiohttp JWT middleware/decorator unit tests.
test_samples/fastapi/empty_agent.py Switches FastAPI sample to decorator-based JWT auth + updates host binding.
test_samples/fastapi/authorization_agent.py Switches FastAPI sample route to decorator-based JWT auth.
libraries/microsoft-agents-hosting-fastapi/microsoft_agents/hosting/fastapi/jwt_authorization_middleware.py Refactors FastAPI middleware to use _authorize_request and adds decorator.
libraries/microsoft-agents-hosting-fastapi/microsoft_agents/hosting/fastapi/cloud_adapter.py Adjusts imports to use HttpAdapterBase from core.
libraries/microsoft-agents-hosting-fastapi/microsoft_agents/hosting/fastapi/_start_agent_process.py Adds license header.
libraries/microsoft-agents-hosting-fastapi/microsoft_agents/hosting/fastapi/init.py Exposes jwt_authorization_decorator in package exports.
libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/http/_http_response.py Minor import ordering cleanup.
libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/http/_channel_service_routes.py Uses direct import for ChannelApiHandlerProtocol.
libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/http/init.py Removes HttpAdapterBase from core.http exports.
libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/authorization/jwt/jwt_token_validator.py Fixes relative imports after package restructuring.
libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/authorization/jwt/_authorize_request.py Adds shared JWT authorization helper.
libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/authorization/jwt/init.py Exposes JwtTokenValidator and _authorize_request.
libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/authorization/init.py Re-exports JwtTokenValidator from the new jwt package.
libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/_http_adapter_base.py Refactors imports to use direct module paths.
libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/init.py Exposes HttpAdapterBase and updates JwtTokenValidator import path.
libraries/microsoft-agents-hosting-aiohttp/microsoft_agents/hosting/aiohttp/jwt_authorization_middleware.py Refactors aiohttp middleware/decorator to use _authorize_request.
libraries/microsoft-agents-hosting-aiohttp/microsoft_agents/hosting/aiohttp/cloud_adapter.py Adjusts imports to use HttpAdapterBase from core.
dev/integration/tests/jwt_validation/test_fastapi_jwt_validation.py Adds FastAPI JWT validation integration tests driven by env fixtures.
dev/integration/tests/jwt_validation/test_aiohttp_jwt_validation.py Adds aiohttp JWT validation integration tests driven by env fixtures.
dev/integration/tests/jwt_validation/jwt_required.env Adds env fixture for “auth required” JWT scenario.
dev/integration/tests/jwt_validation/jwt_anonymous.env Adds env fixture for “anonymous allowed” JWT scenario.
dev/integration/tests/jwt_validation/_helpers.py Adds helper to load AgentAuthConfiguration from env files.
dev/integration/tests/jwt_validation/init.py Adds license header for the integration test package.
dev/integration/tests/auth/test_oauth_continuation.py Adds end-to-end OAuth token-exchange continuation test.
dev/integration/tests/auth/auth.env Adds env fixture for OAuth continuation integration test.
dev/integration/pyproject.toml Adds hosting-aiohttp/hosting-fastapi dependencies for integration test env.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread tests/hosting_aiohttp/test_jwt_authorization_middleware.py Outdated
Comment thread dev/integration/tests/auth/test_oauth_continuation.py
Comment thread test_samples/fastapi/empty_agent.py
Copilot AI review requested due to automatic review settings July 22, 2026 23:10

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 2 comments.

Comments suppressed due to low confidence (3)

libraries/microsoft-agents-hosting-aiohttp/microsoft_agents/hosting/aiohttp/jwt_authorization_middleware.py:22

  • aiohttp middleware looks up the auth config via getattr(request.app, "agent_configuration"), but aiohttp applications store per-app values in the app mapping (e.g., app["agent_configuration"]). As a result, auth_config will always be None and _authorize_request will return a 500 even when the app is configured.
    auth_config = cast(
        AgentAuthConfiguration | None, getattr(request.app, "agent_configuration", None)
    )

tests/hosting_aiohttp/test_jwt_authorization_middleware.py:21

  • Tests set agent_configuration via the private aiohttp Application._state attribute. This is brittle and also doesn't match typical usage in other tests (app["agent_configuration"]), making the tests more coupled to aiohttp internals than necessary.
def _set_agent_configuration(app: web.Application, auth_config: AgentAuthConfiguration):
    app._state["agent_configuration"] = auth_config

test_samples/fastapi/empty_agent.py:88

  • empty_agent.py now binds uvicorn to 127.0.0.1, while authorization_agent.py still uses 0.0.0.0. This makes the samples inconsistent and prevents access from outside the local machine/container when running empty_agent.py.
    uvicorn.run(app, host="127.0.0.1", port=port)

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

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 37 out of 39 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (7)

libraries/microsoft-agents-hosting-aiohttp/microsoft_agents/hosting/aiohttp/jwt_authorization_middleware.py:22

  • aiohttp middleware reads AgentAuthConfiguration via getattr(request.app, "agent_configuration"), but the codebase commonly stores configuration on the app mapping (app["agent_configuration"]). This causes auth_config to be None and all requests to return a 500 in typical setups (e.g., test_samples/otel/src/start_server.py sets APP["agent_configuration"]).
    auth_config = cast(
        AgentAuthConfiguration | None, getattr(request.app, "agent_configuration", None)
    )

tests/hosting_aiohttp/test_jwt_authorization_middleware.py:21

  • Tests configure aiohttp agent auth using an attribute (app.agent_configuration), but the SDK samples and helpers typically use the application mapping key (app["agent_configuration"]). Using the mapping here ensures the test matches real-world usage and catches regressions in middleware configuration lookup.
    dev/integration/tests/jwt_validation/test_aiohttp_jwt_validation.py:102
  • This aiohttp integration test sets agent configuration via an attribute (app.agent_configuration), but the SDK commonly uses the app mapping key (app["agent_configuration"]). Aligning with the mapping avoids false confidence if the middleware only checks one storage location.
    app = web.Application(middlewares=[jwt_authorization_middleware])
    app.agent_configuration = auth_config
    app.router.add_get("/", _claims_handler)

dev/integration/tests/jwt_validation/test_aiohttp_jwt_validation.py:120

  • This aiohttp integration test sets agent configuration via an attribute (app.agent_configuration), but the SDK commonly uses the app mapping key (app["agent_configuration"]). Aligning with the mapping avoids false confidence if the middleware only checks one storage location.
    app = web.Application(middlewares=[jwt_authorization_middleware])
    app.agent_configuration = auth_config_with_invalid_audience(auth_config)
    app.router.add_get("/", _claims_handler)
    client = await aiohttp_client(app)

dev/integration/tests/auth/test_oauth_continuation.py:4

  • This new integration test file is missing the standard repository license header that other newly added test files include in this PR.
import asyncio
import time
from pathlib import Path
from typing import Optional

libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/authorization/jwt/_authorize_request.py:29

  • The docstring Return type refers to _JwtAuthorizationResult, which is not defined here and doesn’t match the function’s declared return type. This makes it harder for callers to understand what to expect.
    Returns:
        _JwtAuthorizationResult: The result of the authorization attempt.
    """

libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/http/init.py:10

  • Removing HttpAdapterBase from microsoft_agents.hosting.core.http breaks the previously exported import path for callers that rely on the http subpackage API surface. Consider re-exporting it here (even if the preferred import is now microsoft_agents.hosting.core.HttpAdapterBase) to preserve backward compatibility.
from ._http_request_protocol import HttpRequestProtocol
from ._http_response import HttpResponse, HttpResponseFactory
from ._channel_service_routes import ChannelServiceRoutes

__all__ = [

Comment thread dev/integration/tests/jwt_validation/test_aiohttp_jwt_validation.py
Copilot AI review requested due to automatic review settings July 23, 2026 16:11

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 37 out of 39 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (5)

libraries/microsoft-agents-hosting-aiohttp/microsoft_agents/hosting/aiohttp/jwt_authorization_middleware.py:22

  • aiohttp JWT middleware now only reads auth config via request.app.agent_configuration; existing samples and scenario harness in this repo set app["agent_configuration"]. With the current getattr-only lookup, auth_config becomes None and _authorize_request returns a 500, breaking JWT auth for those apps.
    auth_config = cast(
        AgentAuthConfiguration | None, getattr(request.app, "agent_configuration", None)
    )

libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/http/init.py:8

  • core.http no longer re-exports HttpAdapterBase. This is a backwards-incompatible API change for consumers importing HttpAdapterBase from microsoft_agents.hosting.core.http (the module docstring suggests this is a public surface). Consider re-exporting it here to preserve compatibility while still allowing the new import path.
from ._http_request_protocol import HttpRequestProtocol
from ._http_response import HttpResponse, HttpResponseFactory
from ._channel_service_routes import ChannelServiceRoutes

libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/authorization/jwt/_authorize_request.py:29

  • Docstring return type is incorrect (_JwtAuthorizationResult is not defined). Since the function returns ClaimsIdentity | HttpResponse, updating the docstring avoids confusion for callers and type readers.
    Returns:
        _JwtAuthorizationResult: The result of the authorization attempt.
    """

test_samples/fastapi/empty_agent.py:88

  • This FastAPI sample now binds Uvicorn to 127.0.0.1, while the other FastAPI sample still uses 0.0.0.0. Binding to localhost prevents access when running in containers/dev-tunnels; consider keeping 0.0.0.0 as the default (or making it configurable) for consistency and usability.
    dev/integration/tests/auth/test_oauth_continuation.py:1
  • This new integration test file is missing the standard MIT license header that other newly-added test files in this PR include. Add the header at the top for consistency and compliance.
import asyncio

Comment thread dev/integration/tests/utils/pytest.py
Copilot AI review requested due to automatic review settings July 23, 2026 17:27
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

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 41 out of 43 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (3)

libraries/microsoft-agents-hosting-aiohttp/microsoft_agents/hosting/aiohttp/jwt_authorization_middleware.py:22

  • aiohttp middleware only looks up agent_configuration via attribute access (getattr(request.app, "agent_configuration", ...)). Much of the repo (and the integration test in this PR) sets this config via the app mapping (app["agent_configuration"]), so auth_config will be None and requests will incorrectly return a 500 "configuration not found" response. Consider supporting both storage patterns for backward compatibility.
    auth_config = cast(
        AgentAuthConfiguration | None, getattr(request.app, "agent_configuration", None)
    )
    auth_header = request.headers.get("Authorization")

dev/integration/tests/utils/pytest.py:21

  • skip_if_no_var only checks for key presence, not whether the env var has a non-empty value. When load_root_env_file=True, dotenv_values may include keys with None values, causing tests to run and then fail later due to missing credentials. Consider treating missing/empty values as unset.
    if load_root_env_file:
        # Load environment variables from the root .env file if specified
        environ = {**os.environ, **dotenv_values(".env")}
    return pytest.mark.skipif(
        any(env_var not in (environ or os.environ) for env_var in env_vars),
        reason=f"Skipping test because one or more environment variables are not set: {', '.join(env_vars)}"
    )

dev/integration/tests/auth/test_oauth_continuation.py:4

  • This new integration test file is missing the standard MIT license header that other newly added test modules in this PR include.
import asyncio
import time
from pathlib import Path
from typing import Optional

Copilot AI review requested due to automatic review settings July 23, 2026 18:21
@rodrigobr-msft
Rodrigo Brandão (rodrigobr-msft) marked this pull request as ready for review July 23, 2026 18:23

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 41 out of 43 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (4)

dev/integration/tests/jwt_validation/test_aiohttp_jwt_validation.py:102

  • aiohttp Application configuration is stored via mapping keys (e.g., app["agent_configuration"]), but this test sets it as an attribute (app.agent_configuration). The JWT middleware reads request.app["agent_configuration"], so these tests will raise KeyError and never exercise token validation.
    token, auth_config = await acquire_real_service_connection_token()
    app = web.Application(middlewares=[jwt_authorization_middleware])
    app.agent_configuration = auth_config
    app.router.add_get("/", _claims_handler)

dev/integration/tests/jwt_validation/test_aiohttp_jwt_validation.py:119

  • Same issue as above: setting agent_configuration as an attribute won’t be seen by the middleware, which looks up request.app["agent_configuration"]. This will cause a KeyError and make the test fail before asserting the 401 response.
    token, auth_config = await acquire_real_service_connection_token()
    app = web.Application(middlewares=[jwt_authorization_middleware])
    app.agent_configuration = auth_config_with_invalid_audience(auth_config)
    app.router.add_get("/", _claims_handler)

dev/integration/tests/auth/test_oauth_continuation.py:2

  • This new integration test file is missing the standard repository license header that’s present in the other newly added test files in this PR. Add the MIT license header at the top for consistency and compliance.
import asyncio
import time

dev/integration/tests/utils/pytest.py:21

  • skip_if_no_var only checks whether environment variable keys exist, not whether they have values. If a variable is present but empty/None (common with .env entries like VAR=), tests will run and then fail later with confusing auth errors. Consider treating missing OR empty values as “not set”.
    return pytest.mark.skipif(
        any(env_var not in (environ or os.environ) for env_var in env_vars),
        reason=f"Skipping test because one or more environment variables are not set: {', '.join(env_vars)}"
    )

Copilot AI review requested due to automatic review settings July 23, 2026 18:25

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 41 out of 43 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (7)

dev/integration/tests/jwt_validation/test_aiohttp_jwt_validation.py:102

  • app.agent_configuration = ... sets an attribute, but the aiohttp JWT middleware reads the configuration from request.app["agent_configuration"]. This will raise KeyError at runtime and the test will never exercise JWT validation for real tokens.
    app = web.Application(middlewares=[jwt_authorization_middleware])
    app.agent_configuration = auth_config
    app.router.add_get("/", _claims_handler)

dev/integration/tests/jwt_validation/test_aiohttp_jwt_validation.py:119

  • Same issue as above: the middleware expects app["agent_configuration"], but this test sets app.agent_configuration, which will not be found and will crash before authorization is evaluated.
    token, auth_config = await acquire_real_service_connection_token()
    app = web.Application(middlewares=[jwt_authorization_middleware])
    app.agent_configuration = auth_config_with_invalid_audience(auth_config)
    app.router.add_get("/", _claims_handler)

dev/integration/tests/utils/pytest.py:19

  • skip_if_no_var only checks for key presence, not whether the variable has a usable value. If a variable is present but empty (common when loaded from a .env template), tests will run and then fail later instead of being skipped.
    if load_root_env_file:
        # Load environment variables from the root .env file if specified
        environ = {**os.environ, **dotenv_values(".env")}
    return pytest.mark.skipif(
        any(env_var not in (environ or os.environ) for env_var in env_vars),

dev/integration/tests/auth/test_oauth_continuation.py:4

  • This new integration test file is missing the repository-standard MIT license header at the top (many other newly added files in this PR include it).
import asyncio
import time
from pathlib import Path
from typing import Optional

libraries/microsoft-agents-hosting-fastapi/microsoft_agents/hosting/fastapi/jwt_authorization_middleware.py:65

  • request is None is not a case FastAPI will produce; if a route is decorated but does not include a Request parameter, the wrapper will raise TypeError before it can return this JSONResponse. Making the wrapper accept *args/**kwargs and locating Request defensively makes the decorator fail gracefully.
    libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/http/init.py:15
  • HttpAdapterBase is no longer re-exported from microsoft_agents.hosting.core.http. If this was previously part of the public surface (it was in __all__), removing it is a breaking change for consumers importing from the http subpackage. Consider re-exporting it here for backward compatibility.
from ._http_request_protocol import HttpRequestProtocol
from ._http_response import HttpResponse, HttpResponseFactory
from ._channel_service_routes import ChannelServiceRoutes

__all__ = [
    "HttpRequestProtocol",
    "HttpResponse",
    "HttpResponseFactory",
    "ChannelServiceRoutes",
]

test_samples/fastapi/empty_agent.py:88

  • Binding uvicorn to 127.0.0.1 prevents the sample from accepting connections from outside the local machine (e.g., Docker, dev tunnels, other devices). Most agent samples need to be reachable externally, so this default can make the sample appear broken in common setups.

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 23, 2026 18:32

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 41 out of 43 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (4)

dev/integration/tests/jwt_validation/test_aiohttp_jwt_validation.py:102

  • This test sets app.agent_configuration as an attribute, but the aiohttp JWT middleware reads configuration via request.app.get("agent_configuration") (mapping key). As written, auth_config will be None and the request should fail with a 500 config-missing response instead of 200.
    token, auth_config = await acquire_real_service_connection_token()
    app = web.Application(middlewares=[jwt_authorization_middleware])
    app.agent_configuration = auth_config
    app.router.add_get("/", _claims_handler)

dev/integration/tests/jwt_validation/test_aiohttp_jwt_validation.py:119

  • Same issue here: setting app.agent_configuration won’t be seen by the middleware (it uses request.app.get("agent_configuration")), so auth_config will be None and the test won’t exercise the invalid-audience path.
    token, auth_config = await acquire_real_service_connection_token()
    app = web.Application(middlewares=[jwt_authorization_middleware])
    app.agent_configuration = auth_config_with_invalid_audience(auth_config)
    app.router.add_get("/", _claims_handler)

libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/http/init.py:15

  • HttpAdapterBase is no longer re-exported from microsoft_agents.hosting.core.http, which is a backwards-incompatible API change for any callers importing it from this package. Consider keeping a compatibility re-export (optionally lazy) and listing it in __all__ so existing imports continue to work.
from ._http_request_protocol import HttpRequestProtocol
from ._http_response import HttpResponse, HttpResponseFactory
from ._channel_service_routes import ChannelServiceRoutes

__all__ = [
    "HttpRequestProtocol",
    "HttpResponse",
    "HttpResponseFactory",
    "ChannelServiceRoutes",
]

dev/integration/tests/auth/test_oauth_continuation.py:4

  • This new integration test module is missing the standard Microsoft copyright/MIT license header that appears in other files under dev/integration/tests/ (and is mentioned in the PR description).
import asyncio
import time
from pathlib import Path
from typing import Optional

Copilot AI review requested due to automatic review settings July 23, 2026 18:37

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 41 out of 43 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (7)

dev/integration/tests/jwt_validation/test_aiohttp_jwt_validation.py:101

  • In the aiohttp global-middleware tests, the app config is assigned via attribute access (app.agent_configuration), but the middleware reads it from app["agent_configuration"]/app.get(...). This will cause the middleware to treat the config as missing and return a 500 instead of exercising JWT validation.
    app.agent_configuration = auth_config

dev/integration/tests/jwt_validation/test_aiohttp_jwt_validation.py:118

  • Same issue as above: assigning agent_configuration as an attribute won’t be visible to the JWT middleware (it reads from app["agent_configuration"]). This test will likely get a 500 response instead of a 401.
    app.agent_configuration = auth_config_with_invalid_audience(auth_config)

dev/integration/tests/auth/test_oauth_continuation.py:1

  • This new integration test file is missing the standard Microsoft copyright/license header present in the other newly added test modules.
import asyncio

libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/http/init.py:15

  • HttpAdapterBase was removed from microsoft_agents.hosting.core.http exports. This is a potentially breaking import-path change for consumers doing from microsoft_agents.hosting.core.http import HttpAdapterBase. Consider re-exporting it here for backward compatibility (even if the preferred import becomes microsoft_agents.hosting.core).
from ._http_request_protocol import HttpRequestProtocol
from ._http_response import HttpResponse, HttpResponseFactory
from ._channel_service_routes import ChannelServiceRoutes

__all__ = [
    "HttpRequestProtocol",
    "HttpResponse",
    "HttpResponseFactory",
    "ChannelServiceRoutes",
]

test_samples/fastapi/empty_agent.py:88

  • This sample now binds uvicorn to 127.0.0.1, while the other FastAPI sample still uses 0.0.0.0. Binding only to localhost can break common sample scenarios (containers/dev tunnels/remote testing). If not intentional, use 0.0.0.0 for consistency and accessibility.
    dev/microsoft-agents-testing/microsoft_agents/testing/core/utils.py:56
  • The new load_sdk_config_connection helper has formatting issues (missing blank line between top-level defs and non-Black-compliant line breaks) that will trigger flake8 style warnings and reduce readability.
def load_sdk_config_connection(connection_name: str = "SERVICE_CONNECTION", env_path: str | Path = ".env") -> AgentAuthConfiguration:
    """Load an AgentAuthConfiguration from a .env file.

    :param connection_name: The connection name to look up in the .env file.
    :param env_path: Path to the .env file (default is ".env").

dev/microsoft-agents-testing/microsoft_agents/testing/core/utils.py:118

  • There should be two blank lines between top-level function definitions (PEP 8 / flake8 E305).
    return generate_token(client_id, client_secret, tenant_id)

def generate_token_from_auth_config(auth_config: AgentAuthConfiguration) -> str:
    """Generates a token using a provided AgentAuthConfiguration.

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

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 41 out of 43 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (5)

libraries/microsoft-agents-hosting-fastapi/microsoft_agents/hosting/fastapi/jwt_authorization_middleware.py:66

  • jwt_authorization_decorator currently defines wrapper(request: Request, *args, **kwargs) but then forces wrapper.__signature__ = inspect.signature(func). If the decorated route’s Request parameter has a different name (e.g. req: Request) or isn’t the first parameter, FastAPI will call the wrapper using the original parameter names and the wrapper will raise TypeError because the required request argument is missing. Make the wrapper accept only *args, **kwargs and extract the Request instance from the passed arguments.
    dev/integration/tests/jwt_validation/test_aiohttp_jwt_validation.py:102
  • These tests set app.agent_configuration = auth_config, but the aiohttp JWT middleware reads configuration via request.app.get("agent_configuration") (mapping-style). With attribute assignment the middleware will see auth_config=None and return a 500 response, so the test won’t exercise the intended path.
    token, auth_config = await acquire_real_service_connection_token()
    app = web.Application(middlewares=[jwt_authorization_middleware])
    app.agent_configuration = auth_config
    app.router.add_get("/", _claims_handler)

dev/integration/tests/jwt_validation/test_aiohttp_jwt_validation.py:119

  • These tests set app.agent_configuration = ..., but the aiohttp JWT middleware looks up configuration via request.app.get("agent_configuration") (mapping-style). Use app["agent_configuration"] = ... so the middleware receives the configuration and the test validates the intended behavior.
    token, auth_config = await acquire_real_service_connection_token()
    app = web.Application(middlewares=[jwt_authorization_middleware])
    app.agent_configuration = auth_config_with_invalid_audience(auth_config)
    app.router.add_get("/", _claims_handler)

dev/integration/tests/auth/test_oauth_continuation.py:4

  • This new test file is missing the standard Microsoft copyright/MIT license header that other new/updated test files include in this PR.
import asyncio
import time
from pathlib import Path
from typing import Optional

libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/http/init.py:15

  • HttpAdapterBase was removed from microsoft_agents.hosting.core.http exports. Any downstream code doing from microsoft_agents.hosting.core.http import HttpAdapterBase will now fail at import time. If the intent is to move the public import to microsoft_agents.hosting.core, consider keeping this re-export for backward compatibility (and deprecating it later) rather than removing it outright.
from ._http_request_protocol import HttpRequestProtocol
from ._http_response import HttpResponse, HttpResponseFactory
from ._channel_service_routes import ChannelServiceRoutes

__all__ = [
    "HttpRequestProtocol",
    "HttpResponse",
    "HttpResponseFactory",
    "ChannelServiceRoutes",
]

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

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 41 out of 43 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (6)

dev/integration/tests/jwt_validation/test_aiohttp_jwt_validation.py:102

  • aiohttp JWT middleware reads the auth config from request.app.get("agent_configuration") (dict-style). These tests set app.agent_configuration as an attribute, so the middleware will see None and return a 500 (missing config) instead of exercising real-token validation.
    token, auth_config = await acquire_real_service_connection_token()
    app = web.Application(middlewares=[jwt_authorization_middleware])
    app.agent_configuration = auth_config
    app.router.add_get("/", _claims_handler)

dev/integration/tests/jwt_validation/test_aiohttp_jwt_validation.py:119

  • Same issue as above: setting app.agent_configuration does not populate request.app["agent_configuration"], so the JWT middleware won't receive the intended configuration and the test will not validate the invalid-audience scenario.
    token, auth_config = await acquire_real_service_connection_token()
    app = web.Application(middlewares=[jwt_authorization_middleware])
    app.agent_configuration = auth_config_with_invalid_audience(auth_config)
    app.router.add_get("/", _claims_handler)

libraries/microsoft-agents-hosting-fastapi/microsoft_agents/hosting/fastapi/jwt_authorization_middleware.py:76

  • jwt_authorization_decorator forces a request: Request positional argument in the wrapper while also overriding __signature__ to match the wrapped function. If a route handler’s signature does not place request first (or omits it), FastAPI will call the wrapper using the overridden signature and this can raise a TypeError at runtime (or authorize the wrong argument). Extract the Request instance from args/kwargs instead of requiring a fixed first parameter.
    libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/http/init.py:9
  • HttpAdapterBase was removed from the microsoft_agents.hosting.core.http public re-exports. This is a breaking change for any consumers importing HttpAdapterBase from ...core.http. Consider re-exporting it here (even if the canonical import is now microsoft_agents.hosting.core.HttpAdapterBase) to preserve backward compatibility.
from ._http_request_protocol import HttpRequestProtocol
from ._http_response import HttpResponse, HttpResponseFactory
from ._channel_service_routes import ChannelServiceRoutes

dev/integration/tests/utils/pytest.py:21

  • skip_if_no_var only checks whether keys exist, not whether they have a usable value. If the variables are present but empty/whitespace (common in .env files), tests will run and likely fail with confusing auth errors. Treat empty values as missing when deciding to skip.
    if load_root_env_file:
        # Load environment variables from the root .env file if specified
        environ = {**os.environ, **dotenv_values(".env")}
    return pytest.mark.skipif(
        any(env_var not in (environ or os.environ) for env_var in env_vars),
        reason=f"Skipping test because one or more environment variables are not set: {', '.join(env_vars)}"
    )

dev/integration/tests/auth/test_oauth_continuation.py:5

  • This new integration test file is missing the standard MIT license header used throughout the repo (most other newly added test files in this PR include it).
import asyncio
import time
from pathlib import Path
from typing import Optional

@rodrigobr-msft
Rodrigo Brandão (rodrigobr-msft) merged commit 4adee93 into main Jul 24, 2026
11 checks passed
@rodrigobr-msft
Rodrigo Brandão (rodrigobr-msft) deleted the users/robrandao/fastapi-jwt branch July 24, 2026 21:36
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.

Provide JWT authorization middleware for hosting-fastapi package similar to hosting-aiohttp

3 participants