This page shows how to enable a shibboleth IDP v4 to authenticate and send attributes to a DocuSign Service Provider .
external references :
In order to validate configuration , the DocuSign Vendor gave us a demo SP to test parameters .
DocuSign needs a TXT record in the client DNS for the associated domain
sample TXT record in DNS zone :
domain.fr IN TXT "docusign=ab64a181-0838-4ae8-9955-4493a6309d34"
There's no way to use our academic Discovery Service (WAYF) , so we need to declare all of our entities IDP in the DocuSign SP :
After declaration of our IDP host and certificate, we can now define IDP SSO / metadata SAML EndPoints :
We also set that we use signed AuthN and use Post Method .
From the Action button on the right side of the IDP lists, we can “show EndPoints” which contains the SP metadata URL that can be downloaded to our IDP for relying party truts .
[root@idptest metadata]# pwd /opt/shibboleth-idp/metadata [root@idptest metadata]# wget https://account-d.docusign.com/organizations/cd450-d4aa-4f12-9b59-1160332/saml2/metadata/aa14dc60-d3ff-4f14-9e59-446044682220
this raw xml file download is quite “rude” to read, for ease of use, it can be renamed to an approprite filename (docusign.xml) and transform it to a more readble xml (indentation) format :
[root@idptest metadata]# mv aa14dc60-d3ff-4f14-9e59-446044682220 docusign.xml [root@idptest metadata]# xmlstarlet fo docusign.xml > docusign-readable.xml
the DocuSign SP certificate is included in these freshly downloaded metadata, we can extract it to a dedicated file that will be loaded in our metadata-provider for sign checking .
get certificate from SP metadata element X509Certificate :
<X509Certificate>MIIGvTCCBaW ... YBlA== </X509Data>
to a docusing.pem file in an openssl x509 file format :
# cat ./metadata/docusign.pem -----BEGIN CERTIFICATE----- MIIGvTCCBaW .. YBlA== -----END CERTIFICATE-----
Now that we have the SP metadata, we can load it in our IDP configuration, and declare that it is signed by the aboved SP certificate :
[root@idptest conf]# vim metadata-providers.xml <!-- SP METADATA DocuSign--> <MetadataProvider id="DocuSign" xsi:type="FileBackedHTTPMetadataProvider" backingFile="%{idp.home}/metadata/docusign.xml" metadataURL="https://account-d.docusign.com/organizations/cd450-d4aa-4f12-9b59-1160332/saml2/metadata/aa14dc60-d3ff-4f14-9e59-446044682220"> <MetadataFilter xsi:type="SignatureValidation" requireSignedRoot="true" certificateFile="%{idp.home}/metadata/docusign.pem"/> <!-- Require a validUntil XML attribute on the root element and make sure its value is no more than 14 days into the future. --> <!-- <MetadataFilter xsi:type="RequiredValidUntil" maxValidityInterval="PT2487H41M6.544S"/> --> </MetadataProvider>
we keept commented the validUntil check , because by default there's no “validUntil” value in docusign SP medatada .
while initial tests failed, we realized that it was related to our shibboleth IDP that encrypts SAML assertions (by design/default/best-practice )
So we need to disable SAML encryption , cf encryptAssertions=“false” for that specific SP c:relyingPartyIds=… below :
[root@idptet conf]# vim relying-party.xml <!-- Container for any overrides you want to add. --> <util:list id="shibboleth.RelyingPartyOverrides"> <!-- Override example that identifies a single RP by name and configures it for SAML 2 SSO without encryption. This is a common "vendor" scenario. --> <bean id="DocuSign" parent="RelyingPartyByName" c:relyingPartyIds="https://account-d.docusign.com/organizations/cd450-d4aa-4f12-9b59-1160332/saml2"> <property name="profileConfigurations"> <list> <bean parent="SAML2.SSO" p:encryptAssertions="false" /> </list> </property> </bean> </util:list>
DocuSign expect by default an identifier (SAML NameID) that is persistent and mapped to the mail attribute . By default and design for dataProtection, shibboleth IDP uses a transcient NameID with a random value . So we need to define a persistent NameID based on the mail attibute for seamless authN with Docusign .
this is done in saml-nameid.xml with activating <ref bean=“shibboleth.SAML2PersistentGenerator” /> and associated saml-nameid.properties :
[root@idptest conf]# vim saml-nameid.xml <!-- SAML 2 NameID Generation --> <util:list id="shibboleth.SAML2NameIDGenerators"> <ref bean="shibboleth.SAML2TransientGenerator" /> <!-- Uncommenting this bean requires configuration in saml-nameid.properties. --> <!-- Jehan --> <ref bean="shibboleth.SAML2PersistentGenerator" /> <!-- <bean parent="shibboleth.SAML2AttributeSourcedGenerator" p:omitQualifiers="true" p:format="urn:oasis:names:tc:SAML:2.0:nameid-format:emailAddress" p:attributeSourceIds="#{ {'mail'} }" > <property name="activationCondition"> <bean parent="shibboleth.Conditions.RelyingPartyId" c:candidate="https://account-d.docusign.com/organizations/cd450-d4aa-4f12-9b59-1160332/saml2" /> </property> </bean> --> </util:list>
I keept shibboleth.SAML2AttributeSourcedGenerator as commented for example above, that was a way to designated a candidate SP dedicated to that NameID Generator, but finally here in this test IDP for docusign , default is persistent ID for all .
in saml-nameid.properties we define the use of the mail attribute associated with a Salt here the idp.persistentId.generator = shibboleth.ComputedPersistentIdGenerator is defined (no need for a database storage)
idp.persistentId.useUnfilteredAttributes = true idp.persistentId.sourceAttribute = mail idp.persistentId.encoding = BASE64 #jehan idp.persistentId.algorithm = SHA idp.persistentId.salt = secretpassfor202101isi # needs to long enough ! # To use a database, use shibboleth.StoredPersistentIdGenerator idp.persistentId.generator = shibboleth.ComputedPersistentIdGenerator
authN is associated with the authn Flow Password process in the IDP:
[root@idpt conf]# grep authn.flows idp.properties idp.authn.flows=Password
and define the ldap backend with a filter based on mail attribute :
[root@idptest conf]# vim ldap.properties idp.authn.LDAP.userFilter = (&(mail={user})(employeeType=Permanent)) ##idp.attribute.resolver.LDAP.searchFilter = (uid=$resolutionContext.principal) idp.attribute.resolver.LDAP.searchFilter = (mail=$resolutionContext.principal)
Now that authN is configured , we also need to send attributes to DocuSign SP in order to match mandatory attributes at first connexion in order to dynamically create account. these are the mandatory attributes :
and optionnally
this one allows to match a permission profile at login, for example sending a employeetyp attribute for Staff people could set their default profile to DS-Sender and for employeetype: employee associated the default profile to simple DS-viewer .
beware that these attributes are define in the interface with their urn/oid code, the “firendlyNames” sn , givenName, mail …. are not mapped
In the IDP we use the attribute-resolver-ldap.xml (or attribute-resolver.xml) file to define our customized for DocuSign for NameID mail attribute and permission (employeeType) attribute .
[root@idptest conf]# grep attribute-resolver-ldap.xml services.xml <value>%{idp.home}/conf/attribute-resolver-ldap.xml</value>
in order to map DocuSign domains ID to our mail domains we need to map values
attribute-resolver.xml mapped employeType
<AttributeDefinition id="employeeType" xsi:type="Mapped"> <InputDataConnector ref="passthroughAttributes" attributeNames="mail" /> <DefaultValue passThru="false"/> <!-- Values Prod --> <ValueMap> <ReturnValue>1601</ReturnValue> <SourceValue caseSensitive="false">(.+)@imte.fr</SourceValue> </ValueMap> <ValueMap> <ReturnValue>1604</ReturnValue> <SourceValue caseSensitive="false">(.+)@imte-atlantic.fr</SourceValue> </ValueMap> ... <ValueMap> <ReturnValue>16049193</ReturnValue> <SourceValue caseSensitive="false">(.+)@mines-ste.fr</SourceValue> </ValueMap> </AttributeDefinition>
idem for staticDSAccountID
<AttributeDefinition id="staticDSAccountID" xsi:type="Mapped"> <InputDataConnector ref="passthroughAttributes" attributeNames="mail" /> <AttributeEncoder xsi:type="SAML2String" name="urn:oid:1.3.6.1.4.1.7391.5" friendlyName="staticDSAccountID" /> <DefaultValue passThru="false"/> <!-- Values DocuSign Prod --> <!-- <ValueMap> <ReturnValue>14219580-a3e2</ReturnValue> <SourceValue caseSensitive="false">(.+)@imte.fr</SourceValue> </ValueMap> --> <ValueMap> <ReturnValue>24035b51-b871-</ReturnValue> <SourceValue caseSensitive="false">(.+)@imte.fr</SourceValue> </ValueMap> <ValueMap> <ReturnValue>76919292-2f64</ReturnValue> <SourceValue caseSensitive="false">(.+)@imte-atlantic.fr</SourceValue> </ValueMap> ... <ValueMap> <ReturnValue>557f440a-a124</ReturnValue> <SourceValue caseSensitive="false">(.+)@mines-ste.fr</SourceValue> </ValueMap> </AttributeDefinition>
because on our campus we use 3 domains names in our users mail addresses, in order to simplify DocuSign domain claims and IDP declaration (+ metadata) , we recorded only one domain at DocuSign SP and manage to rewrite dynamically on our IDP side the 2 other domains to the 1st one , which is the only one declared on DocuSign .
To do that we uses a Mapped Attribute definition like this :
[root@idptest conf]# vim attribute-resolver-ldap.xml <AttributeDefinition id="mail" xsi:type="Mapped"> <AttributeEncoder xsi:type="SAML1String" name="urn:mace:dir:attribute-def:mail" /> <AttributeEncoder xsi:type="SAML2String" name="urn:oid:0.9.2342.19200300.100.1.3" friendlyName="mail" /> <InputDataConnector ref="myLDAP" attributeNames="mail" /> <DefaultValue passThru="true"/> <ValueMap> <ReturnValue>$1@domain1.eu</ReturnValue> <SourceValue>(.+)@domain2.eu</SourceValue> </ValueMap> <ValueMap> <ReturnValue>$1@domain1.eu</ReturnValue> <SourceValue>(.+)@domain3.eu</SourceValue> </ValueMap> </AttributeDefinition>
when a user connect with it's email address of givenName.Surname@domain2.eu , it is finally transmited to DocuSign SP as givenName.Surname@domain1.eu .
We also used a mapped attribute definition in oder to match Roles/Permission of DS-Sender vs DS-viewer , repectivelly 1122394 and 1122395
<AttributeDefinition id="employeeType" xsi:type="Mapped"> <InputDataConnector ref="myLDAP" attributeNames="employeeType" /> <DefaultValue passThru="false"/> <ValueMap> <ReturnValue>1122394</ReturnValue> <SourceValue caseSensitive="false">Permanent</SourceValue> </ValueMap> <ValueMap> <ReturnValue>1122395</ReturnValue> <SourceValue partialMatch="true" caseSensitive="false">Doctorant</SourceValue> </ValueMap> <ValueMap> <ReturnValue>1122395</ReturnValue> <SourceValue partialMatch="true" caseSensitive="false">Vacataire</SourceValue> </ValueMap> </AttributeDefinition>
DocuSign support told us also to send a static value for the accoundID so that automatic profile affectation could be done
we can get our API account ID from the E-Signature parameter screen , left menu ⇒ API-Keys
then we need our IDP to map and send that static value for everyone, so we creted a staticDataconnector and the associated decated static AttributeDefinition for thos DocuSign authZ feature with the creation of a custom staticDSAccountID attribute :
references :
in conf/attribute-resolver-ldap.xml
<!-- disi JP for DocuSign --> <AttributeDefinition xsi:type="Simple" id="staticDSAccountID"> <InputDataConnector ref="staticAttributes" attributeNames="staticDSAccountID" /> <AttributeEncoder xsi:type="SAML2String" name="urn:oid:1.3.6.1.4.1.7399.1.1.1.1" friendlyName="staticDSAccountID" /> </AttributeDefinition> <DataConnector id="staticAttributes" xsi:type="Static"> <Attribute id="staticDSAccountID"> <Value>ai4dc9cfa7-dd39-aad1-884c-2f9b17574224</Value> </Attribute> </DataConnector> </AttributeResolver>
clients in specific site which don't have many public IPs addresses use NAT , and it breaks the flow of using a proxied IDP In that case we must disable “checkAddress” in the IDP (as proxy) configuration .
Analagous to the SP, there's a checkAddress setting on the SAML2.SSO profile configuration bean.
from examples in the doc:
I understand that I can specify the checkAddress attribute only for those “2nd Hand/backends” IDPs of my idp-proxy by listing them specifically in relying-party.xml :
<util:list id="shibboleth.RelyingPartyOverrides"> <bean id="proxyBackendIdps" parent="RelyingPartyByName"> <constructor-arg name="relyingPartyIds"> <list> <value>https://idp.school1.fr/idp/shibboleth</value> <value>https://idp.school2.fr/idp/shibboleth</value> <value>https://multipass.school3.fr/idp/shibboleth</value> </list> </constructor-arg> <property name="profileConfigurations"> <list> <bean parent="SAML2.SSO" p:checkAddress="false" /> </list></property> </bean> </util:list>