Cross-domain single sign-on using SAML 2.0 with WebSphere Liberty

# Configure a service provider-initiated SSO with identity propagation

## This content is part # of # in the series: Cross-domain single sign-on using SAML 2.0 with WebSphere Liberty

Stay tuned for additional content in this series.

## This content is part of the series:Cross-domain single sign-on using SAML 2.0 with WebSphere Liberty

Stay tuned for additional content in this series.

Editor's note: This tutorial was updated on 20 March 2017. The update made was to correct the code in step 4 of 2b. Apply the security configuration to the IdentityServer profile.

You want to expose your cloud services as Java™ Enterprise Edition (Java EE) applications in a hybrid cloud environment. But, how can you ensure the security of these applications by using your existing security infrastructure in a private network?

Part 1 of this series focuses on the standard authentication mechanism between an identity provider (IdP) and a service provider (SP). It is based on the assertion of the user identity information by using Security Assertion Markup Language (SAML) 2.0. It also explains identity propagation between web service calls. Before you continue with this part, you must be familiar with SAML concepts and Java EE development on Eclipse.

## Single sign-on using SAML

Security Assertion Markup Language (SAML) is an XML document that you must write according to the OASIS standard specification, which is also called a SAML token. The SAML token contains information that an application uses to authenticate a user and to perform role-based authorization, according to standard Java Authentication and Authorization Service (JAAS) specifications. By using a SAML token, an application doesn't have to prompt users to provide their credentials. In an application architecture, the identity provider (IdP) plays the role of generating SAML tokens.

The following snippet is an example of a SAML token.

 <?xml version="1.0" encoding="UTF-8"?>
<saml2p:Response xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol"
InResponseTo="_UQ1kDQbfg5KPBBxlMJOEd8jkUOQ1hBLo"
IssueInstant="2017-02-17T08:52:44.650Z"
Version="2.0">
<saml2:Issuer xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">
samlsso.sample.net
</saml2:Issuer>
<saml2p:Status>
<saml2p:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success" />
</saml2p:Status>
<saml2:Assertion xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion"
ID="Assertion1487321564650" IssueInstant="2017-02-17T08:52:44.650Z"
Version="2.0">
<saml2:Issuer>samlsso.sample.net</saml2:Issuer>
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
<ds:SignatureMethod
Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" />
<ds:Reference URI="#Assertion1487321564650">
<ds:Transforms>
<ds:Transform
Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
<ds:DigestValue>nKYxEAMG1LY4H+LqR22KJ/vqyb8=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue>
m5V44OFKU1PMdibileobvVVA8NVZMKRmKAauOin2f+Kr1WQ   [...]   Z/5JcU/qw==
</ds:SignatureValue>
<ds:KeyInfo>
<ds:X509Data>
<ds:X509Certificate>
MRYwFAYKCZImiZPyLGQBGRYGc2FtcGxlMRcwFQYKCZImiZPyLGQBGRYHc2FtbHNzbzEMMAoGA1UE
AxMDaWRwMB4XDTE2MTExNzE0MjkwM1oXDTMwMDcyNzE0MjkwM1owVDETMBEGCgmSJomT8ixkARkW
A25ldDEWMBQGCgmSJomT8ixkARkWBnNhbXBsZTEXMBUGCgmSJomT8ixkARkWB3NhbWxzc28xDDAK
ub83SyYZz509A5/y65sj2iBFaIRCgzm0s+F6jCsyxpRd8fVgOtJ56CNdJkN+Zw7pOGvrD/EIvjn8
8SwCx0GDdxgtP4lfbQ7DhakSLw2hCui7yB9ijbIJjd+1f2IJbIv8TqYH4RG3kQue6i9e47W8Z4jY
URivaX/omXJvoBBBRO6a23n+jrQ7hk9KCACKfCEmlMaO9meg+mXRXyAXLnyaGw7W7qk27/7QB5uH
s2WhXIxy+wt3PrTZ9q2x413/chbQvRfrgxfhUA/GGFiWTa7x+sZ2g6bKLcGPqkjuJRuhNkeWkuOj
thnygoBz58A4f8MBqTUCAwEAAaMhMB8wHQYDVR0OBBYEFO8nqGs+aMkoBg/Ll4rbLZD9+V1mMA0G
i6tYC5qaVDOBn3Kcr0Wy3egObQsQE6lP4J7SCQUn/miBP/hogrc2uDhOuvCrBHvwGxRbhsMPUUCC
73pxGSKFf4tVKbsm8Y1k3840fJwvfc4NKn5JCj0ShEoRMmf/UkcEOTWbOEQrErrCtA3Czxo5JtKh
t8s1RPPg9p6lC5qk9Ob6tnLGh4mtHE9dc4FifMOaccCMTwc5O34jP7AM+avRcyvSw5b0jDhER8re
ubErEwK3ceaoAejlPJQbmMt9SOJfXDS7P1jc4yuWEAolJeA7CsX9xkTb3Rj3
</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</ds:Signature>
<saml2:Subject>
<saml2:NameID>alice</saml2:NameID>
<saml2:SubjectConfirmation
Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
<saml2:SubjectConfirmationData
InResponseTo="_UQ1kDQbfg5KPBBxlMJOEd8jkUOQ1hBLo"
NotOnOrAfter="2017-02-17T09:52:44.650Z"
Recipient="https://localhost:9443/ibm/saml20/sp/acs" />
</saml2:SubjectConfirmation>
</saml2:Subject>
<saml2:Conditions NotBefore="2017-02-17T08:22:44.650Z"
NotOnOrAfter="2017-02-17T09:22:44.650Z">
<saml2:AudienceRestriction>
<saml2:Audience>https://localhost:9443/ibm/saml20/sp
</saml2:Audience>
</saml2:AudienceRestriction>
</saml2:Conditions>
<saml2:AttributeStatement>
<saml2:Attribute Name="Membership">
<saml2:AttributeValue>
CN=CloudServiceUsers,dc=samlsso,dc=sample,dc=net
</saml2:AttributeValue>
<saml2:AttributeValue>
CN=LocalServiceUsers,dc=samlsso,dc=sample,dc=net
</saml2:AttributeValue>
<saml2:AttributeValue>
CN=IdentityRequestors,dc=samlsso,dc=sample,dc=net
</saml2:AttributeValue>
<saml2:AttributeValue>
CN=FrontendServiceUsers,dc=samlsso,dc=sample,dc=net
</saml2:AttributeValue>
</saml2:Attribute>
</saml2:AttributeStatement>
<saml2:AuthnStatement AuthnInstant="2017-02-17T08:52:44.650Z">
<saml2:AuthnContext>
<saml2:AuthnContextClassRef>
</saml2:AuthnContextClassRef>
</saml2:AuthnContext>
</saml2:AuthnStatement>
</saml2:Assertion>
</saml2p:Response>

In this XML example, the most interesting part is the Assertion part. It contains the token issuer, the document signature, the subject, the conditions, and the attribute statement.

• The document Signature holds the SignatureValue (the encryption that is applied to a hash of the entire Assertion element). It also holds the algorithms that are used to produce the hash and the public certificate of the issuer (X509Certificate). The SignatureValue and the hashing algorithms are enough to check that the assertion is trusted. Why is the issuer and its public certificate information there? The reason is that an application that receives the SAML token knows the issuer and the public certificate of the issuer. Also, it can quickly discard an invalid token by comparing these strings. Remember that the check on the signature is expensive because it has to deal with hashing and encryption.
• The Subject element holds the user name in the NameId element.
• The Conditions section holds information about the audience to whom this assertion is intended for. The Subject and Conditions elements also hold information that a receiver uses to perform further security checks on the document.
• The AttributeStatement holds information about the groups to which the user belongs.

When a SAML receiver trusts the token, the user is authenticated. The receiver application uses the user name and groups that are extracted from the SAML token to programmatically create a security context for the user. After the security context is available, the receiver application can start the authorization API (JAAS) to verify whether the user has the required permissions to access protected resources, such as servlets and EJBs.

You can see that it is possible to assert the identity of a user without knowing the user's credentials by using SAML. This approach makes SAML a possible solution to implement SSO across different domains in the network. So, how can a SAML receiver trust a SAML token?

The model that is used to establish the trust is called asymmetric cryptography . This model is based on a pair of keys—a private key and a corresponding public key—that are generated at the same time and linked together by mathematical formulas. A private key is used for encryption, and the corresponding public key is used for decryption. The main concept behind the asymmetric cryptography model is that a piece of data that is encrypted by using a private key can be decrypted only by using the corresponding public key.

In our scenario, the identity provider is the owner of the private key and must not share it with anyone.

The SAML receivers that need to trust the identity provider are configured to hold the identity provider's public key, either in a configuration file or in a truststore. When the identity provider issues a SAML token, the identity provider generates a signature value in a two-step procedure. First, it applies a hashing algorithm to the content of the Assertion element. Second, it encrypts the hash by using the private key. Then, the identity provider adds the signature value to the SignatureValue element of the SAML token and sends the token to the requester.

The SAML receiver application reads the SAML token and performs two operations:

1. It generates the hash of the Assertion element (receiver hash).
2. It decrypts the signature value by using the public key (provider hash).

If the hashes are equal, the receiver can trust the SAML token. Also, it ensures that the content of the signature is not tampered with.

For more information about how SAML authentication works, see SAML assertions across WebSphere Application Server security domains.

### SP-initiated SSO: Redirect or POST bindings

When you implement an SSO for a traditional web application, first, consider the actors that work together to make SSO possible according to the OASIS specifications:

• User agent (UA): The web browser.
• Identity provider (IdP): The application that creates the SAML assertion. This application can challenge the UA to obtain the user credentials, validate these credentials against a user registry, and if they are valid, generate a SAML assertion.
• Service provider (SP): The receiver of the SAML assertion. The SP trusts the IdP because it knows the IdP metadata. The IdP metadata is the IdP public certificate, the IdP issuer name, and the IdP URLs. They are usually configured in a file that is called the idp-metadata.xml file. The SP uses this metadata to perform the assertion of the SAML token in order to authenticate the UA, and to check whether it is authorized to access the required resources, such as pages and servlets.

The following figure shows our sample application, with the SP-initiated SSO actors in the larger box on the left side. The SP is a feature in WebSphere Liberty. The IdP is a web application that we install on a Liberty instance called IdentityServer.

Although free IdP implementations are on the web, they lack a sample configuration. We implement one from scratch and deliver it as a Liberty feature to keep the configuration as simple as possible. We also reference the Open SAML libraries that are available for Liberty so that we don't have to download more open source libraries to generate the assertion. This scenario is not a fully compliant IdP implementation. If you need to move to production, you might want to adopt a product that provides an IdP implementation and support.

The IdP metadata is an XML file that describes the IdP and contains the following information:

• The issuer name (the id element of the idp-metadata.xml file)
• The public key that is used for signing and encryption (the two KeyDescriptor elements)
• The URLs that an SP that trusts this IdP should use in an SP-initiated SSO (SingleSignOnService element with HTTP-POST)

The following example shows the IdP metadata for our scenario.

 <?xml version="1.0"?>
<md:EntityDescriptor id="samlsso.sample.net"
validUntil="2022-10-29T22:57:55Z"
cacheDuration="PT1478213875S" entityID="samlsso.sample.net">
<md:IDPSSODescriptor WantAuthnRequestsSigned="false"
protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
<md:KeyDescriptor use="signing">
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:X509Data>
<ds:X509Certificate>
MRYwFAYKCZImiZPyLGQBGRYGc2FtcGxlMRcwFQYKCZImiZPyLGQBGRYHc2FtbHNzbzEMMAoGA1UE
AxMDaWRwMB4XDTE2MTExNzE0MjkwM1oXDTMwMDcyNzE0MjkwM1owVDETMBEGCgmSJomT8ixkARkW
A25ldDEWMBQGCgmSJomT8ixkARkWBnNhbXBsZTEXMBUGCgmSJomT8ixkARkWB3NhbWxzc28xDDAK
ub83SyYZz509A5/y65sj2iBFaIRCgzm0s+F6jCsyxpRd8fVgOtJ56CNdJkN+Zw7pOGvrD/EIvjn8
8SwCx0GDdxgtP4lfbQ7DhakSLw2hCui7yB9ijbIJjd+1f2IJbIv8TqYH4RG3kQue6i9e47W8Z4jY
URivaX/omXJvoBBBRO6a23n+jrQ7hk9KCACKfCEmlMaO9meg+mXRXyAXLnyaGw7W7qk27/7QB5uH
s2WhXIxy+wt3PrTZ9q2x413/chbQvRfrgxfhUA/GGFiWTa7x+sZ2g6bKLcGPqkjuJRuhNkeWkuOj
thnygoBz58A4f8MBqTUCAwEAAaMhMB8wHQYDVR0OBBYEFO8nqGs+aMkoBg/Ll4rbLZD9+V1mMA0G
i6tYC5qaVDOBn3Kcr0Wy3egObQsQE6lP4J7SCQUn/miBP/hogrc2uDhOuvCrBHvwGxRbhsMPUUCC
73pxGSKFf4tVKbsm8Y1k3840fJwvfc4NKn5JCj0ShEoRMmf/UkcEOTWbOEQrErrCtA3Czxo5JtKh
t8s1RPPg9p6lC5qk9Ob6tnLGh4mtHE9dc4FifMOaccCMTwc5O34jP7AM+avRcyvSw5b0jDhER8re
ubErEwK3ceaoAejlPJQbmMt9SOJfXDS7P1jc4yuWEAolJeA7CsX9xkTb3Rj3
</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</md:KeyDescriptor>
<md:KeyDescriptor use="encryption">
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:X509Data>
<ds:X509Certificate>
MRYwFAYKCZImiZPyLGQBGRYGc2FtcGxlMRcwFQYKCZImiZPyLGQBGRYHc2FtbHNzbzEMMAoGA1UE
AxMDaWRwMB4XDTE2MTExNzE0MjkwM1oXDTMwMDcyNzE0MjkwM1owVDETMBEGCgmSJomT8ixkARkW
A25ldDEWMBQGCgmSJomT8ixkARkWBnNhbXBsZTEXMBUGCgmSJomT8ixkARkWB3NhbWxzc28xDDAK
ub83SyYZz509A5/y65sj2iBFaIRCgzm0s+F6jCsyxpRd8fVgOtJ56CNdJkN+Zw7pOGvrD/EIvjn8
8SwCx0GDdxgtP4lfbQ7DhakSLw2hCui7yB9ijbIJjd+1f2IJbIv8TqYH4RG3kQue6i9e47W8Z4jY
URivaX/omXJvoBBBRO6a23n+jrQ7hk9KCACKfCEmlMaO9meg+mXRXyAXLnyaGw7W7qk27/7QB5uH
s2WhXIxy+wt3PrTZ9q2x413/chbQvRfrgxfhUA/GGFiWTa7x+sZ2g6bKLcGPqkjuJRuhNkeWkuOj
thnygoBz58A4f8MBqTUCAwEAAaMhMB8wHQYDVR0OBBYEFO8nqGs+aMkoBg/Ll4rbLZD9+V1mMA0G
i6tYC5qaVDOBn3Kcr0Wy3egObQsQE6lP4J7SCQUn/miBP/hogrc2uDhOuvCrBHvwGxRbhsMPUUCC
73pxGSKFf4tVKbsm8Y1k3840fJwvfc4NKn5JCj0ShEoRMmf/UkcEOTWbOEQrErrCtA3Czxo5JtKh
t8s1RPPg9p6lC5qk9Ob6tnLGh4mtHE9dc4FifMOaccCMTwc5O34jP7AM+avRcyvSw5b0jDhER8re
ubErEwK3ceaoAejlPJQbmMt9SOJfXDS7P1jc4yuWEAolJeA7CsX9xkTb3Rj3
</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</md:KeyDescriptor>
<md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified
</md:NameIDFormat>
<md:SingleSignOnService
Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
Location="https://localhost/idp/SAMLResponse" />
<md:SingleSignOnService
Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST-SimpleSign"
Location="https://localhost/idp/SAMLResponse" />
<md:SingleSignOnService
Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
Location="https://localhost/idp/SAMLResponse" />
</md:IDPSSODescriptor>
</md:EntityDescriptor>

The IdP authenticates the users with BASIC authentication. In Part 3 of this series, you see how to configure SPNEGO authentication.

How does the SP-initiated SSO work?

1. A user navigates to a URL in the browser (UA), which in turn calls the application (the SP) that exposes the URL. If the resource is protected, the user must be authenticated so that the SP can perform the authorization checks on the required resource.
2. If the user is not authenticated, the SP replies to the UA with a page that automatically posts an authentication request to the IdP by using the URL that is configured in the idp-metadata.xml file.

The following XML file is an example of the authentication request that the SP sends to the IdP.

  <?xml version="1.0" encoding="UTF-8"?>
<saml2p:AuthnRequest xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol"
AssertionConsumerServiceURL="https://localhost:9443/ibm/saml20/sp/acs"
Destination="https://localhost/idp/SAMLResponse" ForceAuthn="false"
ID="_10lsWdAH1e4faN5kuKF3tY0VTVbyqXkf" IsPassive="false"
IssueInstant="2017-02-25T23:49:22.413Z"
ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
Version="2.0">
<saml2:Issuer xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">
https://localhost:9443/ibm/saml20/sp
</saml2:Issuer>
<saml2p:NameIDPolicy
</saml2p:AuthnRequest>
3. When an unauthenticated user lands on the IdP application, the IdP challenges the user to provide his credentials. In our example, the basic authentication form is displayed on the user's browser.
4. The user enters his credentials and, if they are valid, the IdP generates a SAML token.
5. The IdP replies to the UA with a page that automatically posts the SAML token to the AssertionConsumerServiceURL of the SP. (See the previous XML example of authentication request.)
6. The SP receives the SAML token that the UA posts, authenticates the user, and responds to the UA with a redirect to the initial URL that the user calls.

The following figure shows the communication sequence that happens in an SP-initiated SSO. Note the following points:

• The IdP and SP interaction is mediated by the browser. Therefore, these providers don't need to be connected to each other.
• The SP does not rely on any user registry, making it possible to keep the user registry in a private customer network.

### SAML identity propagation

For this solution, you must bind the web services security policy on WebSphere Liberty to propagate the SAML token automatically from the client web application. You can set up web services security at the transport layer or message level. At the transport layer, you set up security based on a Secure Sockets Layer (SSL) or Transport Layer Security (TLS), which protects HTTP message content from point to point.

If you secure web services at the message level, you protect the SOAP contents that are in an HTTP message for a web service. You set up web services security by adding the policy definitions in the web service Web Services Description Language (WSDL) document and configure the policy on the outbound channel of the client and inbound channel for the server.

In our example, we protect web services at both the transport layer and the message level. We set up security at the transport layer by using SOAP over HTTPS. To set up security at the message level, we configure the Web Services Security SAML Token Profile 1.1 so that the client can add the SAML assertion in the SOAP Header and the server can perform the assertion.

## Overview of the local SSO end-to-end solution

In the remainder of this part, we configure three simple Java EE applications, as illustrated in the following figure, to show how the solution works:

• Frontend.ear: This sample web application is deployed on a Liberty Application Server instance (FrontendServer) and configured as an SP.
• CloudServices.ear and LocalServices.ear: Both applications are sample web services applications. They are deployed on two different Liberty instances (CloudServer and LocalServicesSvr) and are configured to demonstrate identity propagation by using SAML.

The following figure illustrates the solution configuration on our local machine.

1

## Set up the SP-initiated SSO solution

This solution is based on the WebSphere Application Server Liberty profile and SAML 2.0. To develop and deploy the solution, you need an Eclipse IDE for Java Enterprise Edition. Before you get started, install a Java SDK, and then install Eclipse and WebSphere Liberty on your development machine.

1. Download and install a Java SDK of version 1.7 or later.
2. Download the release package of Eclipse IDE for Java Developers. (We used the Mars package.)
1. Extract the package to your local file system.
2. Start it to create a new workspace that we use later to import the projects and configure the servers.
3. From the IBM WebSphere Liberty Repository, download Liberty with Java EE 7. (The latest stable version at the time of writing is 16.0.0.4.) Then, extract it to the local file system.
4. Open a browser, go to the WASDev Developer Center, and drag the Install icon to the editor area of the workspace that you opened in the step 3 to install the Liberty tools in your version of Eclipse.

When you install the Liberty tools, in the Confirm Selected Features window, select the check boxes for all tools, including the WebSphere Application Server Migration Toolkit.

In this tutorial, we use the following paths:

• WebSphere Liberty home path: C:\programs\wlp-16.0.0.3 for <WLP_HOME>
• WebSphere Liberty bin path: C:\programs\wlp-16.0.0.3\bin for <WLP_BIN>
• WebSphere Liberty server installation path: C:\programs\wlp-16.0.0.3\usr\servers for <WLP_SERVERS>
1a

### Configure the WebSphere Liberty runtime

After you install the Liberty tools, configure a Liberty server runtime:

1. Select Window -> Preferences.
2. In the Preferences window, select Server -> Runtime Environments. Then, in the Server Runtime Environments pane, click Add.
3. In the New Server Runtime Environment window, select WebSphere Application Server Liberty, and click Next.
4. In the Liberty Runtime Environment window, leave the name as WebSphere Application Server Liberty. For Path, click Browse and select the path on your file system where you extracted Liberty Server. Click OK, and then click Finish.

Your workspace should look similar to the following example.

1b

### Import the sample projects

The sample projects are available on GitHub. You can download the complete set from this compressed file, and extract it to your local file system.

1. From the workspace, select File -> Import.
2. In the Import wizard, select General -> Existing projects into workspace, and click Next.
3. In the Import Projects window, click Browse, and select the path in your local file system where you extracted the projects. Click OK. Select the Copy projects into workspace check box, and then click Finish.

Your workspace should now look similar to the following example.

Tip: If you chose a name other than WebSphere Application Server Liberty, the Workspace Migration wizard prompts you to choose the correct runtime as shown in the following window. Leave all the projects selected. Then, on the next page, choose your Liberty runtime. If you used the correct name of WebSphere Application Server Liberty, this wizard does not display.

1c

### Create the WebSphere Liberty profiles

Create the four Liberty profiles that you need to run the applications that you imported into your workspace:

• IdentityServer: The Liberty instance that is configured as the IdP.
• FrontendServer: The Liberty instance that is configured as an SP. On this instance, we deploy the Frontend.ear application, which contains the web application that we use as a test case for the SP-initiated SSO. In Part 2, we deploy this server configuration and application on IBM Cloud.
• CloudServer: The Liberty instance that is configured with the Web Services Security SAML Token Profile 1.1. On this instance, we deploy the CloudServices.ear application. This application contains a couple of web services operations that authenticate the request, leveraging SAML Identity Propagation over SSL. In Part 2, we deploy this server configuration and application on IBM Cloud.
• LocalServer: The Liberty instance that is configured with the Web Services Security SAML Token Profile 1.1. On this instance, we deploy the LocalServices.ear application. This server instance runs on the local machine to demonstrate how you can access web services that are exposed in a private network from IBM Cloud through the Secure Gateway service. The configuration is similar to the CloudServer configuration.

To create the profiles:

1. Create a Liberty profile:
1. Select File -> New -> Other.
2. In the Select a wizard window, in the search field, type Server, and click Next.
3. In the Define a New Server window, under Select the server type, select WebSphere Application Server Liberty. For Server name, enter IdentityServer. Then, click Next.
4. In the New Liberty Server window, for Server name, enter IdentityServer, and click Finish. If you have existing server instances in your Liberty installation, click New to create a new server instance.
2. For each of the remaining three Liberty profiles, repeat steps a – d, using the names FrontendServer, CloudServer, and LocalServer. After you finish the configuration, your workspace should look similar to the example in the left pane of the following figure.
3. In the right pane of this figure, notice the errors as indicated by the x in the circle in the left margin. These errors occur because the javaee-7.0 feature is enabled, and you must provide a keystore element to solve it. For each of the server configurations, add the following corresponding keystore element (or uncomment the existing keystore element):
• IdentityServer:
		 <keyStore password="passw0rd"
location="${server.output.dir}/resources/security/identitykey.jks"/> • FrontendServer:  <keyStore password="passw0rd" location="${server.output.dir}/resources/security/frontendkey.jks"/>
• CloudServer:
		 <keyStore password="passw0rd"
location="${server.output.dir}/resources/security/cloudkey.jks"/> • LocalServer:  <keyStore password="passw0rd" location="${server.output.dir}/resources/security/localkey.jks"/>
4. Optional: Encrypt the password by using different algorithms, which you can do on the Design tab of the server.xml editor. In this example, for simplicity, we use passw0rd for the default keystore of each server.

The following keystores are generated with the logs and other folders in the <WLP_SERVERS> server directory when you start each server for the first time:

• identitykey.jks in <WLP_SERVERS>\IdentityServer\resources\security\
• frontendkey.jks in <WLP_SERVERS>\FrontendServer\resources\security\
• cloudkey.jks in <WLP_SERVERS>\CloudServer\resources\security\
• localkey.jks in <WLP_SERVERS>\LocalServer\resources\security\
5. Add the other configuration elements to the server.xml file. Because we will have four server instances up and running on the same machine, we cannot use the same port numbers.
1. Change the httpEndpoint element for the IdentityServer as follows:
 <!-- To access this server from a remote client add a host attribute to the following element, e.g. host="*" -->
<httpEndpoint host="*" httpPort="80" httpsPort="443" id="defaultHttpEndpoint"/>
2. Change the httpEndpoint element for the FrontendServer as follows:
 <!-- To access this server from a remote client add a host attribute to the following element, e.g. host="*" -->
<httpEndpoint host="*" httpPort="9080" httpsPort="9443" id="defaultHttpEndpoint"/>

<!-- Automatically expand WAR files and EAR files -->
<applicationManager autoExpand="true"/>

<wasJmsEndpoint enabled="false" wasJmsPort="7277" wasJmsSSLPort="7287"/>
<iiopEndpoint id="defaultIiopEndpoint" iiopPort="2810">
<iiopsOptions iiopsPort="9403" sslRef="defaultSSLConfig"/>
</iiopEndpoint>
3. Change the httpEndpoint element for the CloudServer as follows:
 <!-- To access this server from a remote client add a host attribute to the following element, e.g. host="*" -->
<httpEndpoint host="*" httpPort="9081" httpsPort="9444" id="defaultHttpEndpoint"/>

<!-- Automatically expand WAR files and EAR files -->
<applicationManager autoExpand="true"/>

<wasJmsEndpoint enabled="false" wasJmsPort="7278" wasJmsSSLPort="7288"/>
<iiopEndpoint id="defaultIiopEndpoint" iiopPort="2812">
<iiopsOptions iiopsPort="9404" sslRef="defaultSSLConfig"/>
</iiopEndpoint
4. Change the httpEndpoint element for the LocalServer as follows:
 <!-- To access this server from a remote client add a host attribute to the following element, e.g. host="*" -->
<httpEndpoint host="*" httpPort="9082" httpsPort="9445" id="defaultHttpEndpoint"/>

<!-- Automatically expand WAR files and EAR files -->
<applicationManager autoExpand="true"/>

<wasJmsEndpoint enabled="false" wasJmsPort="7279" wasJmsSSLPort="7289"/>
<iiopEndpoint id="defaultIiopEndpoint" iiopPort="2813">
<iiopsOptions iiopsPort="9405" sslRef="defaultSSLConfig"/>
</iiopEndpoint>
6. Install the following features:
1. Open a command prompt, and enter cd <WLP_BIN>.
2. Run the following command for wsSecuritySaml-1.1 to download and install all the required features to enable the SP-initiated SSO and identity propagation:
 installUtility install wsSecuritySaml-1.1 

You should see the following message after the download and installation are completed successfully:
All assets were successfully installed. Start product validation... Product validation completed successfully.

1d

### Deploy the sample applications

Deploy the projects that you imported before onto your servers:

1. Select the Servers view, right-click the FrontendServer, and select Add and Remove.
2. In the Add and Remove window, click Frontend in the Available box on the left side, and click Add to move it to the Configured box on the right side. Then, click Finish.
3. Repeat the previous two steps for CloudServer to install the CloudServices application, and for LocalServer to install the LocalServices application.

When you are finished, your server view should look like the following example.

2

## Configure the identity provider (IdentityServer)

The IdP is an application that can generate a SAML assertion for an authenticated user. In the following steps, you configure and test the IdP.

2a

### Install the IdentityProvider custom feature on IdentityServer

The sample identity provider application, it.ibm.liberty.sample.identityProvider-1.0_1.0.0.esa, is provided as a Liberty feature, which you can find in the downloadable files for this tutorial.

To install the feature, open a command prompt, and enter: cd <WLP_BIN>. Then, enter the following command, where <absolute-or-relative-path> is the path in the local file system where the feature file is:

                installUtility install <absolute-or-relative-path>\it.ibm.liberty.sample.identityProvider-1.0_1.0.0.esa

After the installation completes successfully, restart your Eclipse workspace to reload the list of available server features.

2b

### Apply the security configuration to the IdentityServer profile

Configure the IdP feature so that you can run it:

1. Create a key in a new keystore. To begin, open a command prompt, and run the following commands:
cd <WLP_SERVERS>\IdentityServer\resources\security\

keytool -genkey -alias idpkey -keystore samlkey.jks -dname "CN=idp,dc=samlsso,dc=sample,dc=net" -storepass idpstorepass -keypass idpkeypass -storetype jks -validity 5000 -keyalg RSA

Note the following explanation for some of the parameters:

• keytool is a command that is available in the JDK installation. When used with the -genkey option, this command generates a private key, referenced with the idpkey alias, in the new samlkey.jks keystore.
• -storepass is the password to access to the samlkey.jks keystore.
• -keypass is the password that you need to access the idpkey entry. IdP needs this information to sign the SAML assertions that it will generate.
• -dname avoids prompting on names and organizations.
2. Now that you have a new keystore, configure the IdentityServer server.xml file.
1. Open the file.
2. On the Source tab, add the following element:
 <keyStore id="saml" password="idpstorepass"
location="${server.config.dir}/resources/security/samlkey.jks"> <keyEntry name="idpkey" keyPassword="idpkeypass"></keyEntry> </keyStore> 3. Enable the identityProvider-1.0 that you installed earlier. 1. Add identityProvider-1.0 to the already opened server.xml into the existing <featureManager> element:  <featureManager> [...] <feature>identityProvider-1.0</feature> </featureManager> 2. Add the <identityProvider> configuration element:  <identityProvider issuer="samlsso.sample.net" keyAlias="idpkey" keyStoreRef="saml" hostname="localhost" port="443"/> Note the following explanation: • Our custom identityProvider feature foresees only a few sets of configuration parameters to allow the IdP application to generate an assertion and to let the SP, which will be configured in the next paragraph, to download the idp-metadata.xml configuration to set up the SP-Initiated SSO. A real IdP software is much more complicated to configure, but to run our example that's enough. • The idpkey is the key entry in the keystore that we generated previously. The saml keystore reference is the id attribute of the samlkey.jks keyStore element. • The SP checks the issuer of the SAML assertion to verify that it is a recognized issuer. It also checks the hostname:port that is used by the SP to forward, in a POST form that is mediated by the browser, the request to generate a SAML to the IdP. • The host name must be the name that you use for the machine where you run the IdentityServer, which is the local machine in this configuration. • The port must be an HTTPS port. In the IdentityServer in this configuration, we set the port to 443 . • Although the host name seems redundant here, it must be provided to the SP so that it knows where the IdP is. It can be also in a different and unreachable network. Because a machine can have different names and IdPs, in our scenario, we have a way to decide which one to use for the idp-metadata.xml generator. For the IdP, you must use a URL that is reachable by the browser that you use to run the test. In this case, we use the local machine browser. 4. Set up a user registry. We use a basic registry for now. Replace the basicRegistry with the following configuration to the still opened server.xml editor: <basicRegistry realm="samlsso.sample.net" ignoreCaseForAuthentication="false"> <group name="CN=IdentityRequestors,DC=samlsso,DC=sample,DC=net"> <member name="rob"/> <member name="max"/> <member name="bobby"/> <member name="alice"/> </group> <group name="CN=FrontendServiceUsers,DC=samlsso,DC=sample,DC=net"> <member name="max"/> <member name="bobby"/> <member name="alice"/> </group> <group name="CN=CloudServiceUsers,DC=samlsso,DC=sample,DC=net"> <member name="bobby"/> <member name="alice"/> </group> <group name="CN=LocalServiceUsers,DC=samlsso,DC=sample,DC=net"> <member name="alice"/> </group> <user name="rob" password="passw0rd"/> <user name="max" password="passw0rd"/> <user name="bobby" password="passw0rd"/> <user name="alice" password="passw0rd"/> </basicRegistry> 5. Configure the role-based authorization for the IdP web application. In the server.xml editor, add:  <authorization-roles id="idp.roles"> <security-role name="IdentityRequestor"> <special-subject type="ALL_AUTHENTICATED_USERS"/> </security-role> <security-role name="Snooper"> <special-subject type="ALL_AUTHENTICATED_USERS"/> </security-role> </authorization-roles> With this configuration, all the basicRegistry users are authorized to perform the role of IdentityRequestor (which protects the SAMLRequest servlet) and Snooper (which protects the snoop servlet). 2c ### Test the identity provider application by using a web browser The IdP is ready to generate the SAML assertions for the authenticated users: 1. Ensure that the IdentityServer is up by running the following tests: • Generate a SAML assertion: http://localhost/idp/SAMLResponse • Access this utility page to download the public certificate and the idp-metadata.xml: http://localhost/idp/SAMLResource • Print some request information: http://localhost/idp/snoop 2. In the BASIC authentication window, enter one of the user name and password combinations from the basicRegistry that you configured previously. The users belong to different groups to allow restrictions on resource access, according to standard JAAS role-based authorization. You can find the entire server.xml configuration, together with the keystores, in the servers folder of the downloadable materials on GitHub. 3 ## Configure the service provider (FrontendServer) Now that you have installed the samlWeb-2.0 feature, you configure it to enable the SP-initiated SSO. 3a ### Apply the service provider configuration to the FrontendServer profile 1. Open the http://localhost/idp/SAMLResource URL, and click IdpMetadata.xml to download the idp-metadata.xml file. Save the file in the <WLP_SERVERS>\FrontendServer\resources\security\ directory. 2. Open the FrontendServer server.xml file, and add the samlWeb-2.0 feature and the wsSecuritySaml-1.1 feature to the existing <featureManager> element:  <featureManager> [...] <feature>samlWeb-2.0</feature> <feature>wsSecuritySaml-1.1</feature> </featureManager> 3. Add the samlWebSso20 to the server.xml entity:  <samlWebSso20 id="sp" allowCustomCacheKey="true" authnRequestsSigned="false" createSession="true" disableLtpaCookie="true" enabled="true" groupIdentifier="Membership" httpsRequired="true" idpMetadata="${server.config.dir}/resources/security/idp-metadata.xml" inboundPropagation="none"
wantAssertionsSigned="true" />

For a full explanation of the configuration parameters, see Configuring SAML Web Browser SSO in Liberty in the IBM Knowledge Center.

Note the following points:

• The allowCustomCacheKey and disableLtpaCookie configurations indicate that the local server should cache the SAML assertion and that we don't want to use the LTPA cookie. With this configuration, if a FrontendServer is clustered and the user lands on a different server, this server should forward it to the IdP to generate a SAML assertion for the user. If not, no SAML assertions are available to propagate the identity to the backend web services.
• idpMetadata points to the file that you just downloaded from the IdP.
3b

### Test the SP-initiated SSO

To test the SP-initiated SSO, start the FrontendServer, and test the SP-initiated SSO at the http://localhost:9080/FrontendWeb/ URL.

To see the SP-initiated SSO sequence, you can use the developer tools for your browser to track the network requests. For example, if you are using Mozilla Firefox, press F12, or select the Options menu and choose Developer to enable the toolbar. The following example shows the SP-initiated SSO sequence for our scenario.

3c

### Import the SSL certificate for the CloudServer

Import the public SSL certificate into the frontendkey.jks keystore to allow the Frontend application to call CloudServices by using HTTPS. We export the public certificate from the CloudServer and then import it into the FrontendServer keystore.

1. Open a command prompt, and enter:
cd <WLP_SERVERS>\CloudServer\resources\security\ 
2. Run the following command to view the CloudServer SSL certificate that is created automatically the first time you started the server:
keytool -list -keystore cloudkey.jks -storepass passw0rd 

The output is similar to the following example, where default is the name of the KeyEntry that contains the key pair:

 Keystore type: jks
Keystore provider: IBMJCE

Your keystore contains 1 entry

default, Nov 17, 2016, keyEntry,
Certificate fingerprint (SHA1): 2D:16:E2:E9:FC:C1:34:26:94:35:50:7C:0C:A4:42:15:A8:4D:BE:68
3. Export the public key that is associated with the default key. From the command prompt, which is already open, in the same directory, run:
keytool -export -alias default -file CloudServer.cer -keystore cloudkey.jks -storepass passw0rd

cd ..\..\..\FrontendServer\resources\security\

keytool -import -alias cloudssl -file ..\..\..\CloudServer\resources\security\CloudServer.cer -keystore frontendkey.jks -storepass passw0rd -storetype jks -keypass cloudkeypass -noprompt

keytool -list -keystore frontendkey.jks -storepass passw0rd

The last command has output similar to the following example:

 Keystore type: jks
Keystore provider: IBMJCE

Your keystore contains 2 entries

cloudssl, Nov 17, 2016, trustedCertEntry,
Certificate fingerprint (SHA1): 2D:16:E2:E9:FC:C1:34:26:94:35:50:7C:0C:A4:42:15:A8:4D:BE:68
default, Nov 17, 2016, keyEntry,
Certificate fingerprint (SHA1): 73:4C:A6:F8:32:3F:3D:0D:87:54:2C:F0:69:74:58:64:72:C6:10:CB

The keystore now has two keys: default and cloudssl. You just added the cloudssl key. The default key entry was added when you created the keystore when you first started the FrontendServer instance. The password for this key is passw0rd, because when you configured the server.xml entity, you provided a password for the whole keystore only.

The default key entry is the certificate that the application server used to provide TLS (like HTTPS). Because we added a new key, we must configure the FrontendServer server.xml entity to add a new keystore reference with the alias and the password that we provided when we imported the CloudServer.cer file.

Error message: Although you can add both entries with their passwords to the default keyStore, the Liberty profile issues the following error message:

[ERROR ] CWPKI0813E: Error while trying to initialize the keymanager for the keystore [C:/programs/wlp-16.0.0.3/usr/servers/FrontendServer/resources/security/frontendkey.jks].The private key password is not correct or the keystore has multiple private keys with different passwords.This keystore can not be used for SSL. Exception message is: [Cannot recover key].
4. Open the FrontendServer server.xml file, switch to the Source tab, and add a new keystore to configure the alias for the new key:
 <keyStore id="trust" password="passw0rd"
location="${server.output.dir}/resources/security/frontendkey.jks"> <keyEntry name="cloudssl" keyPassword="cloudkeypass"></keyEntry> </keyStore> The configuration is done. 5. Start the IdentityServer, FrontendServer, and CloudServer. If you open the application and click the Cloud Service link or Restricted Service in the left menu, you receive the following error message: None of the policy alternatives can be satisfied. This message comes from the CloudServer. (You can check its logs.) This message means that an SSL handshake was performed successfully and the certificate configuration was fine. However, because you haven't configured the CloudServer with the proper policy to manage the incoming SAML assertion, the CloudServer does not know how to manage it. Having the security enabled, because the EJB methods are protected by roles, it claims that it can't satisfy the security policy that it received. As for IdentityServer, the full configuration of the FrontendServer is available in the resource material that you downloaded. 4 ## Configure the web services providers (CloudServer and LocalServer) Set up the security policy for both the CloudServer and LocalServer. Let's start from the certificates. Both servers need to assert a SAML document. 1. Download the IdP public key. To begin, open the http://localhost/idp/SAMLResource URL, and click X509 Certificate to download the samlsso.sample.net.cer certificate. Save the file in the <WLP_SERVERS>\IdentityServer\resources\security\samlsso.sample.net.cer directory. 2. Import all the remaining certificates. Both servers need the samlsso.sample.net.cer certificate. CloudServer also needs the public key of the LocalServer to call the web service that is exposed by the LocalServices application. Open a command prompt, and change directories to the <WLP_SERVERS>\LocalServer\resources\security\ path. Run the following commands: keytool -import -alias saml -file ..\..\..\IdentityServer\resources\security\samlsso.sample.net.cer -keystore localkey.jks -storepass passw0rd -storetype jks -keypass samlpassw0rd -noprompt keytool -export -alias default -file LocalServer.cer -keystore localkey.jks -storepass passw0rd cd ..\..\..\CloudServer\resources\security keytool -import -alias saml -file ..\..\..\IdentityServer\resources\security\samlsso.sample.net.cer -keystore cloudkey.jks -storepass passw0rd -storetype jks -keypass samlpassw0rd -noprompt keytool -import -alias localssl -file ..\..\..\LocalServer\resources\security\LocalServer.cer -keystore cloudkey.jks -storepass passw0rd -storetype jks -keypass localkeypass -noprompt keytool -list -keystore cloudkey.jks -storepass passw0rd The last command shows the three certificates for the CloudServer. You can run the same command for the LocalServer, but it will show only the first two keys.  Keystore type: jks Keystore provider: IBMJCE Your keystore contains 3 entries saml, Nov 17, 2016, trustedCertEntry, Certificate fingerprint (SHA1): 8B:EC:92:91:2E:12:57:7B:B4:DC:4A:A6:7C:3A:E7:AE:D9:11:CC:5A default, Nov 17, 2016, keyEntry, Certificate fingerprint (SHA1): 2D:16:E2:E9:FC:C1:34:26:94:35:50:7C:0C:A4:42:15:A8:4D:BE:68 localssl, Nov 18, 2016, trustedCertEntry, Certificate fingerprint (SHA1): A4:3E:2E:94:9A:E0:E8:04:65:46:43:89:AA:EF:99:6B:9D:A7:06:AF You have now completed the import for both servers. 3. Configure the features, keystores, and policies for both servers: 1. Open the LocalServer and CloudServer server.xml file, and add the wsSecuritySaml-1.1 to the existing <featureManager> element:  <featureManager> [...] <feature>wsSecuritySaml-1.1</feature> </featureManager> 2. Add the new keystore that references the newly created key to the CloudServer:  <keyStore id="trust" password="passw0rd" location="${server.output.dir}/resources/security/cloudkey.jks">
</keyStore>
3. Add the wsSecurityProvider policy configuration to the CloudServer:
 <wsSecurityProvider id="SAMLBearerSSL">
<samlToken clockSkew="5m" requiredSubjectConfirmationMethod="bearer" timeToLive="30m"
wantAssertionsSigned="true"/>
<callerToken groupIdentifier="Membership" mapToUserRegistry="No" name="SAMLtoken"/>
<signatureProperties org.apache.ws.security.crypto.merlin.keystore.alias="samlsso.sample.net"
org.apache.ws.security.crypto.merlin.keystore.type="jks"
org.apache.ws.security.crypto.merlin.file="${server.output.dir}/resources/security/cloudkey.jks" org.apache.ws.security.crypto.merlin.keystore.password="passw0rd" org.apache.ws.security.crypto.merlin.keystore.private.password="samlpassw0rd"/> </wsSecurityProvider> You are now done with the CloudServer. 4. Restart the CloudServer, open the application, and click the Cloud Service link. You should now see the correct reply from that method. Tip: Depending on the user's group, you might or might not have access to that service. If you log in to the IdP as bobby or alice, you will be authorized and see a reply like the following example: service response: Response from CloudMessageService.getMessage at 1479426017208 otherwise following message should appear: java.rmi.AccessException: ; nested exception is: com.ibm.websphere.csi.CSIAccessException: CWWKS9400A: Authorization failed for user max while invoking getMessage on CloudServices. The user is not granted access to any of the required roles: [CloudServiceAccessRole]. 5. Complete the configuration for the LocalServer: 1. Add the new keyStore:  <keyStore id="trust" password="passw0rd" location="${server.output.dir}/resources/security/localkey.jks">
</keyStore>
2. Add the wsSecurityProvider policy configuration:
 <wsSecurityProvider id="SAMLBearerSSL">
<samlToken clockSkew="5m" requiredSubjectConfirmationMethod="bearer" timeToLive="30m"
wantAssertionsSigned="true"/>
<callerToken groupIdentifier="Membership" mapToUserRegistry="No" name="SAMLtoken"/>
<signatureProperties org.apache.ws.security.crypto.merlin.keystore.alias="samlsso.sample.net"
org.apache.ws.security.crypto.merlin.keystore.type="jks"
org.apache.ws.security.crypto.merlin.file="\${server.output.dir}/resources/security/localkey.jks"
</wsSecurityProvider>

You are now done with the LocalServer.

3. Restart the LocalServer, open the application, and click the Restricted Service link. You should now see the correct reply from that method.

Tip: Depending on the user's group, you might or might not have access to that service. If you log in to the IdP as alice, you will be authorized and can see a message from the web service on the LocalServer that is called by the CloudServer, which is called by a Frontend servlet, like the following example:
service response: Response from LocalMessageService.getLocalMessage at 1479426200968

Otherwise, you see the following message:
java.rmi.AccessException: ; nested exception is: com.ibm.websphere.csi.CSIAccessException: CWWKS9400A: Authorization failed for user bobby while invoking getLocalMessage on LocalServices. The user is not granted access to any of the required roles: [LocalServiceAccessRole].

Just like the FrontendServer configuration, the CloudServer and LocalServer configurations are available in the resource material in the servers folder.

## Conclusion

You now have a fully functional, end-to-end SP-initiated SSO, based on WebSphere Application Server Liberty profiles and SAML 2.0, on your local machine. You also propagated the SAML identity between the WebService applications. You can look at the code examples on GitHub for this tutorial to see how it works.

In Part 2, you deploy the FrontendServer and CloudServer in IBM Cloud. You also configure the Secure Gateway to allow the CloudServices application to access the web services that are exposed by the LocalServices application that still runs on a private network that is not accessible from the public Internet that is your local machine. Then, in Part 3, you extend the SSO scenario to integrate the Microsoft Windows authentication, by using the Simple and Protected GSS-API Negotiation Mechanism (SPNEGO) and the Active Directory Domain Services.

## Acknowledgements

The authors thank Massimo Leoni, Carlo Randone, and Richard Kozel for reviewing this article and providing helpful comments, and Pavel Travnik for testing the code.