Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migrated to Confluence 5.3

Overview

This document is targeted towards developers writing a custom portlet that will support Shibboleth Delegated Authentication. For an "out of the box" solution look at using the Web Proxy Portlet with Delegated SAML Authentication

The library was envisioned as a utility that makes the use of delegated SAML authentication easy for portlet developers. SAML designers realized that the authentication, attribute release, and federation of authentication services is very complex. That's why they have promoted the use of authentication and attribute release as "layers" removed from the Web application developers and administered by dedicated security experts.

The goal of this library is to make delegated SAML authentication, where the portal acts as a proxy on behalf of the user, just as easy for the portlet developers.

The reader of this document should be familiar with concepts outside the scope of this library. Unfortunately, this means a lot of concepts, including:

  • Public key cryptography
  • Digital certificates
  • Digital signatures
  • TLS/SSL
  • Web services
  • All standards behind portals
    • Enterprise Java
    • Web filters
    • Portlets

The reader does not have to be an expert in any of these but should understand the concepts. The writer is not an expert in most of these, either.

Design

Background

There are myriad of ways that developers can write portlets. From the low-level servlet-like programming to rich Model-View-Controller (MVC) frameworks, portal developers can choose complexity vs. efficiency or performance vs. size to sample just a few of the choices available to portlet developers.

The overriding design principle of this project is to avoid restricting these choices in any way. For example, the library supports logging, but at the same time allows developer to choose a logging framework to use.

While the library is designed for use with portlets, it should be usable from non-portlet code, provided that a method of obtaining a SAML assertion and optional IdP public key is devised. The only method that works with this library is for portlets running in uPortal.

At the same time, it is important to recognize the value of existing open source frameworks and avoid "reinventing the wheel." For functionality not available with standard Java distributions and portal servers, the designers of this library have turned to existing projects, like Apache Commons HTTP Client or OpenSAML for Java.

Recognizing that portlet developers may use this library to access a Web service, from RESTful, through SOAP to simple HTML, the design of the library concentrated on performing authentication and then relaying on the underlying transport to maintain a secure session with the Web Service Provider (WSP). Apache Commons HTTP Client supports "stateful" Web sessions much like browsers do, and the library makes such a session available to the portlet and steps out of the way.

XML Processing

SAML messages are complex XML documents using a multitude of namespaces. The library discussed here is mostly a proxy passing these messages to work within the SAML 2 Enhanced Client or Proxy (ECP) profile. To keep the XML processing efficient and keeping in mind the need to preserve the SAML assertions' integrity, the XML processing in the library relies on DOM and XPath. SAML assertions are digitally signed and even the most innocuous changes would break the assertions' digital signatures.

Generally speaking, the library processes an XML message like so:

  1. breaks it down into a DOM tree
  2. examines it for validity, if applicable
  3. retrieves and removes information targeted at the ECP
  4. adds information that the recipient may require from the ECP
  5. serializes the message for transmission

The DOM tree of each processed message is not kept. The only exception is the SAML assertion DOM because it is needed in several places.

Delegated SAML Authentication

To accomplish delegated SAML authentication, there are several interactions that all the involved players (user, IdP, portal, portal's SP, portlet, WSP) must perform. These interactions are best illustrated in this diagram from the Shibboleth Web site.
Image Removed

What the above footnotes do not distinguish is which component is responsible for which of these interactions.

Steps 1-4 are initiated by the user, and partly automated by the portal's SP and the IdP via HTTP redirection. The user has to provide credentials to the IdP, unless a valid Single Sign-On (SSO) session was previously established.

Steps 5-8 are performed by the portal and portlet container. A uPortal-compatible library, documented elsewhere, handles these interactions. When step 8 is done, the portlet is ready to request SAML-protected resources from WSP.

Steps 9-14 are initiated by the portlet, but most of the work is performed on portlet's behalf in the library documented here.

Security

The security of the SAML assertion is protected by all involved parties. Its integrity is protected with digital signatures, and the IdP signs the assertion using its private key. The portal's SP and the WSP examine the digital signature when receiving the assertion. The library retrieves elements from the SAML assertion, but it cannot modify anything within the assertion, as that would invalidate the assertion's digital signature. The portlet should treat the assertion the same way. It may be authorized to examine the contents of the assertion, but it may not modify it in any way.

The library's responsibility to protect SAML assertion consists of various levels of assurance that the servers it deals with, IdP and the WSP, are trusted. The library supports various methods of authenticating the servers it communicates with using private/public keys and X.509 certificates.

Both communications with the IdP and with the WSP can use client TLS certificates to authenticate the client, in this case the library/portlet, to the server. When authenticating this way to the IdP the portlet must use the certificate and private key belonging to the portal's SP because, as far as the IdP is concerned, they are one and the same. The library supports PEM-encoded private key and certificate (commonly used with OpenSSL) or a Java standard KeyStore. Those files must me accessible to the process running the portal and the portlet.

The library also supports server authentication. Either the IdP or the WSP can be authenticated using a TrustStore, which is basically a standard Java KeyStore containing server certificates to trust. Obviously, different TrustStores can be used for IdP and WSP authentication. The portal's SP may also convey the IdP public key to the portal and the portlet. This public key may be used to authenticate the IdP, and in this case the library does not examine the IdP's certificate(s) but only the public keys within the IdP's X.509 certificate(s).

Installation

Overview

This library uses Maven for building and installation. A portlet developer in a need to retrieve resources from a SAML-protected Web Service will need to perform two steps to take advantage of this library:

  1. Build the library from sources to make it available as a local Maven resource
  2. Add the library to the developer's portlet project, presumably a Maven project itself

Building the Library

The library is built using Maven. In the future the library may be published to a public Maven repository, and this step will not be necessary. As of this writing, however, this is not available.

To build the library, a developer needs to run the following Maven command:

Code Block

mvn install

This command compiles the code, assembles the binaries into a jar file, and installs it in a local Maven repository.

Since this library has a programming API, it may be desirable to generate Javadoc documentation. This is accomplished with the following command:

Code Block

mvn javadoc:javadoc

Adding the Library to a Portlet Project

To add this library to a Maven portlet project, a developer must add the following dependency to the portlet's pom.xml file:

...


<dependency>
  <groupId>org.jasig.service</groupId>
  <artifactId>delegated-saml-authentication</artifactId>
  <version>1.0</version>
</dependency> 

The names used in this dependency element match the names used in the library's own pom.xml and this matching is what allows Maven to locate the library in the local Maven repository.

Usage

Introduction

The reason this library exists is to facilitate delegated SAML authentication for portlets that act on behalf of the logged on user. It is not the library's responsibility, nor was it its requirement, to retrieve any resources from the SAML-protected WSP. The library uses Apache HttpClient component. The only difference from using HttpClient directly is that instead of creating an instance of HttpClient, a developer obtains it from the library. The initial testing of this library was performed with only HTTP GET as the initial request to retrieve a protected resource. This is because that was the only method that would handle SAML authentication "as-needed." Support to preserve HTTP POST form post elements was recently added to the Shibboleth SP, but this will need to be tested in the future.

There are some things to keep in mind about this:

  • This resource in the initial HTTP GET can exist only for the purpose of completing authentication, and it could be ignored by the portlet.
  • Upon successful authentication, the portlet may use the Apache Commons HTTP Client API and will not be limited to HTTP GET.

Simple Example

With SAML assertion and resource URL available, the portlet is ready to start a delegated SAML authentication session. It first creates a new copy of the SAMLSession class and passes the SAML assertion as the constructor parameter. From the instance of SAMLSession, the portlet obtains an instance of HttpClient and uses the API of the Apache HttpClient from then on.

The delegated SAML authentication business logic is encapsulated in a class called SAMLDelegatedAuthenticationService. There is no need to make an explicit instance of this class, as it is created internally to the library. Here is an illustration of the steps required to retrieve a protected resource using HTTP GET:

...


String samlAssertion = getAssertion();   // Example only.  Portlet obtains a String containing SAML assertion
samlSession = new SAMLSession(samlAssertion);
samlSession.setPortalEntityID(portalEntityID);   // Set the portal's entityID.  The library must provide it to the IdP.
Resource resource = new Resource ();  // Simple example class representing a protected resource
resource.setResourceUrl(url);   // The url variable here is a String initialized elsewhere
getResource(samlSession, resource);  // Portlet's local method detailed below

The following is an example of a getResource method that a portlet may use:

...

delegated-saml-authentication project is a library that handles most of the complexities around delegated Shibboleth authentication for the portlet developer. The library provides a specially configured HttpClient from Commomns Http-Components 4.x that can be used to make requests to a remote service that will automatically use delegated authentication.

You must complete Configuring uPortal to pass the SAML Assertion before any of these steps will be useful in a portlet.

Also a review of the Shibboleth Portal documentation is recommended for an understanding of how the process works under the hood.

Project Configuration

Step 1 - Configuring User Attributes

Portlets must declare names of all person attributes they will need to access, this is mandated by the portlet specification. Portlets list the names of the attributes they will need at runtime in their deployment descriptor, which is the file called portlet.xml. Below is a fragment of the portlet deployment descriptor configured to require SAML assertions and IdP public keys. Even though there may be multiple IdP public keys present, they will appear to the portlet as a single encoded string. The string is not encrypted. It's only Base64-encoded to be compliant with HTTP specifications.

Code Block
xml
xml

<portlet-app xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd"
	version="1.0">

  <portlet>
  <!-- ... -->
  </portlet>

  <user-attribute>
    <description>SAML Assertion</description>
    <name>samlAssertion</name>
  </user-attribute>

  <user-attribute>
    <description>IdP Public Keys</description>
    <name>idpPublicKeys</name>
  </user-attribute>

</portlet-app>

Step 2 - Configuring Project Dependencies

If the portlet is using Maven for build management add the following in the pom.xml

Code Block
xml
xml

<dependency>
    <groupId>org.jasig.service</groupId>
    <artifactId>delegated-saml-authentication</artifactId>
    <version>1.1.0</version>
</dependency> 

If the portlet is not using Maven the JAR can be downloaded from the Maven repository manually: http://repo1.maven.org/maven2/org/jasig/service/delegated-saml-authentication/1.1.0/

Usage

Introduction

The reason this library exists is to facilitate delegated SAML authentication for portlets that act on behalf of the logged on user. It is not the library's responsibility, nor was it its requirement, to retrieve any resources from the SAML-protected WSP. The library uses Apache HttpClient component. The only difference from using HttpClient directly is that instead of creating an instance of HttpClient, a developer obtains it from the library. The initial testing of this library was performed with only HTTP GET as the initial request to retrieve a protected resource. This is because that was the only method that would handle SAML authentication "as-needed." Support to preserve HTTP POST form post elements was recently added to the Shibboleth SP, but this will need to be tested in the future.

There are some things to keep in mind about this:

  • This resource in the initial HTTP GET can exist only for the purpose of completing authentication, and it could be ignored by the portlet.
  • Upon successful authentication, the portlet may use the Apache Commons HTTP Client API and will not be limited to HTTP GET.

Simple Example

With SAML assertion and resource URL available, the portlet is ready to start a delegated SAML authentication session. It first creates a new copy of the SAMLSession class and passes the SAML assertion as the constructor parameter. From the instance of SAMLSession, the portlet obtains an instance of HttpClient and uses the API of the Apache HttpClient from then on.

The delegated SAML authentication business logic is encapsulated in a class called SAMLDelegatedAuthenticationService. There is no need to make an explicit instance of this class, as it is created internally to the library. Here is an illustration of the steps required to retrieve a protected resource using HTTP GET:

Code Block
java
java

protected String getAssertion(PortletRequest request) {
    Map userInfo = (Map) request.getAttribute(PortletRequest.USER_INFO);
    String samlAssertion = (String) userInfo.get("samlAssertion");
    return samlAssertion;
}

protected String getIdPPublicKeys(PortletRequest request) {
    Map userInfo = (Map) request.getAttribute(PortletRequest.USER_INFO);
    String idpPublicKeys = (String) userInfo.get("idpPublicKeys");
    return idpPublicKeys;
}

//Gets or creates the Shibbolized HttpClient, caching the created version in the user's session
protected HttpClient getHttpClient(PortletRequest request) {
    PortletSession portletSession = request.getPortletSession();
    HttpClient client = (HttpClient)portletSession.getAttribute(HTTP_CLIENT_ATTR);
    if (client != null) {
        return client;
    }

    String samlAssertion = getAssertion(request);

    //Configure the connection manager that should be used by the delegated auth integration code
    HttpParams params = new BasicHttpParams();
    ClientConnectionManager clientConnectionManager = this.createClientConnectionManager(request, params);

    //Create the SAMLSession, providing it the assertion and the HttpClient configuration
    SAMLSession samlSession = new SAMLSession(samlAssertion, clientConnectionManager, params);
    samlSession.setPortalEntityID(portalEntityID);   // Set the portal's entityID.  The library must provide it to the IdP.

    //If the SP private key and cert are configured provide them to the SAMLSession
    if (spPrivateKey != null && spCertificate != null) {
        samlSession.setIdPClientPrivateKeyAndCert(spPrivateKey, spCertificate);
    }

    //If the IdP's public keys were provided pass them on to the SAMLSession
    String idpPublicKeys = getIdPPublicKeys(request);
    if (idpPublicKeys != null) {
        samlSession.setIdPServerPublicKeys(idpPublicKeys);
    }

    client = samlSession.getHttpClient();
    portletSession.setAttribute(HTTP_CLIENT_ATTR, client);
    return client;
}

The following is an example of using the Shibbolized HttpClient

Code Block
java
java

private void getResource(PortletRequest request) {
    String url = determineUrl(request); //portlet specific logic to figure out where to make the request to+

    HttpClient client = getHttpClient(request);
    HttpGet method = new HttpGet(resource.getResourceUrl());
  
    // There is no need to check the HTTP response status because the HTTP
    // client will handle normal HTTP protocol flow, including redirects
    // In case of error, HTTP client will throw an exception
    ResponseHandler<String> responseHandler = new BasicResponseHandler();
    String response;
    try {
       response = client.execute(method, responseHandler);
      resource.setResource(response); //Do Something Portlet Specific with the response
    }
    catch (Exception ex) {  // ClientProtocolException or IOException
        log.error("Exception while trying to retrieve the resource.", ex);
    }
    finally {
        //Cleanup after the HttpClient request
        response.getEntity().consumeContent();
    }
}

In this simplest of examples, assuming that SAML protects a RESTful Web Service, at the end of this sequence the portlet may call resource. getResource(), which will return a String representation of what came back from the WSP. Since the data encapsulating classes are stateful, SAMLSession and Resource should not be considered thread-safe.

...

The SAML assertion issued to the portal, the same assertion that the portlet uses as a constructor parameter when creating an instance of the SAMLSession class, contains an Issuer element. This identifies the IdP that issued the assertion. The IdP is identified by what's referred to as entityID. This may sometimes look like a URL, but it does not have to be. A fully-qualified URL is required by the library to obtain a delegated SAML assertion from the IdP and present to the WSP. In order to make this flexible, the library uses a "pluggable" resolver. The resolver wirks works with SAMLSession and an instance of the DelegatedSAMLAuthenticationState class, which is populated with the IdP's entityID. The IdP resolver will resolve the specific URL, or "endpoint." On successful resolution, the resolver places the IdP endpoint into the DelegatedSAMLAuthenticationState class. The resolver needs to implement a simple interface IdPEPRResolver:

...