|
| 1 | +--- |
| 2 | +title: Cross tenant authorization with Microsoft Entra |
| 3 | +description: This article provides information about building multitenant applications and configures authorization in SignalR. |
| 4 | +author: terencefan |
| 5 | +ms.author: tefa |
| 6 | +ms.date: 03/12/2023 |
| 7 | +ms.service: azure-signalr-service |
| 8 | +ms.topic: how-to |
| 9 | +ms.devlang: csharp |
| 10 | +ms.custom: subject-rbac-steps |
| 11 | +--- |
| 12 | + |
| 13 | +# Cross tenant authorization with Microsoft Entra |
| 14 | + |
| 15 | +For security reasons, your server may host in an independent tenant from your Azure SignalR resource. |
| 16 | + |
| 17 | +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`. |
| 18 | + |
| 19 | +This doc helps you create an application in `tenantA` and use it to connect to a SignalR resource in `tenantB`. |
| 20 | + |
| 21 | +## Register a multitenant application in tenant A |
| 22 | + |
| 23 | +The first step is to create a multitenant application, see: |
| 24 | + |
| 25 | +[Quickstart: Register an application in Microsoft Entra ID](/entra/identity-platform/quickstart-register-app) |
| 26 | + |
| 27 | +In the case that you already have a single tenant application. |
| 28 | + |
| 29 | +[Convert single-tenant app to multitenant on Microsoft Entra ID](/entra/identity-platform/howto-convert-app-to-be-multi-tenant) |
| 30 | + |
| 31 | +There are four account types: |
| 32 | + |
| 33 | +- Accounts in this organizational directory |
| 34 | +- Accounts in any organizational directory |
| 35 | +- Accounts in any organizational directory and personal Microsoft accounts |
| 36 | +- Personal Microsoft accounts |
| 37 | + |
| 38 | +Be sure to select either type 2 or type 3 when creating the application. |
| 39 | + |
| 40 | + |
| 41 | + |
| 42 | +Note down the **Application (client) ID** and **Directory (tenant) ID**, they can be useful in the following steps. |
| 43 | + |
| 44 | +## Provision the application in tenant B |
| 45 | + |
| 46 | +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. |
| 47 | + |
| 48 | +Click to learn [differences between App registration and Enterprise applications](/answers/questions/270680/app-registration-vs-enterprise-applications). |
| 49 | + |
| 50 | +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**. |
| 51 | + |
| 52 | +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: |
| 53 | + |
| 54 | +[Create an enterprise application from a multitenant application in Microsoft Entra ID](/entra/identity/enterprise-apps/create-service-principal-cross-tenant) |
| 55 | + |
| 56 | +Enterprise applications in different tenant have different **Directory (tenant) ID**, but share the same **Application (client) ID**. |
| 57 | + |
| 58 | +## Assign roles to the enterprise application |
| 59 | + |
| 60 | +Once you have the enterprise application provisioned in your tenant B. You will be able to assign roles to it. |
| 61 | + |
| 62 | +[!INCLUDE [add role assignments](includes/signalr-add-role-assignments.md)] |
| 63 | + |
| 64 | +## Configure SignalR SDK to use the enterprise application |
| 65 | + |
| 66 | +There are 3 different types of credentials for an application to authenticate itself: |
| 67 | + |
| 68 | +- Certificates |
| 69 | +- Client secrets |
| 70 | +- Federated identity |
| 71 | + |
| 72 | +We strongly recommend you to use the first 2 ways to make cross tenant requests. |
| 73 | + |
| 74 | +### Use Certificates or Client secrets |
| 75 | + |
| 76 | +- `tenantId` should be the ID of your **Tenant B**. |
| 77 | +- `clientId` in both tenants are equal. |
| 78 | +- `clientSecret` and `clientCert` should be configured in **Tenant A**, see [Add credentials](/entra/identity-platform/quickstart-register-app?tabs=certificate%2Cexpose-a-web-api#add-credentials). |
| 79 | + |
| 80 | +If you aren't sure about your tenant ID, see [Find your Microsoft Entra tenant](/azure/azure-portal/get-subscription-tenant-id#find-your-microsoft-entra-tenant) |
| 81 | + |
| 82 | +```csharp |
| 83 | +services.AddSignalR().AddAzureSignalR(option => |
| 84 | +{ |
| 85 | + var credential1 = new ClientSecretCredential("tenantId", "clientId", "clientSecret"); |
| 86 | + var credential2 = new ClientCertificateCredential("tenantId", "clientId", "path-to-cert"); |
| 87 | + |
| 88 | + option.Endpoints = new ServiceEndpoint[] |
| 89 | + { |
| 90 | + new ServiceEndpoint(new Uri("https://<resource1>.service.signalr.net"), credential1), |
| 91 | + new ServiceEndpoint(new Uri("https://<resource2>.service.signalr.net"), credential2), |
| 92 | + }; |
| 93 | +}); |
| 94 | +``` |
| 95 | + |
| 96 | +### Use Federated identity |
| 97 | + |
| 98 | +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. |
| 99 | + |
| 100 | +- [Configure an app to trust an external identity provider](/entra/workload-id/workload-identity-federation-create-trust) |
| 101 | +- [Configure an application to trust a managed identity (preview)](/entra/workload-id/workload-identity-federation-config-app-trust-managed-identity) |
| 102 | + |
| 103 | +You can check this repo: [Entra Cross-Tenant Application Federated Identity Credential (FIC)](https://github.com/arsenvlad/entra-cross-tenant-app-fic-managed-identity) for detailed info and video guide. |
| 104 | + |
| 105 | +When using managed identity as an identity provider, the code should look like this: |
| 106 | + |
| 107 | +- `tenantId` should be the ID of your **Tenant B**. |
| 108 | +- `clientId` in both tenants are equal. |
| 109 | + |
| 110 | +```csharp |
| 111 | +services.AddSignalR().AddAzureSignalR(option => |
| 112 | +{ |
| 113 | + var msiCredential = new ManagedIdentityCredential("msiClientId"); |
| 114 | + |
| 115 | + var credential = new ClientAssertionCredential("tenantId", "appClientId", async (ctoken) => |
| 116 | + { |
| 117 | + // Entra ID US Government: api://AzureADTokenExchangeUSGov |
| 118 | + // Entra ID China operated by 21Vianet: api://AzureADTokenExchangeChina |
| 119 | + var request = new TokenRequestContext([$"api://AzureADTokenExchange/.default"]); |
| 120 | + var response = await msiCredential.GetTokenAsync(request, ctoken).ConfigureAwait(false); |
| 121 | + return response.Token; |
| 122 | + }); |
| 123 | + |
| 124 | + option.Endpoints = [ |
| 125 | + new ServiceEndpoint(new Uri(), "https://<resource>.service.signalr.net"), credential); |
| 126 | + ]; |
| 127 | +}); |
| 128 | +``` |
| 129 | + |
| 130 | +When using external identity providers, the code should look like this: |
| 131 | + |
| 132 | +```csharp |
| 133 | +services.AddSignalR().AddAzureSignalR(option => |
| 134 | +{ |
| 135 | + var credential = new ClientAssertionCredential("tenantId", "appClientId", async (ctoken) => |
| 136 | + { |
| 137 | + // Find your own way to get a token from the external identity provider. |
| 138 | + // The audience of the token should be "api://AzureADTokenExchange", as it is the recommended value. |
| 139 | + return "TheTokenYouGetFromYourExternalIdentityProvider"; |
| 140 | + }); |
| 141 | + |
| 142 | + option.Endpoints = [ |
| 143 | + new ServiceEndpoint(new Uri(), "https://<resource>.service.signalr.net"), credential); |
| 144 | + ]; |
| 145 | +}); |
| 146 | +``` |
| 147 | + |
| 148 | +Debugging token acquisition with the SignalR SDK can be challenging since it depends on the token results. |
| 149 | +We recommend testing the token acquisition process locally before integrating with the SignalR SDK. |
| 150 | + |
| 151 | +```csharp |
| 152 | +var assertion = new ClientAssertionCredential("tenantId", "appClientId", async (ctoken) => |
| 153 | +{ |
| 154 | + // Find your own way to get a token from the external identity provider. |
| 155 | + // The audience of the token should be "api://AzureADTokenExchange", as it is the recommended value. |
| 156 | + return TheTokenYouGetFromYourExternalIdentityProvider; |
| 157 | +}); |
| 158 | + |
| 159 | +var request = new TokenRequestContext(["https://signalr.azure.com/.default"); |
| 160 | +var token = await assertion.GetTokenAsync(assertion); |
| 161 | +Console.log(token.Token); |
| 162 | +``` |
| 163 | + |
| 164 | +The key point is to use an inner credential to get a `clientAssertion` from `api://AzureADTokenExchange` or other trusted identity platforms. Then use it to exchange for a token with `https://signalr.azure.com/.default` audience to access your resource. |
| 165 | +
|
| 166 | +Your goal is to get a token with following claims. Use [jwt.io](https://jwt.io/) to help you decode the token: |
| 167 | +
|
| 168 | +- **oid** |
| 169 | + |
| 170 | + The value should be equal to your enterprise application object ID. |
| 171 | + |
| 172 | + If you don't know where to get it, see [How Retrieve Enterprise Object ID](/answers/questions/1007608/how-retrieve-enterprise-object-id-from-azure-activ) |
| 173 | + |
| 174 | +- **tid** |
| 175 | + |
| 176 | + The value should be equal to the Directory ID of your tenant B. |
| 177 | + |
| 178 | + If you aren't sure about your tenant ID, see [Find your Microsoft Entra tenant](/azure/azure-portal/get-subscription-tenant-id#find-your-microsoft-entra-tenant) |
| 179 | + |
| 180 | +- **audience** |
| 181 | + |
| 182 | + Has to be `https://signalr.azure.com/.default` to access SignalR resources. |
| 183 | +
|
| 184 | +## Next steps |
| 185 | + |
| 186 | +See the following related articles: |
| 187 | + |
| 188 | +- [Microsoft Entra ID for Azure SignalR Service](signalr-concept-authorize-azure-active-directory.md) |
| 189 | +- [Authorize requests to Azure SignalR Service resources with Microsoft Entra applications](signalr-howto-authorize-application.md) |
| 190 | +- [Authorize requests to Azure SignalR Service resources with Managed identities for Azure resources](./signalr-howto-authorize-managed-identity.md) |
|
0 commit comments