Skip to content
John Pfeifer edited this page Dec 4, 2019 · 5 revisions

Welcome to the duo-non-browser wiki!

This functionality is now built into 3.4+. Please use that. https://wiki.shibboleth.net/confluence/display/IDP30/DuoAuthnConfiguration#DuoAuthnConfiguration-AuthAPIandNon-Browser/ECPUse

As with all things Shibboleth, it is not simple but is rather flexible. As with the Duo web authentication flow, it is presumed that you have done the first factor authentication and canonicalized the user name.

The credentials extractor can pull the Duo factor/device/passcode from the the HTTP headers or HTTP parameters. By default, the headers take precedence over the parameters; if nothing is found the defualt behavior is to use "auto" factor and "auto" device (see section 5 below for details on all of the settings). If successful, it will add an instance of edu.umd.idm.shibboleth.idp.authn.context.DuoAuthenticationContext to the authentication context.

The validator calls both the /preauth and /auth Duo AuthAPI endpoints. If successful, it will add an instance of edu.umd.idm.shibboleth.idp.authn.context.DuoResponseContext to the authentication context containing the details. See the Duo documentation for further information on the AuthAPI.

If you are using a proxy in front of your IdP, please see Keith Wessel's note on timeouts.

There are a number of configuration files that you will need to update/create.

1) conf/authn/duo.properties

Go to your duo admin console and create a new AuthAPI integration; then copy the host, iKey, and sKey into a new set of properties:

idp.duo.auth.apiHost = api-xxxxxxxxx.duosecurity.com
idp.duo.auth.integrationKey = yyyyyyyyyyyyyyyyyyyy
idp.duo.auth.secretKey = zzzzzzzzzzzzzzzzzzzzzzzzzzzz

2) conf/authn/general-authn.xml

Add a new authentication flow bean changing the supportedPrincipals to whatever value you are using:

<bean id="authn/DuoAuthAPI" parent="shibboleth.AuthenticationFlow"
        p:forcedAuthenticationSupported="true"
        p:nonBrowserSupported="true">
    <!--
    The list below should be changed to reflect whatever locally- or
    community-defined values are appropriate to represent MFA. It is
    strongly advised that the value not be specific to Duo or any
    particular technology.
    -->
    <property name="supportedPrincipals">
        <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>
    </property>
</bean>

3) conf/authn/mfa-authn-config.xml

Presumably, this is where you are triggering the Duo web flow. To trigger the new flow, set the next flow to authn/DuoAuthAPI. Exactly how to do this will be dependent on your configuration and is left as an exercise for the reader.

4) flows/authn/DuoAuthAPI/DuoAuthAPI-beans.xml

The ExtractDuoApiContext bean can take a number of parameters which will affect its behavior:

parameter default description
autoAuthentiationSupoorted true use "auto" factor/device if no applicable header/parameter found
useHeaders true examine the HTTP headers for Duo factor/device/passcode
useParameters true examine the HTTP parameters for Duo factor/device/passcode
factorHeaderName X-Shiboleth-Duo-Factor the HTTP header name for the Duo factor
deviceHeaderName X-Shiboleth-Duo-Device the HTTP header name for the Duo device
passcodeHeaderName X-Shiboleth-Duo-Passcode the HTTP header name for the Duo passcode
factorParameterName duoFactor the HTTP parameter name for the Duo factor
deviceParameterName duoDevice the HTTP parameter name for the Duo device
passcodeParameterName duoPasscode the HTTP parameter name for the Duo passcode
<?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">

    <bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer" p:placeholderPrefix="%{"
        p:placeholderSuffix="}" />

    <bean class="net.shibboleth.ext.spring.config.IdentifiableBeanPostProcessor" />
    <bean class="net.shibboleth.idp.profile.impl.ProfileActionBeanPostProcessor" />


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

    <bean id="shibboleth.authn.DuoAuthAPI.DuoIntegrationStrategy" class="com.google.common.base.Functions"
        factory-method="constant" c:_0-ref="shibboleth.authn.DuoAuthAPI.DuoIntegration" />

    <bean id="ExtractDuoApiContext" scope="prototype"
        class="edu.umd.idm.shibboleth.idp.authn.duo.impl.ExtractDuoAuthticationContext"
        p:httpServletRequest-ref="shibboleth.HttpServletRequest" />

    <bean id="DuoAuthAuthenticator" scope="prototype"
        class="edu.umd.idm.shibboleth.idp.authn.duo.impl.DuoAuthAuthenticator"
        p:httpClient-ref="shibboleth.NonCachingHttpClient"
        p:objectMapper-ref="shibboleth.JSONObjectMapper" />

    <bean id="DuoPreauthAuthenticator" scope="prototype"
        class="edu.umd.idm.shibboleth.idp.authn.duo.impl.DuoPreauthAuthenticator"
        p:httpClient-ref="shibboleth.NonCachingHttpClient"
        p:objectMapper-ref="shibboleth.JSONObjectMapper" />

    <bean id="ValidateDuoAuthApi" scope="prototype"
        class="edu.umd.idm.shibboleth.idp.authn.duo.impl.ValidateUsernameAgainstDuoAuthApi"
        p:usernameLookupStrategy-ref="shibboleth.authn.Duo.UsernameLookupStrategy"
        p:duoIntegrationLookupStrategy-ref="shibboleth.authn.DuoAuthAPI.DuoIntegrationStrategy"
        p:authAuthenticator-ref="DuoAuthAuthenticator"
        p:preauthAuthenticator-ref="DuoPreauthAuthenticator"
        p:addDefaultPrincipals="#{getObject('shibboleth.authn.Duo.addDefaultPrincipals') ?: true}"
        />

        <import resource="../../../system/flows/authn/duo-authn-beans.xml" />

</beans>

5) flows/authn/DuoAuthAPI/DuoAuthAPI-flow.xml

This is a rudimentary example flow

<flow xmlns="http://www.springframework.org/schema/webflow" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/webflow http://www.springframework.org/schema/webflow/spring-webflow.xsd"
    parent="authn.abstract">

    <!-- This is a simple login flow for Duo authentication. -->

    <var name="thisFlow" class="net.shibboleth.idp.authn.AuthenticationFlowDescriptor" />

    <action-state id="ExtractDuoApiContext">
        <evaluate expression="ExtractDuoApiContext" />
        <evaluate expression="'proceed'" />

        <transition on="proceed" to="ValidateDuoAuthApi" />

        <!-- Fall through to a different flow if basic-auth extract fails on a passive or non-browser request. -->
        <transition on="#{ opensamlProfileRequestContext.getSubcontext(T(net.shibboleth.idp.authn.context.AuthenticationContext)).isPassive() || !opensamlProfileRequestContext.isBrowserProfile() }" to="ReselectFlow" />

        <transition on="NoCredentials" to="LogDuoException" />
        <transition on="InvalidCredentials" to="LogDuoException" />
        <on-exit>
            <set name="thisFlow" value="opensamlProfileRequestContext.getSubcontext(T(net.shibboleth.idp.authn.context.AuthenticationContext)).getAttemptedFlow()" />
        </on-exit>
    </action-state>

    <action-state id="ValidateDuoAuthApi">
        <evaluate expression="ValidateDuoAuthApi" />
        <evaluate expression="'proceed'" />

        <transition on="proceed" to="proceed" />
    </action-state>

    <action-state id="LogDuoException">
        <on-entry>
            <evaluate expression="T(org.slf4j.LoggerFactory).getLogger('net.shibboleth.idp.authn.duo').error('DuoWebException', flowExecutionException.getCause())" />
        </on-entry>
        <evaluate expression="'AuthenticationException'" />
    </action-state>

    <bean-import resource="DuoAuthAPI-beans.xml" />

</flow>

6) Java keystore

If you are not using the default Java keystore for rootCA certificates, you'll need to verify that the rootCA for the DuoAPI url's is included in your keystore. (thanks to Keith W.)