SAMLAuthnConfiguration

Current File(s): conf/authn/saml-authn-config.xml (V4.0), conf/authn/authn.properties (V4.1+), conf/relying-party.xml
Format: Native Spring, Properties (V4.1+)

Overview

The authn/SAML login flow supports the use of a separate SAML 2.0 Identity Provider to authenticate the subject, with the IdP acting as a SAML proxy. This can be accomplished using additional glue via the External login flow, but this flow provides native SAML support with additional features and flexibility without the need to deploy a separate SAML Service Provider implementation.

The majority of this flow's behavior is actually derived from pre-existing configuration sources, in particular the use of SAML metadata (but for the IdP rather than the more usual SP metadata), and, if required, the use of RelyingPartyConfiguration to override behavior for specific IdPs.

The rest of the configuration needed is generally for the decoding and mapping/propagation of Attribute data from SAML, into the IdP, and usually on to SPs, which is common to most proxy authentication scenarios.

General Configuration

There are only two core requirements for this flow to operate:

  • The entityID of the Identity Provider to use must be known somehow.

  • SAML metadata for that Identity Provider must be available via the usual MetadataConfiguration.

The entityID to use can be established either via static configuration, a lookup function, or prior to this flow running by providing some form of IdP Discovery service and toggling the flow's discoveryRequired property (as discussed more generally in the AuthenticationConfiguration topic).

If some form of user interface is required to perform discovery, this must be achieved by implementing that in form of a compliant Discovery service (the standard protocol is based on simple redirects, so is readily implementable).

For simpler cases, a bean named shibboleth.authn.SAML.discoveryFunction can be defined in authn/saml-authn-config.xml to produce the entityID of the IdP to use.

For simpler cases, the idp.authn.SAML.proxyEntityID property can be set, to directly specify an IdP to use, or a bean named shibboleth.authn.SAML.discoveryFunction can be defined (using global.xml is fine) to produce the entityID of the IdP to use.

The same SAML metadata that would drive any typical Shibboleth SP is supported, and (apart from testing) should of course be refreshably-sourced from a trusted third-party with appropriate constraints on validity window, signature validation, and so forth applied.

Metadata representing the SP "half" of the Shibboleth IdP using this feature is of course also standard:

  • The IdP's entityID is presumed to be the same one applying to normal outbound use, but can be overridden if required.

  • The <AssertionConsumerService> for the IdP-acting-as-SP supports only the HTTP-POST binding and is located at the path "/idp/profile/Authn/SAML2/POST/SSO".

  • Encryption is fully supported; you may not have bothered doing anything with the existing encryption key generated at install time but it will be used by default to decrypt incoming SAML and its certificate would need to be published in the metadata you give to the proxied IdP. Of course you are free not to do this and accept plaintext assertions.

  • If you wish to sign requests, you would also need to include the IdP’s usual signing certificate. Note that the primary use case for this is logout, but the proxying support doesn't include any logout integration at present. If logout is making fire, logout and proxying together is quantum physics.

Post-Processing

The bulk of the configuration of this flow is actually not core to the flow's operation but the post-processing of the SAML Assertions that are returned in a successful response.

Attribute Extraction and Filtering

There are two sources of IdPAttribute extraction possible:

The first is a largely automated process to decode SAML Attributes based on standard rules, possibly supplemented by custom rules.

The second is an extension point for implementing any custom requirements you have, primarily for extracting data from the surrounding SAML Assertion or Response into information to operate on. The function is of Java type Function<ProfileRequestContext,Collection<IdPAttribute>> and may be defined in conf/global.xml (V4.1+) or in the legacy conf/authn/saml-authn-config.xml file in older versions.

Once the results have been produced, the AttributeFilterConfiguration is applied to the results. This is a reversal of the usual filtering process of outbound data and operates with the proxying IdP itself as the "requester" and the proxied IdP as the "issuer". It is also often desirable to apply "scope filtering" based on metadata when defining acceptance rules for scoped attributes.

If discriminating based on the issuer isn't sufficient, "Inbound" and "Outbound" policy rules are now provided for limiting policies based on the direction of the transaction.

Example

An IdP proxying for a dedicated system with some discrete attributes might have a filter policy such as the following:

Example inbound filter policy
1 2 3 4 5 6 7 8 9 10 <AttributeFilterPolicy id="proxy"> <PolicyRequirementRule xsi:type="Issuer" value="https://proxy.example.org" /> <AttributeRule attributeID="givenName" permitAny="true" /> <AttributeRule attributeID="sn" permitAny="true" /> <AttributeRule attributeID="mail" permitAny="true" /> <AttributeRule attributeID="eduPersonPrincipalName"> <PermitValueRule xsi:type="ScopeMatchesShibMDScope" /> </AttributeRule> </AttributeFilterPolicy>

Subject Population

As with any other login flow, the product will be an AuthenticationResult containing a Java Subject with some number of Principal objects.  Unlike many of the flows, the source of the Principals is somewhat more complex and flexible, and does not generally lend itself to a trivial determination of a principal name during subject canonicalization.

The content of the Subject produced consists of the following:

  • If the incoming Assertion(s) contained a <saml2:NameID> element, then a NameIDPrincipal is included that wraps that data.

  • The incoming <saml2:AuthnContext> element is used to construct one or more Principal objects that represent the quality of the authentication peformed. This is discussed further below.

  • A ProxyAuthenticationPrincipal is included that will contain the chain of proxied authorities used, beginning with the immediate assertion issuer and including any additional authorities identities in the <saml2:AuthnStatement>, if any.

  • Any IdPAttribute objects resulting from the initial extraction and filtering stage are wrapped in IdPAttributePrincipals.

Most of that is automatic, but the processing of the SAML Authentication Context information is potentially complex because of the need to potentially translate the information received into a different form to align to the values the IdP uses internally and/or passes on to downstream SPs. By default, the information passes through a built-in translation function that relies on a map defined in conf/authn/authn-comparison.xml, discussed in the AuthenticationConfiguration topic. It's possible to override this translation behavior using the profile configuration (see Advanced Behavior below), but this is only required for interoperation with broken SAML products.

Subject Canonicalization

As with all login flows, there is a requirement to process the AuthenticationResult into a normalized principal name but because of the more complex nature of the resulting Subject, this isn't a trivial thing to do. Much as with the X.509 flow, there is no simple "username" to rely on and the system requires some dedicated configuration to know what to do. Generally, either the attribute or SAML2ProxyTransform c14n flows are used, depending on whether the source of the principal name is based on a SAML Attribute or NameID, respectively.

In the former case, the result can be pulled directly from the upstream IdP or actually come from the AttributeResolverConfiguration, so there's a lot of flexibility in what to return, but simple passthrough or transform of a SAML Attribute are easy to express. An example below demonstrates this approach for both V4.0 and newer releases (where the configuration to do this was simplified quite a bit).

Using a NameID is strongly discouraged for lots of reasons, not least because it's quite complex to support, but the SAML2ProxyTransform topic discusses how to configure that support.

Note that a common use case is likely to be importing an opaque identifier such as one of the new Subject ID Attributes. While it's possible to directly expose such a value as the principal name, it is probably the case that a lot of mature proxy deployments would be expected to store and map that value to something of their own. That is not exactly a built-in feature at this point, but the most viable path to such a feature at present is probably to leverage a custom post-authentication interceptor that might detect an unlinked value and provide the user interface necessary to establish and store the linking information so that subsequent logins can retrieve the mapping from a typical directory or data source. Such an approach is beyond the scope of the documentation at this point.

Attribute-Sourced C14N Example

Note that this example assumes that the only use of this c14n feature is for this proxying use case. If you have a more complex scenario in which there are local authentication methods used as well and you use this feature for those, it gets more complex to combine them without breaking things, so take that into consideration.

Let's say you have an IdP supplying a standard eduPersonPrincipalName SAML Attribute to you and you want to build the principal name directly from that.

Assuming things are set up in a default manner, there's already a rule in the AttributeRegistryConfiguration defined to map the SAML Attribute named "urn:oid:1.3.6.1.4.1.5923.1.1.1.6" into a scoped IdPAttribute named "eduPersonPrincipalName".

As is mentioned in the attribute filtering and extraction section above, the IdP won't ingest the attribute to be used in this process unless it's also allowed to pass in by the attribute filter configuration. So, an attribute filter policy must be added to attribute-filter.xml that maps the attribute to be used as an identifier, eduPersonPrincipalName in this example, from the incoming assertion:

Example inbound filter policy
1 2 3 4 5 6 7 <AttributeFilterPolicy id="proxy"> <PolicyRequirementRule xsi:type="Issuer" value="https://proxy.example.org" /> <AttributeRule attributeID="eduPersonPrincipalName"> <PermitValueRule xsi:type="ScopeMatchesShibMDScope" /> </AttributeRule> </AttributeFilterPolicy>

After that, the specific approach varies by version.

You will need to add the following to your AttributeResolverConfiguration:

Addition to attribute-resolver.xml
1 2 3 <AttributeDefinition id="canonicalName" xsi:type="SubjectDerivedAttribute" forCanonicalization="true" principalAttributeName="eduPersonPrincipalName" />

The id there is arbitrary, it just delineates the result as a particular piece of data sourced from the proxied IdP and to be used to establish the normalized name. The forCanonicalization flag designates that the source of this is the intermediate result undergoing c14n and never data that is normally floating around the system after authentication.

Once you have that, you must then configure the attribute post-login c14n flow in the typical manner by pointing it at "canonicalName" as its source:

conf/c14n/subject-c14n.xml
1 2 <!-- Remove comment tags to enable Attribute-based c14n --> <bean id="c14n/attribute" parent="shibboleth.PostLoginSubjectCanonicalizationFlow" />
conf/c14n/attribute-sourced-subject-c14n-config.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <!-- A list of attributes to resolve for normalizing the subject. For example, you might intend to lookup a name in a directory based on what the user entered. You can make this an empty list if you just want to resolve everything you normally would. --> <util:list id="shibboleth.c14n.attribute.AttributesToResolve"> <value>canonicalName</value> </util:list> <!-- A list of attributes to search for a value to produce as the normalized subject name. This will normally be something you resolve above. --> <util:list id="shibboleth.c14n.attribute.AttributeSourceIds"> <value>canonicalName</value> </util:list>

With newer versions, all you have to do is to configure the attribute post-login c14n flow via properties by pointing it at "eduPersonPrincipalName" as a source. The properties to enable this are commented out in conf/c14n/subject-c14n.properties and are noted below.

conf/c14n/subject-c14n.xml
1 2 <!-- Remove comment tags to enable Attribute-based c14n --> <bean id="c14n/attribute" parent="shibboleth.PostLoginSubjectCanonicalizationFlow" />
conf/c14n/subject-c14n.properties
1 2 3 4 idp.c14n.attribute.attributeSourceIds = eduPersonPrincipalName # Allows direct use of attributes via SAML proxy authn, bypasses resolver idp.c14n.attribute.resolveFromSubject = true idp.c14n.attribute.resolutionCondition = shibboleth.Conditions.FALSE

The same general approach demonstrated above works for any attribute you can map in from the response.

Attribute Resolution

Finally, many proxying scenarios will often require some form of attribute pass-through or manipulation of data provided by the proxy as outgoing information. As a shorthand, the SubjectDataConnector can be applied to produce internal or even auto-exported attributes within the AttributeResolverConfiguration.

Advanced Behavior

Customizing the flow's behavior is simple in practice, though a bit counter-intuitive: you treat the proxied Identity Provider as a "relying party" and leverage the usual mechanisms in conf/relying-party.xml to alter behavor using the SAML2.SSO profile configuration bean. That is, the same way you customize the behavior of SAML 2.0 SSO for SPs is also used to customize some of the behavior for SAML 2.0 IdPs.

Since SAML 2.0 SSO is normally one of the default supported profiles, the general use of this flow is enabled simply by providing IdP metadata and invoking the flow. If you were to remove the "SAML2.SSO" bean from the DefaultRelyingParty's profileConfigurations property, the flow would fail to operate. In the same way, creating an override that applies to the proxied IdP with a customized bean will adjust the behavior of the profile in a variety of ways that mirror the use of the settings in the opposite direction.

Not all the settings actually apply to the proxying use case, but most of them have an analog interpretation and some are specific to proxying.

There are also some defaults that act somewhat differently depending on which use case is involved. The forceAuthn setting for example can be turned on normally to forcibly apply that option to the request issued, but the defailt is not actually false when proxying but rather passes through whatever setting was in effect from the original request from the SP.

Similarly, the defaultAuthenticationMethods and authnContextComparison properties default to nothing normally but when proxying cause the original SP's requirements to be expressed to the proxied IdP in possibly translated form.

Note that as with all other profile configuration behavior, it's possible to leverage metadata extension tags to drive these settings.

Reference

The beans defined, or expected to be defined, in authn/saml-authn-config.xml follow:

Bean ID / Type / Default

Description

Bean ID / Type / Default

Description

shibboleth.authn.SAML.discoveryFunction

Function<ProfileRequestContext,String>

Used to derive entityID of IdP to use if Discovery is not being performed prior to flow operation

shibboleth.authn.SAML.externalAuthnPath

String

servletRelative:/Authn/SAML2/POST/SSO

Spring Web Flow redirection expression for the IdP's AssertionConsumerService

shibboleth.authn.SAML.externalAuthnPathStrategy

Function<ProfileRequestContext,String>

Optional function bean to override the redirection expression to use for the protected resource

shibboleth.authn.SAML.resultCachingPredicate

Predicate<ProfileRequestContext>

shibboleth.Conditions.TRUE

Bean that can be defined to control whether to preserve the authentication result in an IdP session

shibboleth.authn.SAML.addDefaultPrincipals

Boolean

false

Whether to add the content of the supportedPrincipals property of the underlying flow descriptor to the resulting Subject

shibboleth.authn.SAML.attributeExtractionStrategy

Function<ProfileRequestContext,Collection<IdPAttribute>>

A custom function to produce additional IdPAttributes from the SAML response

The following beans if needed may be defined in global.xml (or if desired the legacy authn/saml-authn-config.xml file may be used or created):

Bean ID / Type

Default

Description

Bean ID / Type

Default

Description

shibboleth.authn.SAML.discoveryFunction

Function<ProfileRequestContext,String>



Used to derive entityID of IdP to use if not using a static property and IdP Discovery is not performed prior to flow operation

shibboleth.authn.SAML.externalAuthnPathStrategy

Function<ProfileRequestContext,String>



Optional function bean to override the redirection expression to use for the protected resource

shibboleth.authn.SAML.resultCachingPredicate

Predicate<ProfileRequestContext>

true

Bean that can be defined to control whether to preserve the authentication result in an IdP session

shibboleth.authn.SAML.attributeExtractionStrategy

Function<ProfileRequestContext,Collection<IdPAttribute>>



Optional custom function to produce additional IdPAttributes from the SAML response

The properties specific to this flow defined in authn/authn.properties are:

Name / Default

Description

Name / Default

Description

idp.authn.SAML.externalAuthnPath

servletRelative:/Authn/SAML2/POST/SSO

Spring Web Flow redirection expression for the IdP's AssertionConsumerService

idp.authn.SAML.proxyEntityID

Statically-defined entityID of IdP to use for authentication

idp.authn.SAML.outboundMessageHandlerFunction

Optional bean ID of Function<ProfileRequestContext,MessageHandlerException> to run just prior to AuthnRequest signing/encoding step

idp.authn.SAML.inboundMessageHandlerFunction

Optional bean ID of Function<ProfileRequestContext,MessageHandlerException> to run at the late stages of Response decoding/processing

idp.authn.SAML.assertionValidator

Optional bean ID of AssertionValidator to run

The general properties configuring this flow via authn/authn.properties are:

Name

Default

Description

Name

Default

Description

idp.authn.SAML.order

1000

Flow priority relative to other enabled login flows (lower is "higher" in priority)

idp.authn.SAML.nonBrowserSupported

false

Whether the flow should handle non-browser request profiles (e.g., ECP)

idp.authn.SAML.passiveAuthenticationSupported

true

Whether the flow allows for passive authentication

idp.authn.SAML.forcedAuthenticationSupported

true

Whether the flow supports forced authentication

idp.authn.SAML.proxyRestrictionsEnforced

%{idp.authn.enforceProxyRestrictions:true}

Whether the flow enforces upstream IdP-imposed restrictions on proxying

idp.authn.SAML.proxyScopingEnforced

true

Whether the flow considers itself to be proxying, and therefore enforces SP-signaled restrictions on proxying

idp.authn.SAML.discoveryRequired

true

Whether to invoke IdP-discovery prior to running flow

idp.authn.SAML.lifetime

%{idp.authn.defaultLifetime:PT1H}

Lifetime of results produced by this flow

idp.authn.SAML.inactivityTimeout

%{idp.authn.defaultTimeout:PT30M}

Inactivity timeout of results produced by this flow

idp.authn.SAML.reuseCondition

shibboleth.Conditions.TRUE

Bean ID of Predicate<ProfileRequestContext> controlling result reuse for SSO

idp.authn.SAML.activationCondition

shibboleth.Conditions.TRUE

Bean ID of Predicate<ProfileRequestContext> determining whether flow is usable for request

idp.authn.SAML.subjectDecorator



Bean ID of BiConsumer<ProfileRequestContext,Subject> for subject customization

idp.authn.SAML.supportedPrincipals

(see below)

Comma-delimited list of protocol-specific Principal strings associated with flow

idp.authn.SAML.addDefaultPrincipals

false

Whether to auto-attach the preceding set of Principal objects to each Subject produced by this flow

While the default principal support is a typical password-centric set, in most cases the addDefaultPrincipals property is left false and the values used in responses will be mapped from the value supplied by the proxied IdP. However, to handle requests properly, the supportedPrincipals property may need to be adjusted to account for the possible values that SPs should be allowed to request.

To replace the internally defined flow descriptor bean, the following XML is required:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <util:list id="shibboleth.AvailableAuthenticationFlows"> <bean p:id="authn/SAML" parent="shibboleth.AuthenticationFlow" p:order="%{idp.authn.SAML.order:1000}" p:nonBrowserSupported="%{idp.authn.SAML.nonBrowserSupported:false}" p:passiveAuthenticationSupported="%{idp.authn.SAML.passiveAuthenticationSupported:true}" p:forcedAuthenticationSupported="%{idp.authn.SAML.forcedAuthenticationSupported:true}" p:proxyRestrictionsEnforced="%{idp.authn.SAML.proxyRestrictionsEnforced:%{idp.authn.enforceProxyRestrictions:true}}" p:proxyScopingEnforced="%{idp.authn.SAML.proxyScopingEnforced:true}" p:discoveryRequired="%{idp.authn.SAML.discoveryRequired:true}" p:lifetime="%{idp.authn.SAML.lifetime:%{idp.authn.defaultLifetime:PT1H}}" p:inactivityTimeout="%{idp.authn.SAML.inactivityTimeout:%{idp.authn.defaultTimeout:PT30M}}" p:reuseCondition-ref="#{'%{idp.authn.SAML.reuseCondition:shibboleth.Conditions.TRUE}'.trim()}" p:activationCondition-ref="#{'%{idp.authn.SAML.activationCondition:shibboleth.Conditions.TRUE}'.trim()}" p:subjectDecorator-ref="#{getObject('%{idp.authn.SAML.subjectDecorator:}'.trim())}"> <property name="supportedPrincipalsByString"> <bean parent="shibboleth.CommaDelimStringArray" c:_0="#{'%{idp.authn.SAML.supportedPrincipals:}'.trim()}" /> </property> </bean> </util:list>

In older versions and upgraded systems, this list is defined in conf/authn/general-authn.xml. In V4.1+, no default version of the list is provided and it may simply be placed in conf/global.xml if needed.

More Examples