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.

IdP Resolution

While the example above shows how the WSP is identified with the resource URL, it would be a valid question to ask how the library locates the IdP. After all, only an IdP may issue an assertion that is suitable for the WSP. While it would be possible to require that the portlet identify the IdP, it should not be necessary. Not in all cases, anyway.

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:

...

"EPR" in IdPEPRResolver stands for End Point Reference. The implementation of this interface delivered with this library assumes that Shibboleth IdP was used to issue the SAML assertion, and Shibboleth IdP includes an EPR in the assertions it issues. When working with other SAML IdP, portlet developers should write their own implementation of the above interface and set it with a authnState.setIdpResolver method call. SAMLSession defaults to using the included implementation, so at installations using Shibboleth IdP there is no need to instantiate or sent the IdP resolver in the SAMLSession object.

Reusing SAMLSession

SAMLSession encapsulates a great deal of properties. Most of them may not even be useful to portlet developers, but many are. One of the properties is the HttpClient instance that can be reused for "stateful" communications with the WSP. HttpClient from Apache Commons has the default behavior like that of a browser. This means that once a client is set up, it will continue operating in a "stateful" way, including sending cookies that were previously set by the server. These cookies may represent the SAML session and some sort of WSP-specific session. Maintaining these sessions is critical to efficiently communicating with the WSP and not performing authentications unnecessarily.

To reuse the stateful HttpClient, a portlet developer needs to make sure to use the samlSession.getHttpClient method.

Authenticating the IdP

While the library does not enforce it, communicating with the IdP should always be performed over a secure connection, most commonly utilizing TLS. In addition to encrypting all of the information between the IdP and its client, TLS also supports methods of authenticating the server. This can go a long way to assuring that there client, in this case the delegated SAML authentication library, is not a victim of a man-in-the-middle attack. The library supports two different methods of authenticating the IdP:

...

At this time the library only supports a single public key to trust. Internally, the library decodes the key, retrieves all the X.509 certificates presented by the IdP, and looks for a matching public key in one of those certificates. A secure connection to the IdP will only be allowed if a matching public key is found.

Authenticating to the IdP

Just as the IdP client needs to authenticate the IdP it is about to communicate with, the IdP may want to restrict the clients authorized to obtain delegated SAML authentication. This is supported by the library, and it also uses TLS as the means of IdP client authentication. The IdP authenticates its clients by examining the client X.509 certificate. By presenting to the TLS/SSL layer a certificate and private key, the client's certificate with the public key is presented to the IdP. Since, as far as the IdP is concerned, the library/portlet/portal are synonymous with their corresponding SP, the library must use the SP's certificate and private key. There are two methods by which a portlet developer may convey the private key and certificate to the TLS layer:

...

The library will use the keys and/or certificates in its communications with the IdP.

Authenticating the WSP

Just as with the IdP, communications with the WSP may need to be secured using TLS. The WSP will present its certificate to the client, and the client must trust the certificate to allow the connection to proceed. The default behavior is to trust servers whose X.509 certificates have been signed by a recognized CA. Institutions wishing to issue their own certificates, either signed by their own CA or even self-signed certificates, may override the default behavior of Java and Apache HTTP Client. This can be done by providing an alternate TrustStore for communications with the WSP. The following call specifies the TrustStore to use:

...

where trustStore is a file name of a Java KeyStore containing certificates to trust and trustStorePass is the password that was used to secure the KeyStore. The TrustStore may contain certificates of CA, if the WSP has a CA-signed certificate or a certificate of the WSP, if the WSP uses a self-signed certificate. Please note that with this authentication the use of the Resource class becomes a requirement. The Resource class encapsulates the state specific to the WSP, while SAMLSession's state is IdP-specific.

Authenticating to the WSP

As with the IdP, the WSP may require a client TLS certificate to authenticate and trust its clients. The library supports that with TLS client certificates. This is virtually identical to the TLS client certificates that can be presented to the IdP, so rather than repeat that section verbosely, here are the two different method calls to convey the client certificate location to the library:

...

Code Block
java
java
resource = new Resource();
resource.setResourceUrl(url);  //obtained elsewhere
resource.setWSPClientPrivateKeyAndCert (pkFile, certFile);  //one of the methods to authenticate the client to the WSP
resource.setupWSPClientConnection(samlSession);  // This looks for "https" in the Resource URL and the optional port number to use
HttpClient client = samlSession.getHttpClient();

Error Handling

This library acts as a part of security infrastructure. It normally works "behind the scenes" as a communication conduit between the portal, portlet, WSP, and IdP. The errors encountered and produced by this library will virtually always be of "infrastructural" nature, and neither the portlet nor portlet's user will be able to do much to correct them. For example, the portlet may be misconfigured, the IdP may be down, or the portal may keep its session active even after the SAML session has expired. Because of this, the portlet sets all of its error conditions using a runtime exception, DelegatedAuthenticationRuntimeException. This means that no explicit error handling is required to handle this exception. The portlet container will catch these exceptions and deal with them in its own standard manner.

...