Thoughts, musings, ramblings, and rants

Using objectGUID in Shibboleth IdP

I was setting up a new Shibboleth IdP in my lab to run some tests. When integrating it with my Active Directory domain, I wanted to use the objectGUID attribute as a unique user identifier. It meets the requirements of being unique and not reusable, while also not exposing any undesirable information like objectSID would.

The problem is that objectGUID is stored as a binary attribute, so it must be handled differently in the IdP. Shibboleth has the ability to automatically handle binary attributes by listing them in a <BinaryAttributes> tag, but it does so by base64 encoding them. This isn't ideal because some SAML attributes, like eduPersonUniqueId, only allow alphanumeric characters.

The solution I came up with was to allow Shibboleth to do the binary conversion, but then transform the base64 data to hex codes. The result is a user identifier that is unique, not reusable, and entirely composed of alphanumeric values.

Here are the relevant parts of my attribute-resolver.xml setup.

<?xml version="1.0" encoding="UTF-8"?>
<AttributeResolver
        xmlns="urn:mace:shibboleth:2.0:resolver" 
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
        xsi:schemaLocation="urn:mace:shibboleth:2.0:resolver http://shibboleth.net/schema/idp/shibboleth-attribute-resolver.xsd">

    <!-- ========================================== -->
    <!--      Attributes                            -->
    <!-- ========================================== -->

    <AttributeDefinition id="eduPersonPrincipalName" xsi:type="Scoped" scope="%{idp.scope}">
        <InputAttributeDefinition ref="userIdentifier"/>
    </AttributeDefinition>

    <AttributeDefinition id="eduPersonUniqueId" xsi:type="Scoped" scope="%{idp.scope}">
        <InputAttributeDefinition ref="userIdentifier"/>
    </AttributeDefinition>

    <AttributeDefinition id="userIdentifier" xsi:type="ScriptedAttribute" dependencyOnly="true">
        <InputDataConnector ref="activeDirectory" attributeNames="objectGUID"/>
        <Script>
            <![CDATA[
            var base64guid = objectGUID.getValues().get(0);
            var hexguid = '';
            for ( var i=0; i < base64guid.length; i++ ) {
                var hex = base64guid.charCodeAt(i).toString(16);
                if ( hex.length == 1 ) {
                    hex = '0' + hex;
                }
                hexguid += hex;
            }
            userIdentifier.addValue(hexguid);
            ]]>
        </Script>
    </AttributeDefinition>

    <!-- ========================================== -->
    <!--      Data Connectors                       -->
    <!-- ========================================== -->

    <DataConnector
            id="activeDirectory"
            xsi:type="LDAPDirectory"
            ldapURL="%{idp.attribute.resolver.LDAP.ldapURL}"
            baseDN="%{idp.attribute.resolver.LDAP.baseDN}"
            principal="%{idp.attribute.resolver.LDAP.bindDN}"
            principalCredential="%{idp.attribute.resolver.LDAP.bindDNCredential}"
            useStartTLS="%{idp.attribute.resolver.LDAP.useStartTLS}"
            connectTimeout="%{idp.attribute.resolver.LDAP.connectTimeout}"
            responseTimeout="%{idp.attribute.resolver.LDAP.responseTimeout}">
        <FilterTemplate>
            <![CDATA[
            %{idp.attribute.resolver.LDAP.searchFilter}
            ]]>
        </FilterTemplate>
        <BinaryAttributes>objectGUID</BinaryAttributes>
        <ReturnAttributes>givenName sn displayName mail objectGUID</ReturnAttributes>
    </DataConnector>

</AttributeResolver>

#shibboleth