JWT auth middleware refactor - #497
Conversation
…into users/robrandao/fastapi-jwt
…into users/robrandao/fastapi-jwt
…into users/robrandao/fastapi-jwt
There was a problem hiding this comment.
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_requestand 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 +
.envfixtures 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.
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
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__ = [
There was a problem hiding this comment.
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
HttpAdapterBasefrommicrosoft_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 (
_JwtAuthorizationResultis not defined). Since the function returnsClaimsIdentity | 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
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
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_configurationvia 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_varonly checks for key presence, not whether the env var has a non-empty value. Whenload_root_env_file=True,dotenv_valuesmay include keys withNonevalues, 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
There was a problem hiding this comment.
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
aiohttpApplication 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 readsrequest.app["agent_configuration"], so these tests will raiseKeyErrorand 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_configurationas an attribute won’t be seen by the middleware, which looks uprequest.app["agent_configuration"]. This will cause aKeyErrorand 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_varonly checks whether environment variable keys exist, not whether they have values. If a variable is present but empty/None (common with.enventries likeVAR=), 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)}"
)
There was a problem hiding this comment.
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 fromrequest.app["agent_configuration"]. This will raiseKeyErrorat 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 setsapp.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_varonly 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 Noneis not a case FastAPI will produce; if a route is decorated but does not include aRequestparameter, the wrapper will raiseTypeErrorbefore it can return this JSONResponse. Making the wrapper accept*args/**kwargsand locatingRequestdefensively makes the decorator fail gracefully.
libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/http/init.py:15HttpAdapterBaseis no longer re-exported frommicrosoft_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 thehttpsubpackage. 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>
There was a problem hiding this comment.
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_configurationas an attribute, but the aiohttp JWT middleware reads configuration viarequest.app.get("agent_configuration")(mapping key). As written,auth_configwill beNoneand 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_configurationwon’t be seen by the middleware (it usesrequest.app.get("agent_configuration")), soauth_configwill beNoneand 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
HttpAdapterBaseis no longer re-exported frommicrosoft_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
There was a problem hiding this comment.
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 fromapp["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_configurationas an attribute won’t be visible to the JWT middleware (it reads fromapp["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
HttpAdapterBasewas removed frommicrosoft_agents.hosting.core.httpexports. This is a potentially breaking import-path change for consumers doingfrom microsoft_agents.hosting.core.http import HttpAdapterBase. Consider re-exporting it here for backward compatibility (even if the preferred import becomesmicrosoft_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 uses0.0.0.0. Binding only to localhost can break common sample scenarios (containers/dev tunnels/remote testing). If not intentional, use0.0.0.0for consistency and accessibility.
dev/microsoft-agents-testing/microsoft_agents/testing/core/utils.py:56 - The new
load_sdk_config_connectionhelper 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.
There was a problem hiding this comment.
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_decoratorcurrently defineswrapper(request: Request, *args, **kwargs)but then forceswrapper.__signature__ = inspect.signature(func). If the decorated route’sRequestparameter 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 raiseTypeErrorbecause the requiredrequestargument is missing. Make the wrapper accept only*args, **kwargsand extract theRequestinstance 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 viarequest.app.get("agent_configuration")(mapping-style). With attribute assignment the middleware will seeauth_config=Noneand 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 viarequest.app.get("agent_configuration")(mapping-style). Useapp["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
HttpAdapterBasewas removed frommicrosoft_agents.hosting.core.httpexports. Any downstream code doingfrom microsoft_agents.hosting.core.http import HttpAdapterBasewill now fail at import time. If the intent is to move the public import tomicrosoft_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",
]
There was a problem hiding this comment.
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
aiohttpJWT middleware reads the auth config fromrequest.app.get("agent_configuration")(dict-style). These tests setapp.agent_configurationas an attribute, so the middleware will seeNoneand 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_configurationdoes not populaterequest.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_decoratorforces arequest: Requestpositional argument in the wrapper while also overriding__signature__to match the wrapped function. If a route handler’s signature does not placerequestfirst (or omits it), FastAPI will call the wrapper using the overridden signature and this can raise aTypeErrorat runtime (or authorize the wrong argument). Extract theRequestinstance from args/kwargs instead of requiring a fixed first parameter.
libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/http/init.py:9HttpAdapterBasewas removed from themicrosoft_agents.hosting.core.httppublic re-exports. This is a breaking change for any consumers importingHttpAdapterBasefrom...core.http. Consider re-exporting it here (even if the canonical import is nowmicrosoft_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_varonly checks whether keys exist, not whether they have a usable value. If the variables are present but empty/whitespace (common in.envfiles), 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
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:
dev/integration/tests/auth/test_oauth_continuation.py.dev/integration/tests/jwt_validation/test_aiohttp_jwt_validation.py.dev/integration/tests/jwt_validation/_helpers.py.Test configuration and dependencies:
dev/integration/pyproject.tomlto add new dependencies required for the tests, includingmicrosoft-agents-authentication-msal,microsoft-agents-hosting-aiohttp, andmicrosoft-agents-hosting-fastapi.auth.envfor OAuth tests,jwt_anonymous.envandjwt_required.envfor JWT validation tests. [1] [2] [3]pythonpathinpytest.inito improve test discovery.CI/CD pipeline updates:
microsoft_agents_hosting_fastapiwheel during test setup. (.github/workflows/python-package.yml,.azdo/ci-pr.yaml) [1] [2]