Setting up Java Fedlet with Shibboleth IdP

The Java Fedlet is basically a lightweight SAML Service Provider (SP) implementation that can be used to add SAML support to existing Java EE applications. Today we are going to try to set up the fedlet sample application with a Shibboleth IdP (available at testshib.org).

Preparing the fedlet

There is two kind of Java fedlet in general: configured and unconfigured. The configured fedlet is what you can generate on the OpenAM admin console, and that will basically preconfigure the fedlet to use the hosted OpenAM IdP instance, and it will also set up the necessary SP settings. The unconfigured fedlet on the other hand is more like starting from scratch (as the name itself suggests :) ) and you have to perform all the configuration steps manually. To simplify things, today we are going to use the configured fedlet for our little demo.

To get a configured fedlet first you have to install OpenAM of course. Once you have an OpenAM set up, Create a new dummy Hosted IdP (to generate a fedlet it is currently required to also have a hosted IdP):

  • On the Common Tasks page click on Create Hosted Identity Provider.
  • Leave the entity ID as the default value.
  • For the name of the New Circle Of Trust enter cot.
  • Click on the Configure button.

Now to generate the configured fedlet let’s go back to the Common Tasks page and click on Create Fedlet option.

  • Here you can set the Name to any arbitrary string, this will be the fedlet’s entity ID. For the sake of simplicity let’s use the fedlet’s URL as entity ID, e.g., http://fedlet.example.com:18080/fedlet.
  • The Destination URL of the Service Provider which will include the Fedlet setting on the other hand needs to be the exact URL of the fedlet, so for me this is just a matter of copy paste: http://fedlet.example.com:18080/fedlet.
  • Click on the Create button.

This will generate a fedlet that should be available under the OpenAM configuration directory (in my case, it was under ~/openam/myfedlets/httpfedletexamplecom18080fedlet/Fedlet.zip), let’s grab this file and unzip it to a convenient location. Now we need to edit the contents of the fedlet.war itself and modify the contents of the files under the conf folder. As a first step open sp.xml and remove the RoleDescriptor and XACMLAuthzDecisionQueryDescriptor elements from the end of the XML.

At this point in time, we have everything we need for REGISTERing our fedlet on the testshib.org site, so let’s head there and upload our metadata (sp.xml), but in order to prevent clashes with other entity configurations, we should rename the sp.xml file to something more unique first, like fedlet.example.com.xml.

After successful registration the next step is to grab the testshib IdP metadata and add it to the fedlet as idp.xml, but there are some small changes we need to make on the metadata, to make it actually work with the fedlet:

  • Remove the EntitiesDescriptor wrapping element, but make sure you copy the xmlns* attributes to the EntityDescriptor element.
  • Since now the XML has two EntityDescriptor root elements, you should only keep the one made for the IdP (i.e. the one that has the “https://idp.testshib.org/idp/shibboleth” entityID), and remove the other.

The next step now is that we need to update the idp-extended.xml file by replacing the entityID attribute’s value in the EntityConfig element to the actual entity ID of the testshib instance, which should be https://idp.testshib.org/idp/shibboleth.

After all of this we should have all the standard and extended metadata files sorted, so the last thing to sort out is to set up the Circle Of Trust between the remote IdP and the fedlet. To do that we need to edit the fedlet.cot file and update the sun-fm-trusted-providers property to have the correct IdP entity ID:

cot-name=cot
sun-fm-cot-status=Active
sun-fm-trusted-providers=https://idp.testshib.org/idp/shibboleth,http://fedlet.example.com:18080/fedlet
sun-fm-saml2-readerservice-url=
sun-fm-saml2-writerservice-url=

It’s time to start the testing now, so let’s repackage the WAR (so it has all the updated configuration files) and deploy it to an actual web container. After deploying the WAR, let’s access it at http://fedlet.example.com:18080/fedlet. Since there is no fedlet home directory yet, the fedlet suggests to click on a link to create one based on the configuration in the WAR file, so let’s click on it and hope for the best. :)

Testing the fedlet

If we did everything correctly, we end up on a page finally where there are some details about the fedlet configuration, and there are also some links to initiate the authentication process. As a test let’s click on the Run Fedlet (SP) initiated Single Sign-On using HTTP POST binding one, and now we should be facing the testshib login page where you can provide one of the suggested credentials. After performing the login an error message is shown at the fedlet saying Invalid Status code in Response. After investigating a bit further, the debug logs tells us what’s going on under ~/fedlet/debug/libSAML2:

SPACSUtils.getResponse: got response=<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" ID="_0b33f19185348a26fffe9c3a1aa6e652" InResponseTo="s2be040cf929456a64f444527dfc1d7413ce178531" Version="2.0" IssueInstant="2013-12-04T18:39:32Z" Destination="http://agent.sch.bme.hu:18080/fedlet/fedletapplication"><saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">https://idp.testshib.org/idp/shibboleth</saml:Issuer><samlp:Status xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol">
<samlp:StatusCode xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
Value="urn:oasis:names:tc:SAML:2.0:status:Responder">
</samlp:StatusCode>
<samlp:StatusMessage xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol">
Unable to encrypt assertion
</samlp:StatusMessage>
</samlp:Status></samlp:Response>

Now we can also look into the testshib logs (see TEST tab), and that will tell us what was the real problem:

Could not resolve a key encryption credential for peer entity: http://fedlet.example.com:18080/fedlet

So this just tells us that the Shibboleth IdP tries to generate an encrypted assertion for our fedlet instance, however it fails to do so, because it is unable to determine the public certificate for the fedlet. This is happening because the basic fedlet metadata does not include a certificate by default, to remedy this let’s do the followings:

  • Acquire the PEM encoded certificate for the default OpenAM certificate:
    $ keytool -exportcert -keystore ~/openam/openam/keystore.jks -alias test -file openam.crt -rfc
  • Drop the BEGIN and the END CERTIFICATE lines from the cert, so you only have the PEM encoded data, and then you’ll need to add some extra XML around it to look something like this:
    <KeyDescriptor>
    <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
    <ds:X509Data>
    <ds:X509Certificate>
    MIICQDCC...
    </ds:X509Certificate>
    </ds:X509Data>
    </ds:KeyInfo>
    </KeyDescriptor>
    
  • Add the KeyDescriptor under the SPSSODescriptor as a first element.
  • Upload the updated SP metadata with the same filename (fedlet.example.com.xml) again at the testshib site.

Since the decryption process requires the presence of the private key of the certificate, we need to ensure that the private key is available, so let’s do the followings:

  • Copy the ~/openam/openam/keystore.jks file to the fedlet home directory
  • Visit the http://fedlet.example.com:18080/fedlet/fedletEncode.jsp page and enter changeit (the password of the default keystore and private key as well).
  • Grab the encrypted value and create ~/fedlet/.storepass and ~/fedlet/.keypass files containing only the encrypted password.
  • Open up ~/fedlet/sp-extended.xml and ensure that the encryptionCertAlias setting has the value of test.
  • Restart the container, so the changes are picked up.

At this stage we should retry the login process again, and if nothing went wrong you can see all the nice details of the received SAML assertion from testshib.org! It Works! :)

Federation with Shibboleth SP (Apache module)

When you want to achieve Federation between different organizations, you often find yourself in a situation where the products used by the parties are different. Before we go any further I should make the terminology clear:
Identity Provider (IdP): The IdP holds all information about the user (for example in LDAP), and also it is the IdP’s job to authenticate the users, and decide on what kind of informations it shares about the users with other providers.
Service Provider (SP): The easiest way to think about the SP as an extra layer in front of the webapplication. The SP’s job is to authorize pagerequests, and if there is no authenticated session at the SP, initiate an authentication request to the IdP.

Today the goal is to achieve SSO between an OpenAM IdP and a Shibboleth SP with the simplest settings as possible.
This tutorial assumes that you already have a configured OpenAM instance running under idp.example.com.

Configure the IdP

  • Log in to the admin console and on the Common Tasks pane click on the Create Hosted Identity Provider link.
  • Select No for Do you have metadata for this provider
  • Use http://idp.example.com:8080/openam as Name
  • Select the default test Signing Key
  • Use cot as the name of the New Circle of Trust
  • Leave the Attribute mapping table empty
  • Press the Configure then the Finish button

Install and configure Shibboleth SP (on Ubuntu)

This tutorial was done with Ubuntu 11.04. If you have other OS/version it’s possible that the paths/steps will be different for you.

  • Install the Shibboleth SP Apache module:
    apt-get install libapache2-mod-shib2
  • Open the /etc/shibboleth/shibboleth2.xml configuration file using a text editor
  • In SPConfig -> InProcess -> ISAPI -> change the Site tag to:
    <Site id="1" name="sp.example.com"/>
    
  • In SPConfig -> RequestMapper -> RequestMap change the Host tag to:
    <Host name="sp.example.com" applicationId="sp.example.com" />
    
  • In SPConfig change the ApplicationDefaults opening tag to:
    <ApplicationDefaults id="default" policyId="default" 
    entityID="http://sp.example.com/shibboleth" 
    REMOTE_USER="eppn persistent-id targeted-id" 
    signing="false" encryption="false">
    
  • In SPConfig -> ApplicationDefaults -> Sessions change the /Login SessionInitiator‘s opening tag to:
    <SessionInitiator type="Chaining" Location="/Login" 
    isDefault="true" id="Intranet" relayState="cookie" 
    entityID="http://idp.example.com:8080/openam">
    
  • In SPConfig -> ApplicationDefaults -> MetadataProvider create a MetadataProvider:
    <MetadataProvider type="XML" file="idp.xml" />
    
  • In SPConfig -> ApplicationDefaults create an ApplicationOverride:
    <ApplicationOverride id="sp.example.com" 
    entityID="http://sp.example.com/shibboleth" /> 
  • Save the configuration file
  • Make sure that the files referred in SPConfig -> ApplicationDefaults -> CredentialResolver actually exist, and if necessary, generate a self signed certificate (using this guide for example).
  • Open http://idp.example.com:8080/openam/saml2/jsp/exportmetadata.jsp in your browser and save the XML as /etc/shibboleth/idp.xml (as configured in the MetadataProvider tag). (See this post for more information about exporting metadata.)
  • Open the /etc/shibboleth/attribute-map.xml config file and add the following line:
    <Attribute name="urn:oid:0.9.2342.19200300.100.1.1" id="HTTP_UID"/>
    

Prepare the Apache configuration

You need to configure Apache as well to make this setup work:

  • Make sure, that there is an sp.example.com VirtualHost in your Apache config
  • If you want to use .htaccess you need to enable the AllowOverride AuthConfig in the VirtualHost config
  • Create an index.php file in your docroot containing:
    <?php
    phpinfo();
    ?>
    
  • Create a .htaccess file in your docroot containing:
    AuthType shibboleth
    ShibRequireSession On
    require shibboleth
    
  • Enable the Apache Shibboleth module:
    a2enmod shib2
    
  • Restart Apache:
    /etc/init.d/apache2 restart
    

Registering the SP at the IdP

If you’ve done everything right so far, then you can access your Shibboleth SP Metadata at http://sp.example.com/Shibboleth.sso/Metadata. In case the Metadata does not contain a certificate check the logs at /var/log/shibboleth/shibd.log, also please remember that whenever you change the Shibboleth config you need to restart the Shibboleth service:

/etc/init.d/shibd restart

NOTE: OPENAM-792 can cause you troubles while importing the metadata. Make sure you either have the fix for this issue, or you have removed the Extensions tag from the Metadata before uploading it.

If everything is OK with your Metadata open the OpenAM admin console and click on the Register Remote Service Provider link on the Common Tasks pane.

  • You can either upload the Metadata or provide the URL on the config page.
  • Leave the attribute mapping empty
  • Click on the Configure button
  • Go to the Federation page then open the SP’s page in the Entity Providers table
  • Go to the Assertion Processing tab
  • In the Attribute Map list add the following value:
    urn:oasis:names:tc:SAML:2.0:attrname-format:uri|
    urn:oid:0.9.2342.19200300.100.1.1=uid
    
  • Save the configuration and log out

How to test

You just need to open a random URL under sp.example.com, and the htaccess config you created will make sure that the user is authenticated at the IdP. Opening such URLs should result in a redirect to the IdP presenting a login screen for you. After submitting the valid credentials you should be redirected back to the SP application to the originally requested URL. On the phpinfo page you should see the HTTP_UID server variable holding the user’s name.

In case you want to use the REMOTE_USER CGI variable in your applications, you can achieve that by modifying the Shibboleth configuration: in SPConfig -> ApplicationDefaults add HTTP_UID to the beginning of the REMOTE_USER attribute.