Cross tenant authorization with Microsoft Entra

For security reasons, your server may host in an independent tenant from your Azure SignalR resource.

Since managed identity can't be used across tenants, you need to register an application in tenantA and then provision it as an enterprise application in tenantB.

This doc helps you create an application in tenantA and use it to connect to a SignalR resource in tenantB.

Register a multitenant application in tenant A

The first step is to create a multitenant application, see:

Quickstart: Register an application in Microsoft Entra ID

In the case that you already have a single tenant application.

Convert single-tenant app to multitenant on Microsoft Entra ID

There are four account types:

  • Accounts in this organizational directory
  • Accounts in any organizational directory
  • Accounts in any organizational directory and personal Microsoft accounts
  • Personal Microsoft accounts

Be sure to select either type 2 or type 3 when creating the application.

Screenshot of overview information for a registered application.

Note down the Application (client) ID and Directory (tenant) ID, they can be useful in the following steps.

Provision the application in tenant B

The role can't be assigned to the application registered in other tenants. We have to provision it as an external enterprise application in the tenant B.

Click to learn differences between App registration and Enterprise applications.

For short, the enterprise application is a service principal, while the app registration isn't. The enterprise application inherits certain properties from the application object, such as Application (client) ID.

A default service principal is created in the tenant where the app is registered. For other tenants, you need to provision the app to get an enterprise application service principal, see:

Create an enterprise application from a multitenant application in Microsoft Entra ID

Enterprise applications in different tenant have different Directory (tenant) ID, but share the same Application (client) ID.

Assign roles to the enterprise application

Once you have the enterprise application provisioned in your tenant B. You will be able to assign roles to it.

The following steps describe how to assign a SignalR App Server role to a service principal or a managed identity for an Azure SignalR Service resource. For detailed steps, see Assign Azure roles using the Azure portal.

Note

A role can be assigned to any scope, including management group, subscription, resource group, or single resource. To learn more about scope, see Understand scope for Azure RBAC.

  1. In the Azure portal, go to your Azure SignalR Service resource.

  2. Select Access control (IAM) in the sidebar.

  3. Select Add > Add role assignment.

    Screenshot that shows the page for access control and selections for adding a role assignment.

  4. On the Role tab, select SignalR App Server or other SignalR built-in roles depends on your scenario.

    Role Description Use case
    SignalR App Server Access to the server connection creation and key generation APIs. Most commonly used for app server with Azure SignalR resource run in Default mode.
    SignalR Service Owner Full access to all data-plane APIs, including REST APIs, the server connection creation, and key/token generation APIs. For negotiation server with Azure SignalR resource run in Serverless mode, as it requires both REST API permissions and authentication API permissions.
    SignalR REST API Owner Full access to data-plane REST APIs. For using Azure SignalR Management SDK to manage connections and groups, but does NOT make server connections or handle negotiation requests.
    SignalR REST API Reader Read-only access to data-plane REST APIs. Use it when write a monitoring tool that calls readonly REST APIs.
  5. Select Next.

  6. For Microsoft Entra application.

    1. In the Assign access to row, select User, group, or service principal.
    2. In the Members row, click select members, then choose the identity in the pop-up window.
  7. For managed identity for Azure resources.

    1. In the Assign access to row, select Managed identity.
    2. In the Members row, click select members, then choose the application in the pop-up window.
  8. Select Next.

  9. Review your assignment, then click Review + assign to confirm the role assignment.

Important

Newly added role assignments might take up to 30 minutes to propagate.

To learn more about how to assign and manage Azure roles, see these articles:

Configure SignalR SDK to use the enterprise application

There are 3 different types of credentials for an application to authenticate itself:

  • Certificates
  • Client secrets
  • Federated identity

We strongly recommend you to use the first 2 ways to make cross tenant requests.

Use Certificates or Client secrets

  • tenantId should be the ID of your Tenant B.
  • clientId in both tenants are equal.
  • clientSecret and clientCert should be configured in Tenant A, see Add credentials.

If you aren't sure about your tenant ID, see Find your Microsoft Entra tenant

services.AddSignalR().AddAzureSignalR(option =>
{
    var credential1 = new ClientSecretCredential("tenantId", "clientId", "clientSecret", new TokenCredentialOptions { AuthorityHost=AzureAuthorityHosts.AzureChina});
    var credential2 = new ClientCertificateCredential("tenantId", "clientId", "path-to-cert", new TokenCredentialOptions { AuthorityHost=AzureAuthorityHosts.AzureChina});

    option.Endpoints = new ServiceEndpoint[]
    {
        new ServiceEndpoint(new Uri("https://<resource1>.signalr.azure.cn"), credential1),
        new ServiceEndpoint(new Uri("https://<resource2>.signalr.azure.cn"), credential2),
    };
});

Use Federated identity

However, for security reasons, certificates and client secrets might be disabled in your subscription. In this case, you need to either use an external identity provider or try the preview support for managed identity.

When using managed identity as an identity provider, the code should look like this:

  • tenantId should be the ID of your Tenant B.
  • clientId in both tenants are equal.
services.AddSignalR().AddAzureSignalR(option =>
{
    var msiCredential = new ManagedIdentityCredential("msiClientId");

    var credential = new ClientAssertionCredential("tenantId", "appClientId", async (ctoken) =>
    {
        // Entra ID China operated by 21Vianet: api://AzureADTokenExchangeChina
        var request = new TokenRequestContext([$"api://AzureADTokenExchangeChina/.default"]);
        var response = await msiCredential.GetTokenAsync(request, ctoken).ConfigureAwait(false);
        return response.Token;
    });

    option.Endpoints = [
        new ServiceEndpoint(new Uri(), "https://<resource>.signalr.azure.cn"), credential);
    ];
});

When using external identity providers, the code should look like this:

services.AddSignalR().AddAzureSignalR(option =>
{
    var credential = new ClientAssertionCredential("tenantId", "appClientId", async (ctoken) =>
    {
        // Find your own way to get a token from the external identity provider.
        // The audience of the token should be "api://AzureADTokenExchangeChina", as it is the recommended value.
        return "TheTokenYouGetFromYourExternalIdentityProvider";
    });

    option.Endpoints = [
        new ServiceEndpoint(new Uri(), "https://<resource>.signalr.azure.cn"), credential);
    ];
});

Debugging token acquisition with the SignalR SDK can be challenging since it depends on the token results. We recommend testing the token acquisition process locally before integrating with the SignalR SDK.

var assertion = new ClientAssertionCredential("tenantId", "appClientId", async (ctoken) =>
{
    // Find your own way to get a token from the external identity provider.
    // The audience of the token should be "api://AzureADTokenExchangeChina", as it is the recommended value.
    return TheTokenYouGetFromYourExternalIdentityProvider;
});

var request = new TokenRequestContext(["https://signalr.azure.cn/.default");
var token = await assertion.GetTokenAsync(assertion);
Console.log(token.Token);

The key point is to use an inner credential to get a clientAssertion from api://AzureADTokenExchangeChina or other trusted identity platforms. Then use it to exchange for a token with https://signalr.azure.cn/.default audience to access your resource.

Your goal is to get a token with following claims. Use jwt.io to help you decode the token:

  • oid

    The value should be equal to your enterprise application object ID.

    If you don't know where to get it, see How Retrieve Enterprise Object ID

  • tid

    The value should be equal to the Directory ID of your tenant B.

    If you aren't sure about your tenant ID, see Find your Microsoft Entra tenant

  • audience

    Has to be https://signalr.azure.cn/.default to access SignalR resources.

Next steps

See the following related articles: