Versions Compared

Key

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

...

Multiple Duo Integrations
Expand
Code Block
languagexml
<bean id="DefaultDuo" class="net.shibboleth.idp.authn.duo.BasicDuoIntegration"
    p:APIHost="%{idp.duo.apiHost:none}"
    p:applicationKey="%{idp.duo.applicationKey:none}"
    p:integrationKey="%{idp.duo.integrationKey:none}"
    p:secretKey="%{idp.duo.secretKey:none}" />

<bean id="SpecialDuo" class="net.shibboleth.idp.authn.duo.BasicDuoIntegration"
    p:APIHost="%{idp.specialduo.apiHost:none}"
    p:applicationKey="%{idp.specialduo.applicationKey:none}"
    p:integrationKey="%{idp.specialduo.integrationKey:none}"
    p:secretKey="%{idp.specialduo.secretKey:none}" />

<util:map id="DuoIntegrationMap">
	<entry key="default" value-ref="DefaultDuo" />
	<entry key="https://special1.example.org/shibboleth" value-ref="SpecialDuo" />
	<entry key="https://special2.example.org/shibboleth" value-ref="SpecialDuo" />
</util:map>

<bean id="shibboleth.authn.Duo.DuoIntegrationStrategy" parent="shibboleth.ContextFunctions.Scripted"
		factory-method="inlineScript"
        p:customObject-ref="DuoIntegrationMap">
	<constructor-arg>
		<value>
		<![CDATA[
		duo = null;
		rpCtx = input.getSubcontext("net.shibboleth.idp.profile.context.RelyingPartyContext");
		if (rpCtx) {
			duo = custom.get(rpCtx.getRelyingPartyId());
		}
		if (duo == null) {
			duo = custom.get("default");
		}
		duo;
		]]>
		</value>
	</constructor-arg>
</bean>

Integration-Specific Principal Sets

...

Multiple Integrations with Distinct Principal Sets
Expand
Code Block
languagexml
<!-- Turn off default behavior in favor of integration-specific principals below. -->
<util:constant id="shibboleth.authn.Duo.addDefaultPrincipals" static-field="java.lang.Boolean.FALSE" />

<bean id="DefaultDuo" class="net.shibboleth.idp.authn.duo.BasicDuoIntegration"
		p:APIHost="%{idp.duo.apiHost:none}"
		p:applicationKey="%{idp.duo.applicationKey:none}"
		p:integrationKey="%{idp.duo.integrationKey:none}"
		p:secretKey="%{idp.duo.secretKey:none}">
	<property name="supportedPrincipals">
		<list>
			<bean parent="shibboleth.SAML2AuthnContextClassRef"
				c:classRef="http://example.org/ac/classes/mfa/default" />
			<bean parent="shibboleth.SAML1AuthenticationMethod"
				c:method="http://example.org/ac/classes/mfa/default" />
		</list>
	</property>
</bean>

<bean id="SpecialDuo" class="net.shibboleth.idp.authn.duo.BasicDuoIntegration"
		p:APIHost="%{idp.specialduo.apiHost:none}"
		p:applicationKey="%{idp.specialduo.applicationKey:none}"
		p:integrationKey="%{idp.specialduo.integrationKey:none}"
		p:secretKey="%{idp.specialduo.secretKey:none}">
	<property name="supportedPrincipals">
		<list>
			<bean parent="shibboleth.SAML2AuthnContextClassRef"
				c:classRef="http://example.org/ac/classes/mfa/special" />
			<bean parent="shibboleth.SAML1AuthenticationMethod"
				c:method="http://example.org/ac/classes/mfa/special" />
		</list>
	</property>
</bean>

<util:list id="DuoIntegrationList">
	<ref bean="SpecialDuo" />
	<ref bean="DefaultDuo" />
</util:list>

<bean id="shibboleth.authn.Duo.DuoIntegrationStrategy" parent="shibboleth.ContextFunctions.Scripted"
		factory-method="inlineScript"
        p:customObject-ref="DuoIntegrationList">
	<constructor-arg>
		<value>
		<![CDATA[
		duo = null;
		authCtx = input.getSubcontext("net.shibboleth.idp.authn.context.AuthenticationContext");
		iter = custom.iterator();
		while (duo == null && iter.hasNext()) {
			duo = iter.next();
			if (!authCtx.isAcceptable(duo)) {
				duo = null;
			}
		}
		duo;
		]]>
		</value>
	</constructor-arg>
</bean>

Advanced HttpClient Scenarios

...

Assuming you want to do this, the following example to place in conf/authn/duo-authn-config.xml will ensure that the root CA used by Duo is a specific CA. It assumes you've pulled down and stored the CA certificate in a file, and it also assumes the subject name Duo uses continues to be the same wildcard, which seems likely. But if they change their CA, as they probably will someday, you won't know and it will break, so if that bothers you, you should speak to Duo about it.

duo-authn-config.xml
Expand
Code Block
languagexml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context"
    xmlns:util="http://www.springframework.org/schema/util" xmlns:p="http://www.springframework.org/schema/p" xmlns:c="http://www.springframework.org/schema/c"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
                           http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"

    default-init-method="initialize" default-destroy-method="destroy">

    <!-- Require an explicit CA root on Duo AuthAPI calls. -->

    <bean id="shibboleth.authn.Duo.NonBrowser.HttpClientSecurityParameters"
            class="org.opensaml.security.httpclient.HttpClientSecurityParameters">
        <property name="tLSTrustEngine">
            <bean parent="shibboleth.StaticPKIXTrustEngine"
                p:trustedNames="*.duosecurity.com"
                p:checkNames="true">
				<property name="certificates">
					<list>
						<bean class="org.springframework.core.io.FileSystemResource"
							c:_0="%{idp.home}/credentials/duo-ca.pem"/>
					</list>
				</property>
			</bean>
		</property>
    </bean>
    
    <bean id="shibboleth.authn.Duo.NonBrowser.HttpClient"
        
parent="shibboleth.HttpClientFactory"
        p:connectionTimeout="%{idp.httpclient.connectionTimeout:PT1M}"
        p:connectionRequestTimeout="%{idp.httpclient.connectionRequestTimeout:PT1M}"
        p:socketTimeout="%{idp.httpclient.socketTimeout:PT1M}" 
        p:maxConnectionsTotal="%{idp.httpclient.maxConnectionsTotal:100}"
        p:maxConnectionsPerRoute="%{idp.httpclient.maxConnectionsPerRoute:100}"
        p:tLSSocketFactory-ref="shibboleth.SecurityEnhancedTLSSocketFactory" />

</beans>

Reference

true
Localtabgroup
Localtab live
active
Expand
titleBeans

The beans defined, or expected to be defined, in authn/duo-authn-config.xml follow:

Bean ID / Type

Default

Description

shibboleth.authn.Duo.DuoIntegration

DuoIntegration

Derived from settings in duo.properties

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

shibboleth.authn.Duo.NonBrowser.DuoIntegration

DuoIntegration

Derived from settings in duo.properties

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

shibboleth.authn.Duo.DuoIntegrationStrategy

Function<ProfileRequestContext,DuoIntegration>

Optional bean to supply the DuoWeb integration settings dynamically

shibboleth.authn.Duo.NonBrowser.DuoIntegrationStrategy

Function<ProfileRequestContext,DuoIntegration>

Optional bean to supply the Duo AuthAPI integration settings dynamically

shibboleth.authn.Duo.UsernameLookupStrategy

Function<ProfileRequestContext,String>

CanonicalUsernameLookupStrategy

Optional bean to supply username

shibboleth.authn.Duo.resultCachingPredicate

Predicate<ProfileRequestContext>

An optional bean that can be defined to control whether to preserve the authentication result in an IdP session

shibboleth.authn.Duo.addDefaultPrincipals

Boolean

true

Whether to add the content of the supportedPrincipals property of the underlying flow descriptor to the resulting Subject

shibboleth.authn.Duo.NonBrowser.HttpClient

HttpClient

Internal/default HttpClient instance

Overrides the HttpClient implementation and settings to use for the AuthAPI (see HttpClientConfiguration)

shibboleth.authn.Duo.NonBrowser.HttpClientSecurityParameters

HttpClientSecurityParameters

Custom security settings for the AuthAPI calls (see HttpClientConfiguration)

localtab-live
Expand
titleProperties (V4.0)

The properties defined in conf/authn/duo.properties follow:

Name

Default

Function

idp.duo.apiHost    

DuoWeb API hostname assigned to the integration

idp.duo.applicationKey

A secret supplied by you and not shared with Duo; see https://duo.com/docs/duoweb-v2, "Generate an akey".

idp.duo.integrationKey

DuoWeb integration key (supplied by Duo as Client ID)

idp.duo.secretKey

DuoWeb secret key (supplied by Duo as Client secret)

idp.duo.nonbrowser.apiHost

${idp.duo.apiHost}                  

Duo AuthAPI hostname assigned to the integration

idp.duo.nonbrowser.applicationKey


a secret supplied by you and not shared with Duo; see https://duo.com/docs/duoweb-v2, "Generate an akey".

idp.duo.nonbrowser.integrationKey

Duo AuthAPI integration key (supplied by Duo as Client ID)

idp.duo.nonbrowser.secretKey

Duo AuthAPI secret key (supplied by Duo as Client secret)

idp.duo.nonbrowser.header.factor

X-Shibboleth-Duo-Factor

Name of HTTP request header for Duo AuthAPI factor

idp.duo.nonbrowser.header.device

X-Shibboleth-Duo-Device

Name of HTTP request header for Duo AuthAPI device ID or name

idp.duo.nonbrowser.header.passcode

X-Shibboleth-Duo-Passcode

Name of HTTP request header for Duo AuthAPI passcode

idp.duo.nonbrowser.auto

true

Allow the factor to be defaulted in as "auto" if no headers are received

idp.duo.nonbrowser.clientAddressTrusted

true

Pass client address to Duo in API calls to support logging, push display, and network-based Duo policies

localtab-live
Expand
titleProperties (V4.1+)

The Duo-specific properties defined in conf/authn/duo.properties follow:

Name

Default

Description

idp.duo.apiHost    

DuoWeb API hostname assigned to the integration

idp.duo.applicationKey

A secret supplied by you and not shared with Duo; see https://duo.com/docs/duoweb-v2, "Generate an akey".

idp.duo.integrationKey

DuoWeb integration key (supplied by Duo as Client ID)

idp.duo.secretKey

DuoWeb secret key (supplied by Duo as Client secret)

idp.duo.nonbrowser.apiHost

${idp.duo.apiHost}                  

Duo AuthAPI hostname assigned to the integration

idp.duo.nonbrowser.integrationKey

Duo AuthAPI integration key (supplied by Duo as Client ID)

idp.duo.nonbrowser.secretKey

Duo AuthAPI secret key (supplied by Duo as Client secret)

idp.duo.nonbrowser.header.factor

X-Shibboleth-Duo-Factor

Name of HTTP request header for Duo AuthAPI factor

idp.duo.nonbrowser.header.device

X-Shibboleth-Duo-Device

Name of HTTP request header for Duo AuthAPI device ID or name

idp.duo.nonbrowser.header.passcode

X-Shibboleth-Duo-Passcode

Name of HTTP request header for Duo AuthAPI passcode

idp.duo.nonbrowser.auto

true

Allow the factor to be defaulted in as "auto" if no headers are received

idp.duo.nonbrowser.clientAddressTrusted

true

Pass client address to Duo in API calls to support logging, push display, and network-based Duo policies

The general properties configuring this flow via authn/authn.properties are:

Name

Default

Description

idp.authn.Duo.order

1000

Flow priority relative to other enabled login flows (lower is "higher" in priority)

idp.authn.Duo.nonBrowserSupported

false

Whether the flow should handle non-browser request profiles (e.g., ECP)

idp.authn.Duo.passiveAuthenticationSupported

false

Whether the flow allows for passive authentication

idp.authn.Duo.forcedAuthenticationSupported

true

Whether the flow supports forced authentication

idp.authn.Duo.proxyRestrictionsEnforced

%{idp.authn.enforceProxyRestrictions:true}

Whether the flow enforces upstream IdP-imposed restrictions on proxying

idp.authn.Duo.proxyScopingEnforced

false

Whether the flow considers itself to be proxying, and therefore enforces SP-signaled restrictions on proxying

idp.authn.Duo.discoveryRequired

false

Whether to invoke IdP-discovery prior to running flow

idp.authn.Duo.lifetime

%{idp.authn.defaultLifetime:PT1H}

Lifetime of results produced by this flow

idp.authn.Duo.inactivityTimeout

%{idp.authn.defaultTimeout:PT30M}

Inactivity timeout of results produced by this flow

idp.authn.Duo.reuseCondition

shibboleth.Conditions.TRUE

Bean ID of Predicate<ProfileRequestContext> controlling result reuse for SSO

idp.authn.Duo.activationCondition

shibboleth.Conditions.TRUE

Bean ID of Predicate<ProfileRequestContext> determining whether flow is usable for request

idp.authn.Duo.subjectDecorator

Bean ID of BiConsumer<ProfileRequestContext,Subject> for subject customization

idp.authn.Duo.supportedPrincipals

(see below)

Comma-delimited list of protocol-specific Principal strings associated with flow

idp.authn.Duo.addDefaultPrincipals

true

Whether to auto-attach the preceding set of Principal objects to each Subject produced by this flow

As a non-password based flow, the supportedPrincipals property defaults to the following XML:

Code Block
languagexml
<list>
    <bean parent="shibboleth.SAML2AuthnContextClassRef"
        c:classRef="http://example.org/ac/classes/mfa" />
    <bean parent="shibboleth.SAML1AuthenticationMethod"
        c:method="http://example.org/ac/classes/mfa" />
</list>

In property form, this is expressed as (note the trailing commas):

Code Block
idp.authn.Duo.supportedPrincipals = \
    saml2/http://example.org/ac/classes/mfa, \
    saml1/http://example.org/ac/classes/mfa

However, this default is (obviously) intended purely as an illustrative example of how to define your own values, as there are no standard ones to use. Some suggestions are mentioned earlier under General Configuration.

localtab-live
Expand
titleFlow Descriptor XML (V4.1+)

To replace the internally defined flow descriptor bean, the following XML is required:

Code Block
<util:list id="shibboleth.AvailableAuthenticationFlows">
 
    <bean p:id="authn/Duo" parent="shibboleth.AuthenticationFlow"
            p:order="%{idp.authn.Duo.order:1000}"
            p:nonBrowserSupported="%{idp.authn.Duo.nonBrowserSupported:false}"
            p:passiveAuthenticationSupported="%{idp.authn.Duo.passiveAuthenticationSupported:false}"
            p:forcedAuthenticationSupported="%{idp.authn.Duo.forcedAuthenticationSupported:true}"
            p:proxyRestrictionsEnforced="%{idp.authn.Duo.proxyRestrictionsEnforced:%{idp.authn.enforceProxyRestrictions:true}}"
            p:proxyScopingEnforced="%{idp.authn.Duo.proxyScopingEnforced:false}"
            p:discoveryRequired="%{idp.authn.Duo.discoveryRequired:false}"
            p:lifetime="%{idp.authn.Duo.lifetime:%{idp.authn.defaultLifetime:PT1H}}"
            p:inactivityTimeout="%{idp.authn.Duo.inactivityTimeout:%{idp.authn.defaultTimeout:PT30M}}"
            p:reuseCondition-ref="#{'%{idp.authn.Duo.reuseCondition:shibboleth.Conditions.TRUE}'.trim()}"
            p:activationCondition-ref="#{'%{idp.authn.Duo.activationCondition:shibboleth.Conditions.TRUE}'.trim()}"
            p:subjectDecorator-ref="#{getObject('%{idp.authn.Duo.subjectDecorator:}'.trim())}">
        <property name="supportedPrincipalsByString">
            <bean parent="shibboleth.CommaDelimStringArray"
                c:_0="#{'%{idp.authn.Duo.supportedPrincipals:}'.trim()}" />
        </property>
    </bean>
 
</util:list>

In older versions and upgraded systems, this list is defined in conf/authn/general-authn.xml. In V4.1+, no default version of the list is provided and it may simply be placed in conf/global.xml if needed.

...