Discovery helps your Service Provider determine which Identity Provider should be used for authentication of the current user. It is automatically initialized during calls to single sign-on endpoint at scheme://server:port/contextPath/saml/login. SAML Extension supports multiple modes of discovery including the Identity Provider Discovery Service Protocol and Profile.
IDP discovery modes can always be skipped during SSO initialization by specifying HTTP request parameter idp with the entityId of the required IDP, e.g. scheme://server:port/contextPath/saml/login?idp=mySelectedIDP.
The URL where local SP expects discovery response can be included in the SP metadata as one of the extensions. The feature can be enabled by setting property includeDiscoveryExtension to true on bean MetadataGenerator inside MetadataGeneratorFilter, e.g.:
<bean id="metadataGeneratorFilter" class="org.springframework.security.saml.metadata.MetadataGeneratorFilter"> <constructor-arg> <bean class="org.springframework.security.saml.metadata.MetadataGenerator"> <property name="includeDiscoveryExtension" value="true"/> </bean> </constructor-arg> </bean>
The mode is enabled by default and automatically selects the default IDP without performing discovery.
The default IDP can be configured using property defaultIDP on bean metadata in the Spring Security configuration. In case the property isn't set, system will automatically use the first available IDP.
SAML Extension includes a local IDP discovery service which presents user with an IDP selection page. This mode can be enabled by setting property includeDiscovery in the local SP extended metadata to true.
The selection page can be customized using property idpSelectionPath on bean samlIDPDiscovery. System forwards to this page wih a discovery request which includes the following request attributes:
idpDiscoReturnURL - URL to send the IDP selection result to using GET action
idpDiscoReturnParam - name of the GET parameter to include the entity ID of the selected IDP
See the default implementation in sample/src/main/webapp/WEB-INF/security/idpSelection.jsp for an example.
In order to enable external IDP discovery service configure property idpDiscoveryURL in your local SP extended metadata to the external discovery URL. Make sure property idpDiscoveryEnabled is set to true. The remote discovery service needs to support the Identity Provider Discovery Service Protocol and Profile.
Spring SAML Extension supports both SP-initialized and IDP-initialized single sign-on.
SP initialized SSO process can be started in two ways:
User accesses a resource protected by Spring Security which initializes SAMLEntryPoint
User is redirected to the SSO endpoint at e.g. https://www.server.com/context/saml/login
After identification of IDP to use for authentication (for details see Section 9.1, “IDP selection and discovery”), SAML Extension creates an AuthnRequest SAML message and sends it to the selected IDP. Both construction of the AuthnRequest and binding used to send it can be customized using WebSSOProfileOptions object. SAMLEntryPoint determines WebSSOProfileOptions configuration to use by calling method getProfileOptions. The default implementation returns the value specified in property defaultOptions. The method can be overridden to provide custom logic for SSO initialization.
Default settings for WebSSOProfileOptions can be specified in bean samlEntryPoint of your securityContext.xml, e.g.:
<bean id="samlEntryPoint" class="org.springframework.security.saml.SAMLEntryPoint"> <property name="defaultProfileOptions"> <bean class="org.springframework.security.saml.websso.WebSSOProfileOptions"> <property name="binding" value="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"/> <property name="includeScoping" value="false"/> </bean> </property> </bean>
WebSSOProfileOptions supports the following settings:
Table 9.1. org.springframework.security.saml.websso.WebSSOProfileOptions parameters
Property | Description |
---|---|
binding | Default: binding of the first declared SingleSignOnService in IDP metadata. Binding used to send message to IDP. Supported values depend on the SP configuration, typically "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST", "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", "urn:oasis:names:tc:SAML:2.0:bindings:PAOS" and "urn:oasis:names:tc:SAML:2.0:profiles:holder-of-key:SSO:browser". |
providerName | Default: empty. Human readable name of the local SP sent with the authentication request. |
assertionConsumerIndex | Default: empty. When set determines where should IDP send response and which binding to use. Otherwise system uses the default assertion consumer service marked as default, or first applicable. Available indexes can be found in metadata of this service provider. |
nameID | Default: empty. Name ID to request from IDP in the NameIDPolicy. No NameIDPolicy is sent when not specified. Typical values are "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", "urn:oasis:names:tc:SAML:2.0:nameid-format:transient", "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent", "urn:oasis:names:tc:SAML:2.0:nameid-format:encrypted". |
allowCreate | Default: empty. Only applicable when nameID is specified, when true instructs IDP that it is allowed to create new user based on the authentication request. |
passive | Default: false. Sets whether the IdP should refrain from interacting with the user during the authentication process. |
forceAuthn | Default: false. When true IDP is required to re-authenticate user and not rely on previous authentication events. |
includeScoping | Default: true. When true request will include Scoping element. |
allowedIDPs | Default: empty. Values to be included in the Scoping element on top of the IDP message is sent to. Only applicable when includeScoping is set to true. |
proxyCount | Default: 2. Determines value to be used in the proxyCount attribute of the scope in the AuthnRequest. Use zero to disable proxying or value >0 to specify how many hops are allowed. Only applicable when includeScoping is set to true. |
authnContexts | Default: empty. Authentication contexts IDP is allowed to use when authenticating user. See the specification for details. |
authnContextComparison | Default: AuthnContextComparisonTypeEnumeration.EXACT. Mechanism used by IDP to determine authentication method to use. See the specification for details. |
relayState | Default: empty. Value is sent to IDP and provided back to SP as part of the authentication response. |
The AuthnRequest message is sent unencrypted on message level. If needed, encryption should be provided by SSL/TLS on transport layer.
Spring SAML supports reception of Unsolicited Response messages (so called IDP-initialized SSO). In this scenario IDP creates a Response object in the same way as if it was replying to an AuthnRequest message sent from SP, but it omits the InResponseTo parameter. Message is then sent to the AssertionConsumerURL of Spring SAML (typically scheme://server:port/contextPath/saml/SSO) using one of the supported bindings. List of all available endpoints and bindings can be found in the metadata of the Spring SAML application.
Received Unsolicited Respose message is processed and validated in exactly the same way as with SP-Initialized SSO.
Support for unsolicited messages can be disabled in the ExtendedMetadata of remote entities using property supportUnsolicitedResponse.
Spring SAML Extension supports both Local Logout and Single Logout mechanisms.
Local logout terminates only the local session and doesn't affect neither session at IDP, nor sessions at other SPs where user logged in using single sign-on. Local logout can be initialized at scheme://server:port/contextPath/saml/logout?local=true. Call is intercepted by bean samlLogoutFilter which can be configured with the following settings:
Instance of interface org.springframework.security.web.authentication.logout.LogoutSuccessHandler (constructor index 0) which determines operation to perform after successful logout (e.g. redirect to a logout landing page). By default user gets redirected to page logout.jsp.
Instances of interface org.springframework.security.web.authentication.logout.LogoutHandler (constructor index 1) which are responsible for destruction of user's session. The default handler org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler logs the user out by removing the Authentication object, but leaves the HTTP session opened.
It is also possible to configure local logout using standard Spring Security element <security:logout> inside <security:http> block. For example:
<security:http> <security:logout logout-url="/j_logout" logout-success-url="/logout.jsp"/> </security:http>
Global logout implements the SAML 2.0 Single Logout profile which terminates both session at the current SP, the IDP session and sessions at other SPs connected to the same IDP session. Single Logout can be initialized from any of the participating SPs or from the IDP.
Single Logout is currently supported with HTTP-Redirect and HTTP-POST bindings. SOAP binding is not available.
Global logout can be initialized at scheme://server:port/contextPath/saml/logout. System automatically determines which IDP to send the request to based on the currently authenticated user. Single logout can be configured using beans samlLogoutFilter and samlLogoutProcessingFilter with the following options:
Bean samlLogoutFilter can be provided with instances of interface org.springframework.security.web.authentication.logout.LogoutHandler (constructor index 3). The handlers are called before sending SAML 2.0 LogoutRequest to the IDP when initializing Single Logout from the current SP.
Bean samlLogoutProcessingFilter can be provided with instance of interface org.springframework.security.web.authentication.logout.LogoutSuccessHandler (constructor index 0). Handler is called after successful finalization of Single Logout process (reception of LogoutResponse from IDP) and determines operation to perform after logout (e.g. redirect to a logout landing page). By default user gets redirected to page logout.jsp.
Bean samlLogoutProcessingFilter can be provided with instances of interface org.springframework.security.web.authentication.logout.LogoutHandler (constructor index 1). The handlers are called after successful reception of SAML 2.0 LogoutRequest or LogoutResponse from the IDP.
Spring SAML correctly handles SAML 2.0 LogoutRequest messages sent from the IDP and performs logout in case the message is valid. In case of invalid data (missing signature, invalid issuer, invalid issue time, invalid destination, invalid session index, invalid name ID, no user logged in) system responds with SAML 2.0 LogoutResponse with an error Status code.
Successful authentication using SAML token results in creation of an Authentication object by the SAMLAuthenticationProvider. By default instance of org.springframework.security.providers.ExpiringUsernameAuthenticationToken is created. Content of the resulting object can be customized by setting properties of the samlAuthenticationProvider bean in the securityContext.xml. An instance of org.springframework.security.saml.userdetails.SAMLUserDetailsService can be provided to supply application-specific information about the authenticated user.
The Authentication object will by default include string version of the NameID included in the SAML Assertion as itsprincipal. Property forcePrincipalAsString can be used to change this to include the raw NameID element.
The Authentication object is available in pages secured with Spring Security using SecurityContextHolder.getContext().getAuthentication() and is populated with the following values:
Table 9.2. ExpiringUsernameAuthenticationToken values.
Property | Value |
---|---|
Principal | When forcePrincipalAsString = true (default) - String value of NameID included in the SAML Assertion (credential.getNameID().getValue() of type java.lang.String) |
Principal | When forcePrincipalAsString = false AND userDetail = null (default) - NameID object included in the SAML Assertion (credential.getNameID() of type org.opensaml.saml2.core.NameID) |
Principal | When forcePrincipalAsString = false AND userDetail != null - UserDetail object returned from the SAMLUserDetailsService |
Credentials | SAML authentication object including entity ID of local and remote entity, name ID, assertion and relay state (org.springframework.security.saml.SAMLCredential) |
Authorities | Result of getAuthorities() call on the UserDetails object returned from SAMLUserDetailsService, empty list when there's no UserDetail object available. |
Expiration | Value of SessionNotOnOrAfter in the SAML Assertion when avaialble, null otherwise. Authentication object will start returning false on the isAuthenticated() after the expiration time. |
Custom implementation of the SAMLUserDetailsService can be provided as property userDetails of the SAMLAuthenticationProvider. Implementation can perform operation such as parsing of attributes present in the SAML Assertion, e.g.:
package fi.schafer.test.saml; import org.opensaml.saml2.core.Attribute; import org.opensaml.xml.XMLObject; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.saml.SAMLCredential; import org.springframework.security.saml.userdetails.SAMLUserDetailsService; public class TestUserDetails implements SAMLUserDetailsService { @Override public Object loadUserBySAML(SAMLCredential cred) throws UsernameNotFoundException { return cred.getAttributeAsString("accountID"); } }
Population of the authentication object can be further customized by overriding of the getUserDetails, getPrincipal, getEntitlements and getExpirationDate methods in the SAMLAuthenticationProvider.
Assertion used to authenticate user is stored in the SAMLCredential object under property authenticationAssertion. By default the original content (DOM) of the assertion is discarded and system only keeps an unmarshalled version which might slightly differ from the original, e.g. in white-spaces. In order to instruct Spring SAML to keep the assertion in the original form (keep its DOM) set property releaseDOM to false on bean WebSSOProfileConsumerImpl.
Assertion can be serialized to String using the following call:
XMLHelper.nodeToString(SAMLUtil.marshallMessage(credential.getAuthenticationAssertion()))
Key events such as single sign-on and single logout initialization, success or failure can be logged for creation of an audit trail. A custom logger can be created by implementing interface org.springframework.security.saml.log.SAMLLogger and including its bean in the securityContext.xml, e.g.:
<bean id="samlLogger" class="org.springframework.security.saml.log.SAMLDefaultLogger"/>
Two basic implementations are provided by default:
org.springframework.security.saml.log.SAMLEmptyLogger
Doesn't perform any logging, simply ignores all events.
org.springframework.security.saml.log.SAMLDefaultLogger
Logs events as INFO level messages to the log name org.springframework.security.saml.log.SAMLDefaultLogger configurable as described in Section 6.6, “Logging”. Setting property logMessages to true will include content of the SAML messages as part of the log. Logging of exceptions can be disabled by setting logErrors to false. Fields are semicolon separated with the following values:
type of SAML message (AuthNRequest, AuthNResponse, LogoutRequest or LogoutResponse)
result of processing (SUCCESS or FAILURE)
IP address of the peer who made the current request to SP
entity ID of the local SP
entity ID of the remote IDP
identifier of the authenticated user
SAML message (when logMessages is enabled)
text of the error (only for failures, when logErrors is enabled)
The logger is only called for messages which can be correctly received and parsed. For errors which occur before correct parsing see Section 6.5, “Error handling”.