Skip to content

Foundry authentication and GPT-Live

Use an existing Foundry resource and deployed models. Set base_url to the API prefix https://RESOURCE.openai.azure.com/openai/v1, and model to the deployment name. This integration is GPT-Live, not Azure Voice Live or the Realtime API; their routes and session protocols are different.

Text with Microsoft Entra ID

FoundryProvider accepts a caller-created sync or async credential object. Async Azure Identity credentials are preferred for voice applications. Nagents has no runtime Azure SDK dependency, Azure extra, or mandatory Azure imports. azure-identity is declared only in the repository's development dependency group for SDK integration tests. Applications using Azure Identity install it themselves (pip install nagents azure-identity) and choose their credential:

import asyncio
import os
from azure.identity.aio import DefaultAzureCredential
from nagents import FoundryProvider
from nagents.types import Message


async def main():
    async with DefaultAzureCredential() as credential:
        async with FoundryProvider(
            base_url=os.environ["FOUNDRY_ENDPOINT"],
            model=os.environ["FOUNDRY_BACKEND_DEPLOYMENT"],
            credential=credential,
            scope=os.getenv("FOUNDRY_TOKEN_SCOPE", "https://ai.azure.com/.default"),
            api="responses",  # or chat_completions for supported deployments
        ) as provider:
            async for event in provider.generate([Message(role="user", content="Hello")]):
                print(event)


asyncio.run(main())

The small structural TokenCredential and AsyncTokenCredential protocols require get_token(*scopes: str) returning a token or an awaitable token, respectively. A token exposes a read-only .token: str. Both azure.identity.DefaultAzureCredential and azure.identity.aio.DefaultAzureCredential satisfy these contracts directly, as do the corresponding managed/workload/service-principal credentials. Synchronous get_token runs in a worker thread so voice and other event-loop tasks continue; async methods are awaited normally. Decorated methods returning awaitables are also supported. No inheritance from an Azure or Nagents class is required. FoundryProvider adapts this object to the shared internal bearer_token_provider seam, reusing text and Live transports rather than implementing another authentication transport. There is no Codex discovery.

Nagents calls get_token(scope) for every HTTP attempt (including retries), Live REST request, and WebSocket connection/attach/fork. The credential owns token caching and renewal; a fresh method invocation does not necessarily fetch a new token. Custom credentials must return a currently usable token. Tokens are never assigned to api_key. An established WebSocket is not reauthenticated mid-session; reconnect explicitly if the service requires it.

The caller owns the credential: keep it alive until all providers, agents, and Live connections have closed, then await credential.close() or exit its async context. For a sync credential, call its synchronous close() yourself. Cancellation propagates through token acquisition, but cannot stop an already running synchronous SDK call in its worker thread. Keep that credential alive until the call finishes, even after cancelling its awaiting task. The following sync alternative encloses asyncio.run, which drains its default executor before returning, so credential shutdown happens after pending worker calls finish:

from azure.identity import DefaultAzureCredential


async def run_with_credential(credential):
    async with FoundryProvider(
        base_url=os.environ["FOUNDRY_ENDPOINT"],
        model=os.environ["FOUNDRY_BACKEND_DEPLOYMENT"],
        credential=credential,
    ) as provider:
        async for event in provider.generate([Message(role="user", content="Hello")]):
            print(event)


with DefaultAzureCredential() as credential:
    asyncio.run(run_with_credential(credential))

Batch and Realtime reject token-provider auth rather than dropping it; unsupported text contracts also fail at construction. Authenticated endpoints are validated before token acquisition and redirects are not followed. Use HTTPS/WSS (loopback is allowed for offline testing).

API keys

provider = FoundryProvider(
    base_url=os.environ["FOUNDRY_ENDPOINT"],
    model=os.environ["FOUNDRY_BACKEND_DEPLOYMENT"],
    api_key=os.environ["FOUNDRY_API_KEY"],
)

Pass exactly one of credential or api_key; neither is implicitly discovered.

FoundryProvider selects the header by transport and authentication type:

Transport API key Entra credential
Text REST and Live REST/WebRTC Authorization: Bearer <key> Authorization: Bearer <token>
GPT-Live WS, including attach/fork api-key: <key> Authorization: Bearer <token>

OpenAI WebSockets retain Bearer authentication. The legacy Azure deployment provider still uses api-key. Credentials are never placed in query strings, and redirects remain blocked. Do not put a renewable Entra token into api_key.

Hosted and client GPT-Live

For repository development, install the SDK's dev group with poetry install --with dev -E dev -E voice. For application use, install your own SDK: pip install 'nagents[voice]' azure-identity python-dotenv. With this repository checked out:

export FOUNDRY_ENDPOINT=https://RESOURCE.openai.azure.com/openai/v1
export FOUNDRY_LIVE_DEPLOYMENT=YOUR_LIVE_DEPLOYMENT
export FOUNDRY_BACKEND_DEPLOYMENT=YOUR_RESPONSES_DEPLOYMENT
python examples/live/foundry.py --mode hosted --auth entra
python examples/live/foundry.py --mode client --auth entra
python examples/live/foundry.py --mode client --identity workload
python examples/live/foundry.py --mode hosted --identity managed
# Set FOUNDRY_API_KEY to use --auth key instead.

The example accepts --identity, --endpoint, --deployment, --backend, --scope, and --duration. Hosted mode sets LiveConfig(delegation="responses", backend_model=...): Foundry invokes the configured Responses deployment. Client mode supplies a separate Nagents delegation_agent, whose text requests acquire tokens independently. Its provider can be replaced with another backend. The credential context encloses both agents and their shutdown.

LiveAPI(provider) derives REST URLs from that same prefix. WebRTC setup is a trusted-backend call to create_webrtc(sdp, session); relay only the SDP answer to the browser. Do not send the resource key or Entra token to the browser. GPT-Live does not issue ephemeral client keys. WS uses the corresponding wss://RESOURCE.openai.azure.com/openai/v1/live/sessions route, including /{id}/attach and /{id}/fork suffixes.

Credential selection and lifecycle

Use --identity default for Azure's DefaultAzureCredential chain. The SDK tries configured environment credentials, workload identity, managed identity, then its enabled developer credential sources (for example Azure CLI). The exact chain depends on SDK version/platform/options. Nagents neither recreates that chain nor chooses an identity itself. Use SDK exclusion options to constrain DefaultAzureCredential, or the explicit choices below to avoid fallback:

credential = DefaultAzureCredential(
    exclude_environment_credential=True,
    exclude_workload_identity_credential=True,
    exclude_managed_identity_credential=True,
)  # Developer-source chain; still caller-owned, close after providers.

For deployment, configure the intended identity and grant it the appropriate data-plane role/access on the existing resource:

Identity Configuration / async credential option
Workload identity --identity workload: AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_FEDERATED_TOKEN_FILE; WorkloadIdentityCredential()
Managed identity --identity managed: ManagedIdentityCredential() for system-assigned; set AZURE_CLIENT_ID for user-assigned
Service principal --identity service-principal: AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_CLIENT_SECRET; ClientSecretCredential(tenant_id, client_id, client_secret)

Import these alternatives from azure.identity.aio, wrap the selected credential in async with, and pass it as FoundryProvider(credential=credential, ...). Do not commit secrets. Scope defaults in the example to https://ai.azure.com/.default; override FOUNDRY_TOKEN_SCOPE/--scope for the audience required by your cloud/resource.

For existing generic providers, the lower-level async bearer_token_provider callable remains available. Applications may use Azure's official azure.identity.aio.get_bearer_token_provider(credential, scope) with that seam; FoundryProvider accepts the credential directly and does not require this helper.

Service evidence and remaining assumptions

Offline tests verify routing, authentication freshness, shutdown and redirect blocking. Final Azure validation additionally passed actual native hosted and client GPT-Live inference with audio using API keys, DefaultAzureCredential with local login, ClientSecretCredential for a service principal, ManagedIdentityCredential on ACI, and WorkloadIdentityCredential on AKS. These results apply to the tested environment, not all regions or subscriptions.

SP/MI/WI tokens were app-only (idtyp=app, no scp); the tested flows do not require delegated-user tokens. MI/WI principal claims matched the created identities with no fallback. An initial SP 401 resolved after RBAC propagation.

Microsoft's WebRTC guide explicitly shows Bearer API-key authentication. Its WebSocket guide shows Bearer access-token authentication. A separate live Azure test found that Bearer API-key WS authentication returned a redirect, while the api-key header successfully completed hosted and client inference. FoundryProvider therefore uses api-key for API-key WebSocket connections, including attach/fork; it does not follow that redirect. REST key behavior remains as documented. The delegation guide documents hosted Responses and client modes. Token/session expiry, WebRTC and sideband, optional fork/recording/SIP operations, other tool/operation variants, and certificate credentials remain untested against Azure. Confirm those features and alternate scopes against your resource; endpoint derivation alone does not establish Azure feature parity.

All temporary test resources, identities, and role assignments were deleted and cleanup verified; existing resources were retained. The temporary gpt-live-1-test deployment was also deleted. Recreate a suitable deployment or configure an existing deployment before running these examples. See the validation matrix and cleanup record.