Configure cross-tenant authorization with Microsoft Entra

For security reasons, your server might host in a tenant independent from your Azure SignalR Service resource. Because managed identity can't be used across tenants, you need to register an application in tenant A and then provision it as an enterprise application in tenant B. This article helps you create an application in tenant A and use it to connect to an Azure SignalR Service resource in tenant B.

Register a multitenant application in tenant A

The first step is to create a multitenant application. For more information, see Quickstart: Register an application in Microsoft Entra ID.

If you already have a single tenant application, follow the instructions in Convert a 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 the second type or the third type when you create the application.

Screenshot that shows an overview of information for a registered application.

Make a note of the application (client) ID and the directory (tenant) ID for use in the following steps.

Provision the application in tenant B

You can't assign the role to the application registered in other tenants. You have to provision it as an external enterprise application in tenant B. If you need more information, you can learn about the differences between app registration and enterprise applications.

In brief, the enterprise application is a service principal and the app registration isn't. The enterprise application inherits certain properties from the application object, such as the 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. For more information, see Create an enterprise application from a multitenant application in Microsoft Entra ID.

Enterprise applications in different tenants have different directory (tenant) IDs, but they share the same application (client) ID.

Assign roles to the enterprise application

After you have the enterprise application provisioned in your tenant B, you can 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 by using the Azure portal.

Note

You can assign a role 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. On the left pane, select Access control (IAM).

  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. Other Azure SignalR Service built-in roles depend on your scenario.

    Role Description Use case
    SignalR App Server Access to the APIs that create server connections and generate keys. Most commonly used for an app server with an Azure SignalR resource running in Default mode.
    SignalR Service Owner Full access to all data-plane APIs, including REST APIs, the APIs that create server connections, and the APIs that generate keys/tokens. Used for a negotiation server with an Azure SignalR Service resource running in Serverless mode. It requires both REST API permissions and authentication API permissions.
    SignalR REST API Owner Full access to data-plane REST APIs. Used for the Azure SignalR Management SDK to manage connections and groups, but it doesn't make server connections or handle negotiation requests.
    SignalR REST API Reader Read-only access to data-plane REST APIs. Used when you write a monitoring tool that calls read-only 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, choose select members, and 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, choose select members, and then choose the application in the pop-up window.
  8. Select Next.

  9. Review your assignment, and then select 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:

Configure the Azure SignalR Service SDK to use the enterprise application

An application uses three different types of credentials to authenticate itself:

  • Certificates
  • Client secrets
  • Federated identity

We strongly recommend that you use certificates or client secrets to make cross-tenant requests.

Use certificates or client secrets

  • The tenantId parameter is the ID of your tenant B.
  • The clientId parameters in both tenants are equal.
  • The clientSecret and clientCert parameters are configured in tenant A. For more information, 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

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. For more information, see:

When you use managed identity as an identity provider, the code looks like the following example:

  • The tenantId parameter is the ID of your tenant B.
  • The clientId parameters 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 you use external identity providers, the code looks like the following example:

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" because it is the recommended value.
        return "TheTokenYouGetFromYourExternalIdentityProvider";
    });

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

Debugging token acquisition with the Azure SignalR Service SDK is a challenge because it depends on the token results. We recommend that you test the token acquisition process locally before you integrate with the Azure SignalR Service 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" because 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 parameter from api://AzureADTokenExchangeChina or other trusted identity platforms. Then use it to exchange for a token with the https://signalr.azure.cn/.default audience to access your resource.

Your goal is to get a token with the 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 Retrieve an 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: The audience must be https://signalr.azure.cn/.default to access Azure SignalR Service resources.