Modern authentication has been around for a while now, and it’s great. It brought support for the latest and greatest in authentication and authorization protocols and made new scenarios available. It gave us simple, unified experience across devices and platforms and improvements to the Alternate Login ID feature. On top of all that, it enabled proper support for two-factor authentication for all clients and put an end to the Office 2013 RTM fiasco (bye-bye rich clients, rest in peace app passwords!).
Bhargav Shukla recently did a webcast on the subject of MFA, so I will not bore you with the details again. OK, maybe I can remind you about one detail, namely that the ADAL library, responsible for the client-side implementation of Modern authentication, is agnostic about the method of 2FA used. It only cares about the token and allows you to use any 2FA method (or none) to login. This in turn allows you to select among an ever growing list of 2FA providers.
If we stick to what Microsoft is offering, we can choose between the Azure MFA provider for managed identities, or the built-in certificate provider and Azure MFA Server for federated identities (AD FS). In this post we will focus on the later scenario.
Let’s start by doing a quick refresh of how he MFA process works with federated identities. When the user hits any Office 365 resource, he will be redirected to the on-prem AD FS server for authentication. Upon successful (first-factor) authentication, a new set of claims rules can be used to trigger the second-factor authentication process, if desired. Said rules are called Additional Authentication Rules and are configurable on both the Global AD FS level as well as per-application (RPT). If either rule set issues a claim of type “http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod” and value “http://schemas.microsoft.com/claims/multipleauthn”, then MFA challenge is presented to the user.
Upon successful completion of the MFA process, the AD FS server will insert few additional claims and will continue along the pipeline with executing the Authorization and Issuance Transform rules, until finally generating a security token. A nice overview of the process can be found for example in this article.
Configuring Additional Authentication Rules
While we can use the GUI to configure multi-factor authentication policies on both the Global and per-RPT level with AD FS 3.0, there are certain restrictions. Most importantly, each of the options available in the GUI creates a new claims rule with a single condition, basically enforcing an OR configuration (i.e. if any of the conditions selected in the GUI fires, MFA must be performed). Often times this is not desired, as we might have the need to combine several criteria. In such situations, we will have to work with the claims rules directly. This is not specific to the Additional Authentication Rules, the difference here is that unlike the Issuance Authorization and Transform rules, the Additional Authentication Rules (AARs from now on) are not editable directly in the MMC console. So on top of dealing with the dreaded claims rules syntax, we also have to do it via PowerShell.
If the conditions available in the GUI are enough to meet your requirements, you can skip the rest of this section. If not, or if you are simply curious how the process works and not scared to get your hand dirty, read on. Luckily, things are not as hard as they might sound. Let’s take a look at specific example, one of the most commonly requested ones being “enforce MFA when members of specific group logon outside of the corporate network”. In order to create this rule, we need to make sure two conditions are met and as explained above we cannot just rely on the GUI options as those will configure rules that trigger for each condition individually. So we turn to PowerShell, and run the following cmdlet:
Let’s break down the cmdlet. First of all we are configuring this on the RPT level, not globally, thus we are using the Set-AdfsRelyingPartyTrust cmdlet (to configure the Global ones, use Set-AdfsAdditionalAuthenticationRule). The -TargetName parameter specifies the name of the relying party trust, and the -AdditionalAuthenticationRules parameter is used to provide the claims rule. In the rule itself, we are checking whether the "http://schemas.microsoft.com/ws/2012/01/insidecorporatenetwork" claim is present and has a value “false”, indicating that the request came from outside of the corporate network (reminder: this claim is added by the WAP server or any other AD FS proxy replacement).
We are also checking whether the user is a member of the specified group via the “http://schemas.microsoft.com/ws/2008/06/identity/claims/groupsid” claim. As already mentioned, the actual MFA process is triggered by issuing the “http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod” claim with value “http://schemas.microsoft.com/claims/multipleauthn”.
Note that we didn’t include a check for which endpoint the request came from. The reason being that with Modern authentication, every request from ADAL-enabled clients will be hitting the passive endpoint. It’s an easy, albeit dirty way to make sure MFA is enforced and only clients that support it are allowed. Unfortunately, there are still some apps that do not support or have issues with Modern authentication and we might need to consider scenarios in which users are still allowed to use such apps.
Office 365 in particular still supports both the old “OrgId” and the new “EvoSTS” platforms, so both ADAL-enabled and “legacy” clients can authenticate, as long as they have received a valid token from our AD FS server. In other words, it’s up to the client to decide which method for authentication is used. For example, older versions of Outlook or Outlook 2013 with the “ADAL” registry keys disabled will use the old “active” endpoint method and allow Exchange Online to receive the token on behalf of the user, while with ADAL enabled it will communicate directly with the AD FS server on the passive endpoint.
To summarize the above, for Exchange related authentication activities we have to deal with requests that are received either on the passive or the active endpoint. With that in mind, let’s add another rule that will only force MFA if the client is Outlook and the request was received on the passive endpoint:
This rule will make sure that MFA is triggered only on requests from ADAL-enabled Outlook versions, including Outlook 2013, Outlook 2016, the Outlook app on mobile devices, as only those request will hit the passive endpoint. For any other versions, the request will be received on a different endpoint and no MFA will be enforced (which doesn’t mean we cannot apply further restrictions, more about this later). There’s one gotcha here however – if you execute the above cmdlet you will overwrite the rule we configured previously. It only allows you to configure a single rule. If we want to append a new rule to the rule set, we need to follow a different method. Here’s how it will look like:
If we break down the example, the first line stores the existing claims rule to the $old variable. The new claims rule is then appended to it and the rule set is stored in the $newset variable, which in turn is used to feed the claims rules to the Set-AdfsRelyingPartyTrust cmdlet. And this is the end result:
We now have successfully configured several AARs and can follow the same process if additional rules are needed. We have also shown how to use the different claims types available based on the user, device or request context in order to customize the AAR claims rules to best fit our needs. Pretty much every claim recognized by the AD FS server can be used when constructing AARs. One claim needs special treatment when using Modern authentication however - the client application claim (“http://schemas.microsoft.com/2012/01/requestcontext/claims/x-ms-client-application”).
As some of you might recall, this claim is only available for EO related requests and is inserted by the Exchange server during the process of proxying the authentication request to the AD FS on behalf of the client. With modern authentication enabled, this claim will simply not be present in the request, as the client now gets the token directly from the AD FS server and the Exchange server plays no role in the process.
Which doesn’t mean you cannot include the claim in your AARs, as long as you understand that it will not be present for any ADAL-enabled client (i.e. no client that hits the passive endpoint will have this claim populated). For example, you can use something like:
'NOT exists([Type == "http://schemas.microsoft.com/2012/01/requestcontext/claims/x-ms-client-application", Value =~ "Microsoft.Exchange.ActiveSync|Microsoft.Exchange.AutoDiscover|Microsoft.Exchange.RPC|Microsoft.Exchange.WebServices"]) && exists([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");'
This above claims rule will act on any Exchange related traffic coming from clients that are not ADAL-enabled. The NOT clause will make sure that if such client is detected, no MFA challenge is issued. By combining it with the insidecorporatenetwork claim, we are making sure that this rule will trigger only for external requests (remember, all EO related traffic is external if we are not using Modern authentication). In effect, no “legacy” mail clients should be forced to perform MFA, but any other request coming from outside of the corporate network will be subject to MFA, including ADAL-enabled mail clients.
Configuring Issuance Authorization Rules
After extensively covering AARs, let’s now focus on the remaining factors in the authentication pipeline. Most of the people that have dealt with restricting access to some or all Office 365 services in federated scenarios are familiar with the several examples of Issuance Authorization Rules (IARs) officially supported by Microsoft. IARs can still be used to allow/deny access post authentication, be it single- or multi-factor, but with Modern authentication few changes should be considered.
As noted in the previous section, we need to account for two scenarios related to Exchange Online requests, one on the active endpoint, with the x-ms-client-application claim populated, and one on the passive endpoint, without the claim. An updated set of rules that accounts for that fact can be found for example in this article.
Similarly, we have to consider “rich clients”. Lastly, we can configure IARs that explicitly check whether MFA was performed as part of the authentication flow. This can be achieved by examining the value(s) of the “http://schemas.microsoft.com/claims/authnmethodsreferences” claim. Said claim will contain an entry for each method of authentications performed, including the primary. For example, this is how it will look like for authentication performed via username/password for the first factor and phone call for the second one:
As we saw in the previous section, playing with multiple AARs requires some PowerShell magic. While they can be used to block client that are unable to perform MFA, creating rules to account for all possible scenarios can get complicated. IARs are much easier to manage in comparison, as all operations can be performed via the dialog shown on the screenshot below.
So it is a good idea to keep the AARs as simple as possible, and deal with the allow/deny decision in the IARs instead. Usually, it will be enough to check whether the “http://schemas.microsoft.com/claims/authnmethodsreferences” claim contains the “http://schemas.microsoft.com/claims/multipleauthn” value in order to make sure that MFA was performed. As an example, let’s use this claim in a rule to block any external clients that have not performed MFA, with the exception of ActiveSync clients:
NOT exists([Type == "http://schemas.microsoft.com/2012/01/requestcontext/claims/x-ms-forwarded-client-ip", Value =~ “list of allowed IPs"])
&& NOT exists([Type == "http://schemas.microsoft.com/2012/01/requestcontext/claims/x-ms-client-application", Value =~ "Microsoft.Exchange.ActiveSync|Microsoft.Exchange.AutoDiscover"])
&& NOT exists([Type == "http://schemas.microsoft.com/claims/authnmethodsreferences", Value == "http://schemas.microsoft.com/claims/multipleauthn"])
=> issue(Type = "http://schemas.microsoft.com/authorization/claims/deny", Value = "DenyUsersWithClaim");
To break down the rule: the first line basically performs a check whether the request is coming from a known IP address (both legacy and ADAL-enabled clients should return the same IP here). If the request is coming from a known IP, the rule will fail, otherwise it will check the next line. There, we examine the value of the x-ms-client-application claim – if it matches ActiveSync or AutoDiscover, the condition will fail (because of the NOT operator in front) and the rule will not trigger.
Again, the x-ms-client-application will only be present for legacy clients, so for an ADAL-enabled client this condition will be met and continuing to the next line, a check on whether MFA was performed is done by looking at the value of the authnmethodsreferences claim. Since we again use the NOT operator, the Deny action of this rule will only apply if all three conditions are not met: the client is coming from unknown IP, it’s not an ActiveSync application and has not successfully performed MFA challenge.
In other words, all clients except ActiveSync ones should perform MFA if they’re outside of the company network, otherwise access will be denied.
The IARs give us another chance to check on the x-ms-client-user-agent claim as well. While this claim can certainly be used with AARs, as we’ve shown in the previous section, it’s not always a good idea to force MFA based on it. The reason behind this is the lack of reliable standard for the user agent strings returned by different applications, and the fact that said strings can be spoofed. This claim gives us an easy way to filter out certain (groups of) applications, but one has to exercise caution when using rules based on it.
For example the “Outlook” filter we did previously will work just fine for Outlook 2013 or Outlook 2016, but clients such as the Outlook Groups app for WP 8.1 will be affected. Said app simply does not return anything remotely similar to “Outlook” in its user agent string, and sadly is not the only app out there that does so. This puts us in pretty much the same situation we’ve had to deal with when trying to block rich clients: the requests they are sending are simply undistinguishable from AD FS perspective.
Still, the fact that we can force MFA and check whether this additional challenge was successfully performed by such client can help put the security guy’s mind at ease. Maybe we cannot be sure which application was used, but we know that the user accessing it was legitimate one.
One last remark here: if multiple MFA providers are configured, any one of them can be used to perform the additional verification. This actually applies to Primary authentication as well, and to the different options you might have in the actual MFA provider.
For example, if you have the Azure MFA server configured you can setup either the phone call or SMS option. In addition to that, if the security questions bypass option is enabled, answering the questions will still count as successful second-factor authentication. With that in mind, you might want to consider including a more specific checks in your IARs for the type of MFA performed, at least for your sensitive applications.
Another option is to customize your AD FS login page to bring up only the desired method of primary/two-factor authentication. With the changes coming to the AD FS role in Windows Server 2016, we will be able to modify the sign-in page on per-RPT basis.
Final remarks and Summary
Another important change introduced with Modern authentication is the new model of access/refresh tokens. The access token received after successful authentication is short lived, with 1 hour lifetime. Along with it, a refresh token is issued, which can be used to renew the access token without having to go over the full authentication process. The refresh token can remain valid for up to 90 days.
While this certainly makes things easier on the end user, it poses a security risk. Not only the token is issued per device (i.e. will still work if the user changes networks), but having the token allows the user to bypass any MFA requirements. Since there are very few methods to actually revoke a token currently, and Microsoft doesn’t seem to be in a hurry to provide us the additional control features they promised, this is definitely something to have in mind.
In summary, we have reviewed the process of configuring claims rules to take advantage of the improved support for 2FA that Modern authentication provides. The claims rules allow us to force or skip MFA based on certain criteria, as well as to make sure that the user performed the additional authentication. Depending on the criteria type and the number of rules needed, one can either use the GUI or PowerShell to configure those AARs.
While the GUI method is much simpler, using PowerShell allows us to realize some advanced scenarios (the good news here is that AD FS in Windows Server 2016 will bring improved UI to deal with this, via the Access Control Policies). We then proceeded to cover the changes that might be needed in our existing IARs in order to accommodate for the changes Modern authentication brings, as well as the interoperability between AARs and IARs that can help us ensure that we can protect our applications with MFA where needed.