Date: Thu, 28 Mar 2024 21:56:15 +0000 (UTC) Message-ID: <1741270904.7.1711662975057@076fb3df018d> Subject: Exported From Confluence MIME-Version: 1.0 Content-Type: multipart/related; boundary="----=_Part_6_705791180.1711662975057" ------=_Part_6_705791180.1711662975057 Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: quoted-printable Content-Location: file:///C:/exported.html
This document is only applicable to versions prior to 3.3. Do not try to= follow this document if you are using IdPv3.3 or greater.
This document describes how the University of Chicago configured their I= dentity Provider under IdPv3 to behave like it did under IdPv2 with the MCB installed. In our installation all users are forced to u= se Password for initial authentication. Then users can opt-in to havi= ng Duo Security layered on top of their initial authentication.= p>
Note: There are two Duo plugins for Shibboleth v3
University of Chicago uses a Duo plugin developed by Unicon. Later, Duo = Security Inc released their own Shibboleth v3 plugin; see C= ontributions and Extensions. Duo's plugin cannot (easily) support the f= ull MCB functionality described on this page, but it might have some other = advantages. If you're shopping around, look at both.
Create a new webflow /opt/shibboleth-idp/flows/authn/Duo/duo-=
authn-flow.xml
. The contents are below. The changes we m=
ade ensure that on a forceAuthn request, the user is prompted to also enter=
their username-password again (so long as it's not their first pass throug=
h the IdP =E2=80=93 determined by a 2-minute timer from when the IdPSe=
ssion is created).
<flow= xmlns=3D"http://www.springframework.org/schema/webflow" xmlns:xsi=3D"http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=3D"http://www.springframework.org/schema/webflow h= ttp://www.springframework.org/schema/webflow/spring-webflow.xsd" parent=3D"authn.abstract, authn/conditions"> <var name=3D"duoCredential" class=3D"net.unicon.iam.shibboleth.idp.a= uthn.duo.authn.DuoCredential"/> <var name=3D"currentTime" class=3D"java.util.Date" /> <!-- if the IdP session is < 2 minutes then don't check for force= Authn. If it's more than that then check for forceAuthn and if forceAuthn is in play make the user do= a 2nd Password login. This eliminates the problem we had with a user hitting a forceAuthn sit= e as their first SP & then having to double-enter their username/password. --> <decision-state id=3D"checkFirstLogin"> =09<if test=3D"(currentTime.getTime() - opensamlProfileRequestContext.ge= tSubcontext(T(net.shibboleth.idp.session.context.SessionContext)).getIdPSes= sion().getCreationInstant()) > 120000" then=3D"checkForceAuth" else=3D"duo" />=09 </decision-state> <decision-state id=3D"checkForceAuth"> <if test=3D"opensamlProfileRequestContext.getSubcontext(T(net.sh= ibboleth.idp.authn.context.AuthenticationContext)).isForceAuthn()" then=3D"CallPasswordFlow" else=3D"duo" /> </decision-state> <subflow-state id=3D"CallPasswordFlow" subflow=3D"authn/Password">= ; <input name=3D"calledAsSubflow" value=3D"true"/> <transition on=3D"proceed" to=3D"duo"/> </subflow-state> <view-state id=3D"duo" view=3D"duo" model=3D"duoCredential"> <binder> <binding property=3D"signedDuoResponse"/> </binder> <on-render> <evaluate expression=3D"environment" result=3D"viewScope.env= ironment"/> <evaluate expression=3D"opensamlProfileRequestContext.getSubconte= xt(T(net.shibboleth.idp.authn.context.AuthenticationContext))" result=3D"viewScope.authenticationContext"/> <set name=3D"viewScope.request" value=3D"flowRequestContext.= getExternalContext().getNativeRequest()"/> <set name=3D"viewScope.sigRequest" value=3D"duoAuthenticationService.generateSignedRequestTok= en(authenticationContext)"/> <set name=3D"viewScope.apiHost" value=3D"duoAuthenticationSe= rvice.getApiHost()"/> </on-render> <transition on=3D"proceed" bind=3D"true" validate=3D"false" to= =3D"validateDuo"/> </view-state> <action-state id=3D"validateDuo"> <evaluate expression=3D"ValidateDuoResponse"/> <evaluate expression=3D"'proceed'"/> <transition on=3D"proceed" to=3D"proceed"/> </action-state> </flow>
Setup global.xml
to use our local Duo AuthnContextClas=
s instead of the default from the plugin
<= bean id=3D"authn/Duo" parent=3D"shibboleth.AuthenticationFlow" p:nonBrowserSupported=3D"false" p:forcedAuthenticationSupported= =3D"%{duo.forcedAuthenticationSupported:true}"> <property name=3D"supportedPrincipals"> <util:list> <bean parent=3D"shibboleth.SAML2AuthnContextClassRef" c:classRef=3D"http://uchicago.edu/duo" /> <bean parent=3D"shibboleth.SAML2AuthnCon= textClassRef" c:classRef=3D"urn:oasis:names:tc:SAML:2.0:ac:classes:= PasswordProtectedTransport" /> <bean parent=3D"shibboleth.SAML1AuthenticationMethod" c:method=3D"http://uchicago.edu/duo" /> </util:list> </property> </bean>
Setup the attribute-resolver to configure an attribute to release what A= uthnClassContexts a user is allowed to use. We used eduPersonAssuranc= e. In our example, we push Grouper groups to our LDAP server & then use a MappedAttribute to translate the Grouper va= lues to AuthnContextClass values
<res= olver:AttributeDefinition xsi:type=3D"Mapped" xmlns=3D"urn:mace:shibboleth:= 2.0:resolver:ad" id=3D"eduPersonAssurance" sourceAttribute= ID=3D"ucisMemberOf"> <resolver:Dependency ref=3D"directory"/> <resolver:AttributeEncoder xsi:type=3D"SAML2String" xmlns=3D"urn= :mace:shibboleth:2.0:attribute:encoder" name=3D"urn:oid:1.3.6.1.4.1.1466.115.121= .1.15" friendlyName=3D"eduPersonAssurance"/> <ValueMap> <ReturnValue>http://id.incommon.org/assurance/silver</= ReturnValue> <SourceValue>uc:reference:account:assurance:silver:author= ized</SourceValue> </ValueMap> <ValueMap> <ReturnValue>http://uchicago.edu/duo</ReturnValue> <SourceValue>uc:applications:shibboleth:mcb:duo-eligible&= lt;/SourceValue> </ValueMap> <ValueMap> <ReturnValue>urn:oasis:names:tc:SAML:2.0:ac:classes:Passw= ordProtectedTransport</ReturnValue> <SourceValue>uc:applications:shibboleth:mcb:password</= SourceValue> </ValueMap> </resolver:AttributeDefinition>
Configure in idp.properties
the following for AuthN fl=
ows:
# Regul= ar expression matching login flows to enable, e.g. IPAddress|Password idp.authn.flows=3DPassword|Duo # Regular expression of forced "initial" methods when no session exists, # usually in conjunction with the idp.authn.resolveAttribute property below= . idp.authn.flows.initial =3D Password # Set to an attribute ID to resolve prior to selecting authentication flows= ; # its values are used to filter the flows to allow. idp.authn.resolveAttribute =3D eduPersonAssurance
In order to prevent bypassing of MFA during forceAuthn caused by a princ=
ipal switch (another user logs in during the force prompt who has MFA where=
the first user didn't have MFA), you need to further adjust idp=
.properties
and set idp.authn.identitySwitchIsError
to true.
Setup your authn-comparison.xml
to replicate the MCB f=
unctionality of defining for the IdP which contexts will satisfy which othe=
r contexts. In this example Duo and Silver are ok for Password and on=
ly duo is equivalent for PasswordProtectedTransport:
<bea= n id=3D"shibboleth.BetterClassRefMatchFactory" parent=3D"shibboleth.Inexact= MatchFactory"> <property name=3D"matchingRules"> <map> <entry key=3D"urn:oasis:names:tc:SAML:2.0:ac:classes:Pas= sword"> <list> <value>urn:oasis:names:tc:SAML:2.0:ac:classes= :PasswordProtectedTransport</value> <value>http://uchicago.edu/duo</value> =09=09=09<value>http://id.incommon.org/assurance/silver</value>= =20 </list> </entry> =09=09<entry key=3D"urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProte= ctedTransport"> =09=09=09<list> =09=09=09=09<value>http://uchicago.edu/duo</value> =09=09=09=09<!-- remove silver from PPT equivalence as it causes silvere= d users who have opted =09=09=09=09into 2FA forcing to not have 2FA because silver is just as good= as Duo for PPT --> =09=09=09=09<!-- <value>http://id.incommon.org/assurance/silver<= ;/value> --> =09=09=09</list> =09=09</entry>=09 </map> </property> </bean>
In conf/intercept/context-check-intercept-config.xml
d=
efine further the relationship between PasswordProtectedTransport & Duo=
/Silver (which are both initially handled by Password flow). What you=
're doing here is causing the IdP to ensure that if the SP requests the Sil=
ver context that the user was able to meet the context via the eduPersonAss=
urance lookup. If that condition cannot be met, then fail.
<?xml= version=3D"1.0" encoding=3D"UTF-8"?> <beans xmlns=3D"http://www.springframework.org/schema/beans" xmlns:context=3D"http://www.springframework.org/schema/context" xmlns:util=3D"http://www.springframework.org/schema/util" xmlns:p=3D"http://www.springframework.org/schema/p" xmlns:c=3D"http://www.springframework.org/schema/c" xmlns:xsi=3D"http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=3D"http://www.springframework.org/schema/beans ht= tp://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context ht= tp://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/util http:= //www.springframework.org/schema/util/spring-util.xsd" =20 default-init-method=3D"initialize" default-destroy-method=3D"destroy"> <!-- Condition to evaluate to interrupt SSO flows to check the stat= e of the transaction before allowing. =20 Typically the flow itself will be activated based on configuration in r= elying-party.xml, and this controls whether to proceed if the flow is activated. The most common use for th= is flow is to check the set of resolved/filtered attributes and values to see if the user is authorize= d or provisioned into a service. --> <bean id=3D"shibboleth.context-check.Condition" parent=3D"shibboleth= .Conditions.AND"> <constructor-arg> <list> <bean class=3D"net.shibboleth.idp.profile.logic.AuthnCla= ssPredicate" c:authnClassesToMatch-ref=3D"authnClassesToMatch" =09=09 c:authnClassesToForgive-ref=3D"authnClassesToForgive" =09 c:predicateToDelegate-ref=3D"attributePredicate" /> </list> </constructor-arg> </bean> <util:set id=3D"authnClassesToMatch"> =09<value>http://id.incommon.org/assurance/silver</value> =09<value>http://uchicago.edu/duo</value> </util:set> <util:set id=3D"authnClassesToForgive"> <value>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtect= edTransport</value> </util:set> <bean id=3D"attributePredicate" class=3D"net.shibboleth.idp.profile.= logic.SimpleAttributePredicate"> <property name=3D"attributeValueMap"> <map> <entry key=3D"eduPersonAssurance"> <list> <value>http://id.incommon.org/assurance/silve= r</value> =09=09=09=09=09=09<value>http://uchicago.edu/duo</value> </list> </entry> </map> </property> </bean> </beans>
You should now have an IdP that acts like it did under the v2 MCB assuming =
you used Password initial authN and user-opt-in to Duo.
One note about this config. If an SP explicitly requests PasswordP= rotectedTransport, and the user is trying to force all authN to require Duo= (by not allowing PPT in the eduPersonAssurance attribute) the user will no= t be prompted for Duo. The Duo forcing only happens if the AuthnConte= xtClass was either explicitly unspecified or not sent in the AuthnRequest.&= nbsp;
Related articles
Alternative Implementation
University of Chicago uses a Duo plugin developed by Unicon. Later, Duo = Security Inc released their own Shibboleth v3 plugin; see C= ontributions and Extensions. Duo's plugin cannot (easily) support the f= ull MCB functionality described on this page, but it might have some other = advantages. If you're shopping around, look at both.