Could not connect to ExchangeOnline using multi tenant app in microsoft

317 views Asked by At

I have one tenant, Let's say abc.com where I have created one enterprise application. Let's say X-app and it is a multi-tenant app. In the other tenant, Let's say xyz.com in that I have granted all the consent to X-app and I can see X-app in the application section of Azure. Now, Using PowerShell I am trying to connect ExchangeOnline of xyz.com using X-app like.

$tenant = 'xyz.onmicrosoft.com' //of xyz.com
$clientId = "xxxxxxxx-87e9-49fc-888e-xxxxxxxxxx" //of X-app
$clientSecret = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" //of X-app
$tenantId = "xxxxxxxx-e4d4-4859-abf9-xxxxxxxxxxxx" //of xyz.com
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("Content-Type", "application/x-www-form-urlencoded")
$body = "client_id=" + $clientId + "&scope=https%3A%2F%2Foutlook.office365.com%2F.default&grant_type=client_credentials&client_secret=" + $clientSecret

$url = "https://login.microsoftonline.com/" + $tenantId + "/oauth2/v2.0/token"
$response = Invoke-RestMethod $url -Method 'POST' -Headers $headers -Body $body

Connect-ExchangeOnline -Organization $tenant -AccessToken $response.access_token

But it is giving me errors like.

UnAuthorized
At C:\Program Files\WindowsPowerShell\Modules\ExchangeOnlineManagement\3.1.0\netFramework\ExchangeOnlineManagement.psm1:733 char:21
+                     throw $_.Exception;
+                     ~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : OperationStopped: (:) [], UnauthorizedAccessException
+ FullyQualifiedErrorId : UnAuthorized

It is working fine when I use abc.com tenant values($tenant and $tenantId) but I need access of xyz.com tenant's ExchangeOnline.

Below are the permissions of X-app inside xyz.com enter image description here

2

There are 2 answers

8
Rukmini On

Note that: To connect to Exchange online using app only authentication, the Azure AD application must be granted Exchange.ManageAsApp application API permission and Microsoft Entra roles such as Exchange Administrator.

I created Azure AD Multi Tenant application in TenantA and granted API permissions:

enter image description here

I signed in with TenantB user and the application is present in Enterprise application:

enter image description here

enter image description here

In TenantB, make sure to assign any one the roles to the Enterprise application. Refer this MsDoc:

I assigned Exchange Administrator role for the application in both TenantA and TenantB

TenantB:

enter image description here

I am able to connect Exchange online using multi-tenant application successfully:

$tenant = 'TenantB.onmicrosoft.com' 
$clientId = "AppClientID" 
$clientSecret = "TenantAAppClientSecret" 
$tenantId = "TenantBTenantID" 

$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("Content-Type", "application/x-www-form-urlencoded")
$body = "client_id=" + $clientId + "&scope=https%3A%2F%2Foutlook.office365.com%2F.default&grant_type=client_credentials&client_secret=" + $clientSecret

$url = "https://login.microsoftonline.com/" + $tenantId + "/oauth2/v2.0/token"
$response = Invoke-RestMethod $url -Method 'POST' -Headers $headers -Body $body

Connect-ExchangeOnline -Organization $tenant -AccessToken $response.access_token

enter image description here

Reference:

App-only authentication in Exchange Online PowerShell and Security & Compliance PowerShell | Microsoft

0
Philippe Signoret On

Important: Read until the end, including the Warnings section!

From the discussion in chat, we established that you are building a web app which needs to make requests on behalf of the signed-in user using Exchange Online PowerShell cmdlets:

A user at a browser signs into a web app running on a web server, which calls Exchange Online using PowerShell, on behalf of the signed-in user.

The overall process is the following:

  1. The user sign into your web app, during which the app requests access to Exchange Online on behalf of the signed in user.
  2. If the app has not been authorized to access Exchange Online on behalf of the signed-in user, the user will be prompted for consent (assuming they are sufficiently privileged to grant consent).
  3. On successful sign in, the app will receive an access token to Exchange Online. Using this token, the web app runs Connect-ExchangeOnline with the -AccessToken parameter.
  4. The app invokes the desired Exchange Online cmdlet.

You first need to determine what authorization your app requires and for what resource, and add this to the list of required API permissions for your Entra app registration under "API permissions". Since your app will be using Exchange Online PowerShell, the resource service is Exchange Online directly (not Microsoft Graph), which is listed as "Office 365 Exchange Online" under "APIs my organization uses":

"Office 365 Exchange Online", listed under "APIs my organization uses" in the "Request API permissions" pane

As your app is accessing Exchange Online on behalf of a user, your app needs to be granted a delegated permission. Looking at the list of delegated permissions for Exchange Online, I see only one that looks appropriate for management activities such as the ones you'd do with Exchange Online PowerShell: Exchange.Manage:

API permissions for an app registration, including "Exchange.Manage" for "Office 365 Exchange Online"

In your app's code/configuration, you now need to configure how your app initiates the sign-in and requests the access token. The specifics of how to do this are dependent on the platform (e.g. .NET, Python) and libraries (e.g. MSAL) you are using. In all cases, you must use the scope value that identifies that you are requesting an access token to Exchange Online https://outlook.office.com/.default (see Warning 2, below).

Once the sign-in completes and your web app has obtained the access token, it can proceed to connect to Exchange Online PowerShell with:

Connect-ExchangeOnline -Organization "{tenant}" -AccessToken "{access-token}"

For example, using C#, this might look something like:

// ...
PowerShell ps = PowerShell.Create(runspace);
ps.AddCommand("Connect-ExchangeOnline");
ps.AddParameters(new Dictionary<string, object>
{
    ["Organization"] = "contoso.onmicrosoft.com",
    ["AccessToken"] = authResult.AccessToken
});
Collection<PSObject> connectionResult = ps.Invoke();
// ...

Once that cmdlet completes, the web app will be able to run other Exchange Online cmdlets (within the same PowerShell session), as that user (see Warning 3, below). For example (still using C#):

// ...
ps.Commands.Clear();
ps.AddCommand("Get-SafeLinksPolicy");
ps.AddParameter("Identity", "Contoso All");
Collection<PSObject> connectionResult = ps.Invoke();
// ...

Of course, to run any of these Exchange Online cmdlets, the user themselves will need to be sufficiently privileged, see Find the permissions required to run any Exchange cmdlet for more details on what authorization the signed-in user requires.

Warnings

  • Warning 1: While it may technically be possible to invoke PowerShell cmdlets from a web server, this does not mean it is the best option. You will have much better experience and stability if you are able to do the equivalent operation using Microsoft Graph or another API intended for this type of request. I will assume you've already determined PowerShell is the only interface available, and the strategy you are following is supported by Microsoft.
  • Warning 2: We use scope=https://outlook.office.com/.default to generically state "request an access token to Exchange Online", without dynamically identifying the requested delegated permissions, as we might have done with scope=https://outlook.office.com/Exchange.Manage. We use the ".default" pattern because otherwise Microsoft Entra ID will respond with an error message along the lines of "the application requested a scope that doesn't exist on (Microsoft Graph's app ID)". This is surprising because the request was for an Exchange Online scope, not a Microsoft Graph scope. That there is special/strange behavior when requesting this scope is a hint that this might not be entirely expected and may now always work as expected.
  • Warning 3: Be very careful handling multiple runspaces/session in your web app. For example, you want to make sure there is no way two different users accessing your web app (e.g. at the same time) might accidentally be sharing the same Exchange Online PowerShell session.