BerriAI/litellm · error · HTTPException

Microsoft Purview DLP: No proxy-authenticated user identity;

Error message

Microsoft Purview DLP: No proxy-authenticated user identity; bind user_id to the API key (caller-supplied metadata cannot be used for blocking DLP)

What it means

Fail-closed HTTPException from Purview blocking-mode identity resolution. A user identity WAS found, but only via caller-influenceable fields (metadata.user_id, safety_identifier, per-request metadata[user_id_field]). Because those let any caller impersonate another Entra user's Purview policy, blocking DLP refuses them and demands an identity bound to the API key on the proxy side.

Source

Thrown at litellm/proxy/guardrails/guardrail_hooks/microsoft_purview/purview_dlp.py:360

        user_api_key_dict: Any,
    ) -> str:
        """Resolve user ID for blocking (pre_call / post_call) DLP hooks.

        Uses only trusted proxy-authenticated sources (``_resolve_trusted_user_id``).
        Caller-supplied ``UserAPIKeyAuth.end_user_id`` (from request ``user``,
        ``metadata.user_id``, ``safety_identifier``, etc.) and
        ``metadata[user_id_field]`` are rejected (fail closed) because they can
        impersonate another Entra user's Purview policy.

        Raises ``HTTPException`` when no API-key-bound ``user_id`` exists or when
        only caller-influenceable identity fields are available (fail closed).
        """
        trusted_id: Final = self._resolve_trusted_user_id(data, user_api_key_dict)
        if trusted_id:
            return trusted_id

        if self._resolve_user_id(data, user_api_key_dict):
            raise HTTPException(
                status_code=400,
                detail={
                    "error": (
                        "Microsoft Purview DLP: No proxy-authenticated user identity; "
                        "bind user_id to the API key (caller-supplied metadata cannot "
                        "be used for blocking DLP)"
                    ),
                },
            )

        raise HTTPException(
            status_code=400,
            detail={
                "error": (
                    "Microsoft Purview DLP: No proxy-authenticated user identity; "
                    "bind user_id to the API key for blocking DLP"
                ),
            },

View on GitHub (pinned to 77b7c6c40c)

Solutions

  1. Bind the user to the virtual key at key-creation time: POST /key/generate with user_id (or /key/update for existing keys)
  2. With SSO/OIDC auth enabled, send requests with the logged-in session so user identity comes from proxy authentication, not the body
  3. After binding, stop relying on body-supplied user fields for these requests — they are deliberately ignored for blocking DLP

Example fix

# before: identity only in request body
client = OpenAI(api_key=VIRTUAL_KEY)
client.chat.completions.create(model="gpt-4o", messages=msgs, user="alice@corp.com")

# after: bind identity to the key, send plain request
# curl -X POST $PROXY/key/generate -H "Authorization: Bearer $ADMIN" \
#   -d '{"user_id": "alice@corp.com", "models": ["gpt-4o"], ...}'
client = OpenAI(api_key=ALICE_BOUND_KEY)
client.chat.completions.create(model="gpt-4o", messages=msgs)
Defensive patterns

Strategy: validation

Validate before calling

# Admin-side precheck: does this virtual key carry a bound user?
import httpx

def key_has_bound_user(proxy_url: str, key: str, admin_key: str) -> bool:
    info = httpx.get(
        f"{proxy_url}/key/info", params={"key": key},
        headers={"Authorization": f"Bearer {admin_key}"},
    ).json()
    return bool(info.get("key_info", {}).get("user_id"))

Try / catch

try:
    r = guarded_client.chat.completions.create(**params)
except BadRequestError as e:
    if "bind user_id to the API key" in str(e):
        # stop sending body-level user ids; provision a bound key instead
        raise ProvisioningError("route requires key-bound identity")
    raise

Prevention

When it happens

Trigger: Client sends 'user' or metadata.user_id in the /chat/completions body while the virtual key used has no bound user; per-request user param set but the key was created without user_id; only header-injected metadata supplies identity

Common situations: Migrating from another guardrail that trusted request-supplied user ids; teams relying on 'user' field for attribution suddenly adding Purview; SSO-less deployments where every caller shares one admin key

Understand the failure class

Related errors


AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18). Data as JSON: /api/errors/78897cd2928b2498. Report an issue: GitHub.