Apereo CAS - Multifactor Provider Selection

Posted by Misagh Moayyed on May 13, 2019 · 5 mins read ·
Content Unavailable
Your browser is blocking content on this website. Please check your browser settings and try again.
This blog post was originally posted on Apereo GitHub Blog.


Sometimes, it takes more than one multifactor provider to change a lightbulb. With CAS, it is certainly possible to configure more than one provider integration at the same time. The trick, however, is to decide the appropriate provider, should more than one qualify for the same transaction. Imagine you have an application registered with CAS whose multifactor authentication policy is equally deserving of, let’s say, Duo Security as well as Google Authenticator. How would you go about choosing one that makes the most sense?

Our starting position is based on:


So, let’s pretend that our application is registered with CAS as such:

  "@class": "org.apereo.cas.services.RegexRegisteredService",
  "serviceId": "^(https|imaps)://.*",
  "name": "Example",
  "id": 1,
  "description": "This service definition defines a service.",
  "evaluationOrder": 1,
  "multifactorPolicy" : {
    "@class" : "org.apereo.cas.services.DefaultRegisteredServiceMultifactorPolicy",
    "multifactorAuthenticationProviders" : [ "java.util.LinkedHashSet", [ "mfa-duo", "mfa-gauth" ] ]

Provider Rankings

The first and default strategy is to assign ranks to each provider. Ranking of authentication methods is done per provider via specific properties for each in CAS settings. Note that the higher the rank value is, the higher on the security scale it remains. A provider that ranks higher with a larger weight value override others with a lower value.

In practice, this would be:


When CAS sees that the application policy allows for both mfa-duo and mfa-gauth, it evaluates the rank for each and picks the one that outranks the other. In the above example, Google Authenticator will be chosen over Duo Security. If ranks are equal, that would be the equivalent of I am feeling lucky behavior, which is to say unspecified.

Ranking strategies are fine if you are willing to make a decision on behalf of all users. This is CAS, forming an opinion based on pre-defined configuration without taking into account user choice.

Selection Menu

Another option is to put the power back into people’s hands and let them decide. CAS may also be configured to present a menu of qualifying multifactor provider integrations for the authentication attempt, asking the user to choose one that makes the most sense. To enable the selection menu and remove ranking strategies, one would do this:


…which may result into this:


Selection Script

Sometimes, you need more control over the selection process; to account for external system behavior, other variables, etc. In situations like this, CAS allows you to script the selection logic via the magic of Groovy:


…and the script would be as:

import java.util.*

class SampleGroovyProviderSelection {
  String run(final Object... args) {
      def service = args[0]
      def principal = args[1]
      def providers = args[2]
      def logger = args[3]

      logger.info("Selecting a provider for ${principal} from ${providers}")
        Work out the selection process...
        Here, we are taking the easy route to return
        the very first provider available to us.
        You should do better.
      return providers.iterator().next().id


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 know that all other use cases, scenarios, features, and theories certainly are possible as well. Feel free to engage and contribute as best as you can.

Happy Coding,

Misagh Moayyed