locked
Do I need to use AutoDiscoverUrl? RRS feed

  • Question

  • I am developing a vb.net app to search an Office365 (Server) Inbox and Sent Items (folders) for specific email addresses. This is an outgrowth of the original app which connected to the local Outlook (on the desktop machine) folders only to find that I was dealing with an Exchange Server and hence needed to modify the program to process it that way instead.

    I’m having a horrible time just trying to get past the AutoDiscoverUrl stage.

    Here is a small sample of my code:

    		Dim myExchangeService As ExchangeService = New ExchangeService()
    
    		myExchangeService.Url = New Uri("https://outlook.office365.com/ews/exchange.asmx")
    		myExchangeService.Credentials = New WebCredentials(EmailAddress, Password)
    		myExchangeService.EnableScpLookup = True
    
    		myExchangeService.TraceFlags = TraceFlags.EwsRequest Or TraceFlags.EwsResponse
    		myExchangeService.TraceEnabled = True
    		'myExchangeService.TraceListener = ITraceListener ' NOT SURE HOW TO DO THIS IN vb.net
    
    		Try
    			myExchangeService.AutodiscoverUrl(EmailAddress, AddressOf RedirectionUrlValidationCallback)
    		Catch ex As AutodiscoverLocalException
    			Debug.Print("AutodiscoverLocalException: " & ex.Message)
    		End Try
    
    	End Sub
    	Private Shared Function RedirectionUrlValidationCallback(ByVal pRedirectionUrl As String) As Boolean
    
    		Return (pRedirectionUrl = "https://autodiscover-s.outlook.com/autodiscover/autodiscover.xml")
    
    	End Function

    I am getting the basic: "AutodiscoverLocalException: The Autodiscover service couldn't be located" error.

    I have tried changing the WebCredentials to the format (UserName,Password,Domain) and get the same results.

    I came upon something that said to try: Test Email AutoConfiguration in Outlook (CTL-RightClick on the Outlook Icon in the tray). In the log, I get:
    Autodiscover to https://outlook.office365.com/autodiscover/autodiscover.xml starting
    GetLastError=0; httpStatus=401.
    GetLastError=0; httpStatus=200.
    Autodiscover to https://outlook.office365.com/autodiscover/autodiscover.xml Succeeded (0x00000000)

    Is there a way to get past the 401 to the 200 in my program so that the autodiscover succeeds?

    Do I need to use AutoDiscoverUrl, or some other method.

    I’ve been at this for over two weeks now and I’m not any further along than when I started.

    Any ideas would be greatly appreciated.

    Thank you in advance for your time.

    Sincerely,

    Paul Goldstein


    Paul D. Goldstein Forceware Systems, Inc.

    Monday, June 22, 2020 8:48 PM

All replies

  • With the way you have written your code your Autodiscover request is completely redundant because you have already hardcoded the EWS URL in 

    myExchangeService.Url = New Uri("https://outlook.office365.com/ews/exchange.asmx")

    If all the mailboxes you want to search are on Office365 Global Tenant then this will work fine and Autodiscover isn't needed. The reasons you might want Autodiscover is if you app was going to be use by OnPrem user or other national Office365 Tenants (Germany and China).

    That said if you developing a new application to run against  EWS isn't the best choice you can do what you need in the Graph API. You should also consider using oAuth rather then basic Auth as that is going to be disabled next year in Office365 (in new tenants from October) so your app will break at that point.

    Cheers
    Glen

    Tuesday, June 23, 2020 12:30 AM
  • Hi Glen,

    Thanks for writing.
    I went to the "Authenticate an EWS application by using OAuth" page an converted the example into vb.net:

    Private Shared Async Sub subTestMSAL()
    
    		Dim pcaOptions As New PublicClientApplicationOptions With {
    			.ClientId = ConfigurationManager.AppSettings("AppID"),
    			.TenantId = ConfigurationManager.AppSettings("TenantID")}
    		Dim pcaBuilder = PublicClientApplicationBuilder.CreateWithApplicationOptions(pcaOptions).Build
    		Dim ewsScopes01 = {"https://outlook.office.com/EWS.AccessAsUser.All"}
    		Dim authResult01 As AuthenticationResult = Await pcaBuilder.AcquireTokenInteractive(ewsScopes01).ExecuteAsync
    
    		Debug.Print("authResult01.AccessToken: " & authResult01.AccessToken)
    
    		Dim ccaApp As IConfidentialClientApplication = ConfidentialClientApplicationBuilder _
    			.Create(ConfigurationManager.AppSettings("AppID")) _
    			.WithAuthority(AzureCloudInstance.AzurePublic, ConfigurationManager.AppSettings("TenantID")) _
    			.WithClientSecret(ConfigurationManager.AppSettings("ClientSecret"))
    		Dim ewsScopes02 = {"https://outlook.office.com/.default"}
    		Dim authResult02 As AuthenticationResult = Await ccaApp.AcquireTokenForClient(ewsScopes02).ExecuteAsync
    
    		Debug.Print("authResult02.AccessToken: " & authResult02.AccessToken)
    
    	End Sub

    After calling it, the "Sign into your account" form opened.
    After entering the Exchange EmailAddress, I received: "Selected user account does not exist in tenant 'Microsoft Services' and cannot access the application '80d3dd95-bb76-472e-b56e-0912c4a2d56f' in that tenant. The account needs to be added as an external user in the tenant first. Please use a different account."

    I’m a little confused (ya think?). I figured it would be the other way around…add the program’s access to the emailaccount, not the other way around.

    The only reason that I’m going down this path is that I when I first created the app (to search Inbox and Sent Items), it worked fine on my development machine (Outlook...not an Exchange Server). When I installed the program at another site (Exchange), I was missing emails that I knew existed. When I told it to search for all emails (regardless of Recipient or Sender…or Date-Range), I came up with about 600+ messages in the Inbox search. When I checked the Inbox (in Outlook…on Exchange PC), it said that there were 12,000+…then I noticed the 600+ number that were Offline (which matched the number found in my original search). That’s when I realized I had a problem and was pointed in the direction of EWS. After a couple of weeks I asked for help and you responded.

    I’m a novice at this point when they’re talking about Tenants and External Users. Please point me in the right direction.

    Thanks again for your time.

    Sincerely,

    Paul Goldstein


    Paul D. Goldstein Forceware Systems, Inc.

    Tuesday, June 23, 2020 5:21 AM
  • It sounds like the TenantId or App Registration problem, first question is how did you get the TenantId ? Easiest way i know if use a Powershell oneliner eg

    (Invoke-WebRequest https://login.windows.net/youdomain.com/v2.0/.well-known/openid-configuration | ConvertFrom-Json).token_endpoint.Split('/')[3]

    Also when you created the App registration eg https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app you need to make sure that its consented to in the Tenant your going to be using it in. If your going to use it on Multiple customer make sure that its a multi tenant App and somebody will need to consent to it in each tenant before it can be used. You can do this in the portal or use a URL like this in a browser eg

    https://login.microsoftonline.com/youdomain.com/oauth2/authorize?client_id=xxxxx-xxxx-4154-a5cf-976473306060&prompt=admin_consent

    Wednesday, June 24, 2020 12:38 AM
  • Hi Glen,

    Thanks for writing.

    I most likely setup the App Registration incorrectly…however, I found a way around that…probably not the right way to do it, but I got past the TenantID issue and then over the past 48 hours I worked my way through the rest of the code (that used to search for Received-From and Sent-To specific email addresses after a specific date in Outlook…VSTO).

    I ended up leaving out the TenantID (which came from the Directory (tenant) ID field in Azure). So that my code looks like this:

    Dim pcaOptions As New PublicClientApplicationOptions With {
    	.ClientId = ConfigurationManager.AppSettings("AppID"),
    	.RedirectUri = "http://localhost:61659/signin-oidc"} ', DO I NEED THIS?
    	'.TenantId = ConfigurationManager.AppSettings("TenantID")}
    Dim pcaBuilder = PublicClientApplicationBuilder.CreateWithApplicationOptions(pcaOptions).Build
    Dim ewsScopes01 = {"https://outlook.office.com/EWS.AccessAsUser.All"}
    Dim authResult01 As AuthenticationResult = Await pcaBuilder.AcquireTokenInteractive(ewsScopes01).ExecuteAsync
    

    It gave me a warning in the Microsoft Login Screen, but I clicked OK to see what it would do. Then I used the TenantId returned in authResult01 to build the IConfidentialClientApplication

    Dim ccaApp As IConfidentialClientApplication = ConfidentialClientApplicationBuilder.Create(strClientID).WithAuthority(AzureCloudInstance.AzurePublic, authResult01.TenantId).WithTenantId(authResult01.TenantId).WithClientSecret(strClientSecret).Build()
    Dim ewsScopes02 = {"https://outlook.office.com/.default"}
    
    Dim authResult02 As AuthenticationResult = Await ccaApp.AcquireTokenForClient(ewsScopes02).ExecuteAsync
    

    …which I’m not sure that I need because I don’t see any of the Tokens returned by it being used to launch the ExchangeService:

    Dim ewsClient As New ExchangeService
    ewsClient.Url = New Uri("https://outlook.office365.com/EWS/Exchange.asmx")
    ewsClient.Credentials = New OAuthCredentials(authResult01.AccessToken)
    
    Dim fldrInbox As Microsoft.Exchange.WebServices.Data.Folder = Microsoft.Exchange.WebServices.Data.Folder.Bind(ewsClient, WellKnownFolderName.Inbox)
    Dim fldrSentFolders As Microsoft.Exchange.WebServices.Data.Folder = Microsoft.Exchange.WebServices.Data.Folder.Bind(ewsClient, WellKnownFolderName.SentItems)
    

    From there I spent some trial and error time in working out the SearchFilter and SearchFilterCollection logic to find multiple recipients (parents’ emails of my students), or multiple destination emailaddresses (same parents’ email addresses) after a particular date. In VSTO I built some SQL statements for some of these (the Sent Items search), and just some more common Restrict statements (for the Inbox search). The Exchange interface (of course) is totally different, but I figured it out.

    So now, here comes some stupid questions because I’m a real amateur at this point. I previously explained what the original code did in interfacing with VSTO.

    Several weeks ago, I got pointed in the direction of EWS (and Azure) several weeks ago, and I’ve banging my head against the wall trying to get past the AutoDiscoverUrl.

    So…Question #1: What’s the deal with the TenantID? I know that when I tried getting through this after your first response, I noticed that the "Supported Account Types" listed on the App Registration page was for Personal Microsoft Account, so I changed the Manifest:
        "signInAudience""AzureADandPersonalMicrosoftAccount"

    That didn’t make any difference, as I was still receiving the: Selected user account does not exist in tenant 'Microsoft Services' error, which I created a workaround by leaving out the TenantID call in the first AcquireTokenInteractive call. Any ideas? I thought that the signInAudience controlled this. Is it possible that there’s a bug, and that once you register an App as Personal Microsoft Account, there’s no changing it…even if the Manifest says it’s changed? Again, I’m just an amateur on this.

    Question #2: In your original email, you said to switch from OAuth to OAuth2 (I thought that when my iPhone sent me a Microsoft Verification the other night (from my executing the App), that that was 2-Step verification (or OAuth2).

    Question #3: In your original email, you said that I could do all of this in the Graph API. Search Inbox and Sent Items for multiple recipients/destinations with a Date> specification? Save the message to disk (as an .eml or .msg)? If so…where is that documentation.

    Again, thanks for writing and pointing me in the right direction.

    Final Stupid Question: I’m assuming that I can save the AccessToken(s) and have the program run without having to go through the Sign-in process every time. Do they expire? Is there another field that I get returned to tell me to go through the verification process again?

    Thank again.

    Sincerely,
    Paul Goldstein 


    Paul D. Goldstein Forceware Systems, Inc.

    Thursday, June 25, 2020 5:37 AM
  • 1) If you registered the App with a Personal Account it would need to be consented to in whatever tenant you want to use it in. Generally in a corporate tenant you would just register the App in your tenant if your an app developer register it as a Multi tenant app. 

    2) Originally you where using Basic Auth so that wasn't oauth at all, after that you started using the MSAL which uses the v2 Azure endpoint so I don't quite get your question. (Sounds like you using a MSA account rather the an Office365 account it should work but makes it more complicated)

    3) https://developer.microsoft.com/en-us/graph 

     Save the message to disk (as an .eml or .msg)? (with either EWS or the Graph you won't be able to save a Mesages as a MSG file this requires Outlook)

    >>Final Stupid Question: I’m assuming that I can save the AccessToken(s) and have the program run without having to go through the Sign-in process every time. Do they expire? Is there another field that I get returned to tell me to go through the verification process again?

    AccessTokens are only valid for 1 hour, you do get a refresh token which can be used for up to 90 days to generate new AccessToken. MSAL as far as I know doesn't expose the refresh token as they don't want people saving tokens as its a security risk. Most people take advantage of the Cache https://docs.microsoft.com/en-us/azure/active-directory/develop/msal-acquire-cache-tokens for that


    Friday, June 26, 2020 12:51 AM
  • Hi Glen,

    Thanks for writing.

    In trying to figure out the "Supported Account Types" for the app, and why I was receiving the Tenant (error) message…I tried registering a new app (I’m going to have to do this in the long run (with the "Master" app, since I’m working with a test app now loosely titled: TestExchangeWebApp01 which is what I setup a few weeks ago). So when I clicked on "Register an Application" (yesterday) and received the first screen, there was error/warning along the top that says:  This application will not be associated with any directory and will be subject to limitations. You should not create production apps outside of a directory. So, in exploring what was going on, it pointed me in the direction of Subscriptions. When I clicked on Subscriptions, it gave me another screen, and at the bottom it says: You don't have any subscriptions

    So, I work for a non-profit (house of worship teaching middle school students), and I write code (since 1982) for myself (now…to make life easier on myself…but originally on a mainframe as a systems programmer) and for colleagues that do the same sort of education work that I do. My own Azure account is NOT with the non-profit that I work for, but I’m trying to access the Exchange account that’s my work email account (and they have Azure). So…I found a way around it, by telling the MSAL screen that I am ignoring the warning (since it’s generated by a program that I wrote…which I trust)…do I still need a subscription with my private Azure account? Yes…I’d like my colleagues (at other non-profits that do the same sort of work that I do) to be able to use the software that I’ve developed. I do use some GoogleAPI’s that I have an account with in order to access them (mainly location services)…but since the volume is so low…I’m not billed. Is this similar?

    I’m familiar with Refreshing Access tokens (in Zoho). I’ll read the link that you’ve provided and decide how to deploy the logic in my code so that I don’t do something stupid and leave a bug that will blow the thing up when it assumes that it has a valid token instead of requesting it again.

    BTW, here’s the code that I converted from a C# example that I found to save the email message from EWS as an .eml file:

    For Each itmCurrent As Item In findInbox
    	Dim emailCurrent As EmailMessage = EmailMessage.Bind(ewsClient, itmCurrent.Id, propsInbox)
    	Dim mimeCurrent As MimeContent = emailCurrent.MimeContent
    	Using fsCurrent As FileStream = New FileStream(strStudentEmailsDir & "\" & emailCurrent.DateTimeReceived.ToString("yyyy-MM-dd HH-mm-ss") & "_Received_.eml", FileMode.Create)
    		fsCurrent.Write(mimeCurrent.Content, 0, mimeCurrent.Content.Length)
    	End Using
    	Dim lviCurrent As ListViewItem = New ListViewItem({emailCurrent.DateTimeReceived.ToString(), emailCurrent.From.Name.ToString() & "(" + emailCurrent.From.Address.ToString() & ")", emailCurrent.Subject, (If((emailCurrent.HasAttachments), "Yes", "No")), emailCurrent.Id.ToString()})
    	lstMsg.Items.Add(lviCurrent)
    Next

    I did receive an error once during testing…it seemed to trip over an email that it previously didn’t have any problems with (in the .Bind statement). When I was in debug mode, I noticed that many of the properties of itmCurrent had error messages associated with them (instead of values being passed in by EWS), so I had to add Try/Catch logic to intercept an XmlException. I’m not sure why I got it, but if it leaves out processing one email message…I guess that I can live with it. I might try and see if I can have EWS search for the errored EmailMessage directly and try and .Bind it again. Weird.

    Microsoft Graph: Is EWS going away? Using Graph, can I search the Inbox folder for emails from multiple addresses and date-ranged? Can I search the Sent Items folder for multiple recipients and date-ranged? Can I save the messages to disk using the Filestream logic in the above example? I’ve built what I needed in my test program, now all that I have to do is copy the code to the master application and make a few modifications to interface with the emailaddress fields that the Outlook VSTO routine is employing…figure about 30-60 minutes tops to update the master application. BTW, it will have to be registered in Azure to use EWS.

    Again, thanks for your help.
    I tried for weeks to get this going and your feedback helped me solve it in just a few days.

    Take care,

    Paul Goldstein


    Paul D. Goldstein Forceware Systems, Inc.

    Friday, June 26, 2020 4:32 AM
  • You should be able to create an App registration outside of directory using "Accounts in any organizational directory and personal Microsoft accounts" . That said personally I've always done it where there is an underlying directory and Office365 tenant. If you post the ClientId from your registration i can tell you if it will work okay in Mutil tenant but if you getting an Access token and as long as the audience is correct eg "https://outlook.office365.com" it should be okay (you an check you tokens using https://jwt.io/)

    One thing to point out is all the Mailboxes your going to access need to be Office365 Mailboxes. If anybody that want to run you app is using Exchange OnPrem then you will need to use basic/integrated auth for those clients.

    >>Microsoft Graph: Is EWS going away?

    No but it a legacy API so if you writing a Greenfield application that is just running against cloud mailboxes its better to use the Graph as it will have more longevity and will be easy to expand if you want to add new functionality in the future. (But EWS will work fine and if you have OnPrem mailboxes will save you having two different code bases to server clients).  

    Cheers
    Glen

     
    Monday, June 29, 2020 12:16 AM
  • Hi Glen,

    Thanks for writing…hope you had a nice weekend.

    Okay…here’s what I didn’t have setup from the start, and what I did (temporarily I guess) to get it to work last week:
    I didn’t setup a proper domain, so I was getting an Unverified warning…which I ignored since I wrote the app, and know that there’s nothing in it that would compromise my own security. That said…I’m going about securing a domain name and creating a website with a .well-know

    myOutlookApp = New Microsoft.Office.Interop.Outlook.Application
    mpnNamespace = myOutlookApp.GetNamespace("MAPI")
    If (mpnNamespace.ExchangeConnectionMode = OlExchangeConnectionMode.olNoExchange) Then
    	Call subSearchUsingOutlook
    Else
    	Call subSearchUsingExchange()
    End If
    

    What would I need to do to determine if they are OnPrem? I lost you on the basic/integrated auth line…what does that mean as compared to the Oauth2 that I am now employing?

    Thanks again.

    Take care,

    Paul Goldstein

    n/microsoft-identity-association.json page.

    Remember…I’m amateur when it comes to this stuff. I was never on Azure because up until a few weeks ago, I had no need it my app to use EWS…that all changed when I couldn’t access any emails over a month old. The program has always interfaced with Outlook to get my Appointments…but usually one looks forward in time and not back, so that was never an issue.

    I need the program to be flexible to handle all possibilities (right now that has to be within MS when it comes to reading/searching mail…I have a Gmail account, so it’s possible that I could try testing that scenario as well). So if I want to focus on MS/Outlook/Exchange…I already know that I can use the following code to determine Outlook or Exchange:


    Paul D. Goldstein Forceware Systems, Inc.

    Monday, June 29, 2020 3:43 AM
  • >>What would I need to do to determine if they are OnPrem? I lost you on the basic/integrated auth line…what does that mean as compared to the Oauth2 that I am now employing?

    This is what I do 

    https://gsexdev.blogspot.com/2020/06/modifying-your-ews-managed-api-code-to.html

    It covers Office365, Hybrid Modern Authentication (so OnPrem Exchange but Authentication is using Azure) and Basic/Integrated. Its C# but I think you should be okay with it.

    Cheers
    Glen

    Tuesday, June 30, 2020 12:01 AM
  • Hi Glen,

    Thanks for writing.
    I had to put out a few fires and setup the website needed to verify a new domain.

    Now I have a new problem: I created the website, put the json file at: http://<mydomain>/.well-known/microsoft-identity-association.json and when I click on [verify and save domain], I’m receiving:

    Verification of publisher domain failed. The JSON file located at <mydomain>/.well-known/microsoft-identity-association.json has a content length that is not set or otherwise invalid. [FUNRk]

    I’ve tried looking this up, but every discussion that I find doesn’t seem to come up with a solution.
    I can enter the URL from my browser, and it returns the raw JSON data…so, what else am I supposed to do.
    Any ideas?

    Thanks again for your time.

    Sincerely,
    Paul Goldstein

    n and


    Paul D. Goldstein Forceware Systems, Inc.

    Friday, July 3, 2020 4:44 AM
  • Hi Glen,

    I did some more research and found that I needed a TXT record at the domain host, so I took care of that and registered the domain with Azure.

    Then I went back to my test program (interacting with MS Graph) and when I got the "Sign into your account"
    screen, instead of it saying Unverifed, I get the following:

    I’m pretty sure that this is what I’m supposed to be getting…but I’m just checking with you…remember, I’m an amateur.

    One more thing…I did bring this up previously but wanted some clarification (I think I asked about Tokens and their refreshing and expiring). In my test program, I’m logging in and running the Mail Searching test…and that’s it (the program was created to test searching Exchange Inbox and Sent Items mailitems, and does so when the program launches…not from a button on a screen). In the real scenario, the actual/live program will be up and running and the user will be navigating through Students’ accounts and selected whether or not to view their email histories (from a separate form and button that form). When the button is pressed, will the "Sign into your account" screen load every time, or just the first time that the routine is called after the program is launched (I usually have it up and running most of the time since I use it to type up notes and perform other tracking (reports) of the students in the database)?.

    Thanks again for your time and have a great weekend.

    Take care,

    Paul Goldstein


    Paul D. Goldstein Forceware Systems, Inc.

    Friday, July 3, 2020 6:04 PM
  • Sure the image you posted in a Consent dialogue which is normal (if the app hasn't been consented to in the Tenant).

    >>When the button is pressed, will the "Sign into your account" screen load every time, or just the first time that the routine is called after the program is launched (I usually have it up and running most of the time since I use it to type up notes and perform other tracking (reports) of the students in the database)?.

    It hard to answer this one as I don't understand you app, but generally you would need a login to obtain an accessToken and then its really how you persist that token (eg the token cache etc). If you using the MSAL then you would call AcquireTokenSilent which should give you the token if its cache or refersh the token if its expired. Or if not it will throw a MsalUiRequiredException that will mean you will need an interactive logon.

    Cheers
    Glen

    Sunday, July 5, 2020 11:48 PM
  • Hi Glen,

    Thanks for writing.

    I’ve got most of the form up and running, now it’s just some cosmetic items to cleanup. I have a couple of questions (in no particular order), and again…because I’m an amateur:

    1) I’ve got the entire MSAL and searching of the Inbox and Sent Items emails…and saving them to disk (as .eml files) in the same Sub. The problem that I’m having is that after I save the EMailMessages to disk, I need to call the Sub that populates a Listbox on the screen with the: Subject and DateTime (DateReceived or SentOn depending on which Folder it was found in). The populate the Listbox Sub (subLoadEmailsFromDir()) is called: A) When the form is loaded (for messages previously found, B) The SearchUsingExchange Sub completes, or C) The SearchUsingOutlook Sub completes.
    The problem that I’m having is that the SearchUsingExchange Sub is a Shared Async Sub (because of the Await .AcquireToken... calls) and I can’t figure out how to wait until it completes. Here is the code for calling it:

    Call subSearchUsingExchange()
    Call subLoadEmailsFromDir()

    and the sub is declared as:

    Private Shared Async Sub SubSearchUsingExchange()

    I need to wait for subSearchUsingExchange to finish, before calling subLoadEmailsFromDir(), but I can’t figure this out. My only prior experience of Waiting is with a Timer (that I use in another form frmCalendar…the main form…for updating info on it periodically)

    2) My other question pertains to my previous posting about Tokens. I found a GItHub example (https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/wiki/AcquireTokenSilentAsync-using-a-cached-tokenRecommended call pattern in public client applications with MSAL.NET 3.x) that I translated into vb.net, but I’m pretty sure that I’m missing on what to do with the Token for the next time. 
    Here is my code for acquiring the tokens in SubSearchUsingExchange:

    Private Shared Async Sub subSearchUsingExchange()
    
    	Dim strClientID As String = ConfigurationManager.AppSettings("AppID")
    	Dim strClientSecret As String = ConfigurationManager.AppSettings("ClientSecret")
    	Dim strTenantID As String = ConfigurationManager.AppSettings("TenantID")
    	Dim strAuthority As String = $"https://login.microsoftonline.com/{strTenantID}"
    	Dim pcaOptions As New PublicClientApplicationOptions With {
    		.ClientId = ConfigurationManager.AppSettings("AppID"),
    		.TenantId = ConfigurationManager.AppSettings(strTenantID),
    		.RedirectUri = "https://login.microsoftonline.com/common/oauth2/nativeclient"}
    	Dim pcaBuilder = PublicClientApplicationBuilder.CreateWithApplicationOptions(pcaOptions).Build
    	Dim ewsScopes01 = {"https://outlook.office.com/EWS.AccessAsUser.All"}
    	Dim intReceivedCnt, intSentCnt As Integer
    	Dim authResult01 As AuthenticationResult
    	Dim AppAccounts = Await pcaBuilder.GetAccountsAsync
    	Dim boolTokenSilentError As Boolean = False
    	Try
    		authResult01 = Await pcaBuilder.AcquireTokenSilent(ewsScopes01, AppAccounts.FirstOrDefault).ExecuteAsync
    	Catch ex As MsalUiRequiredException
    		boolTokenSilentError = True
    		Debug.Print("Error in AcquireTokenSilent: " & ex.Message)
    	End Try
    
    	If (boolTokenSilentError) Then
    		authResult01 = Await pcaBuilder.AcquireTokenInteractive(ewsScopes01).ExecuteAsync
    	End If
    
    	Dim ewsClient As New ExchangeService
    	ewsClient.Url = New Uri("https://outlook.office365.com/EWS/Exchange.asmx")
    	ewsClient.Credentials = New OAuthCredentials(authResult01.AccessToken)
    
    	Dim fldrInbox As Microsoft.Exchange.WebServices.Data.Folder = Microsoft.Exchange.WebServices.Data.Folder.Bind(ewsClient, WellKnownFolderName.Inbox)
    	Dim fldrSentFolders As Microsoft.Exchange.WebServices.Data.Folder = Microsoft.Exchange.WebServices.Data.Folder.Bind(ewsClient, WellKnownFolderName.SentItems)
    

    followed by the SearchFilter and Find logic.
    So…where do I put the the Token found in authResult01 of AcquireTokenInteractive, if I want to use it in AcquireTokenSilent?

    Any suggestions would be greatly appreciated.

    Take care,

    Paul Goldstein

      

    Paul D. Goldstein Forceware Systems, Inc.

    Wednesday, July 8, 2020 4:31 AM
  • 1. If its Async why don't you just using await ? or run the Async code synchronously

    2. MSAL does the caching for you so there isn't anything you need to do (other then test it)

     
    Sunday, July 12, 2020 11:18 PM
  • Hi Glen,

    Thanks for writing.
    I’ve worked through most of my issues and only have a couple left.
    I think I have some dead code…not sure if it’s necessary.

    At the beginning of the routine that searches via EWS, I’m using the following AcquireTokenSilent logic:

    Dim strClientID As String = ConfigurationManager.AppSettings("AppID")
    Dim strClientSecret As String = ConfigurationManager.AppSettings("ClientSecret")
    Dim strTenantID As String = ConfigurationManager.AppSettings("TenantID")
    Dim strAuthority As String = $"https://login.microsoftonline.com/{strTenantID}"
    Dim pcaOptions As New PublicClientApplicationOptions With {
    	.ClientId = ConfigurationManager.AppSettings("AppID"),
    	.TenantId = ConfigurationManager.AppSettings(strTenantID),
    	.RedirectUri = "https://login.microsoftonline.com/common/oauth2/nativeclient"}
    Dim pcaBuilder = PublicClientApplicationBuilder.CreateWithApplicationOptions(pcaOptions).Build
    Dim ewsScopes01 = {"https://outlook.office.com/EWS.AccessAsUser.All"}
    Dim intReceivedCnt, intSentCnt As Integer
    Dim authResult01 As AuthenticationResult ' = Await pcaBuilder.AcquireTokenInteractive(ewsScopes01).ExecuteAsync
    Dim AppAccounts = Await pcaBuilder.GetAccountsAsync
    Dim boolTokenSilentError As Boolean = False
    Dim boolGetToken As Boolean
    Dim strAccessToken As String = fncGetRegistryVal("EWS-AccessToken")
    Dim dteTokenExpiration As Date
    Try
      authResult01 = Await pcaBuilder.AcquireTokenSilent(ewsScopes01, AppAccounts.FirstOrDefault).ExecuteAsync
    Catch ex As MsalUiRequiredException
      boolTokenSilentError = True
      Debug.Print($"Error in AcquireTokenSilent: {ex.Message}")
    End Try

    This always fails and then I use: AcquireTokenInteractive logic (if the Token has expired).

    So…is the AcquireTokenSilent logic correct?
    Do I need to include it if it always fails?
    I commented it out during some testing, and it didn’t appear affect anything.
    Or…most likely…am I missing the point on something in my Azure configuration and I should be receiving a token in AcquireTokenSilent in specific circumstances?

    Thanks for your time in advance.

    Take care,

    Paul Goldstein


    Paul D. Goldstein Forceware Systems, Inc.

    Monday, July 20, 2020 9:44 PM
  • AcquireTokenSilent will just pull the token from the cache if its available  so really has nothing to do with the configuration more to do with the context your running the application in which sound like the default caching mechanism may not work for you.  I'd suggest you read through https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/wiki/token-cache-serialization


    Wednesday, July 22, 2020 6:52 AM