Apereo CAS - Bypass Multifactor Authentication

Posted by Misagh Moayyed on May 13, 2022 · 8 mins read ·

Apereo CAS supports a wide array of multifactor authentication providers. Each multifactor provider is equipped with options to allow for conditional bypassing of multifactor authentication, where such rules are typically based on data gathered during the initial primary authentication event. Once the provider is chosen to honor the authentication request, bypass rules are then consulted to calculate whether the provider should ignore the request and skip MFA conditionally. In this blog post, we will examine some of the options available to handle bypass scenarios of multifactor providers. While all listed options should apply to all supported multifactor authentication providers supported by Apereo CAS, in this post we will specifically focus on the integration with Duo Security.

Our starting position is based on the following:


Apereo CAS provides a default bypass policy implementation for each multifactor provider that can be configured through CAS properties. Each multifactor provider implementation, such as one available for Duo Security would consult this policy for bypass events and conditions. Bypass rules typically operate and evaluate conditions based on data that is assembled during the primary authentication event. This data may be related to metadata collection about the authentication transaction itself, such as the credential type, authentication date/time, etc. It is also possible, and perhaps more common, to design bypass rules based on data collected about the authenticated subject in form of a principal attribute.

Assuming you have a multifactor authentication flow based on Duo Security, let’s break this down.

Bypass via Authentication

Assuming the authentication transaction ultimately collects an authentication attribute called bypass, you may instruct the provider to skip the multifactor flow if this attribute is found and its value(s) matches against a specific regular-expression pattern:


The collection and construction of the bypass authentication attribute are up to you; typically such attributes are assembled as part of a custom authentication scheme via dedicated authentication handlers.

If you wanted, you could certainly take this one step further and skip multifactor if a certain authentication handler is involved to handle the primary authentication attempt:


…or a certain type of credential type is used:


Bypass via Principal

Suppose that the LDAP authentication facility in Apereo CAS can collect a multivalued attribute as group with values such as student, advanced, remote once the user is successfully authenticated. Just as before, you may instruct the provider to skip the multifactor flow if this principal attribute is found and its value(s) matches against a specific regular-expression pattern:


Bypass for Application

Application policies defined in Apereo CAS as registered services may of course be assigned a special block to skip multifactor authentication flows:

  "@class" : "org.apereo.cas.services.CasRegisteredService",
  "serviceId" : "^https://app.example.org/.+",
  "id" : 100,
  "multifactorPolicy" : {
    "@class" : "org.apereo.cas.services.DefaultRegisteredServiceMultifactorPolicy",
    "bypassEnabled" : "true"

This will bypass multifactor authentication for all forms of the application whose URL matches the specified pattern, regardless of the authentication transaction or the traits of the authenticated subject. Of course, we could narrow this policy down to a specific set of users that carry a special attribute:

  "@class" : "org.apereo.cas.services.CasRegisteredService",
  "serviceId" : "^https://app.example.org/.+",
  "id" : 100,
  "multifactorPolicy" : {
    "@class" : "org.apereo.cas.services.DefaultRegisteredServiceMultifactorPolicy",
    "bypassPrincipalAttributeName": "group",
    "bypassPrincipalAttributeValue": "advanced.*",

Scripted Bypass

When all other rules fail, you may of course design and script your bypass policy:

def run(final Object... args) {
    def authentication = args[0]
    def principal = args[1]
    def registeredService = args[2]
    def provider = args[3]
    def logger = args[4]
    def httpRequest = args[5]
    logger.info("Bypassing multifactor for provider ${provider.id}")
    return false

The outcome of the script, if true, indicates that multifactor authentication for the requested provider should proceed. Otherwise false indicates that multifactor authentication for this provider should be skipped and bypassed. Remember to specify the location of the Groovy script for the appropriate provider as well:


Need Help?

If you have questions about the contents and the topic of this blog post, or if you need additional guidance and support, feel free to send us a note and ask about consulting and support services.


I hope this review was of some help to you and I am sure that both this post as well as the functionality it attempts to explain can be improved in any number of ways. Please feel free to engage and contribute as best as you can.

Misagh Moayyed