ProxyAuthentication
Overview
The IdP supports “proxying”, by which we mean using one of its supported federation or SSO protocols to authenticate a subject by implementing the “relying party” role and making requests to another system operating in the “authenticating party” role in those protocols. In some ways this is “just like any other authentication method” we support, and in others, it introduces a lot of unusual considerations and advanced features to be aware of. It can be very simple, but it can also grow more complex quickly depending on the features of the additional system. For example, a SAML IdP that’s not fully SAML compliant can often cause a great deal of hassle in comparison to one that is.
This page includes some generic discussion of proxying-related features affecting how authentication works in the IdP.
More detailed/specific material and recipes for setting up proxying support can be found for the two supported proxying protocols:
SAML (included with the IdP)
OpenID Connect (via plugin)
Note that there are other cases where we essentially are proxying, specifically the DuoOIDC plugin, which is effectively a specialized case of proxying with OpenID Connect, but with a lot of conventions and assumptions specific to Duo’s service. Over time, they’re evolving it (as we expected) more and more into a generically compatible OP, and it’s not impossible that the two cases might merge in the future. For now that case is still a bit more “specialized”.
Some of the considerations here are still applicable, but were generally handled with more “opinionated” approaches because there are assumptions one can make with Duo that aren’t possible more generally. The biggest are knowing who the subject is in a precise way before proxying, and knowing that the same organization is administratively in control of both systems.
Much of this page relates less to the proxying “act” itself and more to the aspects that affect the rest of the transaction; that is, the original handling of a request and response for a downstream SP/RP for which proxying is being performed simply to carry out the authentication step.
While in general most SP/RP systems using the IdP aren’t going to care about proxying, at least in SAML there are some quite detailed rules built into the standard that govern how proxying should function and allowing constraints to be placed on the IdP in doing so, as well as governing additional information that IdP is supposed to provide about it. Some of these constraints apply to any proxying scenario if the originating SP is using SAML, even if the proxying that takes place uses a different protocol.
You will find that virtually no software beyond Shibboleth supports these proxying controls and that they will be in violation of the standard with regard to how they work if you make use of them. This is familiar territory to Shibboleth deployers. Nevertheless, we are a compliant product and we don’t ignore them and you will have to be aware of them or turn them off.
IdP/OP Discovery
A basic requirement of using most identity protocols as a proxy is knowing which IdP/OP/etc. to redirect to for the authentication (who to proxy to, in other words). While this may often be a single, fixed system identity, in the event that it isn’t some sort of process has to happen to determiine the target system: this is termed IdP Discovery.
The IdP supports calling out to a standardized discovery service. Discovery can be enabled for login flows designed to allow for proxying by enabling the corresponding flow-specific "discoveryRequired" property in authn/authn.properties and setting the idp.authn.discoveryURL property to the location of the discovery service. (Most login flows default to NOT requiring discovery outside of the flows specifically dedicated to proxying.)
A successful discovery result will make the value returned available internally such that it will be used automatically by login flows designed to take advantage of it.
Login flows designed specifically for proxying will also support an additional property used to “bypass” discovery and tell them which system to use explicitly (refer to the documentation for those flows).
Enforcement of Proxying Restrictions
With SAML, at least, there are message elements that have to do with enforcement of proxying restrictions in two different directions, both for determining whether (and to whom) to proxy a request to, and later for enforcing resrictions on accepting proxied authentication when responding. Essentially, the IdP applies rules before it makes a request to another system and when processing the response form it, and we have settings that control this behavior independently.
Upstream Controls
The “upstream” direction is controlled with a SAML element called <Scoping>. It allows an SP to disallow proxying, or to control the length of the proxy chain (as proxying can be repeated in a recursive way). The IdP doesn't normally enforce this for most login flows because it assumes that it isn't proxying. Out of the box, only the formally proxying login flows (SAML and the OpenID RP plugin) are designated to enforce this control, but the flow-specific “proxyScopingEnforced” property can be enabled for any login flow if applicable.
Of course, this also means you can bypass the control when really proxying by turning it off. Doing so is a standards violation and may be a generally slimy thing to do, but that depends on the relationship amongst the various systems. If an organization is operating all of the systems involved itself and an SP has a bug causing it to signal a rule it doesn’t really care about, nobody is going to feel too bad about ignoring it. The IdP just doesn’t know that’s the case unless you tell it that it’s ok to do this.
Downstream Controls
In the “downstream” direction, there are similar restrictions applied based on constraints that could be added by the upstream system, and the global and flow-specific “proxyRestrictionsEnforced” properties control whether this is enforced on the way back in. A global property called idp.authn.proxyRestrictionsEnforced (which can be overridden by flow-specific properties) defaults this to “true” for all flows.
Basically the idea is that an upstream IdP could in theory issue the proxying IdP a SAML Assertion that says “you can’t use this to proxy to another SP”. Since the IdP is only ever accepting an Assertion to do exactly that, it will by default recognize this and fail unless it’s told not to. As in the upstream case, it may be fine to do that if you know the upstream system “doesn’t really mean it” but the IdP can’t assume that.
Translating and Mapping Request/Response Fields
Certain aspects of a request to the IdP can impact the content of the IdP proxied request and conversely the content of a proxied response to the IdP can impact the response it issues to the original relying party.
Forced Authentication
The IdP assumes by default that a request asking for forced authentication (the ForceAuthn flag in SAML, the prompt and max-age in OpenID Connect) should be relayed to a proxied IdP. This can be overridden on a per-IdP basis via Profile Configuration by explicitly turning off the setting.
Note that SAML does not have an equivalent to max-age, so this setting can’t be translated directly; the specific protocols involved in both “legs” of a transaction will affect exactly what the IdP can do.
Authentication Type Mapping
A potentially complex issue arises when it comes to passing SAML <AuthnContextClassRef> and OpenID acr values back and forth across the proxy boundary. In many cases, the values used on the inside and outside have to be different for technical or non-technical reasons. There are a variety of customization options for mapping across the boundary to deal with this problem.
Mapping Based on Standard Constructs
The “expected” means of dealing with this is to assume that the upstream IdP is actually standards-compliant and uses the <AuthnContext>or acr mechanisms to inform the IdP of what happened. The IdP provides pluggable hooks for this case, but defaults to allowing the use of pre-defined map beans to express the relationships in both directions so that you can easily define the rules declaratively without any code.
These map beans are defined in conf/authn/authn-comparison.xml (if you upgraded from an old version, you can compare your copy to the distributed version to see the new settings):
shibboleth.PrincipalProxyRequestMappings
These are rules for turning the values that apply to, or are contained in, SP/RP requests into new values to express in a request issued to a proxied IdP.
shibboleth.PrincipalProxyResponseMappings
These are rules for turning the values that appear in proxied IdP responses into new values to capture in the resulting subject's authentication record and subsequently potentially used in responses to SP/RPs.
These maps contain custom Principal beans that encapsulate the SAML and OpenID “types” needed by the system to distinguish between values used by one protocol vs. another (and of course are not limited to those protcols).
In both cases:
Omitting an explicit mapping rule will result in the value being copied unmodified.
A key with an empty list as a value will be suppressed and not included at all.
Explicit mappings will turn an instance of the key into one or more values.
This is sufficient for most basic proxying scenarios, and is adaptable to all supported protocols now and in the future, but it only works when people demand proper behavior from their software and their vendors. Alas…
Mapping Based on SAML Attributes
Non-compliant products commonly misuse the SAML Attribute feature to provide authentication method information. (This is entirely invalid for a number of reasons.)
There is an advanced hook for this defined on the SAML2.SSO profile configuration, called authnContextTranslationStrategyEx.
This is a complex hook because it has to be very open-ended, but it is a Function that produces a Collection<Principal> to add to the authenticated Subject. (Typically for downstream SAML 2 use, these would be AuthnContextClassRefPrincipal objects but can include the right type for OpenID Connect use or any other Principal class.)
V5.2.1 included a built-in public class to implement this for you using a flexible mapper that will consume decoded IdPAttributes produced during the validation of the proxied response, and can map those into the Principal objects needed for you declaratively. Of course this only handles translation into the Response issued by the proxying IdP since no SP can generally send you an Attribute to signal a requested value, so it’s for the Response direction only.
The class (with doc link) is net.shibboleth.idp.saml.saml2.profile.config.AttributeSourcedAuthnContextTranslationStrategy and an example follows. This would nominally be placed into relying-party.xml and in the example is installed via an override. The class supports passing through values, mapping them to other values, and suppressing/ignoring values from the source IdPAttributes. Only String values are supported.
For the time being, this class can only map from a given String value to a single given Principal and is not implemented with a Multimap to allow for the same key to turn into multiple values. This will be addressed with a new mappingsEx setting added in V5.3 to help with cross-protocol proxying whereby a single source value consumed via any protocol can be expressed in terms of any other protocol.
Attribute/Claim Considerations
Broadly speaking, proxying is used for a couple of different reasons:
Purely authentication
More complex integration of systems and data
While proxying often starts out with a simpler goal of centralizing authentication somewhere external to the IdP (so purely about authentication), it often quickly becomes interesting to pass more data via this channel. In more complex use cases, proxying may be used to connect different organizations' systems (federation), with the proxying IdP acting as an identity gateway with more advanced data requirements.
From the proxying perspective, the IdP provides services to address two attribute-related functions:
Translating information from various complex representations and naming conventions (the Attribute Registry service). The components used to decode information are discussed under these topics:
Controlling what data to accept from the upstream system (the Attribute Filtering service)
These are the two services to configure to minimally allow the proxying IdP to consume and potentially make use of information supplied via SAML, OpenID Connect, or any other protocol.
The Registry service comes pre-packaged with rules for a number of standard (or at least conventional) attributes and has a simple syntax for defining additional rules consuming data of various types. Any data the system doesn’t have a rule for will simply be ignored and dropped on the floor.
Once data is translated/decoded into a form the IdP understands, it still won’t accept data without a filter policy telling it to do so (*). This function is the inverse of the more usual use of the Attribute Filtering service, in that it controls acceptance rather than “release” of data to other systems. It functions identically however; the only difference is the sort of policy rules that make sense to use and the result of an affirmative result (allowing data in rather than allowing data out).
Most of the time, traditional policies in the IdP tend to reference a “Requester”, either directly or indirectly, as the basis of release rules. Acceptance policies, in contrast, tend to be based on “Issuer”-oriented rules.
(*) Notably, some proxying methods may include options you can set to essentially skip the filtering step at least in part by automatically accepting any data for which an explicit policy isn’t defined and applied.
Note that filtering in this direction does not “overlap” with controlling what data might eventually be released to an SP; you can accept data here for whatever purpose without worrying about any leakage out later. The IdP has no capabilities for “globally” releasing any data unless you explicitly create very broad release rules that do so.
Finally, many proxying scenarios may 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 directly/auto-exported attributes within the AttributeResolverConfiguration, and they can be used as-is or leveraged as inputs to other AttributeDefinitions or DataConnectors.
Filtering Example
A simple example of an inbound filtering rule to accept data from a particular IdP:
<AttributeFilterPolicy id="Example-Rule">
<PolicyRequirementRule xsi:type="Issuer" value="https://upstream.example.org/idp" />
<AttributeRule attributeID="samlSubjectID">
<PermitValueRule xsi:type="AND">
<Rule xsi:type="NOT">
<Rule xsi:type="ValueRegex" regex="@"/>
</Rule>
<Rule xsi:type="ScopeMatchesShibMDScope"/>
</PermitValueRule>
</AttributeRule>
</AttributeFilterPolicy>Subject Canonicalization
As with all login flows, when proxying there is still a requirement to process the result into a normalized/simple principal name. Because of the more complex nature of the resulting Subject, this sometimes isn't as trivial to do as with simpler authentication methods. Much as with the X.509 flow, there is no standard "username" to rely on and the system requires more explicit configuration to know what to do.
It is true that both SAML and OpenID Connect have various concepts that one might think of as a username, but the fact is they essentially can carry many different forms of such a thing, and in fact the most standardized ones may not be all that suitable for proxying. In a more typical enterprise proxy scenario, some form of local identifier carried in an Attribute/Claim is quite likely to be better than a more “standard” mechanism designed more for federated use.
The flexibility of these protocols means that extracting something “simple” from them is something you have to instruct the IdP how to do.
Typical Approaches
Generally, the two c14n features to choose from are:
AttributeSourcedPostLoginC14NConfiguration(maps a decoded IdPAttribute value from the upstream token into a principal name)
SAML2ProxyTransformPostLoginC14NConfiguration (an older method used to process the
<NameID>element when proxying with SAML)
If using SAML and you really need to rely on the <NameID> element’s value as your username, with V5.2+ you can now almost certainly ignore the second option in favor of defining a decoding rule for the relevant <NameID> Format to turn it directly into an IdPAttribute and just use the first option. We will eventually deprecate the second option at some point simply due to the additional complexity involved.
With the first option, the result can be pulled directly from data decoded from the upstream IdP or actually can come from the AttributeResolverConfiguration, so there's a lot of flexibility in what to return, but simple passthrough or transform of a SAML <Attribute> (or with V5.2+ a <NameID>) is easy to express. An example below demonstrates this approach.
Note that a common use case is likely to be importing an opaque identifier such as one of the new Subject ID Attributes in SAML or the OpenID sub claim. While it's possible to directly expose such a value as the principal name, it is often the case that a lot of mature proxy deployments would be expected to store and map that value to something in their own IAM systems.
That is not a built-in feature (the IdP tends to do as little “writing of state” as possible), but the most viable path to this 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
Per the advice above, this section outlines a complete example of how to leverage a decoded IdPAttribute as the “username” produced by a proxied login.
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 with those methods, it gets more complex to combine them without breaking things, so take that into consideration. However, this complexity is also greatly reduced with V5.2+ as outlined in the AttributeSourcedPostLoginC14NConfigurationtopic, as you can now create multiple “copies” of this feature that won’t overlap.
Let's say you have an enterprise IdP supplying a standard “employeeNumber” SAML <Attribute>to you and you want to build the principal name directly from that because it’s a common identifier across your IAM stack.
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:2.16.840.1.113730.3.1.3into an IdPAttribute with the ID of "employeeNumber".
As noted under “Attribute/Claim Considerations” earlier, the IdP won't ingest the data to be used in this process unless that data is able to pass through the AttributeFilterConfiguration. So, an filter policy must be added to attribute-filter.xml that “accepts” the IdPAttribute to be used as a username, “employeeNumber” in this example, from the incoming token:
Example inbound filter policy
<AttributeFilterPolicy id="proxy">
<PolicyRequirementRule xsi:type="Issuer" value="https://upstream.example.org/idp" />
<AttributeRule attributeID="employeeNumber" permitAny="true" />
</AttributeFilterPolicy>(Note that this same rule works regardless of the proxying protocol used because once the information is decoded, it’s not handled differently by any subsequent steps.)
Then, you must direct the IdP to use the attribute-sourced post-login c14n method. How will depend on version.
Finally, you must confgure that c14n mthod and direct it to use the decoded "employeeNumber" as a source. The properties to enable this are commented out in conf/c14n/subject-c14n.properties:
conf/c14n/subject-c14n.properties
idp.c14n.attribute.attributeSourceIds = employeeNumber
# Allows direct use of attributes via SAML proxy authn, bypasses resolver
idp.c14n.attribute.resolveFromSubject = true
idp.c14n.attribute.resolutionCondition = shibboleth.Conditions.FALSEThe same general approach demonstrated above works for any IdPAttribute you can map in from the response.
The last property there is important, as it bypasses the actual running of the Attribute Resolver unnecessarily in order to simply pull in a decoded IdPAttribute from the upstream IdP. If instead you need to do something more complex that requires mapping an incoming value via a directory or database, then in fact you may need to run the resolver here and would probably be referencing an IdPAttribute produced by that step.