Requiring Multi-Factor Authentication

SAML includes an optional feature during login requests called <RequestedAuthnContext> that acts as a signal to the Identity Provider to require a particular kind of authentication be used, instead of the default of leaving the decision up to the IdP. This feature is generic, but the most common use case for this feature tends to be requiring the use of some form of MFA.

Between the time that the feature was designed in 2004 and real-world use of the feature started to appear, it became clear that it was a bad idea to try to codify specific technical approaches directly into the values being requested, because this isn't future-proofed. Generally it's more common to use community-defined values that represent general approaches to the problem (e.g., the REFEDS MFA Profile). In the future it may be even more common to align the values used to some kind of scale of authentication "quality" that's even more generic.

The actual value used doesn't really matter mechnically, so this example applies regardless of the actual value used. The REFEDS value is used in the example that follows.

Usage

There are two parts to integrating specific authentication requirements into the SP: enforcement and issuing requests. Both halves are needed, but enforcement is the more crucial of the two because achieving stronger security depends on it.

While it may be tempting to skip this step, simply requesting the right form of authentication is not sufficient. The SAML protocol includes capabilities that easily allow a smart attacker to circumvent your request process and the enforcement step is essential if you actually want to require the use of any particular authentication method.

Enforcing Use

Enforcement is an authorization function, much like requiring a user to posess a particular attribute value. In this case, the requirement is for the SAML assertion to include an appropriate <AuthnContextClassRef> value.

In the Shibboleth SP, the <AuthnContextClassRef> value received is exposed in the usual way, in a server variable or header, named "Shib-AuthnContext-Class".

If the requirement can be expressed statically (that is, based on the properties of the request such that all matching requests must carry the context), then you can apply static access control rules to check for the proper value and block access otherwise.

This can be done using the Apache configuration with:

1 require authnContextClassRef https://refeds.org/profile/mfa

(Note that if you have other require rules, you will need to take care to wrap them in a <RequireAll> block because Apache applies OR logic by default.)

For IIS and other servers, the SP software includes a portable feature for applying access control rules in the SP configuration; the documentation includes examples.

Before proceeding further, you should apply the appropriate rules in a development environment to ensure that existing access to your system is blocked by an appropriate rule when plain authentication is done. Having done so, you can proceed with the second half of the process.

Requiring Use

Obviously the goal is not to simply block access, but to actually make sure users that access your system normally will automatically be prompted to use a second factor at the appropriate times. This is possible in SAML by including information in the request message that tells the IdP to do this for you. Specifically, you must include a <RequestedAuthnContext> element containing the desired <AuthnContextClassRef> value to use.

In the case of Shibboleth, there are a few methods for decorating requests, depending on how the application is integrated with the SP. In the common case of an "active" protection strategy that automatically redirects to the IdP based on URL pattern, you can add a setting to Apache:

1 ShibRequestSetting authnContextClassRef https://refeds.org/profile/mfa

You can also, or on IIS must, use the SP's RequestMap feature, applying an XML attribute, authnContextClassRef="https://refeds.org/profile/mfa", to a particular mapped location.

If a "passive/lazy" protection strategy is used, then your application is in control of the process of requesting a session. In this case, the application's redirect to the SP's session initiation endpoint must include an additional encoded query string parameter (authnContextClassRef=https%3a%2f%2frefeds.org%2fprofile%2fmfa).

Note that when this strategy is involved, applications have the opportunity for flexible "step-up" models in which users may be forced to elevate their authentication strength based on the actions they perform. While that is also possible with URL-based schemes, that can be difficult with many application frameworks because of the way URLs may be overloaded by them for different functions.

Error Handling

The other requirement to make use of this feature is error handling. A properly configured IdP that does NOT support a value requested by the SP is obligated to return an error to the SP. Handling that case is the SP's responsibility. Generally a static error response is not going to be suitable for most applications because it's not simple to distinguish this particular error in a generic error template. The SP has a feature to redirect to an application script to handle errors with the script able to switch off of some of the query string parameters to detect the particular SAML status involved.

Example

Assume the SP is configured with the following <Errors> element:

shibboleth2.xml
1 2 3 4 ... <Errors supportContact="help@example.org" redirectErrors="http://example.org/error" /> ...

Upon an authentication context-based error, the user's browser would be redirected to the following URL. (Note that line breaks have been added and parameters decoded for readability.)

1 2 3 4 5 6 7 8 http://sp.example.org/error? &requestURL=https://sp.example.org/Shibboleth.sso/SAML2/POST &errorType=opensaml::FatalProfileException &errorText=SAML response contained an error. &RelayState=https://example.org/secure/getattrs &entityID=https://idp.example.org/idp/shibboleth &statusCode=urn:oasis:names:tc:SAML:2.0:status:Responder &statusCode2=urn:oasis:names:tc:SAML:2.0:status:AuthnFailed