简体   繁体   中英

Microsoft.Exchange.WebServices.Data.ServiceResponseException: 'There are no public folder servers available.'

further to this question , i have the same problem. PubFolder on Prem, users in O365

I have fetched and added the routing headers from Glen's post but still get the error

GetToken works... https://learn.microsoft.com/en-us/exchange/client-developer/exchange-web-services/how-to-authenticate-an-ews-application-by-using-oauth

GetX headers works... https://learn.microsoft.com/en-us/exchange/client-developer/exchange-web-services/public-folder-access-with-ews-in-exchange

--->> ewsClient.FindFolders(WellKnownFolderName.PublicFoldersRoot, new FolderView(10))

Microsoft.Exchange.WebServices.Data.ServiceResponseException: 'There are no public folder servers available.'

       static async System.Threading.Tasks.Task Test3()
{
    string ClientId = ConfigurationManager.AppSettings["appId"];
    string TenantId = ConfigurationManager.AppSettings["tenantId"];
    string secret = ConfigurationManager.AppSettings["clientSecret"];
    string uMbox = ConfigurationManager.AppSettings["userId"];
    string uPwd = ConfigurationManager.AppSettings["userPWD"];

// Using Microsoft.Identity.Client 4.22.0
//https://learn.microsoft.com/en-us/exchange/client-developer/exchange-web-services/how-to-authenticate-an-ews-application-by-using-oauth//
    var cca = ConfidentialClientApplicationBuilder
        .Create(ClientId)
        .WithClientSecret(secret)
        .WithTenantId(TenantId)
        .Build();

    var ewsScopes = new string[] { "https://outlook.office365.com/.default" };

    try
    {
        var authResult = await cca.AcquireTokenForClient(ewsScopes)
            .ExecuteAsync();

        // Configure the ExchangeService with the access token
        var ewsClient = new ExchangeService();
        ewsClient.Url = new Uri("https://outlook.office365.com/EWS/Exchange.asmx");
        ewsClient.Credentials = new OAuthCredentials(authResult.AccessToken);
        ewsClient.ImpersonatedUserId =
            new ImpersonatedUserId(ConnectingIdType.SmtpAddress, uMbox);


        AutodiscoverService autodiscoverService = GetAutodiscoverService(uMbox, uPwd);

        GetUserSettingsResponse userResponse = GetUserSettings(autodiscoverService, uMbox, 3, UserSettingName.PublicFolderInformation, UserSettingName.InternalRpcClientServer);
        string pfAnchorHeader= userResponse.Settings[UserSettingName.PublicFolderInformation].ToString();
        string pfMailboxHeader = userResponse.Settings[UserSettingName.InternalRpcClientServer].ToString(); ;

        // Make an EWS call
        var folders = ewsClient.FindFolders(WellKnownFolderName.MsgFolderRoot, new FolderView(10));
        foreach (var folder in folders)
        {
            Console.WriteLine($"Folder: {folder.DisplayName}");
        }

        //get Public folder root
        //Include x-anchormailbox header
        Console.WriteLine("X-AnchorMailbox value for public folder hierarchy requests: {0}", pfAnchorHeader);
        Console.WriteLine("X-PublicFolderMailbox value for public folder hierarchy requests: {0}", pfMailboxHeader);

        //var test3 = GetMailboxGuidAddress(ewsClient, pfAnchorHeader, pfMailboxHeader, uMbox);

        ///https://learn.microsoft.com/en-us/exchange/client-developer/exchange-web-services/how-to-route-public-folder-content-requests <summary>
        ewsClient.HttpHeaders.Add("X-AnchorMailbox", userResponse.Settings[UserSettingName.PublicFolderInformation].ToString());
        //ewsClient.HttpHeaders.Add("X-AnchorMailbox", "SharedPublicFolder@contoso.com");
        ewsClient.HttpHeaders.Add("X-PublicFolderMailbox", userResponse.Settings[UserSettingName.InternalRpcClientServer].ToString());

        try
        {
            var pubfolders = ewsClient.FindFolders(WellKnownFolderName.PublicFoldersRoot, new FolderView(10));
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
            throw;
        }
        
        foreach (var folder in folders)
        {
            Console.WriteLine($"Folder: {folder.DisplayName}");
        }

    }
    catch (MsalException ex)
    {
        Console.WriteLine($"Error acquiring access token: {ex}");
    }
    catch (Exception ex)
    {
        Console.WriteLine($"Error: {ex}");
    }

    if (System.Diagnostics.Debugger.IsAttached)
    {
        Console.WriteLine("Hit any key to exit...");
        Console.ReadKey();
    }
}

public static AutodiscoverService GetAutodiscoverService(string username, string pwd)
{
    AutodiscoverService adAutoDiscoverService = new AutodiscoverService();
    adAutoDiscoverService.Credentials = new WebCredentials(username, pwd);
    adAutoDiscoverService.EnableScpLookup = true;
    adAutoDiscoverService.RedirectionUrlValidationCallback = RedirectionUrlValidationCallback;
    adAutoDiscoverService.PreAuthenticate = true;
    adAutoDiscoverService.TraceEnabled = true;
    adAutoDiscoverService.KeepAlive = false;
    return adAutoDiscoverService;
}

public static GetUserSettingsResponse GetUserSettings(
         AutodiscoverService service,
         string emailAddress,
         int maxHops,
         params UserSettingName[] settings)
{
    Uri url = null;
    GetUserSettingsResponse response = null;

    for (int attempt = 0; attempt < maxHops; attempt++)
{
        service.Url = url;
        service.EnableScpLookup = (attempt < 2);

        response = service.GetUserSettings(emailAddress, settings);

        if (response.ErrorCode == AutodiscoverErrorCode.RedirectAddress)
        {
            url = new Uri(response.RedirectTarget);
        }
        else if (response.ErrorCode == AutodiscoverErrorCode.RedirectUrl)
        {
            url = new Uri(response.RedirectTarget);
        }
        else
        {
            return response;
        }
    }

    throw new Exception("No suitable Autodiscover endpoint was found.");
}
    

Your code won't work against an OnPrem Public folder tree as EWS in Office365 won't proxy to an OnPrem Exchange Org (even if hybrid is setup). (Outlook MAPI is a little different and allows this via versa setup but in that case it never proxies either it just makes a different connection to that store and its all the Outlook client doing this).

Because your trying to use the client credentials oauth flow for that to work onPrem you must have setup hybrid modern authentication https://learn.microsoft.com/en-us/microsoft-365/enterprise/hybrid-modern-auth-overview?view=o365-worldwide . Then you need to acquire a token with an audience set to the local OnPrem endpoint. (this is usually just your onPrem ews endpoint's host name but it should be one of the service principal names configured in your hybrid auth setup Get-MsolServicePrincipal). So in your code you would change

var ewsScopes = new string[] { "https://outlook.office365.com/.default" };

to

var ewsScopes = new string[] { "https://OnPrem.whatever.com/.default" };

which will then give you a token with an audience set for the onprem server then you need to send the EWS request to that endpoint so change that eg

ewsClient.Url = new Uri("https://OnPrem.whatever.com/EWS/Exchange.asmx");

if Hybird Modern Auth is setup then you need to default back to use Integrated or Basic Authenticaiton.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM