Skip to content

Simplifying ClaimsIdentity anonymous state - #540

Open
Rodrigo Brandão (rodrigobr-msft) wants to merge 12 commits into
mainfrom
users/robrandao/claims-identity
Open

Simplifying ClaimsIdentity anonymous state#540
Rodrigo Brandão (rodrigobr-msft) wants to merge 12 commits into
mainfrom
users/robrandao/claims-identity

Conversation

@rodrigobr-msft

@rodrigobr-msft Rodrigo Brandão (rodrigobr-msft) commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

This pull request refactors the ClaimsIdentity class and its usage throughout the codebase to deprecate the is_authenticated property in favor of a new allow_anonymous property, simplifying the handling of anonymous and authenticated identities. It updates the logic for identity creation, token validation, and adapter methods to consistently use the new approach, and revises tests and warnings accordingly.

ClaimsIdentity Refactor and Deprecation:

  • Refactored ClaimsIdentity to deprecate the is_authenticated property and introduce allow_anonymous, with corresponding warnings for deprecated usage. Updated constructor and methods to support this change, and improved docstrings and type hints. (microsoft_agents/hosting/core/authorization/claims_identity.py)
  • Updated all instantiations and usages of ClaimsIdentity to remove the is_authenticated parameter and rely on the new logic for anonymous and authenticated identities. (jwt_token_validator.py, channel_service_adapter.py, _http_adapter_base.py, conversation.py) [1] [2] [3] [4] [5] [6] [7] [8] [9] [10]

Test and Assertion Updates:

  • Updated test assertions and test utilities in both aiohttp and FastAPI integration tests to check for allow_anonymous instead of authenticated, and to expect authentication_type to be None for anonymous requests. (test_aiohttp_jwt_validation.py, test_fastapi_jwt_validation.py) [1] [2] [3] [4] [5] [6] [7] [8]
  • Added a new test to verify that empty claims result in an anonymous identity. (test_conversation.py)

Deprecation Warning Management:

  • Added new warning filters to pytest.ini to suppress the newly introduced deprecation warnings for is_authenticated.

Minor API and Type Improvements:

  • Improved type hints and return types for several methods in ClaimsIdentity for better clarity and Python 3.10+ compatibility. [1] [2]

Test Adapter Updates:

  • Updated test adapters to remove the deprecated is_authenticated parameter when creating ClaimsIdentity objects. (test_aiohttp_cloud_adapter.py, test_fastapi_cloud_adapter.py) [1] [2]

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 identity/claims handling in hosting-core to simplify anonymous ClaimsIdentity usage, and modernizes the activity models with updated type annotations and safer defaults. It aims to streamline authentication flows (especially anonymous) and improve model robustness.

Changes:

  • Refactors ClaimsIdentity construction/usage across adapters and JWT validation, introducing an allow_anonymous helper.
  • Updates activity models to modern | None typing and uses default_factory=list to avoid mutable defaults.
  • Adjusts proactive conversation claims reconstruction to safely fall back to anonymous identities when claims are empty.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/channel_service_adapter.py Switches anonymous-auth decision logic to ClaimsIdentity.allow_anonymous and threads it into client creation.
libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/authorization/jwt/jwt_token_validator.py Updates validated/anonymous identity construction to the new ClaimsIdentity API.
libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/authorization/claims_identity.py Refactors ClaimsIdentity initialization and adds allow_anonymous.
libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/app/proactive/conversation.py Improves proactive identity reconstruction when claims are missing/empty.
libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/_http_adapter_base.py Defaults missing request identity to ClaimsIdentity() for anonymous scenarios.
libraries/microsoft-agents-activity/microsoft_agents/activity/agents_model.py Updates pick_properties to safely handle None inputs.
libraries/microsoft-agents-activity/microsoft_agents/activity/activity.py Modernizes field typings/defaults and refactors create_reply / trace helpers.
Suppressed comments (2)

libraries/microsoft-agents-activity/microsoft_agents/activity/activity.py:1017

  • get_conversation_reference currently declares an uninitialized activity_id and returns early, making the real ConversationReference construction below unreachable and returning an invalid reference.
        activity_id: str | None
        return ConversationReference(activity_id=activity_id)

libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/app/proactive/conversation.py:103

  • identity_from_claims uses the deprecated is_authenticated parameter, which now logs a warning on every call. Since is_authenticated is being deprecated, prefer setting authentication_type instead.
        if not claims:
            return ClaimsIdentity()
        return ClaimsIdentity(claims=dict(claims), is_authenticated=True)

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread libraries/microsoft-agents-activity/microsoft_agents/activity/activity.py Outdated
Copilot AI review requested due to automatic review settings August 14, 2026 16:33

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

Suppressed comments (3)

libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/app/proactive/conversation.py:47

  • Conversation.__init__ has a stray self.identity expression when claims is a ClaimsIdentity. This will raise AttributeError during construction and prevents proactive conversations from being created from a ClaimsIdentity.
        if isinstance(claims, ClaimsIdentity):
            self.claims: dict[str, str] = Conversation.claims_from_identity(claims)
            self.identity

libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/authorization/claims_identity.py:41

  • ClaimsIdentity.__init__ leaves is_authenticated as None when the caller omits it (e.g., JWT validation now constructs ClaimsIdentity(decoded_token, security_token=token)). Downstream code and tests treat validated identities as authenticated (is_authenticated is True), so this change can silently flip behavior. Consider deriving is_authenticated when it isn’t explicitly provided.
        self.claims = claims or {}
        if is_authenticated is not None:
            logger.warning(
                "The 'is_authenticated' parameter is deprecated and will be removed in future versions. Please use 'authentication_type' instead."
            )

libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/app/proactive/conversation.py:103

  • Conversation.identity_from_claims still uses the deprecated is_authenticated parameter, which will emit a warning on every call after the refactor. Since the identity already has non-empty claims, it can rely on ClaimsIdentity’s default authentication inference instead.
        if not claims:
            return ClaimsIdentity()
        return ClaimsIdentity(claims=dict(claims), is_authenticated=True)

Copilot AI review requested due to automatic review settings August 14, 2026 16:58
@rodrigobr-msft
Rodrigo Brandão (rodrigobr-msft) marked this pull request as ready for review August 14, 2026 16:58

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

Suppressed comments (4)

libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/authorization/claims_identity.py:60

  • allow_anonymous currently ignores the deprecated is_authenticated constructor parameter (stored in _is_authenticated). This breaks the intended compatibility behavior and causes cases like ClaimsIdentity(is_authenticated=True) with empty claims to still allow anonymous access (and will fail the added tests).
        """Returns True if the identity allows anonymous access, otherwise False."""
        return not self.authentication_type and not self.claims

    @property
    def is_authenticated(self) -> bool:

libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/authorization/claims_identity.py:130

  • get_token_audience is annotated as returning str | None, but the current implementation always returns a str (either the Agents SDK scope or an app://... audience). The optional return type needlessly forces callers to handle None and may introduce type-checking noise.
    libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/app/proactive/conversation.py:103
  • identity_from_claims still passes the deprecated is_authenticated=True parameter. This will emit a deprecation warning on every proactive identity reconstruction and contradicts the stated goal of removing reliance on deprecated auth flags; the identity will be considered authenticated based on non-empty claims anyway.
        if not claims:
            return ClaimsIdentity()
        return ClaimsIdentity(claims=dict(claims), is_authenticated=True)

libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/authorization/claims_identity.py:36

  • self.claims = claims or {} treats an explicitly provided empty dict the same as None and replaces it with a new dict. This is inconsistent with the behavior for non-empty dicts (where the passed object is preserved) and can surprise callers that pass {} intentionally.

This issue also appears in the following locations of the same file:

  • line 56
  • line 130
        self.claims = claims or {}

Copilot AI review requested due to automatic review settings August 14, 2026 17:02

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

Suppressed comments (2)

libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/authorization/claims_identity.py:44

  • claims or {} replaces a caller-provided empty dict with a new dict, which can unexpectedly drop reference identity and makes it impossible to intentionally pass an empty claims mapping. Prefer an explicit None check so ClaimsIdentity(claims={}) preserves the provided object while still avoiding shared defaults; also set _is_authenticated before later properties rely on it.
        self.claims = claims or {}
        if is_authenticated is not None:
            logger.warning(
                "The 'is_authenticated' parameter is deprecated and will be removed in future versions."
            )

libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/app/proactive/conversation.py:103

  • Conversation.identity_from_claims still passes the deprecated is_authenticated parameter, which will emit warnings for a normal (non-empty) claims restore path. Since non-empty claims already imply an authenticated identity in the new model, drop the deprecated parameter to avoid noisy logs and keep the API surface consistent.
        if not claims:
            return ClaimsIdentity()
        return ClaimsIdentity(claims=dict(claims), is_authenticated=True)

Copilot AI review requested due to automatic review settings August 14, 2026 17:19

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

Suppressed comments (4)

tests/hosting_core/authorization/test_claims_identity.py:97

  • ClaimsIdentity.is_authenticated emits a DeprecationWarning on access, so this assertion needs to capture the warning (or avoid the deprecated property) to keep tests passing under warnings-as-errors.
        assert identity.is_authenticated is expected

tests/hosting_core/authorization/test_claims_identity.py:39

  • This test currently accesses the deprecated is_authenticated property without capturing the emitted DeprecationWarning. With warnings-as-errors enabled, this will fail the test run.
        assert identity.security_token == "token"
        assert identity.is_authenticated is True

libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/authorization/claims_identity.py:44

  • self.claims = claims or {} will replace a caller-provided empty dict with a new dict (breaking object identity / mutation expectations). Also _is_authenticated is assigned but never used. Prefer an explicit None check and drop the unused attribute to avoid confusion and subtle bugs.
        self.claims = claims or {}
        if is_authenticated is not None:
            warnings.warn(
                "The 'is_authenticated' parameter is deprecated and will be removed in future versions.",
                DeprecationWarning,

libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/authorization/claims_identity.py:31

  • The authentication_type docstring currently says None means "not authenticated", but this class now derives authentication from presence of claims and uses allow_anonymous for anonymous handling. Updating the wording will prevent confusion for SDK consumers (especially since validated identities may have authentication_type=None).
        :param authentication_type: A string representing the type of authentication used.
            None values indicate that the identity is not authenticated.

Comment thread tests/hosting_core/app/proactive/test_conversation.py
Comment thread tests/hosting_core/authorization/test_claims_identity.py
Comment thread tests/hosting_core/authorization/test_claims_identity.py
Comment thread tests/hosting_core/authorization/test_claims_identity.py
Copilot AI review requested due to automatic review settings August 14, 2026 17: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 13 out of 13 changed files in this pull request and generated no new comments.

Suppressed comments (4)

libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/app/proactive/conversation.py:102

  • identity_from_claims still passes the deprecated is_authenticated parameter. Since authentication is now derived from the presence of claims, this argument is unnecessary and will emit a deprecation warning.
        if not claims:
            return ClaimsIdentity()
        return ClaimsIdentity(claims=dict(claims), is_authenticated=True)

libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/authorization/claims_identity.py:34

  • claims or {} will replace a caller-provided empty dict (or dict subclass) with a new dict, which is an observable behavior change. Use an explicit is None check so passing {} preserves the provided object while still avoiding shared defaults.
        self.claims = claims or {}

libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/authorization/claims_identity.py:57

  • allow_anonymous currently returns False when authentication_type is set to "Anonymous" (even if there are no claims). This changes behavior from the previous anonymous detection logic and can break callers/tests that still use authentication_type="Anonymous" to represent anonymous identities (e.g. tests/hosting_core/authorization/test_authorize_request.py:44). Consider treating "Anonymous" as anonymous during the deprecation window.
    def allow_anonymous(self) -> bool:
        """Returns True if the identity allows anonymous access, otherwise False."""
        return not self.authentication_type and not self.claims

libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/authorization/claims_identity.py:131

  • get_token_audience is annotated as returning str | None, but the current implementation always returns a str (either app://... or AuthenticationConstants.AGENTS_SDK_SCOPE). Consider tightening the return type to avoid forcing callers into unnecessary None handling.
    def get_token_audience(self) -> str | None:

Copilot AI review requested due to automatic review settings August 14, 2026 18:33

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 no new comments.

Suppressed comments (3)

libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/authorization/claims_identity.py:54

  • get_claim_value() is annotated to return str | None, but claims values may be non-strings (JWT numeric timestamps, arrays, etc.). Returning object | None better reflects reality and avoids misleading callers.
    def get_claim_value(self, claim_type: str) -> str | None:
        """Gets the value of a specific claim type from the claims dictionary.

        :param claim_type: The type of claim to retrieve.
        :return: The value of the claim if found, otherwise None.
        """
        return self.claims.get(claim_type)

libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/authorization/claims_identity.py:46

  • self._is_authenticated is assigned but never read anywhere, which makes the deprecation path harder to reason about and suggests stale state is being kept around unnecessarily.
        self.authentication_type = authentication_type
        self.security_token = security_token
        self._is_authenticated = is_authenticated

libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/authorization/claims_identity.py:17

  • ClaimsIdentity.claims is typed as dict[str, str], but JwtTokenValidator.validate_token() passes the full decoded JWT payload (which can include non-string values like ints/lists, e.g. exp, nbf, or aud arrays). This makes the public type hints inaccurate and can cause type-checking friction for SDK consumers.

This issue also appears on line 48 of the same file.

    claims: dict[str, str]
    authentication_type: str | None
    security_token: str | None  # deprecated, will be removed in future versions

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.

Anonymous mode: process_proactive never passes use_anonymous, so proactive continuation fails with 'TENANT_ID is not set'

2 participants