Share via

App registration, api permission for https://ai.azure.com for on behalf of flow

Marcus Vinicius Cursino Suares 20 Reputation points
2026-03-26T17:48:58.2566667+00:00

I am attempting to use the On-Behalf-Of flow to access a Foundry project on behalf of a user who is authenticated in my application.

However, it appears that this is not supported, as there is no available mechanism to grant admin consent for the required scope. I would like to confirm whether this limitation is intentional and, if so, understand the rationale behind this design.

Here is a simple example of what I am trying to do:

from azure.ai.projects import AIProjectClient
from azure.ai.agents.models import ListSortOrder
from azure.identity import AzureCliCredential, OnBehalfOfCredential
import time

TENANT_ID = "<YOUR_TENANT_ID>"
CLIENT_ID = "<YOUR_CLIENT_ID>"
CLIENT_SECRET = "<YOUR_CLIENT_SECRET>"

PROJECT_ENDPOINT = "https://<YOUR_FOUNDRY_ENDPOINT>/api/projects/<YOUR_PROJECT_ID>"
PROJECT_HEADER = "<YOUR_PROJECT_HEADER>"
AGENT_ID = "<YOUR_AGENT_ID>"
MODEL_NAME = "<YOUR_MODEL_NAME>"
USER_PROMPT = "<YOUR_USER_PROMPT>"


def build_obo_credential() -> OnBehalfOfCredential:
    cli_credential = AzureCliCredential()

    app_id_uri_scope = f"api://{CLIENT_ID}/.default"
    user_token = cli_credential.get_token(app_id_uri_scope)

    return OnBehalfOfCredential(
        tenant_id=TENANT_ID,
        client_id=CLIENT_ID,
        client_secret=CLIENT_SECRET,
        user_assertion=user_token.token,
    )


def main() -> None:
    credential = build_obo_credential()

    project = AIProjectClient(
        credential=credential,
        endpoint=PROJECT_ENDPOINT,
    )

    agent = project.agents.get_agent(AGENT_ID)

    start = time.time()
    thread = project.agents.threads.create(
        messages=[
            {
                "role": "user",
                "content": USER_PROMPT,
            }
        ]
    )
    print(f"Created thread, ID: {thread.id} (took {time.time() - start:.2f}s)")

    start = time.time()
    run = project.agents.runs.create_and_process(
        thread_id=thread.id,
        agent_id=agent.id,
        model=MODEL_NAME,
    )
    print(f"Run created and processed (took {time.time() - start:.2f}s)")

    if run.status == "failed":
        print(f"Run failed: {run.last_error}")
    else:
        start = time.time()
        messages = project.agents.messages.list(
            thread_id=thread.id,
            order=ListSortOrder.ASCENDING,
        )
        print(f"Messages listed (took {time.time() - start:.2f}s)")

        for message in messages:
            if message.text_messages:
                print(f"{message.role}: {message.text_messages[-1].text.value}")

    project.agents.threads.delete(thread.id)


if __name__ == "__main__":
    main()

Azure OpenAI Service
Azure OpenAI Service

An Azure service that provides access to OpenAI’s GPT-3 models with enterprise capabilities.

{count} votes

1 answer

Sort by: Most helpful
  1. Q&A Assist
    2026-03-26T17:49:21.9766667+00:00
    AI generated content. This question contains an answer created with AI from Q&A Assist. Learn more

    The On-Behalf-Of (OBO) flow requires that the middle-tier application can obtain delegated permissions to the downstream API via consent (user or admin) and that the resource exposes those delegated scopes and supports OBO.

    From the provided information:

    1. OBO prerequisites
      • The downstream resource must expose delegated scopes.
      • An admin must be able to grant consent for those delegated scopes to the middle-tier app, or the resource must preauthorize the app.
      • The middle-tier then uses OnBehalfOfCredential (or a raw OBO token request) to redeem the incoming user token for a new token to the downstream API.
      This is the pattern described for OBO:
      • The middle-tier sends a token request with:
        • grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer
        • requested_token_use=on_behalf_of
        • assertion = user token
        • scope = delegated scopes of the downstream API
      • The app must have admin consent for those delegated permissions beforehand.

      “Because this is not an interactive authentication flow, an application using it must have admin consent for any delegated permissions before requesting tokens for them.”

    2. Consent and .default behavior
      • .default represents the app’s statically configured permissions (admin-consented) for a resource.
      • It cannot be combined with other delegated scopes in the same request.
      • For OBO, the middle-tier app must already have the delegated permissions consented in Entra ID.

      “You must not combine .default with other delegated scopes… This will result in AADSTS70011 errors… When in doubt, split the token requests to avoid scope-type conflicts.”

    3. Implication for https://ai.azure.com / Foundry
      • If the Foundry/https://ai.azure.com resource does not expose delegated scopes in Entra ID and does not provide a way to grant admin consent for those scopes, then it does not support the standard delegated-permission/OBO pattern described in the Microsoft identity platform docs.
      • In that case, OBO to that resource is effectively not supported, because the mandatory building blocks (delegated scopes + admin consent) are missing.
    4. Intent and rationale (from the identity platform perspective)
      • The identity platform’s OBO design assumes:
        • A resource explicitly defines delegated scopes.
        • Tenants can control which apps may act on behalf of users via consent.
      • If a resource chooses not to expose delegated scopes (for example, only supporting application permissions or its own token model), that is a resource-level design decision, not a limitation of the OBO protocol itself.
      • The rationale typically is:
        • Tighter control over who can act on behalf of users.
        • Preference for app-only or project-level authorization models instead of user-delegated access.

    Given the above, the behavior observed (no way to grant admin consent for a delegated scope for https://ai.azure.com / Foundry) aligns with a resource that does not support OBO with delegated permissions. In such a case, the supported pattern is usually application-level authentication (client credentials) rather than OBO.

    To use OBO successfully with a downstream API, the downstream resource must:

    1. Publish delegated scopes in Entra ID.
    2. Allow admin consent for those scopes to the middle-tier app.
    3. Accept OBO token requests as described in the Microsoft identity platform OBO documentation.

    If any of these are missing for https://ai.azure.com, OBO is not available for that resource.


    References:

    0 comments No comments

Your answer

Answers can be marked as 'Accepted' by the question author and 'Recommended' by moderators, which helps users know the answer solved the author's problem.