If you have a DAG and you're using a frontend load balancer or DNS round robin, disabling legacy auth at the organization level will not work and will cause everything to go haywire. Be careful disabling legacy auth at the org level if you have a cluster.
The article Disabling Legacy Authentication in Exchange Server 2019 walks you through disabling legacy authentication in your on premise exchange environment. Similar steps can be found on docs.microsoft.com.
It would appear that the steps are all well and good if you're only running only one on premise exchange server. If you're running a DAG cluster with multiple servers handling requests then things don't go as planned. If you disabled legacy authentication at the organization level it is my experience that authentication will only work if you're hitting the node in the cluster that contains the database that the user's mailbox resides on. If you hit the other node you'll get 401 authentication errors if you're watching the traffic in fiddler. You will also get some duplicate key exceptions in the event logs. Existing outlook clients my flash the authentication dialog on the screen randomly, so quick you can't even really see it. If you try to create new profiles and you're hitting the wrong node then it won't work.
The issue seems to be that when requests are proxied between nodes then legacy authentication needs to be enabled. When you disable legacy auth at the org level it also disables legacy auth for the proxying connection. The solution is to enable legacy auth on the computer accounts of the exchange servers.
The way I did this was I created a authentication policy called "Allow Legacy Auth" which is the exact opposite of "Block Legacy Auth" in the articles above.
New-AuthenticationPolicy -Name "Allow Legacy Auth"
I then manually set msExchAuthPolicyLink on the exchange server computer objects in AD to use this new "Allow Legacy Auth" authentication policy. The way I did this was I set the authentication policy on a test user to the new "Allow Legacy Auth" policy and then I copied the value of the msExchAuthPolicyLink attribute from that user; pasting the value into the msExchAuthPolicyLink attribute on the exchange server computer object. It should look something like this:
CN=Allow Legacy Auth,CN=Auth Policies,CN=DOMAINNAME,CN=Microsoft Exchange,CN=Services,CN=Configuration,DC=DOMAINNAME,DC=com
You can do this with ADSIEDIT or AD users and computers snap in. Be sure to restart IIS on all your nodes after doing this.
Now when I disable legacy auth at the org level things actually work.
It doesn't appear as though Microsoft fully tested disabling legacy auth with clusters. The feature needs more work, so implement this solution at your own risk. Even after enabling legacy auth on the exchange computer accounts you may still get a bunch of unhandled exceptions in the event log about duplicate keys like the following:
Log Name: Application Source: MSExchange Front End HTTP Proxy Event ID: 1003 Task Category: Core Level: Error Keywords: Classic User: N/A Description: [Ews] An internal server error occurred. The unhandled exception was: Microsoft.Exchange.Collections.TimeoutCache.DuplicateKeyException: Cannot add a duplicate key. Use Insert instead at Microsoft.Exchange.Security.Authentication.FederatedAuthService.CacheReader.AddEntry(String userKey, Int32 userPolicy, ConfigWrapper config) at Microsoft.Exchange.Security.Authentication.FederatedAuthService.BasicAuthPolicyRepo.GetUserPolicy(String userKey, Int32 traceId, Int32& userPolicy, HttpApplication httpApplication, IRecipientSession recipientSession, IConfigurationSession configSession, ConfigWrapper config) at Microsoft.Exchange.Security.Authentication.FederatedAuthService.BasicAuthPolicyEvaluator.IsBasicAuthAllowed(String userKey, String protocolName, Int32 traceId, HttpApplication httpApplication, IRecipientSession recipientSession, IConfigurationSession configSession, ConfigWrapper config) at Microsoft.Exchange.HttpProxy.ProxyModule.IsLegacyAuthAllowed(HttpApplication httpApplication) at Microsoft.Exchange.HttpProxy.ProxyModule.OnPostAuthenticateInternal(HttpApplication httpApplication) at Microsoft.Exchange.Common.IL.ILUtil.DoTryFilterCatch(Action tryDelegate, Func`2 filterDelegate, Action`1 catchDelegate)
Log Name: Application Source: ASP.NET 4.0.30319.0 Event ID: 1309 Task Category: Web Event Level: Warning Keywords: Classic User: N/A Description: Event code: 3005 Event message: An unhandled exception has occurred. Event ID: f227e48f4d474111873957cee43f41ff Event sequence: 2 Event occurrence: 1 Event detail code: 0 Application information: Application domain: /LM/W3SVC/2/ROOT/EWS-2-132518667113847979 Trust level: Full Application Virtual Path: /EWS Application Path: C:\Program Files\Microsoft\Exchange Server\V15\ClientAccess\exchweb\EWS\ Process information: Process ID: 12016 Process name: w3wp.exe Account name: NT AUTHORITY\SYSTEM Exception information: Exception type: DuplicateKeyException Exception message: Cannot add a duplicate key. Use Insert instead
Log Name: Application Source: MSExchange Autodiscover Event ID: 1 Task Category: Web Level: Error Keywords: Classic User: N/A Description: Unhandled Exception "Cannot add a duplicate key. Use Insert instead" Stack trace: at Microsoft.Exchange.Security.Authentication.FederatedAuthService.CacheReader.AddEntry(String userKey, Int32 userPolicy, ConfigWrapper config) at Microsoft.Exchange.Security.Authentication.FederatedAuthService.BasicAuthPolicyRepo.GetUserPolicy(String userKey, Int32 traceId, Int32& userPolicy, HttpApplication httpApplication, IRecipientSession recipientSession, IConfigurationSession configSession, ConfigWrapper config) at Microsoft.Exchange.Security.Authentication.FederatedAuthService.BasicAuthPolicyEvaluator.IsBasicAuthAllowed(String userKey, String protocolName, Int32 traceId, HttpApplication httpApplication, IRecipientSession recipientSession, IConfigurationSession configSession, ConfigWrapper config) at Microsoft.Exchange.Security.Authentication.BackendRehydrationModule.IsLegacyAuthAllowed(HttpContext httpContext) at Microsoft.Exchange.Security.Authentication.BackendRehydrationModule.OnAuthenticateRequest(Object source, EventArgs args) at System.Web.HttpApplication.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() at System.Web.HttpApplication.ExecuteStepImpl(IExecutionStep step) at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)
Microsoft needs to fix this. Unhandled exceptions are not good. They will cause the app pools to recycle. Microsoft support's recommended fix is to move your mailboxes to the cloud 😒