AD FS and MFA – configuring multiple additional authentication rules

Ever since Microsoft bought PhoneFactor 3 years ago, they have been heavily investing in incorporating it into different products, both on-prem and in the cloud. And they have no intention on slowing down – AD FS vNext will have ‘native’ integration with Azure MFA, eliminating the need to deploy the on-prem MFA server for organizations that sync to Azure AD. Apart from that, they are also making additional improvements on how the auth process works, with the switch to Modern auth and the introduction of the Access control policies for AD FS. Even with AD FS 3.0, it’s just a matter of few simple clicks to set up AD FS to require MFA only when accessing resources from outside of the corporate network, and things get even better in vNext. So there is no more need to play with those pesky claims rules?

For those that will stick to older version of AD FS however, and for people that want even more customizability, the claims rules are here to stay. In this post, I will briefly discuss how we can configure multiple additional authentication rules, so we can have a different behavior for MFA depending on the device or client used, the location of the user or any other information presented as a claim. The AdditionalAuthenticationRules were introduced with AD FS 3.0, and they use the same familiar claims rules syntax. They can be applied both Globally, or per specific Replying Party trust, and as the name suggests they will be executed after the initial Authentication takes place. The process is described in this excellent post by Ramiro Calderon, and here we will cover one more detail.

Basically, I want to just point out that the additional authentication rules are very similar to ‘regular’ claims rules and we can use every claim about the user/device with them. Those can include things such as UPN or group membership, but also information on the client (user agent string), the IP the request come from, the protocol used, etc. And, we can create and enforce multiple rules, but to do so we will have to rely on PowerShell in most scenarios that make sense! As detailed in the blog post above, if we use the GUI to configure the additional authentication rules, a new rule is created for each of the selected conditions, effectively resulting in OR-er configuration. For example this is what we will get if we enable off of them:

PS C:\> (Get-AdfsRelyingPartyTrust “O365”).AdditionalAuthenticationRules
c:[Type == “http://schemas.microsoft.com/ws/2008/06/identity/claims/groupsid”, Value == “S-1-5-21-1315440946-3826617302-920981253-512”]
=> issue(Type = “http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod”, Value = “http://schemas.microsoft.com/claims/multipleauthn”);
 
c:[Type == “http://schemas.microsoft.com/ws/2008/06/identity/claims/primarysid”, Value == “S-1-5-21-1315440946-3826617302-920981253-500”]
=> issue(Type = “http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod”, Value = “http://schemas.microsoft.com/claims/multipleauthn”);
 
c:[Type == “http://schemas.microsoft.com/2012/01/devicecontext/claims/isregistereduser”, Value == “false”]
=> issue(Type = “http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod”, Value = “http://schemas.microsoft.com/claims/multipleauthn”);
 
exists([Type == “http://schemas.microsoft.com/2012/01/devicecontext/claims/registrationid”])
=> issue(Type = “http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod”, Value = “http://schemas.microsoft.com/claims/multipleauthn”);
 
c:[Type == “http://schemas.microsoft.com/ws/2012/01/insidecorporatenetwork”, Value == “false”]
=> issue(Type = “http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod”, Value = “http://schemas.microsoft.com/claims/multipleauthn”);
 
c:[Type == “http://schemas.microsoft.com/ws/2012/01/insidecorporatenetwork”, Value == “true”]
=> issue(Type = “http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod”, Value = “http://schemas.microsoft.com/claims/multipleauthn”);

In some cases however using this OR configuration is not enough – we might want to combine several criteria within a single rule and even apply multiple such rules. As you might have noticed from the output above, PowerShell stores the rules as a string and each of the above represent a separate rule. If we want to extract them one by one, we can use the split method:

PS C:\> $rules = (Get-AdfsRelyingPartyTrust “O365”).AdditionalAuthenticationRules -split “`r`n`r`n”
PS C:\> $rules[0]
c:[Type == “http://schemas.microsoft.com/ws/2008/06/identity/claims/groupsid”, Value == “S-1-5-21-1315440946-3826617302-920981253-512”]
=> issue(Type = “http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod”, Value = “http://schemas.microsoft.com/claims/multipleauthn”);

PowerShell is definitely not the best tool to do text editing, so any changes to the actual rules are best done in proper editor. Once we have decided which changes to make however, we will have to rely on PowerShell to set or update the rules. Say for example we want to test the following scenario – force MFA for Chrome and Opera for requests coming outside of the corporate environment. The additional authentication rule that we want to configure in this case will look something like:

‘c:[Type == “http://schemas.microsoft.com/ws/2012/01/insidecorporatenetwork”, Value == “false”]
&& c1:[Type == “http://schemas.microsoft.com/2012/01/requestcontext/claims/x-ms-client-user-agent”, Value =~ “(Opera)|(Chrome)”]
&& c2:[Type == “http://schemas.microsoft.com/2012/01/requestcontext/claims/x-ms-endpoint-absolute-path”, Value =~ “(/adfs/ls)|(/adfs/oauth2)”]
=> issue(Type = “http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod”, Value = “http://schemas.microsoft.com/claims/multipleauthn”);’

where we have also added the x-ms-endpoint-absolute-path claim to make sure we only enforce this on the Passive endpoint (doesn’t make much difference for browser traffic, but still). Again, as there is no way to configure a rule with multiple conditions via the GUI, we have to set it up via PowerShell. Assuming we start without any other additional authentication rules, we have:

PS C:\> Set-AdfsRelyingPartyTrust -TargetName “O365” -AdditionalAuthenticationRules ‘c:[Type == “http://schemas.microsoft.com/ws/2012/01/insidecorporatenetwork”, Value == “false”] && c1:[Type == “http://schemas.microsoft.com/2012/01/requestcontext/claims/x-ms-client-user-agent”, Value =~ “(Opera)|(Chrome)”] && c2:[Type == “http://schemas.microsoft.com/2012/01/requestcontext/claims/x-ms-endpoint-absolute-path”, Value =~ “(/adfs/ls)|(/adfs/oauth2)”] => issue(Type = “http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod”, Value = “http://schemas.microsoft.com/claims/multipleauthn”);’
 
PS C:\> (Get-AdfsRelyingPartyTrust “O365”).AdditionalAuthenticationRules
c:[Type == “http://schemas.microsoft.com/ws/2012/01/insidecorporatenetwork”, Value == “false”]
&& c1:[Type == “http://schemas.microsoft.com/2012/01/requestcontext/claims/x-ms-client-user-agent”, Value =~ “(Opera)|(Chrome)”]
&& c2:[Type == “http://schemas.microsoft.com/2012/01/requestcontext/claims/x-ms-endpoint-absolute-path”, Value =~ “(/adfs/ls)|(/adfs/oauth2)”]
=> issue(Type = “http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod”, Value = “http://schemas.microsoft.com/claims/multipleauthn”);

Next, we decide that we want our new helpdesk member Gosho to always be a subject of MFA, as should be all the members of a particular AD group, regardless of location, workload or client used. An example claims rule for this scenario will look like:

‘c:[Type == “http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn”, Value =~ “(?i)gosho@sts.michev.info”]
&& c1:[Type == “http://schemas.microsoft.com/ws/2008/06/identity/claims/groupsid”, Value == “S-1-5-21-1315440946-3826617302-920981253-512”]
=> issue(Type = “http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod”, Value = “http://schemas.microsoft.com/claims/multipleauthn”);’

where we have provided the UserPrincipalName of Gosho and the SID on the group. As we already have one rule configured, we cannot directly use the -AdditionalAuthenticationRules parameter in this case. Instead, we will store the old rule(s) in a string variable and append to it:

PS C:\> $old = (Get-AdfsRelyingPartyTrust “O365”).AdditionalAuthenticationRules
PS C:\> $old
c:[Type == “http://schemas.microsoft.com/ws/2012/01/insidecorporatenetwork”, Value == “false”]
&& c1:[Type == “http://schemas.microsoft.com/2012/01/requestcontext/claims/x-ms-client-user-agent”, Value =~ “(Opera)|(Chrome)”]
&& c2:[Type == “http://schemas.microsoft.com/2012/01/requestcontext/claims/x-ms-endpoint-absolute-path”, Value =~ “(/adfs/ls)|(/adfs/oauth2)”]
=> issue(Type = “http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod”, Value = “http://schemas.microsoft.com/claims/multipleauthn”);
 
PS C:\> $new = $old + ‘c:[Type == “http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn”, Value =~ “(?i)gosho@sts.michev.info”] && c1:[Type == “http://schemas.microsoft.com/ws/2008/06/identity/claims/groupsid”, Value == “S-1-5-21-1315440946-3826617302-920981253-512”] => issue(Type = “http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod”, Value = “http://schemas.microsoft.com/claims/multipleauthn”);’
 
PS C:\> $new
c:[Type == “http://schemas.microsoft.com/ws/2012/01/insidecorporatenetwork”, Value == “false”]
&& c1:[Type == “http://schemas.microsoft.com/2012/01/requestcontext/claims/x-ms-client-user-agent”, Value =~ “(Opera)|(Chrome)”]
&& c2:[Type == “http://schemas.microsoft.com/2012/01/requestcontext/claims/x-ms-endpoint-absolute-path”, Value =~ “(/adfs/ls)|(/adfs/oauth2)”]
=> issue(Type = “http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod”, Value = “http://schemas.microsoft.com/claims/multipleauthn”);
 
c:[Type == “http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn”, Value =~ “(?i)gosho@sts.michev.info”] && c1:[Type == “http://schemas.microsoft.com/ws/2008/06/identity/claims/groupsid”, Value == “S-1-5-21-1315440946-3826617302-920981253-512”] => issue(Type = “http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod”, Value = “http://schemas.microsoft.com/claims/multipleauthn”);

Next, we use the $new variable to properly configure the set of claims rules:

PS C:\> $newset = New-AdfsClaimRuleSet -ClaimRule $new
PS C:\> $newset
 
ClaimRules ClaimRulesString
———- —————-
{c:[Type == “http://schemas.microsoft.com/ws/2012/01/insidecorporatenetwork”, Value == “false”]… c:[Type == “http://schemas.microsoft.com/ws/2012/01/insidecorporatenetwork”, Value == “false”]…

and then we set them via:

PS C:\> Set-AdfsRelyingPartyTrust -TargetName “O365” -AdditionalAuthenticationRules $newset.ClaimRulesString

Here’s how the end result will look like:

PS C:\> (Get-AdfsRelyingPartyTrust “O365”).AdditionalAuthenticationRules
c:[Type == “http://schemas.microsoft.com/ws/2012/01/insidecorporatenetwork”, Value == “false”]
&& c1:[Type == “http://schemas.microsoft.com/2012/01/requestcontext/claims/x-ms-client-user-agent”, Value =~ “(Opera)|(Chrome)”]
&& c2:[Type == “http://schemas.microsoft.com/2012/01/requestcontext/claims/x-ms-endpoint-absolute-path”, Value =~ “(/adfs/ls)|(/adfs/oauth2)”]
=> issue(Type = “http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod”, Value = “http://schemas.microsoft.com/claims/multipleauthn”);
 
c:[Type == “http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn”, Value =~ “(?i)gosho@sts.michev.info”]
&& c1:[Type == “http://schemas.microsoft.com/ws/2008/06/identity/claims/groupsid”, Value == “S-1-5-21-1315440946-3826617302-920981253-512”]
=> issue(Type = “http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod”, Value = “http://schemas.microsoft.com/claims/multipleauthn”);

So, to recap the process, here are the steps needed to configure multiple additional authentication rules for AD FS:

  1. Save the existing rules to a variable
    $old = (Get-AdfsRelyingPartyTrust “O365”).AdditionalAuthenticationRules
  2. Append any new rules to the variable
    $new = $old + ‘new claims rule goes here’
  3. Prepare the new set of rules
    $newset = New-AdfsClaimRuleSet -ClaimRule $new
  4. And finally, set the new rules
    Set-AdfsRelyingPartyTrust -TargetName “O365” -AdditionalAuthenticationRules $newset.ClaimRulesString

Oh, and just to be on the safe side, before making any changes, do a backup! To backup all rules for a specific trust, use:

PS C:\> (Get-AdfsRelyingPartyTrust “O365”).AdditionalAuthenticationRules | out-file “C:\Users\vasil\Desktop\AAR.txt”

and to restore from a backup, use:

PS C:\> Set-AdfsRelyingPartyTrust -TargetName “Device Registration Service” -AdditionalAuthenticationRulesFile “C:\Users\vasil\Desktop\AAR.txt”

This entry was posted in Office 365, PowerShell. Bookmark the permalink.

Leave a Reply

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