0

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.");
}
    
RobG
  • 53
  • 6

1 Answers1

0

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.

Glen Scales
  • 20,495
  • 1
  • 20
  • 23
  • thanks Glen. I believe we have Hybrid Modern Auth setup. did you mean in the last sentance "if hybrid modern auth is NOT setup" ? Will try with the endpoint change – RobG Sep 30 '22 at 07:06
  • apologies... appears Hybrid is not correctly configured as no OnPrem server is reported as MsolServicePrincipal, hence no token acan be obtained. have asked Exchange admin to check config. – RobG Sep 30 '22 at 07:43