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 "http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod" and value "http://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:

  • http://schemas.microsoft.com/ws/2008/06/identity/claims/groupsid:

    You can use this claim to find out if the user belongs to a specific group. 

  • http://schemas.microsoft.com/2012/01/devicecontext/claims/isregistereduser:

    When the value of this claim is true, it means that the user who authenticated is the one who originally registered the device.

    When the value of this claim is false, it means that the user who authenticated is NOT the one who originally registered the device, which could be OK in the case of shared devices.

    If you only care about the device itself being registered, you might want to use the EXISTS operator against this claim

  • http://schemas.microsoft.com/2012/01/requestcontext/claims/x-ms-endpoint-absolute-path:

    Since MFA is supported only for browser applications, you can use this claim to tell apart browser from non-browser requests, in case you have both kinds of protocols in the same relying party trust, which is the typical case when issuing tokens to a federation provider STS.

  • http://schemas.microsoft.com/ws/2012/01/insidecorporatenetwork:

    When the value is false, it means the request came through a web application proxy. When true, it means the request came directly to the STS.

  • http://schemas.microsoft.com/2012/12/certificatecontext/* (multiple claim types):

    Claims that represent different fields and extensions of the X509 client certificate when used as an authentication method.

    One interesting use case here is to use the EKU claim (http://schemas.microsoft.com/2012/12/certificatecontext/extension/eku) to ascertain whether the user used a smart card (the exact EKU depends upon the PKI infrastructure of the customer).

  • http://schemas.microsoft.com/claims/authnmethodsreferences:

    Used to indicate all authentication methods used to authenticate the user.

    When the MFA rules are executed, this claim will contain the authentication method performed in the first stage.

    When the Authorization or Issuance Transform rule set is executed, this claim will contain both the first and second factor authentication methods, if the MFA occurred (step 5 above).

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 == "http://schemas.microsoft.com/ws/2008/06/identity/claims/groupsid", Value == "<<Group SID>>"]

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

Value = "http://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 == "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");

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 == "http://schemas.microsoft.com/2012/01/devicecontext/claims/isregistereduser"])

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

Value = "http://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 == "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");

 

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 == "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");

 

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");

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 == "http://schemas.microsoft.com/ws/2012/01/insidecorporatenetwork", Value == "false"] &&

[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");

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 == "http://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 == "http://schemas.microsoft.com/ws/2012/01/insidecorporatenetwork", Value == "false"] &&

c1:[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");

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 http://schemas.microsoft.com/claims/authnmethodsreferences contains the list of authentications methods performed. It will hold the value http://schemas.microsoft.com/claims/multipleauthn if MFA was performed.

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");

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