Tip #1313: Blocked by conditional access

We are back from the travel bursts, some reorganization, and “hold my beer I’m too busy to do it myself” spurts. The first post after this short hiatus is not going to be about Power Automate, however tempting it might be. We’ll get to that subject later on this week. In the meantime, something a bit more pressing.

Imagine you have working code, something innocuous, something perhaps like this fragment:

using (var client = new CrmServiceClient(connStr))
{
  if (client.IsReady)
  {
    Trace.WriteLine(
      $"User={client.GetMyCrmUserId()}");
    Trace.WriteLine(
      $"Org={client.ConnectedOrgFriendlyName}");
  }
  else
  {
    Trace.WriteLine("NOT READY");
  }
}

You admire this bastion of reliability for a second then go get a refill for a drink of your choice. When you come back 5 minutes later, your defenses are crushed and the code stopped working (a.k.a NOT READY message). No, this is not a joke, yes, participants of the Toronto BAD Masterclass witnessed it in real time and yes, it did happen in real life though perhaps less dramatic, just replace going for a drink with going home and coming back the next morning.

The worst part is that the logs won’t give you anything. Even if you enable logging as per Use Trace Not Console nugget of wisdom, you’d get an infamous fault exception at best:

Error: 
An unsecured or incorrectly secured fault
was received from the other party.
See the inner FaultException
for the fault code
and detail.

It does read like a mediocre haiku when you format it like that, doesn’t it?

If you have your own authentication implementation that goes a bit deeper (as we’ve done in our PHP Toolkit), you may get a less cryptic message

AADSTS53003: Blocked by conditional access.

Which is still a mystery – what conditional access? As with any murder story, there are two parts to whodunnit.

Part 1: The diligent

Unbeknownst to you, a curious but, unfortunately, overzealous and not very thorough Azure AD administrator discovered baseline protection policies and, among those, ability to block legacy authentication. “We don’t use POP, IMAP, SMTP, do we” – reasonably argued the admin and enabled the policy.

Turns out, “legacy authentication protocol” is a very broad and not very well defined scope that includes any user-level protocol where MFA cannot be applied. That unfortunately, includes WS-Trust authentication that is widely used by our beloved SOAP client that, as of the time of writing, is still a foundation of the SDK client including CrmServiceClient from XrmTooling.

Part 2: The oblivious

You guessed it – the connection string in the example above was using username/password combination. Perhaps, it was a historical oversight. Perhaps, despite the server-to-server authentication being available for a long time, the coder was clinging to the non-interactive user logins. Whatever is the reason, combined with the shooting in the dark admin, the code fell apart.

It was a hunting accident. Admin was aiming for a sitting duck (or so they thought), the code just happened to be in the line of sight.

Part 3: The solution

Review your code for any authentication that still uses username/password and replace it with the OAuth equivalent. If the process is interactive, use ADAL libraries. If the process is in a dungeon, use server-to-server authentication variation of those. Easiest way to deal with these scenario is to use XrmTooling with its plethora of authentication methods, both interactive and not so.

But whatever you do

STOP USING named Office 365 account for authentication

Tîpp Jäår

If you have Azure AD Premium, you should be able to buy some extra time by adding a policy exception that would let WS-Trust through. I wouldn’t recommend it though – could get messy and it’s better to fix the code, anyway.

Cover photo by kat wilcox from Pexels

Leave a Reply

Your email address will not be published. Required fields are marked *