Files: conf/idp.properties, conf/intercept/consent-intercept-config.xml, messages/message.properties, views/intercept/attribute-release.vm, views/intercept/terms-of-use.vm
Format: Native Spring
Overview
The IdP includes the ability to require user consent to attribute release, as well as presenting a "terms of use" message prior to completing a login to a service, a simpler "static" form of consent.
Consent is implemented by the intercept/attribute-release
and intercept/terms-of-use
interceptor flows.
Enabling Module (V4.1+)
For V4.1+, configuring and using this feature requires that you first enable the "idp.intercept.Consent" module if it isn't already enabled. Systems upgraded from older releases generally come pre-enabled due to the prior state of the configuration tree.
(Windows)
C:\opt\shibboleth-idp> bin\module.bat -t idp.intercept.Consent || bin\module.bat -e idp.intercept.Consent
(Other)
$ bin/module.sh -t idp.intercept.Consent || bin/module.sh -e idp.intercept.Consent
Attribute Release Consent
Attribute release consent requires users to accept the release of attributes to Service Providers during front-channel authentication profiles that include attribute data in the response.
Users are prompted to consent to attribute release :
on initial access to service provider resources
on release of an attribute to which consent has not been previously given
when an attribute previously consented to is no longer released
(optionally) when the value of an attribute previously consented to changes, see ConsentConfiguration#Attribute Release Value Comparison.
Attribute release consent is enabled for profiles that do user authentication via the postAuthenticationFlows
property in conf/relying-party.xml.
Default relying party configuration
<bean id="shibboleth.DefaultRelyingParty" parent="RelyingParty">
<property name="profileConfigurations">
<list>
<!-- SAML 1.1 and SAML 2.0 AttributeQuery are disabled by default. -->
<!--
<bean parent="Shibboleth.SSO" p:postAuthenticationFlows="attribute-release" />
<ref bean="SAML1.AttributeQuery" />
<ref bean="SAML1.ArtifactResolution" />
-->
<bean parent="SAML2.SSO" p:postAuthenticationFlows="attribute-release" />
<ref bean="SAML2.ECP" />
<ref bean="SAML2.Logout" />
<!--
<ref bean="SAML2.AttributeQuery" />
-->
<ref bean="SAML2.ArtifactResolution" />
</list>
</property>
</bean>
Per Attribute Consent
To allow users to select the attributes they wish to be released, provided the attributes are not being released via a back-channel exchange, you can set the idp.consent.allowPerAttribute property in conf/idp.properties.
Rejection
When a user rejects consent to attribute release, an AttributeReleaseRejected
event will be signalled. The text presented to the user may be modified via standard ErrorHandlingConfiguration and via messages/messages.properties (see the Messages tab below).
Attribute Display
Localization
The names and values of attributes displayed during consent may be customized. By default, the locale-aware attribute display name and display value are displayed. Customizing the localization information is generally handled through the rules defined in the AttributeRegistryConfiguration.
Selective Display
By default, users are prompted to consent to release all attributes unless specifically suppressed on a per-attribute basis. Suppressed attributes are released to relying parties but are not displayed to users. A prompted list, ignored list, and match expressions determine whether consent should be obtained for an attribute based on the attribute ID.
To prevent an attribute from being displayed, add the attribute ID to the ignored list or exclude it by a match expression. The ignored list and match expression override the prompted list.
Type | Description | Bean name conf/intercept/consent-intercept-config.xml |
---|
Prompted | Attribute IDs for which users should be prompted to consent | shibboleth.consent.attribute-release.PromptedAttributeIDs 4.1
shibboleth.consent.attribute-release.WhitelistedAttributeIDs (DEPRECATED)
|
Ignored | Attribute IDs for which users should not be prompted to consent | shibboleth.consent.attribute-release.IgnoredAttributeIDs 4.1
shibboleth.consent.attribute-release.BlacklistedAttributeIDs (DEPRECATED)
|
Regex | Attribute IDs matching a regular expression that users should be prompted to consent | shibboleth.consent.attribute-release.MatchExpression |
Order
Attributes are, by default, displayed in the natural order of their IDs. Deployers may wish to customize the order in which attributes are displayed to users, in order to present the most relevant or personal attributes first.
The order in which attributes are displayed to users may be customized by providing a list of attribute IDs. Attributes not in the list will still be sorted in their natural order, but subsequent to attributes in the list. Define the bean named shibboleth.consent.attribute-release.AttributeDisplayOrder in conf/intercept/consent-intercept-config.xml representing the desired order. The values of the list are attribute IDs.
The following example displays the mail
attribute first and then all other attributes in alphabetic order by ID:
<bean id="shibboleth.consent.attribute-release.CustomAttributeIDComparator" class="org.example.CustomAttributeIDComparator" />
For advanced customization of the attribute display order, a custom Comparator may be provided. Define a bean named shibboleth.consent.attribute-release.AttributeIDComparator in conf/intercept/consent-intercept-config.xml which implements Comparator<String>. For example:
<bean id="shibboleth.consent.attribute-release.CustomAttributeIDComparator" class="org.example.CustomAttributeIDComparator" />
Consent Duration
Users may choose from three options when deciding the duration of their consent to attribute release. The duration options and descriptive text may be customized via messages/messages.properties.
Duration Option | Description |
---|
"Ask me again at next login" | Users will be prompted to consent to attribute release at every log in. This is implemented by not storing consent. |
| The default. Users will be prompted to consent to attribute release if attributes have changed since consent was previously given. |
"Do not ask me again" | Optional. Users will not be prompted to consent to attribute release again. All attributes will be released to any service provider. The presence of this option is controlled by the idp.consent.allowGlobal property. |
Value Comparison
By default, users are prompted to consent to attribute release if a "new" attribute is released or if an "old" attribute is no longer released. "New" and "old" in this context indicate whether consent has already been given to an attribute ID regardless of the attribute's values. In other words, by default, users are not prompted to consent to attribute release if an attribute's values change.
To prompt users to consent to attribute release if the values of an attribute have changed, set idp.consent.compareValues to true in conf/idp.properties
Prompting users to consent to attribute release if an attribute's value changes requires additional storage capability, because the hash of an attribute's values must be stored for comparison. If client-side storage without use of HTML Local Storage is used to store consent, comparing attribute values may reduce the number of records that may be stored. Since a consent record is stored for every Service Provider, this may increase how often users are prompted to consent to attribute release
Terms Of Use Consent
Consent to terms of use is not enabled by default. To enable it, you will need to :
Configure terms of use message(s) to suit your needs.
Enable the terms of use intercept flow for the cases it should appear.
Configuring Messages
The "terms" are managed as Spring messages via the messages/messages.properties file (or localized versions of it) (see ConsentConfiguration#Messages).
The title and message of the terms of use page displayed to users is derived using the bean named shibboleth.consent.terms-of-use.Key, which defaults to the relying party ID. For example:
https\://sp.example.org = sp-example-org
sp-example-org.title = Example Terms of Use
sp-example-org.text = This is an example Terms of Use
You may add additional terms of use messages and web page titles specific to relying parties using this mechanism. Each key is mapped to a specific prefix used in the corresponding pair of message properties.
Customizing the Mapping of Relying Party to Terms of Use Message and Title
To customize the mapping of relying party to terms of use message and title, you override the shibboleth.consent.terms-of-use.Key bean in conf/intercept/consent-intercept-config.xml. The following example use a utility class available in the Guava library to turn a map into a compliant Function that is composed with a call to lookup the relying party ID. The example isn't all that interesting, but it illustrates the indirection. You can map multiple SPs to a single vale, and so forth.
<bean id="shibboleth.consent.terms-of-use.Key" parent="shibboleth.Functions.Compose">
<constructor-arg name="g">
<bean class="com.google.common.base.Functions" factory-method="forMap" c:defaultValue="terms-of-use">
<constructor-arg name="map">
<map>
<entry key="https://sp.example.org/shibboleth" value="example-terms" />
<entry key="https://other.example.org/shibboleth" value="example-terms" />
</map>
</constructor-arg>
</bean>
</constructor-arg>
<constructor-arg name="f">
<ref bean="shibboleth.RelyingPartyIdLookup.Simple" />
</constructor-arg>
</bean>
Using One Terms of Use Message for Every Relying Party
To configure a single terms of use page for every relying party, override the bean named shibboleth.consent.terms-of-use.Key in conf/intercept/consent-intercept-config.xml:
<bean id="shibboleth.consent.terms-of-use.Key" parent="shibboleth.Functions.Constant">
<constructor-arg value="my-terms"/>
</bean>
and define the terms of use message and title in messages/messages.properties:
my-terms = my-tou
my-tou.title = Example Terms of Use
my-tou.text = This is an example Terms of Use
Enabling Terms Of Use Intercept Flow
To enable the flow, you would add terms-of-use
to the postAuthenticationFlows
profile setting in conf/relying-party.xml.
For example for use with SAML 2.0 requests, replace:
<bean parent="SAML2.SSO" p:postAuthenticationFlows="attribute-release" />
with :
<bean parent="SAML2.SSO" p:postAuthenticationFlows="#{ {'terms-of-use', 'attribute-release'} }" />
Rejection of Terms of Use Consent
When a user rejects consent to terms of use, a TermsRejected
event will be signaled. The text presented to the user may be modified via standard ErrorHandlingConfiguration and via messages/messages.properties (see ConsentConfiguration#Messages).
User Interface
The user interface for the attribute release and terms of use consent flows are implemented as Velocity templates, views/intercept/attribute-release.vm and views/intercept/terms-of-use.vm respectively.
The templates can be customized in a similar way to the login pages and other views.
Messages displayed to users may be localized in the standard Spring way, for example, by providing messages/messages_de.properties. Some translations are already included in the distribution.
Revoking Consent
Users may revoke previous consent choices by selecting a checkbox on the login page (views/login.vm).
The text of the checkbox displayed on the login page is set by the idp.attribute-release.revoke message ID, overridable in messages/messages.properties.
Storage
In order to remember users' consent choices and to prompt users to consent to attribute release if attributes change, users' consent choices must be persisted by a storage service. User consent may be stored either client-side (cookies or HTML5 Local Storage) or server-side (database). The default is to store consent client-side via Local Storage, not out of any particular belief that it makes sense, but because it allows easy deployment of the feature for demonstration.
The storage service used to store consent is configured by the idp.consent.StorageService property in conf/idp.properties
See StorageConfiguration for more on the configuration of various approaches to storage.
Limiting Records
While the default client-side storage solution is HTML Local Storage, it is possible to use cookies. Because cookies provide limited storage capability, the number of stored consent records is also very limited. By default, for client-side storage via cookies, the maximum number of stored consent records is 10. Depending on the number of attributes released and whether released attribute values are compared, the default limit of 10 may be increased. It is not clear what a reasonable default value should be, but it may be significantly higher. We were conservative with the default.
If server-side storage is used, the number of stored consent records should probably be unlimited, represented by a limit of -1 or 0.
The maximum number of stored consent records is configured via the idp.consent.maxStoredRecords property in conf/idp.properties
Retention in V4.0
A map is supported to store numbers which refer to attribute IDs in order to reduce the size of consent storage records if desired. The default mapping of attribute IDs to numbers is defined by the shibboleth.consent.DefaultAttributeSymbolics bean, internally to the system.
Additional mappings may be added to the shibboleth.consent.AttributeSymbolics bean in conf/intercept/consent-intercept-config.xml
Retention in V4.1
The default lifetime for consent storage records is 1 year, and may be configured via idp.consent.storageRecordLifetime in conf/idp.properties. When consent storage records expire, they will no longer by visible via the storage service. Actual timing of deletion is specific to the storage implementation.
Record Format
Consent storage records consist of a key and value, like all StorageRecords.
The (default) storage key for consent records is a concatenation of the user key and the relying party ID, though this is customizeable.
The storage value for consent records is the JSON-serialized form of a Map of Consent objects.
A per-user index is maintained in order to expire or limit the number of stored records.
{
"jdoe:https://sp1.example.org":{
"v":"[{\"id\":307},{\"id\":\"mail\"},{\"id\":\"uid\"}]",
"x":1479847202110
},
"jdoe:https://sp2.example.org":{
"v":"[{\"id\":307},{\"id\":\"mail\"},{\"id\":\"uid\"}]",
"x":1479847206825
},
"jdoe:https://sp3.example.org":{
"v":"[{\"id\":307},{\"id\":\"mail\"},{\"id\":\"uid\",\"appr\":false}]",
"x":1479847566214
},
"jdoe:_key_idx":{
"v":"[\"jdoe:https://sp1.example.org\",\"jdoe:https://sp2.example.org\",\"jdoe:https://sp3.example.org\"]"
}
}
Auditing
By default, consent audit logs are written to logs/idp-consent-audit.log as defined in conf/logback.xml.
The format of consent audit logs are defined by the shibboleth.consent.attribute-release.AuditFormattingMap and shibboleth.consent.terms-of-use.AuditFormattingMap beans in conf/intercept/consent-intercept-config.xml
See ConsentAuditFields and also the AuditLoggingConfiguration for additional fields available without additional work.
Consent flows (like any other interceptor flow) may be run conditionally based on ActivationConditions.
Because the attribute release flow has an internal condition attached already, customizing the condition for it requires combining the default activation condition with the custom activation condition. By default, the attribute release flow is not activated if both (1) attributes are not pushed and (2) per attribute consent is enabled.
The terms of use flow does not have a default activation condition.
Example
Consent flows may be activated based on the presence (or absence) of a particular attribute or value for a user.
The following example activates the attribute release flow if an attribute is present by combining the default activation condition with a custom condition:
V4.0
Example activation condition in conf/intercept/profile-intercept.xml
<bean id="intercept/attribute-release" parent="shibboleth.consent.AttributeReleaseFlow"
p:activationCondition-ref="MyCondition" />
<bean id="MyCondition" parent="shibboleth.Conditions.AND">
<constructor-arg>
<list>
<!-- The default condition from system/conf/profile-intercept-system.xml -->
<bean parent="shibboleth.Conditions.OR">
<constructor-arg>
<bean parent="shibboleth.Conditions.NOT">
<constructor-arg value="%{idp.consent.allowPerAttribute:false}" />
</bean>
</constructor-arg>
<constructor-arg>
<bean class="net.shibboleth.idp.saml.profile.config.logic.IncludeAttributeStatementPredicate" />
</constructor-arg>
</bean>
<!-- A custom condition -->
<bean class="net.shibboleth.idp.profile.logic.SimpleAttributePredicate" p:useUnfilteredAttributes="true">
<property name="attributeValueMap">
<map>
<entry key="eppn">
<list>
<value>*</value>
</list>
</entry>
</map>
</property>
</bean>
</list>
</constructor-arg>
</bean>
V4.1+
To combine your own condition with the system default, you would define your bean in, e.g., conf/global.xml:
Custom condition bean in global.xml
<bean id="example.AttributeReleaseCondition"
class="net.shibboleth.idp.profile.logic.SimpleAttributePredicate"
p:useUnfilteredAttributes="true">
<property name="attributeValueMap">
<map>
<entry key="eppn">
<list>
<value>*</value>
</list>
</entry>
</map>
</property>
</bean>
Then define the property idp.consent.attribute-release.activationCondition to example.AttributeReleaseCondition (your custom bean's ID) to install it and it will be combined with the system's default condition for you
Reference
Beans
Beans defined, or expected to be defined, in conf/intercept/consent-intercept-config.xml :
Bean ID / Type | Function |
---|
shibboleth.consent.terms-of-use.Key Function<ProfileRequestContext,String> | Function which returns the key used to (1) lookup terms of use messages and when concatenated with the user key to (2) lookup storage records, defaults to the relying party ID |
shibboleth.consent.attribute-release.rp.Key 4.1 Function<ProfileRequestContext,String> | Function which returns the key which is concatenated with the user key to lookup storage records, defaults to the relying party ID. Overriding this bean allows a single consent record to cover multiple Relying Parties. |
shibboleth.consent.attribute-release.PromptedAttributeIDs 4.1 shibboleth.consent.attribute-release.WhitelistedAttributeIDs (DEPRECATED) Set<String> | List of attribute IDs for which consent should be obtained |
shibboleth.consent.attribute-release.IgnoredAttributeIDs 4.1 shibboleth.consent.attribute-release.BlacklistedAttributeIDs (DEPRECATED) Set<String> | List of attribute IDs for which consent should not be obtained |
shibboleth.consent.attribute-release.MatchExpression Pattern | Regular expression matching the attribute IDs for which consent should be obtained |
shibboleth.consent.attribute-release.AuditFormattingMap Map<String,List<String>> | Attribute release audit log format, maps logger name to audit fields |
shibboleth.consent.terms-of-use.AuditFormattingMap Map<String,List<String>> | Terms of use audit log format, maps logger name to audit fields |
shibboleth.consent.PreConsentAuditExtractors Map<String,Function<ProfileRequestContext,Object>> | Audit fields in addition to the default fields populated at the start of the consent flow |
shibboleth.consent.ConsentAuditExtractors Map<String,Function<ProfileRequestContext,Object>> | Audit fields in addition to the default fields used when writing the audit log |
shibboleth.consent.AttributeSymbolics Map<String> | Limits storage record size by mapping attribute IDs to numbers |
shibboleth.consent.DefaultAttributeSymbolics Map<String> | Default map that can be used as a parent bean for the bean above to merge in additional values |
shibboleth.consent.AttributeQuery.Condition Predicate<ProfileRequestContext> | Condition that determines whether to apply prior consent decisions in server-side storage to back-channel query requests. Defaults to FALSE. |
Properties
Relevant properties defined in conf/idp.properties :
Property | Type | Default | Function |
---|
idp.consent.StorageService | Bean ID | shibboleth.ClientPersistentStorageService | Name of storage service used to store users' consent choices |
idp.consent.userStorageKey | Bean ID | shibboleth.consent.PrincipalConsentStorageKey | DEPRECATED Name of function used to return the String storage key representing a user, defaults to the principal name |
idp.consent.attribute-release.userStorageKey | Bean ID | shibboleth.consent.PrincipalConsentStorageKey | Replacement for "idp.consent.userStorageKey" specific to attribute-release flow |
idp.consent.terms-of-use.userStorageKey | Bean ID | shibboleth.consent.PrincipalConsentStorageKey | Replacement for "idp.consent.userStorageKey" specific to terms-of-use flow |
idp.consent.userStorageKeyAttribute | String | uid | DEPRECATED Attribute whose value is the storage key representing a user, only used when idp.consent.userStorageKey = shibboleth.consent.AttributeConsentStorageKey |
idp.consent.attribute-release.userStorageKeyAttribute | String | uid | Replacement for "idp.consent.userStorageKeyAttribute" specific to attribute-release flow |
idp.consent.terms-of-use.userStorageKeyAttribute | String | uid | Replacement for "idp.consent.userStorageKeyAttribute" specific to terms-of-use flow |
idp.consent.attribute-release.activationCondition 4.1 | Bean ID | shibboleth.Conditions.TRUE | Optional condition to apply to control activation of attribute-release flow along with system default behavior |
idp.consent.terms-of-use.activationCondition 4.1 | Bean ID | shibboleth.Conditions.TRUE | Optional condition to apply to control activation of terms-of-use flow |
idp.consent.allowDoNotRemember | Boolean | true | Whether not remembering/storing consent is allowed |
idp.consent.allowGlobal | Boolean | true | Whether consent to any attribute and to any relying party is allowed |
idp.consent.allowPerAttribute | Boolean | false | Whether per-attribute consent is allowed |
idp.consent.compareValues | Boolean | false | Whether attribute values and terms of use text are stored and compared for equality |
idp.consent.maxStoredRecords | Integer | 10 | Maximum number of records stored when using space-limited storage (e.g. cookies), 0 = no limit |
idp.consent.expandedMaxStoredRecords | Integer | 0 | Maximum number of records stored when using larger/server-side storage, 0 = no limit |
idp.consent.storageRecordLifetime | Duration | P1Y4.0 Infinite4.1+ | Time in milliseconds to expire consent storage records |
Messages
Relevant messages overridable via messages/messages.properties:
Message | Text |
---|
no-release.title | Title of error page displayed when attribute release consent is rejected |
no-release.message | Text of error page displayed when attribute release consent is rejected |
no-terms.title | Title of error page displayed when terms of use consent is rejected |
no-terms.message | Text of error page displayed when terms of use consent is rejected |
V3 Compatibility
An IdP can be upgraded to the current version without any changes, but there are issues around the sorting and hashing of attribute values that may cause unexpected re-prompting for consent even when the actual values of an attribute haven't changed.
V4.1 resolves this bug going forward but cannot correct the issue retroactively.
Prior to V4.1, avoiding use of consent with multi-valued attributes is one workaround.