SAML 2.0 and Multiple Applications
OpenIG As a SAML 2.0 Service Provider describes how to set up OpenIG as a SAML 2.0 service provider for a single application, using OpenAM as the identity provider. This chapter describes how to set up OpenIG as a SAML 2.0 service provider for two applications, still using OpenAM as the identity provider.
Before you try the samples described here, familiarize yourself with OpenIG SAML 2.0 support by reading and working through the examples in OpenIG As a SAML 2.0 Service Provider. Before you start, you should have OpenIG protecting the sample application as a SAML 2.0 service provider, with OpenAM working as identity provider configured as described in that tutorial.
Installation Overview
In this chapter you use the Fedlet configuration from OpenIG As a SAML 2.0 Service Provider to create a configuration for each new protected application. You then import the new configurations as SAML 2.0 entities in OpenAM. If you subsequently edit a configuration, import it again.
In the following examples, the first application has entity ID sp1 and runs on the host sp1.example.com, the second application has entity ID sp2 and runs on the host sp2.example.com. To prevent unwanted behavior, the applications must have different values.
| Task | See Section(s) |
|---|---|
Prepare the network. |
|
Prepare the configuration for two OpenIG service providers. |
|
Import the service provider configurations into OpenAM. |
|
Add OpenIG routes. |
Preparing the Network
Configure the network so that browser traffic to the application hosts is proxied through OpenIG.
Add the following addresses to your hosts file: sp1.example.com and sp2.example.com.
127.0.0.1 localhost openam.example.com openig.example.com app.example.com sp1.example.com sp2.example.com
Configuring the Circle of Trust
Edit the $HOME/.openig/SAML/fedlet.cot file you created in OpenIG As a SAML 2.0 Service Provider to include the entity IDs sp1 and sp2, as in the following example:
cot-name=Circle of Trust
sun-fm-cot-status=Active
sun-fm-trusted-providers=openam,sp1,sp2
sun-fm-saml2-readerservice-url=
sun-fm-saml2-writerservice-url=
Configuring the Service Provider for Application One
To configure the service provider for application one, you can use the example files Configuration File for Application One and Extended Configuration File for Application One, saving them as sp1.xml and sp1-extended.xml. Alternatively, follow the steps below to use the files you created in OpenIG As a SAML 2.0 Service Provider.
-
Copy the SAML configuration files
sp.xmlandsp-extended.xmlyou created in OpenIG As a SAML 2.0 Service Provider, and save them as$HOME/.openig/SAML/sp1.xmland$HOME/.openig/SAML/sp1-extended.xml. -
Make the following changes in
sp1.xml:-
For
entityID, changesptosp1. TheentityIDmust match the application. -
On each line that starts with
LocationorResponseLocation, changesp.example.comtosp1.example.com, and add/metaAlias/sp1at the end of the line.
For an example of how this file should be, see Configuration File for Application One.
-
-
Make the following changes in
sp1-extended.xml:-
For
entityID, changesptosp1. -
For
SPSSOConfig metaAlias, changesptosp1. -
For
appLogoutUrl, changesptosp1. -
For
hosted=, make sure that the value is1.
For an example of how this file should be, see Extended Configuration File for Application One.
-
<!--
- sp1-xml.txt
- Set the entityID
- Set metaAlias/<sp-name> at the end of each of the following lines:
- Location
- ResponseLocation
- Note that AssertionConsumerService Location attributes include the metaAlias.
-->
<EntityDescriptor
entityID="sp1"
xmlns="urn:oasis:names:tc:SAML:2.0:metadata">
<SPSSODescriptor
AuthnRequestsSigned="false"
WantAssertionsSigned="false"
protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
<SingleLogoutService
Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
Location="http://sp1.example.com:8080/saml/fedletSloRedirect/metaAlias/sp1"
ResponseLocation="http://sp1.example.com:8080/saml/fedletSloRedirect/metaAlias/sp1"/>
<SingleLogoutService
Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
Location="http://sp1.example.com:8080/saml/fedletSloPOST/metaAlias/sp1"
ResponseLocation="http://sp1.example.com:8080/saml/fedletSloPOST/metaAlias/sp1"/>
<SingleLogoutService
Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP"
Location="http://sp1.example.com:8080/saml/fedletSloSoap/metaAlias/sp1"/>
<NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</NameIDFormat>
<AssertionConsumerService
isDefault="true"
index="0"
Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
Location="http://sp1.example.com:8080/saml/fedletapplication/metaAlias/sp1"/>
<AssertionConsumerService
index="1"
Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact"
Location="http://sp1.example.com:8080/saml/fedletapplication/metaAlias/sp1"/>
</SPSSODescriptor>
<RoleDescriptor
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:query="urn:oasis:names:tc:SAML:metadata:ext:query"
xsi:type="query:AttributeQueryDescriptorType"
protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
</RoleDescriptor>
<XACMLAuthzDecisionQueryDescriptor
WantAssertionsSigned="false"
protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
</XACMLAuthzDecisionQueryDescriptor>
</EntityDescriptor>
<!--
- sp1-extended.xml
- Set the entityID.
- Set the SPSSOConfig metaAlias attribute.
- Set the value of appLogoutUrl.
- Set the value of hosted to 1.
-->
<EntityConfig xmlns="urn:sun:fm:SAML:2.0:entityconfig"
xmlns:fm="urn:sun:fm:SAML:2.0:entityconfig"
hosted="1"
entityID="sp1">
<SPSSOConfig metaAlias="/sp1">
<Attribute name="description">
<Value></Value>
</Attribute>
<Attribute name="signingCertAlias">
<Value></Value>
</Attribute>
<Attribute name="encryptionCertAlias">
<Value></Value>
</Attribute>
<Attribute name="basicAuthOn">
<Value>false</Value>
</Attribute>
<Attribute name="basicAuthUser">
<Value></Value>
</Attribute>
<Attribute name="basicAuthPassword">
<Value></Value>
</Attribute>
<Attribute name="autofedEnabled">
<Value>false</Value>
</Attribute>
<Attribute name="autofedAttribute">
<Value></Value>
</Attribute>
<Attribute name="transientUser">
<Value>anonymous</Value>
</Attribute>
<Attribute name="spAdapter">
<Value></Value>
</Attribute>
<Attribute name="spAdapterEnv">
<Value></Value>
</Attribute>
<Attribute name="fedletAdapter">
<Value>com.sun.identity.saml2.plugins.DefaultFedletAdapter</Value>
</Attribute>
<Attribute name="fedletAdapterEnv">
<Value></Value>
</Attribute>
<Attribute name="spAccountMapper">
<Value>com.sun.identity.saml2.plugins.DefaultLibrarySPAccountMapper</Value>
</Attribute>
<Attribute name="useNameIDAsSPUserID">
<Value>false</Value>
</Attribute>
<Attribute name="spAttributeMapper">
<Value>com.sun.identity.saml2.plugins.DefaultSPAttributeMapper</Value>
</Attribute>
<Attribute name="spAuthncontextMapper">
<Value>com.sun.identity.saml2.plugins.DefaultSPAuthnContextMapper</Value>
</Attribute>
<Attribute name="spAuthncontextClassrefMapping">
<Value>
urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport|0|default
</Value>
</Attribute>
<Attribute name="spAuthncontextComparisonType">
<Value>exact</Value>
</Attribute>
<Attribute name="attributeMap">
<Value>employeenumber=employeenumber</Value>
<Value>mail=mail</Value>
</Attribute>
<Attribute name="saml2AuthModuleName">
<Value></Value>
</Attribute>
<Attribute name="localAuthURL">
<Value></Value>
</Attribute>
<Attribute name="intermediateUrl">
<Value></Value>
</Attribute>
<Attribute name="defaultRelayState">
<Value></Value>
</Attribute>
<Attribute name="appLogoutUrl">
<Value>http://sp1.example.com:8080/saml/logout</Value>
</Attribute>
<Attribute name="assertionTimeSkew">
<Value>300</Value>
</Attribute>
<Attribute name="wantAttributeEncrypted">
<Value></Value>
</Attribute>
<Attribute name="wantAssertionEncrypted">
<Value></Value>
</Attribute>
<Attribute name="wantNameIDEncrypted">
<Value></Value>
</Attribute>
<Attribute name="wantPOSTResponseSigned">
<Value></Value>
</Attribute>
<Attribute name="wantArtifactResponseSigned">
<Value></Value>
</Attribute>
<Attribute name="wantLogoutRequestSigned">
<Value></Value>
</Attribute>
<Attribute name="wantLogoutResponseSigned">
<Value></Value>
</Attribute>
<Attribute name="wantMNIRequestSigned">
<Value></Value>
</Attribute>
<Attribute name="wantMNIResponseSigned">
<Value></Value>
</Attribute>
<Attribute name="responseArtifactMessageEncoding">
<Value>URI</Value>
</Attribute>
<Attribute name="cotlist">
<Value>Circle of Trust</Value></Attribute>
<Attribute name="saeAppSecretList">
</Attribute>
<Attribute name="saeSPUrl">
<Value></Value>
</Attribute>
<Attribute name="saeSPLogoutUrl">
</Attribute>
<Attribute name="ECPRequestIDPListFinderImpl">
<Value>com.sun.identity.saml2.plugins.ECPIDPFinder</Value>
</Attribute>
<Attribute name="ECPRequestIDPList">
<Value></Value>
</Attribute>
<Attribute name="ECPRequestIDPListGetComplete">
<Value></Value>
</Attribute>
<Attribute name="enableIDPProxy">
<Value>false</Value>
</Attribute>
<Attribute name="idpProxyList">
<Value></Value>
</Attribute>
<Attribute name="idpProxyCount">
<Value>0</Value>
</Attribute>
<Attribute name="useIntroductionForIDPProxy">
<Value>false</Value>
</Attribute>
<Attribute name="spSessionSyncEnabled">
<Value>false</Value>
</Attribute>
<Attribute name="relayStateUrlList">
</Attribute>
</SPSSOConfig>
<AttributeQueryConfig metaAlias="/attrQuery">
<Attribute name="signingCertAlias">
<Value></Value>
</Attribute>
<Attribute name="encryptionCertAlias">
<Value></Value>
</Attribute>
<Attribute name="wantNameIDEncrypted">
<Value></Value>
</Attribute>
<Attribute name="cotlist">
<Value>Circle of Trust</Value>
</Attribute>
</AttributeQueryConfig>
<XACMLAuthzDecisionQueryConfig metaAlias="/pep">
<Attribute name="signingCertAlias">
<Value></Value>
</Attribute>
<Attribute name="encryptionCertAlias">
<Value></Value>
</Attribute>
<Attribute name="basicAuthOn">
<Value>false</Value>
</Attribute>
<Attribute name="basicAuthUser">
<Value></Value>
</Attribute>
<Attribute name="basicAuthPassword">
<Value></Value>
</Attribute>
<Attribute name="wantXACMLAuthzDecisionResponseSigned">
<Value>false</Value>
</Attribute>
<Attribute name="wantAssertionEncrypted">
<Value>false</Value>
</Attribute>
<Attribute name="cotlist">
<Value>Circle of Trust</Value>
</Attribute>
</XACMLAuthzDecisionQueryConfig>
</EntityConfig>
Configuring the Service Provider for Application Two
-
Copy the SAML configuration files
sp1.xmlandsp1-extended.xmlyou created in Configuring the Service Provider for Application One, and save them as$HOME/.openig/SAML/sp2.xmland$HOME/.openig/SAML/sp2-extended.xml. -
In both files, replace all incidences of
sp1withsp2. To prevent unwanted behavior. application two must have different values to application one.
Importing Service Provider Configurations Into OpenAM
For each new protected application, import a SAML 2.0 entity into OpenAM. If you subsequently edit a service provider configuration, import it again.
-
Log in to OpenAM console as administrator.
-
On the Federation tab, select the Entity Providers table and click Import Entity.
The Import Entity Provider page is displayed.
-
For the metadata file, select File and upload
sp1.xml. For the extended data file, select File and uploadsp1-extended.xml. -
Repeat the previous step to upload
sp2.xmlandsp2-extended.xmlforsp2. -
Log out of the OpenAM console.
Preparing OpenIG Configurations
For each new protected application, prepare an OpenIG configuration. The configurations in this section follow the example in OpenIG As a SAML 2.0 Service Provider.
Preparing the Base Configuration File
Edit the base configuration file, $HOME/.openig/config/routes/config.json, so that it does not rebase incoming URLs. The following example file differs from that used in earlier tutorials:
{
"handler": {
"type": "Router"
},
"heap": [
{
"name": "LogSink",
"type": "ConsoleLogSink",
"config": {
"level": "DEBUG"
}
},
{
"name": "capture",
"type": "CaptureDecorator",
"config": {
"captureEntity": true,
"captureContext": true
}
}
]
}
Restart OpenIG to put the configuration changes into effect.
Preparing Routes for Application One
Set up the following routes for application one:
-
$HOME/.openig/config/routes/05-federate-sp1.json, to redirect the request for SAML authentication. After authentication, this route logs the user in to the application. -
$HOME/.openig/config/routes/05-saml-sp1.json, to map attributes from the SAML assertion into the context, and then redirect the request back to the first route.
To prevent unspecified behavior, the keys for session-stored values in the routes for application one, for example, session.sp1Username, must not be the same as those for application two.
{
"handler": {
"type": "DispatchHandler",
"config": {
"bindings": [
{
"condition": "${empty session.sp1Username}",
"handler": {
"type": "StaticResponseHandler",
"config": {
"status": 302,
"reason": "Found",
"headers": {
"Location": [
"http://sp1.example.com:8080/saml/SPInitiatedSSO?metaAlias=/sp1"
]
}
}
}
},
{
"handler": {
"type": "Chain",
"config": {
"filters": [
{
"type": "StaticRequestFilter",
"config": {
"method": "POST",
"uri": "http://app.example.com:8081",
"form": {
"username": [
"${session.sp1Username}"
],
"password": [
"${session.sp1Password}"
]
}
}
}
],
"handler": "ClientHandler"
}
}
}
]
}
},
"condition": "${matches(request.uri.host, 'sp1.example.com') and not matches(request.uri.path, '^/saml')}"
}
{
"handler": {
"type": "SamlFederationHandler",
"config": {
"comment": "Use unique session properties for this SP.",
"assertionMapping": {
"sp1Username": "mail",
"sp1Password": "employeenumber"
},
"authnContext": "sp1AuthnContext",
"sessionIndexMapping": "sp1SessionIndex",
"subjectMapping": "sp1SubjectName",
"redirectURI": "/sp1"
}
},
"condition": "${matches(request.uri.host, 'sp1.example.com') and matches(request.uri.path, '^/saml')}"
}
Preparing Routes for Application Two
Set up the following routes for application two:
-
$HOME/.openig/config/routes/05-federate-sp2.json, to redirect the request for SAML authentication. After authentication, this route logs the user in to the application. -
$HOME/.openig/config/routes/05-saml-sp2.json, to map attributes from the SAML assertion into the context, and then redirect the request back to the first route.
To prevent unspecified behavior, the keys for session-stored values in the routes for application two, for example, session.sp2Username, must not be the same as those for application one.
{
"handler": {
"type": "DispatchHandler",
"config": {
"bindings": [
{
"condition": "${empty session.sp2Username}",
"handler": {
"type": "StaticResponseHandler",
"config": {
"status": 302,
"reason": "Found",
"headers": {
"Location": [
"http://sp2.example.com:8080/saml/SPInitiatedSSO?metaAlias=/sp2"
]
}
}
}
},
{
"handler": {
"type": "Chain",
"config": {
"filters": [
{
"type": "StaticRequestFilter",
"config": {
"method": "POST",
"uri": "http://app.example.com:8081",
"form": {
"username": [
"${session.sp2Username}"
],
"password": [
"${session.sp2Password}"
]
}
}
}
],
"handler": "ClientHandler"
}
}
}
]
}
},
"condition": "${matches(request.uri.host, 'sp2.example.com') and not matches(request.uri.path, '^/saml')}"
}
{
"handler": {
"type": "SamlFederationHandler",
"config": {
"comment": "Use unique session properties for this SP.",
"assertionMapping": {
"sp2Username": "mail",
"sp2Password": "employeenumber"
},
"authnContext": "sp2AuthnContext",
"sessionIndexMapping": "sp2SessionIndex",
"subjectMapping": "sp2SubjectName",
"redirectURI": "/sp2"
}
},
"condition": "${matches(request.uri.host, 'sp2.example.com') and matches(request.uri.path, '^/saml')}"
}
Test the Configuration
If you use the example configurations described in this chapter, try the SAML 2.0 web single sign-on profile with application one by selecting either of the following links and logging in to OpenAM with username george and password costanza:
-
The link for SP-initiated SSO.
-
The link for IDP-initiated SSO.
Similarly, try the SAML 2.0 web single sign-on profile with application two by selecting either of the following links and logging in to OpenAM with username george and password costanza:
-
The link for SP-initiated SSO.
-
The link for IDP-initiated SSO.
If you have not configured the examples exactly as shown in this guide, then adapt the SSO links accordingly.