Under the hood tour on Multi-Factor Authentication in ADFS – Part 1: Policy

One of the new features we introduced in AD FS in Windows Server 2012 R2 is Multi-Factor Authentication (MFA) for WS-Federation, SAML-P and OAuth protocols.

In this post, I want to talk about some of the ways in which you can configure AD FS to implement several MFA policies to accomplish different authentication requirements.

Before authoring the policies, you need to prepare your AD FS farm for MFA by registering and enabling at least one MFA authentication provider (such as Windows Azure Multi-Factor Authentication or the built-in X509 Client Cert authentication). Once you configure your farm, then you are ready for the next step and decide how to engage MFA in your applications.

Engaging MFA for a relying party is very similar to way you configure its authorization requirements: using the claims engine and the claims pipeline.

  • The claims pipeline now includes a new stage before issuing the token to determine whether or not MFA should be engaged.
  • There is a new claim rule set property called 'AdditionalAuthenticationRules' in both the ADFS global properties as well as the Relying Party trust object.

The way all the pieces above work together is depicted in the following diagram:

The sequence goes as follows:

  1. The authentication request comes from the browser after a POST action, containing the user credentials (either fresh or SSO), information about the device ( "Device Context" in the diagram), and the request ( "Request Context" in the diagram)
  2. The first factor authentication layer (Box A above) validates the identity of the user and the device. The first stage of authentication is completed, and the user attributes, user credentials, device, and request information are converted to claims.
  3. The claim acceptance rules of the "Active Directory" claims provider trust (box B) is executed. The output set of this stage is used in subsequent stages (Issuance, Authorization, and … MFA J).
  4. Both the Global and Relying Party MFA AdditionalAuthenticationRules claim rule sets are executed. (Box C). If the output claim set of either rule set contains a claim of type "https://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod" and value "https://schemas.microsoft.com/claims/multipleauthn", then MFA will engage.
  5. If MFA is engaged, AD FS will call the logic of the MFA provider (box D) to render the web pages to challenge the user, who in turn POSTS the response back. The system also augments the claim set from step 2 above, indicating that MFA was performed (the MFA provider could potentially add more claims to the claim set depending upon its implementation).
  6. The Authorization (Box E) and Issuance Transform Rules (Box F) are executed the same way as previous versions of AD FS to produce a permit/deny decision and RP claims respectively.

Now, everything boils down to what claims can we use and what the language allows us to do. While the full list of available claims is quite large, I would like to focus on the claims that can give us the most interesting MFA pivots:

Knowing that, we can now leverage the whole power of the AD FS Claims Language and Pipeline to go over some MFA Policy examples.

Let's start with the three options we streamlined in the AD FS Management Snap-in: Users/Groups, Devices, and Locations. I pasted the MMC UI for reference:

Scenario 1: MFA for certain groups or users

Per the table above, we use the Group SID claim:

c:[Type == "https://schemas.microsoft.com/ws/2008/06/identity/claims/groupsid", Value == "<<Group SID>>"]

=> issue(Type = "https://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod",

Value = "https://schemas.microsoft.com/claims/multipleauthn");

Note that the issuance condition (right side of the => operator). That will always be the same whenever you want to tell AD FS to perform MFA.

Scenario 2: MFA for unregistered (non workplace joined) devices

We can do this in two different ways. One is to require MFA if the user who registered the device is NOT the one who is authenticated using the "isregistereduser" claim (this is the one that the MMC snap-in uses):

c:[Type == "https://schemas.microsoft.com/2012/01/devicecontext/claims/isregistereduser", Value == "false"]

=> issue(Type = "https://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod",

Value = "https://schemas.microsoft.com/claims/multipleauthn");

Alternatively, if you don't need to discriminate between the user who registered the device from the one who is authenticated (for example, shared devices), then you can use the EXISTS operator and check for any of the claims associated with the workplace joined state. I personally use the registrationid claim:

NOT EXISTS([Type == "https://schemas.microsoft.com/2012/01/devicecontext/claims/isregistereduser"])

=> issue(Type = "https://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod",

Value = "https://schemas.microsoft.com/claims/multipleauthn");

Scenario 3: MFA for users coming from outside the corporate network

In this case, we use the "insidecorporatenetworkclaim" the same way we used a Boolean claim in the example above

c:[Type == "https://schemas.microsoft.com/ws/2012/01/insidecorporatenetwork", Value == "false"]

=> issue(Type = "https://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod",

Value = "https://schemas.microsoft.com/claims/multipleauthn");

 

As I mentioned above, the three scenarios above are the ones exposed in the AD FS management UI.

Now, let's take a look at a few more that require using the claims language directly:

Scenario 4: Tweak the MMC scenarios to be AND instead of OR

Each check box in the management UI corresponds to one rule, which means a logical OR operation when they are put together. For example, when you check both "Unregistered devices" and "Extranet", the rule set will generate two rules, that will look like this:

c:[Type == "https://schemas.microsoft.com/2012/01/devicecontext/claims/isregistereduser", Value == "false"]

=> issue(Type = "https://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod",

Value = "https://schemas.microsoft.com/claims/multipleauthn");

 

c:[Type == "https://schemas.microsoft.com/ws/2012/01/insidecorporatenetwork", Value == "false"]

=> issue(Type = "https://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod",

Value = "https://schemas.microsoft.com/claims/multipleauthn");

For scenarios that require a logical AND operation between the conditions above me, you should combine them in a single rule and use the … AND … operator. If you want to enable MFA only when coming from extranet with a device that is not joined to the workplace, then the rule might look like this:

[Type == "https://schemas.microsoft.com/ws/2012/01/insidecorporatenetwork", Value == "false"] &&

[Type == "https://schemas.microsoft.com/2012/01/devicecontext/claims/isregistereduser", Value == "false"]

=> issue(Type = "https://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod",

Value = "https://schemas.microsoft.com/claims/multipleauthn");

Scenario 5: MFA and Office 365/Azure Active Directory

Given that MFA is plugged into the authentication pipeline for browser applications, if the MFA claim rules generate the claim that will engage MFA over WS-Trust will cause the request to fail with the following message in the ADFS Admin event log channel, with event ID 325. As you can see, this looks pretty much like an access denied failure.

The Federation Service could not authorize token issuance for caller 'DOMAIN\User'

The caller is not authorized to request a token for the relying party 'urn:dumptoken'. See event 501 with the same Instance ID for caller identity.

This becomes a problem for federation provider scenarios, such as Azure Active Directory and Office 365. In this case, there is a single RP trust to represent both rich clients that use WS-Trust (such as Lync and Outlook), as well as browser based applications (like Sharepoint Online and OWA). In that case, we have to use the claim "x-ms-endpoint-absolute-path" which contains the URL through which the token request came in to AD FS in order to derive the protocol of the request as follows:

  • If the URL contains /adfs/ls, then the protocol is either WS-Federation or SAML Protocol. Both of these support MFA.
  • If the URL contains /adfs/oauth2, then protocol is OAuth, which also supports MFA. (I added this for the sake of completeness of determining the protocol based on the URL of the request. Office 365 does not use OAuth)

Using the regex operations of the claims language, the condition above looks like this:

[Type == "https://schemas.microsoft.com/2012/01/requestcontext/claims/x-ms-endpoint-absolute-path",

Value =~ "(/adfs/ls)|(/adfs/oauth2)"]

You can use this condition with any of the rules we discussed above to craft a more complex policy, using the AND operator. For example, if you want to require MFA for office 365 web applications when coming from extranet, then the rule is:

c:[Type == "https://schemas.microsoft.com/ws/2012/01/insidecorporatenetwork", Value == "false"] &&

c1:[Type == "https://schemas.microsoft.com/2012/01/requestcontext/claims/x-ms-endpoint-absolute-path",

Value =~ "(/adfs/ls)|(/adfs/oauth2)"]

=> issue(Type = "https://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod",

Value = "https://schemas.microsoft.com/claims/multipleauthn");

Scenario 6: Deny authorization to the relying party if MFA was not performed

This one is a bit different from the previous examples because the rule to accomplish this will be in the authorization rule set, instead of the MFA rule set. As I mentioned above, the claim https://schemas.microsoft.com/claims/authnmethodsreferences contains the list of authentications methods performed. It will hold the value https://schemas.microsoft.com/claims/multipleauthn if MFA was performed.

NOT EXISTS([Type == "https://schemas.microsoft.com/claims/authnmethodsreferences",

Value == "https://schemas.microsoft.com/claims/multipleauthn"])

=> issue(Type = "https://schemas.microsoft.com/authorization/claims/deny",

Value = "DenyUsersWithClaim");

Conclusion

As you can see, with the set of claims available and the richness of the claims language, AD FS offers a lot of flexibility when it comes to engage MFA in your browser applications. We only went through a couple of examples, but you can do a lot more!

Stay tuned for more details on MFA on AD FS!.

Now, your turn:

How would you author the policy for an application with the following constraints:

  1. Allow single factor authentication (as in, no MFA) from workplace joined devices
  2. Skip MFA when using Smart Card if the device is NOT joined to the workplace
  3. Do MFA in any other case

Hint: you might need the claim language's add issuance statement