tech#entra-id#microsoft-foundry#azure#authentication#multitenant

Building a multi-tenant Microsoft Foundry chat with Microsoft Entra ID

Published 👁 —
Building a multi-tenant Microsoft Foundry chat with Microsoft Entra ID

Even within the same corporate group, each group company may manage its employees in a separate Microsoft Entra ID tenant. This article covers a scenario in which several customer companies share one AI chat service operated by a service provider.

Rather than consolidating users as guests in the service provider’s Microsoft Entra ID tenant, the solution uses the employee accounts managed by each customer company. It combines a multi-tenant App Registration for organizational accounts with an Enterprise Application created in each customer tenant. Identity management, including MFA, Conditional Access, and account deactivation when an employee leaves, remains with each customer company, while the service provider’s API restricts access to an allowlist of Tenant IDs.

This article describes the architecture used for a Microsoft Foundry chat demo, how to add a tenant, the validation required by the API, and the causes of the HTTP 403 errors encountered during implementation. All environment-specific values, including Tenant IDs, Application IDs, and resource names, have been replaced with placeholders.

Table of Contents

Key takeaways

We needed to meet four requirements:

For these requirements, a multi-tenant App Registration for standard Workforce tenants was a better fit than an external tenant in Microsoft Entra External ID.

The service provider owns one App Registration. When a customer company consents to the app, a Service Principal is created in that company’s tenant and can be managed as an Enterprise Application in the Microsoft Entra admin center. Users remain Member users in their customer company’s tenant and are not copied into the service provider’s tenant.

However, configuring an app as multi-tenant does not by itself prevent arbitrary organizations from using it. This architecture limits access at three layers.

Control pointWhat it controlsImplementation in this architecture
Customer company’s home tenantIdentity verification and sign-in conditionsUse the customer company’s MFA, Conditional Access, and account lifecycle management
Service provider’s APIOrganizations allowed to use the serviceCompare the validated access token’s tid with a Tenant ID allowlist
Customer company’s Enterprise ApplicationUsers allowed within the tenantEnable assignment requirements as needed and assign users or groups

The browser also checks the Tenant ID so it can quickly tell users that their organization is not allowed. This improves the user experience, but the security boundary is the API-side token validation and Tenant ID check.

The connection to Foundry authenticates with a user-assigned Managed Identity attached to the App Service and Azure RBAC, while a Private Endpoint restricts the network path. End users do not need a Foundry API key or Azure RBAC permissions.

Requirements and architecture

We built a lightweight AI chat service operated as one system by the service provider. Users from multiple customer companies can sign in with their own Microsoft Entra ID accounts.

Architecture diagram showing a multi-tenant App Registration in the service provider's Entra ID and Enterprise Applications in customer companies A and B, allowing employees managed by each company to use a shared web app with their existing company accounts
Users remain in their customer company’s Entra ID. Each customer company’s Enterprise Application represents its relationship with the service provider’s App Registration.
ItemArchitecture used
UsersEmployees in the Workforce tenants of allowed customer companies
Web appASP.NET Core 10 on Azure App Service for Linux
FrontendSPA, MSAL Browser, and Authorization Code Flow with PKCE
APIPOST /api/chat, served from the same App Service as the SPA
AuthenticationMulti-tenant App Registration for organizational accounts
Delegated API scopeaccess_as_user
Organization authorizationCompare tid with a Tenant ID allowlist in the API
Modelgpt-4o-mini deployed in Microsoft Foundry
Foundry authenticationUser-assigned Managed Identity and Azure RBAC
Foundry networkPublic Network Access disabled; access restricted to a Private Endpoint
MonitoringApplication Insights and Log Analytics

The request flow is as follows:

  1. The user signs in through their customer company’s Microsoft Entra ID.
  2. The customer company’s tenant evaluates MFA and Conditional Access.
  3. The SPA obtains an access token for the Web API.
  4. The API validates the signature, issuer, audience, expiration, access_as_user scope, and tid.
  5. App Service uses its Managed Identity to obtain a token for Foundry.
  6. The app calls the model through App Service VNet integration and the Private Endpoint.

Only the web app is exposed to the internet. Foundry’s data plane cannot be reached from the public network.

App Registration and Enterprise Application roles

Understanding the difference between an App Registration and an Enterprise Application is central to this architecture.

Microsoft documentation describes the Application object as the app’s global definition and the Service Principal as its local representation in each tenant. An Application object has a one-to-many relationship with Service Principals.

ObjectWhere it is createdRole
Application objectService provider’s owning tenantHolds the app-wide definition, including redirect URIs, exposed scopes, and supported account types
Service PrincipalEach customer company’s tenantHolds tenant-specific settings such as consent, user assignments, and sign-in controls
Enterprise ApplicationDisplayed in the Microsoft Entra admin centerProvides the interface for managing a Service Principal within a tenant

When a customer company’s administrator grants consent, a Service Principal is created in that company’s tenant. The company can then assign users to its Enterprise Application or disable sign-in.

The object created is an instance of the app, not a copy of an employee account. Users continue to sign in as Member users in their own tenant.

Configure Microsoft Entra ID

We created one app registration in the service provider’s Azure management tenant.

SettingValue
NameAn app name users can easily recognize, such as Group Corp AI Chat Demo
Supported account typesMultiple Entra ID tenants
signInAudienceAzureADMultipleOrgs
Redirect URI platformSingle-page application (SPA)
SPA Redirect URIhttps://<APP_HOST>/, exactly matching MSAL’s redirectUri
API Identifier URIapi://<APPLICATION_ID>
Delegated scopeaccess_as_user
Who can consent?Admins and users
API permissionsDelegated access_as_user permission for the app’s own API
MSAL authorityhttps://login.microsoftonline.com/organizations
Client secretDo not create one

In the current Microsoft Entra admin center, select Multiple Entra ID tenants under Supported account types when registering the app. This corresponds to AzureADMultipleOrgs in the manifest’s signInAudience. Do not select Any Entra ID Tenant + Personal Microsoft accounts, which also allows personal Microsoft accounts, because this architecture targets only organizational accounts from customer companies.

If you enter a Redirect URI during registration, select Single-page application (SPA) as the platform. You can also omit it during registration and add it later under Authentication. In either case, the URI must exactly match the Redirect URI in the SPA’s authentication request, including the trailing / and any path.

The SPA requests a scope in this form:

api://<APPLICATION_ID>/access_as_user

After exposing the scope under Expose an API, open API permissions, Add a permission, and then My APIs. Add access_as_user from the same app’s Delegated permissions. This registers access_as_user as a permission required for the app’s basic operation and lets the customer company’s administrator review the consent request. If permissions change later, users or tenants that previously consented may need to consent again.

A SPA is a public client that runs in the browser and cannot securely store a client secret. We used Authorization Code Flow with PKCE to obtain an access token for the API on behalf of the signed-in user.

The organizations authority is an entry point that accepts any Microsoft Entra organizational account. Using it alone does not express which customer companies are allowed to use the app.

Check allowed tenants at two layers

The API uses Microsoft.Identity.Web to validate access tokens.

We pass the Tenant ID allowlist as an array in the App Service settings.

AllowedTenantIds__0=<TENANT_ID_A>
AllowedTenantIds__1=<TENANT_ID_B>
AllowedTenantIds__2=<TENANT_ID_C>

The API compares the Tenant ID claim from the framework-validated access token with the allowlist and returns HTTP 403 for a tenant that is not allowed.

We use Microsoft.Identity.Web’s standard functionality to validate the scope.

app.MapPost("/api/chat", ChatAsync)
    .RequireAuthorization()
    .RequireScope("access_as_user");

Microsoft identity platform documentation states that a multi-tenant API must associate tid with iss and verify the issuer of the signing key when validating a token. Do not simply decode a JWT and trust the claim strings.

The SPA also checks the account.tenantId in the MSAL sign-in result. For an organization that is not allowed, it does not set an active account or enable the chat input. Browser-side behavior can be modified, however, so this check is not a reason to omit API-side validation.

If unauthorized organizations must be blocked before token issuance, another option is to ask users to select their company before sign-in and direct them to the Tenant ID-specific authority for an allowed tenant.

Onboard a customer company

We followed these steps to add a customer company:

  1. Confirm the customer company’s Tenant ID.
  2. Add the Tenant ID to the web app’s AllowedTenantIds.
  3. Have an administrator in the target tenant consent to access_as_user.
  4. Confirm that an Enterprise Application was created in the target tenant.
  5. Sign in with an account from the allowed customer company and verify the API call.
  6. Confirm that an API call from a tenant that is not allowed returns HTTP 403.

The admin consent URL has this format:

https://login.microsoftonline.com/<TENANT_ID>/v2.0/adminconsent
  ?client_id=<APPLICATION_ID>
  &scope=api%3A%2F%2F<APPLICATION_ID>%2Faccess_as_user
  &redirect_uri=<URL_ENCODED_REDIRECT_URI>

Remove the line breaks from the actual URL. Admin consent is a privileged operation, so the customer company’s administrator should review the requested scope and its purpose. Microsoft documentation likewise recommends carefully reviewing requested permissions before granting tenant-wide admin consent.

Restrict users within each customer company

A Tenant ID allowlist does not control which people within an allowed company may use the app. To limit access to specific employees or groups, configure the Enterprise Application in each customer company’s tenant.

  1. In the Microsoft Entra admin center, open Entra ID and then Enterprise applications.
  2. Open Properties for the app.
  3. Set Assignment required? to Yes.
  4. Under Users and groups, assign the users or groups that may use the app.
  5. Verify the behavior for both assigned and unassigned users.

When assignment is required, users who have not been assigned are denied sign-in and access token issuance. Microsoft documentation also states that user consent is not allowed for an app with this setting enabled, so tenant-wide admin consent is required.

To enforce app-specific roles in the API, define app roles in the App Registration and validate the access token’s roles claim. You must then assign those roles to users or groups through each customer company’s Enterprise Application.

For this demo, we prioritized verifying cross-tenant behavior. The API therefore requires only access_as_user and a Tenant ID on the allowlist. Per-user controls can be added through each customer company’s Enterprise Application.

Call Foundry without keys over a private network

End-user authentication and authentication from App Service to Foundry are separate concerns.

The end user’s access token is for calling the Web API. We do not forward it to Foundry. Instead, the App Service uses its assigned user-assigned Managed Identity.

  1. Assign a user-assigned Managed Identity to the App Service.
  2. Disable local key authentication on the Foundry resource.
  3. Assign the Cognitive Services OpenAI User role to the Managed Identity.
  4. Use DefaultAzureCredential in the app to obtain a Microsoft Entra token.
  5. Disable Public Network Access for Foundry.
  6. Enable VNet integration for the App Service, using a dedicated subnet.
  7. Create a Foundry Private Endpoint in a separate subnet.
  8. Configure the required Private DNS Zone and VNet Link.

Microsoft documentation explains how to use Microsoft Entra ID and Azure RBAC for keyless authentication to Foundry Models, and how to disable key authentication after confirming compatibility. App Service VNet integration provides outbound access from the app to the VNet.

Managed Identity controls which workload may call the service. Private Endpoint controls which network path can reach it. We combined authentication and network controls instead of substituting one for the other.

This architecture does not give end users any of the following information or permissions:

Why we did not use External ID

We also considered an external tenant in Microsoft Entra External ID, but these users are employees in customer companies’ Workforce tenants, not general consumers.

Decision factorMulti-tenant App RegistrationExternal tenant in External ID
Primary usersMicrosoft Entra organizational accounts, including accounts from other companiesCustomers or general users of the company’s own app
Where users resideRemain in the customer company’s Workforce tenantCustomer users are created in the external tenant
Customer company’s MFA and Conditional AccessRemain under the customer company’s home tenantDesigned separately as customer policies in the external tenant
User access controls for each customer companyAssign users through the customer company’s Enterprise ApplicationCentrally managed in the external tenant
Fit for these requirementsFits the requirement to use each customer company’s existing identitiesDoes not fit because users would be created in a separate directory

External ID is not inferior; it serves a different audience and places responsibility for identity management elsewhere. We chose External ID for apps where customers register through self-service, and a multi-tenant App Registration when existing organizational accounts should remain under the customer company’s management.

For more about External ID and its implementation, see Introduction to Microsoft Entra External ID (2026 edition) and Building an authentication platform with email OTP registration.

Implementation pitfalls

HTTP 403 after authentication

Sign-in and JWT validation succeeded, but requests returned HTTP 403 before reaching the chat API. There were two causes:

For the demo, we simplified the required conditions to access_as_user plus the Tenant ID allowlist and used Microsoft.Identity.Web’s RequireScope for scope validation. We determined how to retrieve the Tenant ID only after inspecting the claim set that the framework actually produced.

Tenant IDs added to the settings do not take effect

After we added the allowlist to the App Service settings, the running app sometimes continued to return the old list.

We resolved this by saving the entire array at once, restarting the App Service, and using GET /api/config to confirm which Tenant IDs had been loaded at runtime. A diagnostic API was useful because it showed not just the configured values, but the values the app had actually loaded.

If a diagnostic API returns Tenant IDs, limit the exposure of environment details by restricting it to authenticated administrators, masking the values, or disabling it after verification.

Foundry and model deployments conflict

When we reapplied the Bicep deployment, the model child resource began updating while the Foundry account was still being updated, resulting in AccountProvisioningStateInvalid.

We resolved the issue by making the model deployment explicitly depend on completion of the Foundry project, which serialized the account and model updates.

Model availability alone cannot determine the region

In the region we initially selected, the target subscription’s App Service Total Regional VMs quota was 0. Although the model could be deployed there, the web app could not, so we moved to a region where both App Service and the model were available.

Before deployment, check the App Service Plan’s regional quota in addition to the model’s regional availability and quota.

Verification

For this demo, we verified the following:

Do not test only the successful path for allowed users. Also confirm that unauthenticated users, tenants that are not allowed, and unassigned users are rejected.

Additional production considerations

RAG, file uploads, and persistent conversation history were outside the scope of this demo. Before moving to production, consider at least the following:

Architectures that support multiple customer companies require offboarding procedures for contract termination and organizational restructuring from the outset, not just onboarding procedures.

Summary

Even when the service provider and customer companies use different Microsoft Entra ID tenants, a multi-tenant App Registration makes it possible to provide one system to multiple customer companies without copying users into the service provider’s tenant.

The five key design points are:

  1. The service provider owns the App Registration, and a Service Principal is created as an Enterprise Application for each customer company.
  2. Users remain Member users in their customer company’s tenant and continue to use that company’s MFA, Conditional Access, and account management.
  3. Do not rely on the organizations authority alone. Compare the validated token’s tid with an allowlist in the API.
  4. To restrict individual users, require assignment in each customer company’s Enterprise Application.
  5. Combine Managed Identity authentication with Private Endpoint network controls for Foundry.

Instead of choosing External ID simply because users are external, choose an approach based on who should continue to manage the existing organizational identities. This makes the division of responsibilities between the service provider and customer companies easier to define, even when several group companies share one system.

Official sources