DocuSign

This page shows how to enable a shibboleth IDP v4 to authenticate and send attributes to a DocuSign Service Provider .

external references :

DocuSign demo SP

In order to validate configuration , the DocuSign Vendor gave us a demo SP to test parameters .

https://appdemo.docusign.com

Claim domains

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"

Declare IDP

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 :

Configure IDP End Points

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 .

DocuSign SP Metadata

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

SP metadata certificate

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-----

load SP metadata in our IDP

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 .

Docusign SAML specificities

Cannot Encrypt assertions

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>

NameID

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)

Mapped Attributes

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 :

  • sn (surname)
  • givenName
  • mail

and optionnally

  • permissionProfileId

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 :!:

shib IDP attribute-resolver

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>

mapped attributes

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>

Mail rewriting

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 .

Permission map

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>

static AttributeDefinition for AccountID

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>

Special case when unsing NAT ( checkAddress="false" )

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.

https://wiki.shibboleth.net/confluence/display/IDP4/SAML2SSOConfiguration#55804373d9264505e7b248218c3ea26c3fd35a11

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>