Skip to content

CSCfi/shibboleth-idp-authn-shibsp

Repository files navigation

Shibboleth IdP v5: Shibboleth SP authentication

License Build Status

Overview

This module implements an authentication flow for Shibboleth Identity Provider v5 exploiting attributes provided by Shibboleth Service Provider. The module can be used for outsourcing the authentication to another SAML IdP instead of prompting and validating the user credentials itself.

NOTE! The IdP v5 natively supports SAML authentication, with many additional features compared to this plugin. The main purpose of this plugin is to serve in smooth transition to V4 for the existing IdP v3 deployments.

Prerequisities and compilation

mvn package

After successful compilation, the target directory contains shibboleth-idp-authn-shibsp-<version>.zip archive.

Deployment

After compilation, the module's JAR-files must be deployed to the IdP Web application. Also, the module's authentication flow and its bean definitions must be deployed to the IdP. Depending on the IdP installation, the module deployment may be achieved for instance with the following sequence:

unzip target/shibboleth-idp-authn-shibsp-<version>.zip
cp shibboleth-idp-authn-shibsp-<version>/edit-webapp/WEB-INF/lib/* /opt/shibboleth-idp/edit-webapp/WEB-INF/lib
cp -r shibboleth-idp-authn-shibsp-<version>/flows/* /opt/shibboleth-idp/flows
cp shibboleth-idp-authn-shibsp-<version>/conf/* /opt/shibboleth-idp/conf/authn
cd /opt/shibboleth-idp
sh bin/build.sh

The final command will rebuild the war-package for the IdP application.

Configuration

The distribution package contains three different authentication flows: authn/Shib, authn/ShibExample and authn/ShibExternal. The first one is an abstract flow that contains general steps exploited by the other two flows. Typically, the deployer doesn't need to customize the abstract flow or its beans.

authn/ShibExample

This flow assumes that the URL location where the end-user is accessing the IDP is protected by Shibboleth SP. In other words, the Shibboleth SP must be configured to provide the Apache environment and/or HTTP headers to the location. Typically the URL location corresponds to the endpoint where Shibboleth IdP receives the incoming authentication request, for instance https://idp.example.org/idp/profile/SAML2/Redirect/SSO.

<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/Shib">

    <view-state id="ExternalTransfer" view="externalRedirect:#{flowRequestContext.getActiveFlow().getApplicationContext().getBean('shibboleth.authn.ShibExample.externalAuthnPath')}&amp;forceAuthn=#{opensamlProfileRequestContext.getSubcontext(T(net.shibboleth.idp.authn.context.AuthenticationContext)).isForceAuthn()}&amp;isPassive=#{opensamlProfileRequestContext.getSubcontext(T(net.shibboleth.idp.authn.context.AuthenticationContext)).isPassive()}&amp;target=#{flowExecutionUrl}%26_eventId_proceed%3D1">
        <transition to="ValidateShibFlowAuthentication" />
    </view-state>
    
</flow>

The example above assumes that you have a bean called shibboleth.authn.ShibExample.externalAuthnPath configured in the file /opt/shibboleth-idp/conf/authn/shib-authn-config.xml. The bean must contain the desired Shibboleth SP Handler location and its parameters.

authn/ShibExternal

In comparison to the previous flow, this flow is more flexible regarding the Shibboleth SP configuration. On the other hand, the configuration requires some extra steps, explained below.

<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/Shib">

    <view-state id="ExternalTransfer" view="externalRedirect:#{flowRequestContext.getActiveFlow().getApplicationContext().getBean('shibboleth.authn.ShibExternal.externalHandler')}&amp;forceAuthn=#{opensamlProfileRequestContext.getSubcontext(T(net.shibboleth.idp.authn.context.AuthenticationContext)).isForceAuthn()}&amp;isPassive=#{opensamlProfileRequestContext.getSubcontext(T(net.shibboleth.idp.authn.context.AuthenticationContext)).isPassive()}&amp;target=#{flowRequestContext.getActiveFlow().getApplicationContext().getBean('shibboleth.authn.ShibExternal.externalAuthServlet')}%3Fconversation=#{flowExecutionContext.getKey().toString()}">
        <on-render>
            <evaluate expression="opensamlProfileRequestContext.getSubcontext(T(net.shibboleth.idp.authn.context.AuthenticationContext)).getSubcontext(T(net.shibboleth.idp.authn.context.ExternalAuthenticationContext), true).setFlowExecutionUrl(flowExecutionUrl + '&amp;_eventId_proceed=1')" />
            <evaluate expression="externalContext.getNativeRequest().getSession().setAttribute('conversation' + flowExecutionContext.getKey().toString(), new net.shibboleth.idp.authn.impl.ExternalAuthenticationImpl(opensamlProfileRequestContext))" />
        </on-render>
        <transition to="ValidateShibExternalAuthentication" />
    </view-state>
    
</flow>

The example above assumes that you have a bean called shibboleth.authn.ShibExternal.externalAuthnPath configured in the file /opt/shibboleth-idp/conf/authn/shib-authn-config.xml. The bean must contain the desired Shibboleth SP Handler location and its parameters. Also, a bean called shibboleth.authn.ShibExternal.externalAuthServlet must be configured in the same file, and it must correspond to the location of the fi.csc.shibboleth.authn.impl.ShibbolethSpAuthnServlet servlet.

...
    <bean id="shibboleth.authn.ShibExternal.externalHandler" class="java.lang.String"
        c:_0="https://idp.example.org/override/Shibboleth.sso/Login?entityID=https://idp2.example.org/idp/shibboleth" />

    <bean id="shibboleth.authn.ShibExternal.externalAuthServlet" class="java.lang.String"
        c:_0="https://idp.example.org/idp/Authn/ShibExternal" />
...

The servlet is configured in the web.xml (usually /opt/shibboleth-idp/edit-webapp/WEB-INF/web.xml).

...
    <servlet>
        <servlet-name>ShibbolethSpAuthnServlet</servlet-name>
        <servlet-class>fi.csc.shibboleth.authn.impl.ShibbolethSpAuthnServlet</servlet-class>
        <load-on-startup>2</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>ShibbolethSpAuthnServlet</servlet-name>
        <url-pattern>/Authn/ShibExternal</url-pattern>
    </servlet-mapping>
...

It should be noted that with the authn/ShibExternal flow, it is possible to configure multiple flows with different SP Handler configurations, by using ApplicationOverride.

Finally, you will need to add the new authentication flow definition(s) to /opt/shibboleth-idp/conf/authn/general-authn.xml:

<bean id="authn/ShibExample" parent="shibboleth.AuthenticationFlow"
            p:nonBrowserSupported="false" p:forcedAuthenticationSupported="true"/>

The flow definition must also be enabled via idp.authn.flows variable in /opt/shibboleth-idp/conf/idp.properties.

Attribute resolution

The attributes provided by Shibboleth SP can be converted into IdP attributes in the following way:

1. Enable attribute idPAttribute and/or header population into Subject

In the /opt/shibboleth-idp/flows/authn/Shib/shib-beans.xml, enable populateAttributes and/or populateHeaders settings for the bean ValidateShibbolethAuthentication.

The example above enables population of both attributes and headers:

   <bean id="ValidateShibbolethAuthentication"
            class="fi.csc.shibboleth.authn.impl.ValidateShibbolethAuthentication" scope="prototype"
            p:classifiedMessages-ref="shibboleth.authn.Shib.ClassifiedMessageMap"
            p:resultCachingPredicate="#{getObject('shibboleth.authn.Shib.resultCachingPredicate')}"
            p:usernameAttribute="eppn" p:populateHeaders="true" p:populateAttributes="true"
            p:populateIdpAttributes="true" />

2. Enable principal serializers

In order to serialize the Shibboleth attributes and/or headers, the following serializers need to be added into shibboleth.PrincipalSerializers list in opt/shibboleth-idp/conf/global.xml:

  • fi.csc.shibboleth.authn.principal.impl.ShibAttributePrincipalSerializer for attributes
  • fi.csc.shibboleth.authn.principal.impl.ShibHeaderPrincipalSerializer for headers

The example below enables serialization of both attributes and headers. The other serializers are the same ones that are defined in the /opt/shibboleth-idp/system/conf/general-authn-system.xml (see bean shibboleth.DefaultPrincipalSerializers).

<bean id="shibboleth.PrincipalSerializers"
        class="org.springframework.beans.factory.config.ListFactoryBean">
    <property name="sourceList">
        <list>
            <bean class="fi.csc.shibboleth.authn.principal.impl.ShibAttributePrincipalSerializer" />
            <bean class="fi.csc.shibboleth.authn.principal.impl.ShibHeaderPrincipalSerializer" />
            <bean class="net.shibboleth.idp.authn.principal.impl.UsernamePrincipalSerializer" />
            <bean class="net.shibboleth.idp.authn.principal.impl.LDAPPrincipalSerializer" />
            <bean class="net.shibboleth.idp.authn.duo.impl.DuoPrincipalSerializer" />
            <bean class="net.shibboleth.idp.authn.principal.impl.IdPAttributePrincipalSerializer" />
            <bean class="net.shibboleth.idp.authn.principal.impl.PasswordPrincipalSerializer"
                p:dataSealer="#{(systemProperties.contains('idp.sealer.storeResource') or systemProperties.contains('idp.sealer.keyStrategy')) ? getObject('shibboleth.DataSealer') : null}" />
            <bean class="net.shibboleth.idp.authn.principal.impl.ProxyAuthenticationPrincipalSerializer" />
            <bean class="net.shibboleth.idp.saml.authn.principal.impl.NameIDPrincipalSerializer" depends-on="shibboleth.OpenSAMLConfig" />
        </list>
     </property>
</bean>

3. Modify attribute-resolver.xml

After enabling the serializers, you're able to read the corresponding principals' contents in the attribute resolver. See the example below for reading the information for eppn attribute. The same logic should work for other attributes too.

    <resolver:AttributeDefinition id="eppn" xsi:type="ad:Script">
        <AttributeEncoder xsi:type="SAML2ScopedString" name="urn:oid:1.3.6.1.4.1.5923.1.1.1.6"
            friendlyName="eduPersonPrincipalName" encodeType="false" />
        <ad:Script><![CDATA[
          authnContext = resolutionContext.getParent().getSubcontext("net.shibboleth.idp.authn.context.AuthenticationContext");
          subject = authnContext.getAuthenticationResult().getSubject();
          principals = subject.getPrincipals(Java.type("fi.csc.shibboleth.authn.principal.impl.ShibAttributePrincipal").class);
          iterator = principals.iterator();
          while (iterator.hasNext()) {
              principal = iterator.next();
              if ("eppn".equals(principal.getType())) {
                  eppn.addValue(principal.getValue());
              }
          }
	]]></ad:Script>

The filtering of attributes is configured in the similar way as for any Shibboleth IdP attributes in attribute-filter.xml.