1) The SAMLTokenProvider
The SAMLTokenProvider can issue SAML 1.1 and SAML 2.0 tokens. To request a SAML 1.1 token, the client must use one of the following Token Types:
- http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1
- urn:oasis:names:tc:SAML:1.0:assertion
- http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0
- urn:oasis:names:tc:SAML:2.0:assertion
- List<AttributeStatementProvider> attributeStatementProviders - A list of objects that can add attribute statements to the token.
- List<AuthenticationStatementProvider> authenticationStatementProviders - A list of objects that can add authentication statements to the token.
- List<AuthDecisionStatementProvider> authDecisionStatementProviders - A list of objects that can add authorization decision statements to the token.
- SubjectProvider subjectProvider - An object used to add a Subject to the token.
- ConditionsProvider conditionsProvider - An object used to add a Conditions statement to the token.
- boolean signToken - Whether to sign the token or not. The default is true.
- Map<String, SAMLRealm> realmMap - A map of realms to SAMLRealm objects.
2) Realms in the TokenProviders
As explained in the previous post, the TokenProvider interface has a method that takes a realm parameter:
- boolean canHandleToken(String tokenType, String realm) - Whether this TokenProvider implementation can provide a token of the given type, in the given realm
Unlike the SCTProvider, the SAMLTokenProvider does not ignore the realm parameter to the "canHandleToken" method. Recall that the SAMLTokenProvider has a property "Map<String, SAMLRealm> realmMap". The canHandleToken method checks to see if the given realm is null, and if it is not null then the realmMap *must* contain a key which matches the given realm. So if the STS implementation is designed to issue tokens in different realms, then the realmMap of the SAMLTokenProvider must contain the corresponding realms in the key-set of the map.
The realmMap property maps realm Strings to SAMLRealm objects. The SAMLRealm class contains the following properties:
- String issuer - the Issuer String to use in this realm
- String signatureAlias - the keystore alias to use to retrieve the private key the SAMLTokenProvider uses to sign the generated token
- void setSignatureUsername(String signatureUsername)
- void setIssuer(String issuer)
- void setCallbackHandler(CallbackHandler callbackHandler)
- void setSignatureCrypto(Crypto signatureCrypto)
3) Populating SAML Tokens
In the previous section we covered how a generated SAML token is signed, how to configure the key used to sign the assertion, and how to set the Issuer of the Assertion. In this section we will describe how to populate the SAML Token itself. The SAMLTokenProvider is designed to be able to issue a wide range of SAML Tokens. It does this by re-using the SAML abstraction library that ships with Apache WSS4J, which defines a collection of beans that are configured and then assembled in a CallbackHandler to create a SAML assertion.
3.1) Configure a Conditions statement
The SAMLTokenProvider has a "ConditionsProvider conditionsProvider" property, which can be used to configure the generated Conditions statement which is added to the SAML Assertion. The ConditionsProvider has a method to return a ConditionsBean object, and a method to return a lifetime in seconds. The ConditionsBean holds properties such as the not-before and not-after dates, etc. The SAMLTokenProvider ships with a default ConditionsProvider implementation that is used to insert a Conditions statement in every SAML token that is generated. This implementation uses a default lifetime of 5 minutes, and set the Audience Restriction URI of the Conditions Statement to be the received "AppliesTo" address, which is obtained from the TokenProviderParameters object.
The DefaultConditionsProvider can be configured to change the lifetime of the issued token. If you want to remove the ConditionsProvider altogether from the generation assertion, or implement a custom Conditions statement, then you must implement an instance of the ConditionsProvider interface, and set it on the SAMLTokenProvider.
3.2) Configure a Subject
The SAMLTokenProvider has a "SubjectProvider subjectProvider" property, which can be used to configure the Subject of the generated token, regardless of the version of the token. The SubjectProvider interface defines a single method to return a SubjectBean, given the token provider parameters, the parent Document of the assertion, and a secret key to use (if any). The SubjectBean contains the Subject name, name-qualifier, confirmation method, and KeyInfo element, amongst other properties. The SAMLTokenProvider ships with a default SubjectProvider implementation that is used to insert a Subject into every SAML Token that is generated.
The DefaultSubjectProvider has a single configuration method to set the subject name qualifier. It creates a subject confirmation method by checking the received key type. The subject name is the name of the principal obtained from TokenProviderParameters. Finally, a KeyInfo element is set on the SubjectBean under the following conditions:
- If a "SymmetricKey" Key Type algorithm is specified by the client, then the secret key passed through to the SubjectProvider is encrypted with the X509Certificate of the recipient, and added to the KeyInfo element. How the provider knows the public key of the recipient will be covered later.
- If a "PublicKey" KeyType algorithm is specified by the client, the X509Certificate that is received as part of the "UseKey" request is inserted into the KeyInfo element of the Subject.
To add a custom Subject element to an assertion, you must create your own SubjectProvider implementation, and set it on the SAMLTokenProvider.
3.3) Adding Attribute Statements
The SAMLTokenProvider has a "List<AttributeStatementProvider> attributeStatementProviders" property, which can be used to add AttributeStatments to the generated assertion. Each object in the list adds a single Attribute statement. The AttributeStatementProvider contains a single method to return an AttributeStatementBean given the TokenProviderParameters object. This contains a SubjectBean (for SAML 1.1 assertions), and a list of AttributeBeans. The AttributeBean object holds the attribute name/qualified-name/name-format, and a list of attribute values, amongst other properties.
If no statement provider is configured in the SAMLTokenProvider, then the DefaultAttributeStatementProvider is invoked to create an Attribute statement to add to the assertion. It creates a default "authenticated" attribute, and also creates separate Attributes for any "OnBehalfOf" or "ActAs" elements that were received in the request. If the received OnBehalfOf/ActAs element was a UsernameToken, then the username is added as an Attribute. If the received element was a SAML Assertion, then the subject name is added as an Attribute.
3.4) Adding Authentication Statements
The SAMLTokenProvider has a "List<AuthenticationStatementProvider> authenticationStatementProviders" property, which can be used to add AuthenticationStatements to the generated assertion. Each object in the list adds a single Authentication statement. The AuthenticationStatementProvider contains a single method to return an AuthenticationStatementBean given the TokenProviderParameters object. This contains a SubjectBean (for SAML 1.1 assertions), an authentication instant, authentication method, and other properties. No default implementation of the AuthenticationStatementProvider interface is provided in the STS, so if you want to issue Authentication Statements you will have to write your own.
3.5) Adding Authorization Decision Statements
The SAMLTokenProvider has a "List<AuthDecisionStatementProvider> authDecisionStatementProviders" property, which can be used to add AuthzDecisionStatements to the generated assertion. Each object in the list adds a single statement. The AuthDecisionStatementProvider contains a single method to return an AuthDecisionStatementBean given the TokenProviderParameters object. This contains a SubjectBean (for SAML 1.1 assertions), the decision (permit/indeterminate/deny), the resource URI, a list of ActionBeans, amongst other properties. No default implementation of the AuthDecisionStatementProvider interface is provided in the STS.
Note that for SAML 1.1 tokens, the Subject is embedded in one of the Statements. When creating a SAML 1.1 Assertion, if a given Authentication/Attribute/AuthzDecision statement does not have a subject, then the standalone Subject is inserted into the statement. Finally, once a SAML token has been created, it is stored in the cache (if one is configured), with a lifetime corresponding to that of the Conditions statement. A TokenProviderResponse object is created with the DOM representation of the SAML Token, the SAML Token ID, lifetime, entropy bytes, references, etc.