Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Tip

This plugin does not depend on the use of the original Duo login flow and enabling that feature/module is not required in order to install and use this plugin. They can even theoretically co-exist in older versions of the IdP, but this hasn't been extensively tested.

...

By default, the first-factor must produce an “official” username as part of post-login canonicalization which the DuoOIDC flow can use as the Duo username in the second-factor authentication request. In unusual cases it is possible to customize the source of the username.

...

Expand
titleMFA Configuration

Here we describe an example MFA flow using both the MFA and Password flows (in addition to the new DuoOIDC flow). Of course, this example assumes you have also enabled those modules in addition to this plugin/module, as per their documentation pages.

For testing, the credential store of the Password flow can be configured from a simple flat file of usernames/passwords, for example, using the HTPasswdCredentialValidator in conf/authn/password-authn-config.xml as shown below. If this file is missing, you have not yet enabled the "idp.authn.Password" module.

Example password validator
Code Block
languagexml
<util:list id="shibboleth.authn.Password.Validators">
    <bean parent="shibboleth.HTPasswdValidator" p:resource="%{idp.home}/conf/authn/htpasswd.txt" />
</util:list>

Or, you may already have configured a 'production' ready password validator e.g. using LDAP. Either way, your next step is to compose both these flows into a suitable multi-factor authentication flow. An example flow defined in conf/authn/mfa-authn-config.xml file is shown below. If this file is missing, you have not yet enabled the "idp.authn.MFA" module.

Example MFA configuration
Code Block
languagexml
<util:map id="shibboleth.authn.MFA.TransitionMap">
        <!-- First rule runs the Password login flow. -->
        <entry key="">
            <bean parent="shibboleth.authn.MFA.Transition" p:nextFlow="authn/Password" />
        </entry>

        <!-- Second rule runs a function if Password succeeds, to determine whether an additional factor is required. -->        
        <entry key="authn/Password">
            <bean parent="shibboleth.authn.MFA.Transition" p:nextFlowStrategy-ref="checkSecondFactor" />
        </entry>
        <!-- An implicit final rule will return whatever the final flow returns. -->
    </util:map>

    <!-- Example script to see if second factor is required. Currently just returns the DuoOIDC flow -->
    <bean id="checkSecondFactor" parent="shibboleth.ContextFunctions.Scripted" factory-method="inlineScript">
        <constructor-arg>
            <value>
            <![CDATA[
                nextFlow = "authn/DuoOIDC";

                // Check if second factor is necessary for request to be satisfied.
                //authCtx = input.getSubcontext("net.shibboleth.idp.authn.context.AuthenticationContext");
               // mfaCtx = authCtx.getSubcontext("net.shibboleth.idp.authn.context.MultiFactorAuthenticationContext");
                //if (mfaCtx.isAcceptable()) {
                //    nextFlow = null;
                //}
                
                nextFlow;   // pass control to second factor or end with the first
            ]]>
            </value>
        </constructor-arg>
    </bean>

In summary, the IdP will run the "authn/Password" flow followed by the "authn/DuoOIDC" flow. More complex business/orchestration logic can be added to the checkSecondFactor script if required, see the MFA page for a full discussion of the possibilities.

After configuring the MFA orchestration logic appropriately, you should consider how to represent the multi-factor authentication mechanism to the outside world.

Expand
titleAuthentication Context Classes (i.e., Supported Principals)

As the DuoOIDC plugin typically runs after a first-factor authentication method orchestrated by the MFA flow, the MFA flow must present to the system a supportedPrincipals collection compatible with this type of authentication mechanism alongside any other factor(s) used.  In the SAML 2.0 world (and nowadays more generally, e.g., OpenID Connect) these are specified as an Authentication Context Class.

Those already set by default on the MFA flow are described by the idp.authn.MFA.supportedPrincipals property in the conf/authn/authn.properties file. (If your system has been upgraded from V4.0 or earlier, then you may not have migrated to the property-centric settings V4.1 allows, and may have the supported Principals enumerated in conf/authn/general-authn.xml.) Either way,these need to be adjusted to also include those that are also exposed by the "authn/DuoOIDC" flow.

This flow defaults to an example set defined by the idp.authn.DuoOIDC.supportedPrincipals property in conf/duo-oidc.properties. If not set, it will fall back to the definition of the idp.authn.Duo.supportedPrincipals property.

As for what these values should be:

Info

Supported Principals

There is no "standard" context class (or SAML 1 authentication method) to represent most forms of MFA, and experience has shown that it's a bad idea to create a strong coupling between applications and the exact technologies that you use for authentication.

As a result, the default configuration contains only a placeholder value to use that you will need to change, but there is currently no standard value to use. One possible choice to consider is the REFEDS profile, but your particular deployment may or may not satisfy its requirements. Regardless of specifics, the approach is a good one in general: a generic URI representing the use of MFA.

For more advanced supportedPrincipal configurations, see below.

...

Expand
titleAudit Logging (1.3.0+)

Since v1.3.0 it is possible to enable audit logging for Duo2FA interactions by setting the property idp.duo.oidc.audit.enabled to true in /conf/authn/duo-oidc.properties. From v1.4.0 onward you also need to enable the general authentication audit logging on the IdP using the property idp.authn.audit.enabled in /conf/authn.properties.

Once enabled, audit log statements are routed through a logger named Shibboleth-Audit.DuoOIDC. From v1.4.0 onward, you can change the name of this logger using the property idp.duo.oidc.audit.category in /conf/authn/duo-oidc.properties.

Without further configuration, the audit statements will appear in the normal idp-audit.log log file. This may be sufficient for your needs, if not and you would rather those exclusively appear in a new audit log file, you will need to add a new Logback appender and logger to the logback.xml file, for example:

Code Block
<appender name="IDP_DUO2FA_AUDIT" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <File>${idp.logfiles}/idp-duo2fa-audit.log</File>

        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${idp.logfiles}/idp-duo2fa-audit-%d{yyyy-MM-dd}.log.gz</fileNamePattern>
            <maxHistory>${idp.loghistory}</maxHistory>
        </rollingPolicy>

        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <charset>UTF-8</charset>
            <Pattern>%msg%n</Pattern>
        </encoder>
</appender>
    
<!-- Set additivity false if you do not want DuoOIDC events in the standard audit log -->
<logger name="Shibboleth-Audit.DuoOIDC" level="ALL" additivity="false">
    <appender-ref ref="${idp.audit.appender:-IDP_DUO2FA_AUDIT}"/>
</logger>

Audit Format

The default audit format is shown below (the fields are described in the table underneath).

Code Block
%AAF|%a|%T|%DuoU|%DuoRedirect|%DuoCID|%DuoReqS|%DuoRespS|%DuoTXID|%DuoDID|%DuoDN|%DuoR|%DuoF

Custom Audit Format V1.3.0

For version 1.3.0, the audit format can be adjusted by specifying your own bean, shibboleth.authn.DuoOIDC.AuditFormattingMap, in /conf/authn/duo-oidc-authn-config.xml.

Custom Audit Format V1.4.0+

For version 1.4.0+, the audit format can be adjusted using the idp.duo.oidc.audit.format property in /conf/authn/duo-oidc.properties.

Audit Logging Fields

Field

Description

AAF

The ID of the currently running authentication flow i.e. authn/DuoOIDC in this case

DuoRedirect

The location of the IdP’s callback endpoint Duo will redirect the user too after successful authentication

DuoCID

The Duo client identifier used for this request

DuoTXID

The transaction ID contained in the Duo 2FA response

DuoDID

The ID of the device used to authenticate the user

DuoDN

The friendly name of the device used to authenticate the user

DuoR

The authentication reason

DuoF

The factor used for 2FA e.g. a Security Key or SMS.

DuoU

The username of the authenticating user sent in the Duo 2FA request

DuoRespS

The state OAuth 2.0 parameter returned in the Duo response

DuoReqS

The state OAuth 2.0 parameter sent to Duo in the 2FA request (client authorization request)

Expand
titlePasswordless Authentication using Duo (V2.1+)

V2.1 and later versions of the plugin can be configured to support passwordless authentication using FIDO2/WebAuthn technology, such as platform authenticators like TouchID and Windows Hello, Passkeys, or hardware tokens that support PINs. Refere to the DuoOIDCPasswordless topic for complete documentation.

Reference

Expand
titleBeans (General)

Bean ID / Type

Default

Description

shibboleth.authn.DuoOIDC.DuoIntegration

DuoOIDCIntegration

Derived from properties in conf/authn/duo-oidc.properties

Defines a single/static Duo OIDC Integration with Duo, you can override this bean to supply a non-property-configured alternative

shibboleth.authn.DuoOIDC.DuoIntegrationStrategy

Function<ProfileRequestContext,DuoOIDCIntegration>


Optional bean to supply the Duo OIDC integration settings dynamically

shibboleth.authn.DuoOIDC.UsernameLookupStrategy

Function<ProfileRequestContext,String>

CanonicalUsernameLookupStrategy

Optional bean to supply username

shibboleth.authn.DuoOIDC.resultCachingPredicate

Predicate<ProfileRequestContext>

shibboleth.Conditions.TRUE

Bean ID controlling whether to preserve the authentication result in an IdP session

shibboleth.authn.DuoOIDC.CleanUpHook

Consumer<ProfileRequestContext>

Bean that removes the DuoOIDAuthenticationContext from the tree

A cleanup hook that is executed on successful authentication.

shibboleth.authn.DuoOIDC.jwt.claims.CleanUpHook

Consumer<ProfileRequestContext>

Bean that removes the nonce value from the DuoOIDAuthenticationContext

A cleanup hook to execute after either successful or unsuccessful claims validation

shibboleth.authn.DuoOIDC.DuoTokenClaimsVerifier

JWTClaimsValidation

DefaultDuoTokenClaimsVerifier Claims verification in accordance with the Duo specification. Also OIDC compliant for the special Duo id_token case.

Duo result token (OIDC id_token) claims verifier using a 'chain' of ClaimsValidators e.g. audience, issuer, expiration checks etc. You can either replace the claims validator completely, change some of the behavior of existing validators individually, or add to a new validation check using a custom BiFunction, see shibboleth.authn.DuoOIDC.ExtendedClaimsValidator.

shibboleth.authn.DuoOIDC.jwt.IssuerLookupStrategy

BiFunction<ProfileRequestContextJWTClaimsSet, String>

Combines the HTTPS scheme, with the Duo API Hostname, and the Duo token IssuerPath.

Lookup strategy that returns the OIDC issuer. An issuer contains the scheme, host, and optionally, port and path components that identify the id_token issuer.

shibboleth.authn.DuoOIDC.jwt.AudienceLookupStrategy

BiFunction<ProfileRequestContext,JWTClaimsSet, String>

The clientID of the Duo Integration pertaining to the request.

Lookup the client_id for the Relying Party.

shibboleth.authn.DuoOIDC.jwt.UsernameLookupStrategy

BiFunction<ProfileRequestContext,JWTClaimsSet, String>

The authenticating principal's username from the context pertaining to the request.

Lookup the authenticating principal's username to match Duo's preferred_username field in the id_token.

shibboleth.authn.DuoOIDC.jwt.AuthTimeActivationCondition

BiPredicate<ProfileRequestContext,JWTClaimsSet>

Returns true if forced authentication has been requested by the Relying Party.

Should the auth_time field be validated for the given request?

shibboleth.authn.DuoOIDC.jwt.NonceLookupStrategy

BiFunction<ProfileRequestContext,JWTClaimsSet, String>

The nonce that was used in the authorization request and stored in the Duo authentication context.

Lookup the nonce that was used in the authorization request and should be present in the id_token.

shibboleth.authn.DuoOIDC.jwt.NonceActivationCondition

BiPredicate<ProfileRequestContext,JWTClaimsSet>

Returns true iff the id_token contains a nonce.

Should we validate the nonce value in the id_token?

shibboleth.authn.DuoOIDC.RequiredOIDCClaims

Set<String>

Used by the DefaultDuoTokenClaimsVerifier above. Defaults to those claims required by the OIDC specification (https://openid.net/specs/openid-connect-core-1_0.html#IDToken)

The names of the claims required to be present in the Duo result token (OIDC id_token).

shibboleth.authn.DuoOIDC.ContextToPrincipalMappingStrategy

Function<ProfileRequestContext,Collection<Principal>>


Map information in the ProfileRequestContext, most likely in the Duo result id_token, to a collection of Principals the execution of the flow supports. See this advanced topic.

shibboleth.authn.DuoOIDC.ExtendedClaimsValidator

BiFunction<JWTClaimsSet,ProfileRequestContext,JWTValidationException>


Optional BiFunction extension point for custom claims validation of the Duo token

shibboleth.authn.DuoOIDC.PreDuoPopulateAuditExtractors1.3.0

Map<String,Function<ProfileRequestContext,Object>>

List of Pre-Duo 2FA redirect audit extractors that take the profile request context and return an object (likely a String) to append to the log statement.

shibboleth.authn.DuoOIDC.PostDuoPopulateAuditExtractors1.3.0

Map<String,Function<ProfileRequestContext,Object>>

List of Post-Duo 2FA audit extractors that take the profile request context and return an object (likely a String) to append to the log statement.

shibboleth.authn.DuoOIDC.AuditFormattingMap1.3.0(only)

Map<String, String>

Map of logging categories to audit formatting strings for Duo 2FA audit logging

...