Skip to content

Python: [Feature]: Pass user identity to the AI Search Provider #6864

Description

@damienaicheh

Description

Today when we use the AI Search Provider using the agent framework package with the AzureAISearchContextProvider class we can't pass the token of the user to authenticate it to AI Search, this will be great to have it.

https://github.com/microsoft/agent-framework/blob/main/python/samples/02-agents/context_providers/azure_ai_search/search_context_agentic.py

Code Sample

Here is the workaround I did to implement it:


from collections.abc import Sequence

from agent_framework import Message
from agent_framework.azure import AzureAISearchContextProvider
from azure.core.credentials_async import AsyncTokenCredential

# Scope used to request a Microsoft Entra token for Azure AI Search query-time
# permission enforcement (the same token is forwarded as the query source identity).
SEARCH_TOKEN_SCOPE = "https://search.azure.com/.default"


class IdentityAwareAzureAISearchContextProvider(AzureAISearchContextProvider):
    """Azure AI Search provider that enforces document-level permissions at query time.

    The index was populated with ``GroupIds`` / ``UserIds`` permission fields that the
    indexer synchronizes from the source ADLS Gen2 ACLs. To have the service trim the
    retrieval results for the caller, every agentic retrieval request must carry the
    caller's Microsoft Entra token in the ``x-ms-query-source-authorization`` header.

    This subclass forwards that token (acquired from ``query_source_credential``, which
    defaults to the provider credential) on each agentic retrieve call.
    """

    def __init__(
        self,
        *args: object,
        query_source_credential: AsyncTokenCredential | None = None,
        **kwargs: object,
    ) -> None:
        super().__init__(*args, **kwargs)
        self._query_source_credential = query_source_credential or self.credential
        self._retrieve_patched = False

    async def _agentic_search(self, messages: Sequence[Message]) -> list[Message]:
        await self._ensure_knowledge_base()
        self._patch_retrieve_with_identity()
        return await super()._agentic_search(list(messages))

    def _patch_retrieve_with_identity(self) -> None:
        client = self._retrieval_client
        if self._retrieve_patched or client is None:
            return

        original_retrieve = client.retrieve
        query_source_credential = self._query_source_credential

        async def retrieve_with_identity(*args: object, **kwargs: object):
            if not kwargs.get("x_ms_query_source_authorization"):
                access_token = await query_source_credential.get_token(
                    SEARCH_TOKEN_SCOPE
                )
                kwargs["x_ms_query_source_authorization"] = access_token.token
            return await original_retrieve(*args, **kwargs)

        client.retrieve = retrieve_with_identity  # type: ignore[method-assign]
        self._retrieve_patched = True


And used it like this:



        aisearch_context_provider = IdentityAwareAzureAISearchContextProvider(
            source_id="search_provider",
            endpoint=search_endpoint,
            credential=credential,
            mode="agentic",
            knowledge_base_name=knowledge_base_name,
            knowledge_base_output_mode="answer_synthesis",
            retrieval_reasoning_effort="low",
        )

Language/SDK

Both

Metadata

Metadata

Assignees

No one assigned

    Labels

    pythonUsage: [Issues, PRs], Target: PythontriageUsage: [Issues], Target: All issues that still need to be triaged

    Fields

    No fields configured for Feature.

    Projects

    Status
    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions