Web Services Single Sign On with Shibboleth
Shibboleth is a Single Sign On solution for web resources, developed by the internet2 syndicate. Because Shibboleth is based on HTTP and the Security Assertion Markup Language in short SAML, it is also an interesting alternative for commercial IT usage. This article describes a simple authenticator for Axis2 enabling the use of Shibboleth also for web services single sign on.
Shibboleth offers two profiles for authentication. The Browser/Artifact profile simplifies the implementation of clients, and as far as our experience goes it is more suitable for web services than the Browser/Post profile. Therefore the solution introduced here is based on the Browser/Artifact profile. To use the Authenticator for Web Services, a Shibboleth configuration with Identity- and service provider is necessary. Detailed informations about configuring Shibboleth can be found at the Shibboleth Wiki.
How to prepare an Axis2 Web-Application for SSO
This chapter describes how the Axis2 web application can be integrated into a service provider. The service provider is based on the Apache HTTPD server. The configuration can be made in the central configuration file httpd.conf.
In our example the Axis2 web application is running in the Tomcat web container. Therefore, in the following lines we connect the Apache web server with Tomcat via the JK-Connector. Detailed information of how to configure the connector can be found in the Tomcat Connector documentation.
LoadModule jk_module modules\mod_jk-apache-2.2.4.so
JkWorkersFile D:\java\apache-tomcat-6.0.14\conf\jk\workers.properties
JkMount /axis2/* ajp13
The Apache web server turns to the Shibboleth Service Provider, by loading the module mod_shib. The Service Provider can be configured via the file shibboleth.xml.
LoadModule mod_shib C:\opt\shibboleth-sp\libexec\mod_shib_22.so
ShibSchemaDir C:/opt/shibboleth-sp/share/xml/shibboleth
ShibConfig C:/opt/shibboleth-sp/etc/shibboleth/shibboleth.xml
The last step is the protection of the Axis2 web application using Shibboleth. To gain access to the web application, clients have to authenticate themselves via Shibboleth and to identify them as valid users.
<Location /axis2> AuthType shibboleth ShibRequireSession On require valid-user </Location>
On the server side a web application with web services differs in nothing from common web applications. The configuration matches the configuration of any other JEE web application.
Authentication of Web Service Client
The authentication with Shibboleth is based on client sided HTTP redirects. If a client tries to access a protected resource, the call will be redirected to an Identity Provider, in short IdP. Then the client has to authenticate itself to the IdP. This can be made with any authentication mechanism. In our example we have used the BASIC HTTP Authentication with username and password. After a successful authentication the client is redirected once more to the protected resource. With the renewed inquiry to the service provider, in short SP, the client transmits an artifact-id which the SP uses to request information about the client at the IdP. This method is working with human clients which are not surprised from a suddenly appearing Login and which can cope with it. Unfortunately, the client libraries of the web services implementations dont know how to manage the login and the redirect.
Shibboleth Authenticator for Web Service Clients
To authenticate clients with Shibboleth we have developed a simple authenticator that can manage a redirect to the IdP with subsequent authentication. After the authentication a cookie will be send to the web service client. After that the client can connect to the service as usual.
Authentication with the Authenticator
Listing 1 shows a client that authenticates to Shibboleth installation with the Axis2BrowserArtifactAuthenticator. The web service we used here, determines the financial institute to a bank code number. The only change towards a client without authentication is the line in boldface. The port object with the name stub as well as username and password will be given to the authenticator. Per Web Service only one authentication is necessary, no matter how many operations are called
BLZServiceStub stub=new BLZServiceStub(); Axis2BrowserArtifactAuthenticator.authenticatePwd(stub, "user", "geheim"); GetBankDocument req=GetBankDocument.Factory.newInstance(); GetBankType getBank=req.addNewGetBank(); getBank.setBlz("67352565"); System.out.println("Kreditinstitut: "+stub.getBank(req).getGetBankResponse().getDetails().getBezeichnung());
Diagram 1: Operation of the SSO for web of service with Shibboleth
source code description of the Authenticator
The authenticator uses the HTTP client from the Jakarta projects. Because Axis2 uses the Jakarta HTTP client itself, only the Axis2 libraries are necessary for compilation and execution of the authenticator. Listing 2 shows the source code of the authenticator. At the beginning a HTTPClient and a post method is initialized. The address of the web service endpoint is given to the constructor of PostMethod. The Authenticator reads this address from the port object stub. After sending the POST request it is checked if the SP answers with a redirect to the IdP. From the answer of the SP the address of the IdP is read from the Location-header. Because we know that the request is redirected to the login page of the IdP, which expects authentication data, we send username and password. The scope is defined for all following HTTP calls by AuthScope. ANY. Usually the IdP is accessed via SSL. Hence, a server authentication via a certificate is necessary. If the server certificate was not signed by a root certification authority, a certificate must be installed at the client. For example, via the file cacerts (JRE_HOME\lib\security). In the next step the real GET request is constructed. For this the HTTPMethod authMethod is created. Via
method.getResponseHeaders("location")[0].getValue()
the address of the IdP is read out of the header field location. Afterwards it is attempted to send the GET Request. An exception message is thrown if the request fails due to wrong authentication data. Amongst others, a session Cookie is included in the answer of the server. Since this session cookie is used from the SP for authentication, a renewed login by requesting a second resource at the SP, to which the user has access rights, is not necessary. This cookie will be returned by the server by a Header Field and can be read out by the method :
authMethod.getRequestHeader("cookie")
The method setCookie () attaches the cookie to the stub of the service client. For this a simple ArrayList which contains elements of the type Header, is provided and the cookie is added as single element. After that the cookie is attached to the service client of the stub also as a header field.
stub._getServiceClient().getOptions().setProperty(HTTPConstants.HTTP_HEADERS, headers)
Now the web service client can access the resources without authenticating himself again by sending the cookie with each request.
Conclusion
The solution shown here, allows an enterprise-wide or even cross-departmental access protection with single sign on for web services with little effort. Existing web services clients can be augmented with Shibboleth authentication with one single line code. On the server side the web application containing the service implementation need no change. To protect a service with Shibboleth, it is enough to install the server at a Service Provider. The authenticator introduced here can be easily rewritten for JAX-WS clients or other web services implementations.
26.10.2007, Thomas Bayer, Marco Hippler
source code of the Authenticator
/** * This code is free for commercial and non-commercial use. * This code is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * * @author Thomas Bayer, Marco Hippler * */ package com.thomas_bayer.shibboleth; import java.io.IOException; import java.util.ArrayList; import java.util.List; import javax.net.ssl.SSLHandshakeException; import org.apache.axis2.transport.http.HTTPConstants; import org.apache.commons.httpclient.Header; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpException; import org.apache.commons.httpclient.HttpMethod; import org.apache.commons.httpclient.UsernamePasswordCredentials; import org.apache.commons.httpclient.auth.AuthScope; import org.apache.commons.httpclient.methods.GetMethod; import org.apache.commons.httpclient.methods.PostMethod; import com.thomas_bayer.blz.stub.BLZServiceStub; public class Axis2BrowserArtifactAuthenticator { public static void authenticatePwd(BLZServiceStub stub, String user, String password) throws IOException, HttpException { HttpClient client = new HttpClient(); HttpMethod method = new PostMethod(stub._getServiceClient().getOptions().getTo().getAddress()); int status = client.executeMethod(method); if ((status >= 300) && (status < 400)) { client.getState().setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(user, password)); HttpMethod authMethod = new GetMethod(method .getResponseHeaders("location")[0].getValue()); try { client.executeMethod(authMethod); } catch (SSLHandshakeException e) { throw new RuntimeException( "Server Certificate couldnt be verified. "); } setCookie(authMethod.getRequestHeader("cookie"), stub); } else { throw new RuntimeException("No redirect to IdP received."); } } public static void setCookie(Header cookie, BLZServiceStub stub) { List<Header> headers = new ArrayList<Header>(); headers.add(cookie); stub._getServiceClient().getOptions().setProperty( HTTPConstants.HTTP_HEADERS, headers); } }
Sources:
Shibboleth Wiki: https://spaces.internet2.edu/display/SHIB/WebHome
Tomcat connector: http://tomcat.apache.org/connectors-doc/