Passkey registration problems
Passkeys in Microsoft Authenticator are fantastic, assuming you can get users logged in to the app successfully. Unfortunately, many companies that are serious about rolling out phishing resistant authentication are also serious about requiring device compliance and/or app protection policies, and they have run into issues where where these policies prevent users from creating passkeys.
Microsoft documents this problem here:
All is not lost though, because Microsoft gives us a few options for how to work around this issue:
This first workaround uses filter for apps, but that is nearly impossible becuase CA targets the resource and not the client app, so we don’t have a great way to inventory this. On top of that, filter for apps is unnecessarily complicated for addressing this issue :)
This second workaround using “OR compliant device” is a big no-go for many orgs. Push and Passwordless never required enrolling personal devices into Intune, so this change adds another point of contention for users and IT that will negatively affect the perception and overall success of passkey deployment.
This final workaround is to temporarily exclude users with compensating controls, but there is no automated option for time-bound or triggered exclusion in Conditional Access provided by Microsoft. I have created several automated solutions for this, but it was still very hard to minimize the gap this method introduces for many orgs.
Unfortunately, none of these options were very good… That was until one of my brilliant co-workers noticed that excluding the Azure Credential Configuration Endpoint Service (ea890292-c8c8-4433-b5ea-b09d0668e1a6) allowed for passkey registration with APP enforced.
After some significant testing, this is the path that I will be recommending for our passkey deployments, and I hope this helps everyone with their deployments too :)
Azure Credential Configuration Endpoint Service
So what is this service principal? While there is no public documentation, and thus no definitive answer, my research seems to indicate this service principal is responsible for the registration of security information gathered by apps such as Microsoft Authenticator and the My Signins portal against a user.
To understand what is happening, let’s look at the new(ish) audience reporting on the sign-in logs for failed attempts to register with Compliance or APP requirements:
Compliance | App Protection Policies |
---|---|
![]() | ![]() |
Even though we’re using Microsoft Authenticator and the sign-in logs show the App Id/Name as Microsoft Authenticator, that isn’t the resource referenced here. Remember how they renamed “All cloud apps” to “All resources”? That’s because we’re protecting the resources that apps use, not the app we are using, and in this case, we are requesting an access token that Microsoft Graph can use to talk to the applications in the audience. It almost has a Resource-Based Kerberos Constrained Delegation feel to it :)
Exclusions leads to success, but at what cost?
Any time we plan on making an exclusion, our first questions should be what kind of gap are we creating, what can we do about it (compensating control), and is that acceptable? Fortunately, I got the benefit of knowing this exclusion worked without trial and error, but this process has also proven useful in several other scenarios when working with clients where we were still getting blocked despite making what we thought were appropriate exclusions.
When looking at the sign-in logs (like the images in the previous section), you may only see the Application Id but no name in your tenant. This is because the preconsented service pricnipal has not been registered to your tenant, and you will need to register this it before you can select it in Conditional Access:
1Invoke-MgGraphRequest -Method POST -Uri "https://graph.microsoft.com/v1.0/servicePrincipals" -Body (@{ appId = "ea890292-c8c8-4433-b5ea-b09d0668e1a6" } | ConvertTo-Json)
1New-MgServicePrincipal -AppId 'ea890292-c8c8-4433-b5ea-b09d0668e1a6'
1New-EntraServicePrincipal -AppId 'ea890292-c8c8-4433-b5ea-b09d0668e1a6'
With this completed, we can now actually find the resource in the app picker in Conditional Access, and I would recommend duplicating your existing Conditional Access policies rather than modifying the existing one, then add the exclusion to the new policy like this:
Because it is impossible to know the downstream effects of excluding this resource (without Microsoft providing explicit documentation), I would recommend creating a security group for users who will need to register passkeys, include that group in the new policy, and exclude them from the existing policy. This method is much safer than excluding entirely from Compliance or APP, so the timing of removal from the group isn’t likely as important.
I would recommend everyone investigate the login events that have used this resource using the following queries. This should give you a better understanding of how it is being used in your environment:
1$events = @()
2$uri = "https://graph.microsoft.com/beta/auditLogs/signIns?\`$filter=conditionalAccessAudiences/any(i:i eq 'ea890292-c8c8-4433-b5ea-b09d0668e1a6')&\`$top=999"
3do {
4 $response = Invoke-MgGraphRequest -Method GET -Uri $uri
5 $events += $response.value
6 $uri = $response.'@odata.nextLink'
7} while ($uri)
8$events | Select-Object appId,appDisplayName,clientAppUsed,resourceDisplayName,resourceId,servicePrincipalId | Group-Object AppDisplayName | Format-List
1$events = Get-MgBetaAuditLogSignIn -Filter "conditionalAccessAudiences/any(i:i eq 'ea890292-c8c8-4433-b5ea-b09d0668e1a6')" -All
2 $events | Select-Object appId,appDisplayName,clientAppUsed,resourceDisplayName,resourceId,servicePrincipalId | Group-Object AppDisplayName | Format-List
1$events = Get-EntraAuditSignInLog -Filter "conditionalAccessAudiences/any(i:i eq 'ea890292-c8c8-4433-b5ea-b09d0668e1a6')" -All
2$events | Select-Object appId,appDisplayName,clientAppUsed,resourceDisplayName,resourceId,servicePrincipalId | Group-Object AppDisplayName | Format-List
Closing the gap
As mentioned previously, it might not be a good idea to leave this exclusion on our default policy. I have done fairly extensive testing to verify that compliance and app protection policies still apply to all apps with this exclusion in place, but there may be more nuanced techniques that allow for a platform credential registration from a device that might not normally be allowed or something along those lines.
Instead, I recommend using the two policy approach, put users who haven’t registered yet in a group, target them with the policy that has an exclusion and then exclude that group for the existing policy. We can use several automation methods to automatically remove the user from the group once they have completed passkey registration - periodically query the audit logs and remove them or trigger automation based on a KQL query.
1AuditLogs
2| where OperationName == @"Add Passkey (device-bound)"
3| where parse_json(AdditionalDetails)[1]["value"] == "de1e552d-db1d-4423-a619-566b625cdc84"
I have a very similar automation to ensure emergency access accounts are added to any Conditional Access policy that is modified, and I am working on a blog post that outlines each method right now. I will come back and update this post with references that can be used with these methods as soon as I am finished, but for now, you can check out the automations here and make modifications:
Or you can wait for me to create the new Logic Apps and such :)