[java-identity-provider] branch master updated: IDP-1494 - Login flow for proxied SAML authentication

Scott Cantor cantor.2 at osu.edu
Wed Nov 27 10:14:15 EST 2019


This is an automated email from the git hooks/post-receive script.

scantor pushed a commit to branch master
in repository java-identity-provider.

View the commit online:
http://git.shibboleth.net/view/?p=java-identity-provider.git;a=commit;h=f093b5b8d22ff92c9e7bd48e1944f91c8bebef2d

The following commit(s) were added to refs/heads/master by this push:
       new  f093b5b   IDP-1494 - Login flow for proxied SAML authentication
f093b5b is described below

commit f093b5b8d22ff92c9e7bd48e1944f91c8bebef2d
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Wed Nov 27 10:14:09 2019 -0500

    IDP-1494 - Login flow for proxied SAML authentication
    
    https://issues.shibboleth.net/jira/browse/IDP-1494
    
    Add in processing of Scoping element.
    Add cross-protocol proxy count config control.
    Add option to ignore request content for compatibility.
    Rename some new properties for consistency.
    Some misc. test changes.
---
 .../admin/BasicAdministrativeFlowDescriptor.java   |  48 ++++++-
 .../config/AuthenticationProfileConfiguration.java |  14 ++
 .../idp/authn/context/AuthenticationContext.java   |  55 +++++++-
 .../MockAuthenticationProfileConfiguration.java    |  22 +++
 .../authn/impl/BaseAuthenticationContextTest.java  |   8 +-
 .../idp/cas/config/impl/LoginConfiguration.java    |  44 +++++-
 .../impl/BuildAuthenticationContextAction.java     |   7 +-
 idp-conf/src/main/resources/conf/errors.xml        |   1 +
 .../system/conf/relying-party-mddriven.xml         |   6 +
 .../resources/system/conf/relying-party-system.xml |   3 +
 .../system/flows/authn/proxy/saml-proxy-beans.xml  |   2 +-
 .../system/flows/saml/saml-abstract-beans.xml      |   5 +-
 .../config/BrowserSSOProfileConfiguration.java     |  44 +++++-
 .../config/BrowserSSOProfileConfiguration.java     |   3 +
 .../config/navigate/ProxyCountLookupFunction.java  |  52 +++++++
 .../impl/InitializeAuthenticationContext.java      | 153 ++++++++++++++++++++-
 .../saml/saml2/profile/impl/AddAuthnRequest.java   |   6 +-
 .../profile/impl/ProcessRequestedAuthnContext.java |   6 +-
 .../impl/SAML2StringAttributeTranscoderTest.java   |   9 +-
 .../SAML2XMLObjectAttributeTranscoderTest.java     |  35 +++--
 .../impl/InitializeAuthenticationContextTest.java  | 147 ++++++++++++++++----
 .../impl/ProcessRequestedAuthnContextTest.java     |   3 +-
 22 files changed, 591 insertions(+), 82 deletions(-)

diff --git a/idp-admin-api/src/main/java/net/shibboleth/idp/admin/BasicAdministrativeFlowDescriptor.java b/idp-admin-api/src/main/java/net/shibboleth/idp/admin/BasicAdministrativeFlowDescriptor.java
index 5974034..8263296 100644
--- a/idp-admin-api/src/main/java/net/shibboleth/idp/admin/BasicAdministrativeFlowDescriptor.java
+++ b/idp-admin-api/src/main/java/net/shibboleth/idp/admin/BasicAdministrativeFlowDescriptor.java
@@ -31,6 +31,7 @@ import javax.annotation.Nullable;
 import net.shibboleth.idp.profile.config.AbstractProfileConfiguration;
 import net.shibboleth.idp.profile.config.SecurityConfiguration;
 import net.shibboleth.utilities.java.support.annotation.ParameterName;
+import net.shibboleth.utilities.java.support.annotation.constraint.NonNegative;
 import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
 import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
 import net.shibboleth.utilities.java.support.annotation.constraint.NotLive;
@@ -99,6 +100,9 @@ public class BasicAdministrativeFlowDescriptor extends AbstractProfileConfigurat
     /** Whether to mandate forced authentication for the request. */
     @Nonnull private Predicate<ProfileRequestContext> forceAuthnPredicate;
 
+    /** Lookup function to supply proxyCount property. */
+    @Nonnull private Function<ProfileRequestContext,Integer> proxyCountLookupStrategy;
+    
     /** Builder factory for XMLObjects needed in UIInfo emulation. */
     @Nonnull private final XMLObjectBuilderFactory builderFactory;
     
@@ -123,6 +127,7 @@ public class BasicAdministrativeFlowDescriptor extends AbstractProfileConfigurat
         defaultAuthenticationMethodsLookupStrategy = FunctionSupport.constant(null);
         authenticationFlowsLookupStrategy = FunctionSupport.constant(null);
         postAuthenticationFlowsLookupStrategy = FunctionSupport.constant(null);
+        proxyCountLookupStrategy = FunctionSupport.constant(null);
     }
     
     /** {@inheritDoc} */
@@ -237,7 +242,7 @@ public class BasicAdministrativeFlowDescriptor extends AbstractProfileConfigurat
                     ((SAMLObjectBuilder<org.opensaml.saml.ext.saml2mdui.Logo>) 
                             builderFactory.<org.opensaml.saml.ext.saml2mdui.Logo>getBuilderOrThrow(
                                     org.opensaml.saml.ext.saml2mdui.Logo.DEFAULT_ELEMENT_NAME)).buildObject();
-            logo.setURL(src.getValue());
+            logo.setURI(src.getValue());
             logo.setXMLLang(src.getLang());
             logo.setHeight(src.getHeight());
             logo.setWidth(src.getWidth());
@@ -256,7 +261,7 @@ public class BasicAdministrativeFlowDescriptor extends AbstractProfileConfigurat
             final InformationURL url =
                     ((SAMLObjectBuilder<InformationURL>) builderFactory.<InformationURL>getBuilderOrThrow(
                             InformationURL.DEFAULT_ELEMENT_NAME)).buildObject();
-            url.setValue(s.getValue());
+            url.setURI(s.getValue());
             url.setXMLLang(s.getLang());
             uiInfo.getInformationURLs().add(url);
         }
@@ -273,7 +278,7 @@ public class BasicAdministrativeFlowDescriptor extends AbstractProfileConfigurat
             final PrivacyStatementURL url =
                     ((SAMLObjectBuilder<PrivacyStatementURL>) builderFactory.<PrivacyStatementURL>getBuilderOrThrow(
                             PrivacyStatementURL.DEFAULT_ELEMENT_NAME)).buildObject();
-            url.setValue(s.getValue());
+            url.setURI(s.getValue());
             url.setXMLLang(s.getLang());
             uiInfo.getPrivacyStatementURLs().add(url);
         }
@@ -477,6 +482,43 @@ public class BasicAdministrativeFlowDescriptor extends AbstractProfileConfigurat
     }
 
     /** {@inheritDoc} */
+    @Nullable public Integer getProxyCount(@Nullable final ProfileRequestContext profileRequestContext) {
+        final Integer count = proxyCountLookupStrategy.apply(profileRequestContext);
+        if (count != null) {
+            Constraint.isGreaterThanOrEqual(0, count, "Proxy count must be greater than or equal to 0");
+        }
+        return count;
+    }
+
+    /**
+     * Sets the maximum number of times an assertion may be proxied outbound and/or
+     * the maximum number of hops between the relying party and a proxied authentication
+     * authority inbound.
+     * 
+     * @param count proxy count
+     * 
+     * @since 4.0.0
+     */
+    public void setProxyCount(@Nullable @NonNegative final Integer count) {
+        if (count != null) {
+            Constraint.isGreaterThanOrEqual(0, count, "Proxy count must be greater than or equal to 0");
+        }
+        proxyCountLookupStrategy = FunctionSupport.constant(count);
+    }
+
+    /**
+     * Set a lookup strategy for the maximum number of times an assertion may be proxied outbound and/or
+     * the maximum number of hops between the relying party and a proxied authentication authority inbound.
+     *
+     * @param strategy  lookup strategy
+     * 
+     * @since 4.0.0
+     */
+    public void setProxyCountLookupStrategy(@Nonnull final Function<ProfileRequestContext,Integer> strategy) {
+        proxyCountLookupStrategy = Constraint.isNotNull(strategy, "Lookup strategy cannot be null");
+    }
+    
+    /** {@inheritDoc} */
     @Override public int hashCode() {
         return getId().hashCode();
     }
diff --git a/idp-authn-api/src/main/java/net/shibboleth/idp/authn/config/AuthenticationProfileConfiguration.java b/idp-authn-api/src/main/java/net/shibboleth/idp/authn/config/AuthenticationProfileConfiguration.java
index 4ee4f91..2187fe9 100644
--- a/idp-authn-api/src/main/java/net/shibboleth/idp/authn/config/AuthenticationProfileConfiguration.java
+++ b/idp-authn-api/src/main/java/net/shibboleth/idp/authn/config/AuthenticationProfileConfiguration.java
@@ -27,6 +27,7 @@ import javax.annotation.Nullable;
 import org.opensaml.profile.context.ProfileRequestContext;
 
 import net.shibboleth.idp.profile.config.ProfileConfiguration;
+import net.shibboleth.utilities.java.support.annotation.constraint.NonNegative;
 import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
 import net.shibboleth.utilities.java.support.annotation.constraint.NotLive;
 import net.shibboleth.utilities.java.support.annotation.constraint.Unmodifiable;
@@ -94,6 +95,19 @@ public interface AuthenticationProfileConfiguration extends ProfileConfiguration
     boolean isForceAuthn(@Nullable final ProfileRequestContext profileRequestContext);
     
     /**
+     * Gets the maximum number of times an assertion may be proxied outbound and/or
+     * the maximum number of hops between the relying party and a proxied authentication
+     * authority inbound.
+     * 
+     * @param profileRequestContext current profile request context
+     * 
+     * @return maximum number of times an assertion or authentication may be proxied
+     * 
+     * @since 4.0.0
+     */
+    @NonNegative @Nullable Integer getProxyCount(@Nullable final ProfileRequestContext profileRequestContext);
+    
+    /**
      * Get whether this profile is for functionality local to the IdP.
      * 
      * <p>Most authentication profiles are non-local, designed to issue security tokens to other
diff --git a/idp-authn-api/src/main/java/net/shibboleth/idp/authn/context/AuthenticationContext.java b/idp-authn-api/src/main/java/net/shibboleth/idp/authn/context/AuthenticationContext.java
index fc9abd1..f0aa6cb 100644
--- a/idp-authn-api/src/main/java/net/shibboleth/idp/authn/context/AuthenticationContext.java
+++ b/idp-authn-api/src/main/java/net/shibboleth/idp/authn/context/AuthenticationContext.java
@@ -25,9 +25,11 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.function.Function;
 import java.util.stream.Collectors;
 
@@ -41,6 +43,7 @@ import net.shibboleth.idp.authn.principal.PrincipalSupportingComponent;
 import net.shibboleth.idp.authn.principal.ProxyAuthenticationPrincipal;
 import net.shibboleth.idp.profile.context.RelyingPartyContext;
 import net.shibboleth.utilities.java.support.annotation.constraint.Live;
+import net.shibboleth.utilities.java.support.annotation.constraint.NonNegative;
 import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
 import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
 import net.shibboleth.utilities.java.support.logic.Constraint;
@@ -83,7 +86,13 @@ public final class AuthenticationContext extends BaseContext {
     
     /** Name of a proxied authentication source to use. */
     @Nullable private String authenticatingAuthority;
+    
+    /** Allowable proxy count upstream. */
+    @Nullable @NonNegative private Integer proxyCount;
 
+    /** Allowable proxied sources of authority. */
+    @Nullable @NonnullElements private Set<String> proxiableAuthorities;
+    
     /** Lookup strategy for a fixed event to return from validators for testing. */
     @Nullable private Function<ProfileRequestContext,String> fixedEventLookupStrategy;
     
@@ -132,6 +141,8 @@ public final class AuthenticationContext extends BaseContext {
         stateMap = new HashMap<>();
         
         resultCacheable = true;
+        
+        proxiableAuthorities = new HashSet<>();
     }
 
     /**
@@ -359,7 +370,6 @@ public final class AuthenticationContext extends BaseContext {
      * @since 3.4.0
      */
     @Nonnull public AuthenticationContext setMaxAge(@Nullable final Duration age) {
-        
         Constraint.isFalse(age != null && (age.isNegative() || age.isZero()), "MaxAge must be null or greater than 0");
         
         maxAge = age;
@@ -397,6 +407,49 @@ public final class AuthenticationContext extends BaseContext {
     }
     
     /**
+     * Get the allowable number of hops upstream to permit for proxied authentication.
+     * 
+     * <p>This follows SAML semantics, but is not strictly specific to it.</p>
+     * 
+     * @return proxy count, null for no limit, zero for no proxying
+     * 
+     * @since 4.0.0
+     */
+    @Nullable @NonNegative public Integer getProxyCount() {
+        return proxyCount;
+    }
+    
+    /**
+     * Set the allowable number of hops upstream to permit for proxied authentication.
+     * 
+     * @param count proxy count, null for no limit, zero for no proxying
+     * 
+     * @return this context
+     * 
+     * @since 4.0.0
+     */
+    @Nonnull public AuthenticationContext setProxyCount(@Nullable @NonNegative final Integer count) {
+        if (count != null) {
+            Constraint.isGreaterThanOrEqual(0, count, "Proxy count cannot be negative");
+        }
+        proxyCount = count;
+        return this;
+    }
+    
+    /**
+     * Get a live set of the authorities to which proxying is suggested.
+     * 
+     * <p>This follows SAML semantics and is non-critical, but is not strictly specific to it.</p>
+     * 
+     * @return advisory set of authorities
+     * 
+     * @since 4.0.0
+     */
+    @Nonnull @NonnullElements @Live public Set<String> getProxiableAuthorities() {
+        return proxiableAuthorities;
+    }
+    
+    /**
      * Get optional lookup strategy to return a fixed event to return from credential validation
      * to exercise error and warning logic.
      * 
diff --git a/idp-authn-api/src/test/java/net/shibboleth/idp/authn/config/MockAuthenticationProfileConfiguration.java b/idp-authn-api/src/test/java/net/shibboleth/idp/authn/config/MockAuthenticationProfileConfiguration.java
index 06c6361..a03d514 100644
--- a/idp-authn-api/src/test/java/net/shibboleth/idp/authn/config/MockAuthenticationProfileConfiguration.java
+++ b/idp-authn-api/src/test/java/net/shibboleth/idp/authn/config/MockAuthenticationProfileConfiguration.java
@@ -34,6 +34,7 @@ import com.google.common.base.Predicates;
 import net.shibboleth.idp.authn.config.AuthenticationProfileConfiguration;
 import net.shibboleth.idp.profile.config.AbstractProfileConfiguration;
 import net.shibboleth.idp.profile.config.SecurityConfiguration;
+import net.shibboleth.utilities.java.support.annotation.constraint.NonNegative;
 import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
 import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
 import net.shibboleth.utilities.java.support.annotation.constraint.NotLive;
@@ -60,6 +61,9 @@ public class MockAuthenticationProfileConfiguration extends AbstractProfileConfi
     /** ForceAuthn predicate. */
     @Nonnull private Predicate<ProfileRequestContext> forceAuthnPredicate;
     
+    /** Proxy count. */
+    @Nonnull private Integer proxyCount;
+    
     /**
      * Constructor.
      * 
@@ -161,4 +165,22 @@ public class MockAuthenticationProfileConfiguration extends AbstractProfileConfi
         return forceAuthnPredicate.test(profileRequestContext);
     }
 
+    /** {@inheritDoc} */
+    @Nullable @NonNegative public Integer getProxyCount(@Nullable final ProfileRequestContext profileRequestContext) {
+        return proxyCount;
+    }
+    
+    /**
+     * Set proxy count.
+     * 
+     * @param count the count
+     */
+    public void setProxyCount(@Nullable @NonNegative final Integer count) {
+        if (count != null) {
+            proxyCount = Constraint.isGreaterThanOrEqual(0, count, "Proxy count cannot be negative");
+        } else {
+            proxyCount = null;
+        }
+    }
+
 }
\ No newline at end of file
diff --git a/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/BaseAuthenticationContextTest.java b/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/BaseAuthenticationContextTest.java
index bc5f27f..9a09e09 100644
--- a/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/BaseAuthenticationContextTest.java
+++ b/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/BaseAuthenticationContextTest.java
@@ -22,25 +22,25 @@ import net.shibboleth.idp.authn.context.AuthenticationContext;
 import net.shibboleth.idp.profile.RequestContextBuilder;
 import net.shibboleth.idp.profile.context.navigate.WebflowRequestContextProfileRequestContextLookup;
 
+import java.util.List;
+
 import org.opensaml.core.OpenSAMLInitBaseTestCase;
 import org.opensaml.profile.context.ProfileRequestContext;
 import org.springframework.webflow.execution.RequestContext;
 
-import com.google.common.collect.ImmutableList;
-
 /** Base class for further action tests. */
 public class BaseAuthenticationContextTest extends OpenSAMLInitBaseTestCase {
 
     protected RequestContext src;
     protected ProfileRequestContext prc;
-    protected ImmutableList<AuthenticationFlowDescriptor> authenticationFlows;
+    protected List<AuthenticationFlowDescriptor> authenticationFlows;
 
     protected void initializeMembers() throws Exception {        
         src = new RequestContextBuilder().buildRequestContext();
         prc = new WebflowRequestContextProfileRequestContextLookup().apply(src);
         prc.addSubcontext(new AuthenticationContext(), true);
 
-        authenticationFlows = ImmutableList.of(new AuthenticationFlowDescriptor(),
+        authenticationFlows = List.of(new AuthenticationFlowDescriptor(),
                 new AuthenticationFlowDescriptor(), new AuthenticationFlowDescriptor());
         authenticationFlows.get(0).setId("test1");
         authenticationFlows.get(1).setId("test2");
diff --git a/idp-cas-impl/src/main/java/net/shibboleth/idp/cas/config/impl/LoginConfiguration.java b/idp-cas-impl/src/main/java/net/shibboleth/idp/cas/config/impl/LoginConfiguration.java
index ea5ee9d..850460f 100644
--- a/idp-cas-impl/src/main/java/net/shibboleth/idp/cas/config/impl/LoginConfiguration.java
+++ b/idp-cas-impl/src/main/java/net/shibboleth/idp/cas/config/impl/LoginConfiguration.java
@@ -34,6 +34,7 @@ import com.google.common.base.Predicates;
 
 import net.shibboleth.idp.authn.config.AuthenticationProfileConfiguration;
 import net.shibboleth.idp.saml.authn.principal.AuthnContextClassRefPrincipal;
+import net.shibboleth.utilities.java.support.annotation.constraint.NonNegative;
 import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
 import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
 import net.shibboleth.utilities.java.support.annotation.constraint.NotLive;
@@ -76,6 +77,9 @@ public class LoginConfiguration extends AbstractProtocolConfiguration
     /** Whether to mandate forced authentication for the request. */
     @Nonnull private Predicate<ProfileRequestContext> forceAuthnPredicate;
 
+    /** Lookup function to supply proxyCount property. */
+    @Nonnull private Function<ProfileRequestContext,Integer> proxyCountLookupStrategy;
+    
     /** Creates a new instance. */
     public LoginConfiguration() {
         super(PROFILE_ID);
@@ -85,6 +89,7 @@ public class LoginConfiguration extends AbstractProtocolConfiguration
         defaultAuthenticationContextsLookupStrategy = FunctionSupport.constant(null);
         nameIDFormatPrecedenceLookupStrategy = FunctionSupport.constant(null);
         forceAuthnPredicate = Predicates.alwaysFalse();
+        proxyCountLookupStrategy = FunctionSupport.constant(null);
     }
 
     /** {@inheritDoc} */
@@ -246,7 +251,44 @@ public class LoginConfiguration extends AbstractProtocolConfiguration
     public void setForceAuthnPredicate(@Nonnull final Predicate<ProfileRequestContext> condition) {
         forceAuthnPredicate = Constraint.isNotNull(condition, "Forced authentication predicate cannot be null");
     }
-            
+
+    /** {@inheritDoc} */
+    @Nullable public Integer getProxyCount(@Nullable final ProfileRequestContext profileRequestContext) {
+        final Integer count = proxyCountLookupStrategy.apply(profileRequestContext);
+        if (count != null) {
+            Constraint.isGreaterThanOrEqual(0, count, "Proxy count must be greater than or equal to 0");
+        }
+        return count;
+    }
+
+    /**
+     * Sets the maximum number of times an assertion may be proxied outbound and/or
+     * the maximum number of hops between the relying party and a proxied authentication
+     * authority inbound.
+     * 
+     * @param count proxy count
+     * 
+     * @since 4.0.0
+     */
+    public void setProxyCount(@Nullable @NonNegative final Integer count) {
+        if (count != null) {
+            Constraint.isGreaterThanOrEqual(0, count, "Proxy count must be greater than or equal to 0");
+        }
+        proxyCountLookupStrategy = FunctionSupport.constant(count);
+    }
+
+    /**
+     * Set a lookup strategy for the maximum number of times an assertion may be proxied outbound and/or
+     * the maximum number of hops between the relying party and a proxied authentication authority inbound.
+     *
+     * @param strategy  lookup strategy
+     * 
+     * @since 4.0.0
+     */
+    public void setProxyCountLookupStrategy(@Nonnull final Function<ProfileRequestContext,Integer> strategy) {
+        proxyCountLookupStrategy = Constraint.isNotNull(strategy, "Lookup strategy cannot be null");
+    }
+
     /** {@inheritDoc} */
     @Override
     @Nonnull @NotEmpty protected String getDefaultTicketPrefix() {
diff --git a/idp-cas-impl/src/main/java/net/shibboleth/idp/cas/flow/impl/BuildAuthenticationContextAction.java b/idp-cas-impl/src/main/java/net/shibboleth/idp/cas/flow/impl/BuildAuthenticationContextAction.java
index 8028677..3e98888 100644
--- a/idp-cas-impl/src/main/java/net/shibboleth/idp/cas/flow/impl/BuildAuthenticationContextAction.java
+++ b/idp-cas-impl/src/main/java/net/shibboleth/idp/cas/flow/impl/BuildAuthenticationContextAction.java
@@ -72,13 +72,18 @@ public class BuildAuthenticationContextAction
         ac.setForceAuthn(request.isRenew());
         ac.setIsPassive(false);
 
+        final LoginConfiguration config = configLookupFunction.apply(profileRequestContext);
+
         if (!ac.isForceAuthn()) {
-            final LoginConfiguration config = configLookupFunction.apply(profileRequestContext);
             if (config != null) {
                 ac.setForceAuthn(config.isForceAuthn(profileRequestContext));
             }
         }
         
+        if (config != null) {
+            ac.setProxyCount(config.getProxyCount(profileRequestContext));
+        }
+        
         profileRequestContext.addSubcontext(ac, true);
         profileRequestContext.setBrowserProfile(true);
     }
diff --git a/idp-conf/src/main/resources/conf/errors.xml b/idp-conf/src/main/resources/conf/errors.xml
index 1be4f6e..a2e8ca4 100644
--- a/idp-conf/src/main/resources/conf/errors.xml
+++ b/idp-conf/src/main/resources/conf/errors.xml
@@ -69,6 +69,7 @@
         
         <entry key="UnableToEncode" value-ref="shibboleth.SAML2Status.UnsupportedBinding" />
 
+        <entry key="AccessDenied" value-ref="shibboleth.SAML2Status.RequestDenied" />
         <entry key="MessageReplay" value-ref="shibboleth.SAML2Status.RequestDenied" />
         <entry key="MessageExpired" value-ref="shibboleth.SAML2Status.RequestDenied" />
         <entry key="MessageAuthenticationError" value-ref="shibboleth.SAML2Status.RequestDenied" />
diff --git a/idp-conf/src/main/resources/system/conf/relying-party-mddriven.xml b/idp-conf/src/main/resources/system/conf/relying-party-mddriven.xml
index bb06b8e..eb11b1d 100644
--- a/idp-conf/src/main/resources/system/conf/relying-party-mddriven.xml
+++ b/idp-conf/src/main/resources/system/conf/relying-party-mddriven.xml
@@ -275,6 +275,9 @@
         <property name="nameIDFormatPrecedenceLookupStrategy">
             <bean parent="shibboleth.MDDrivenListProperty" p:propertyName="nameIDFormatPrecedence" />
         </property>
+        <property name="proxyCountLookupStrategy">
+            <bean parent="shibboleth.MDDrivenIntProperty" p:propertyName="proxyCount" />
+        </property>
     </bean>
     
     <bean id="SAML1.AttributeQuery.MDDriven" parent="AbstractMDDrivenSAMLProfile" lazy-init="true"
@@ -642,6 +645,9 @@
                 <constructor-arg value="false" />
             </bean>
         </property>
+        <property name="proxyCountLookupStrategy">
+            <bean parent="shibboleth.MDDrivenIntProperty" p:propertyName="proxyCount" />
+        </property>
     </bean>
 
     <bean id="CAS.ProxyConfiguration.MDDriven" parent="AbstractMDDrivenCASProfile" lazy-init="true"
diff --git a/idp-conf/src/main/resources/system/conf/relying-party-system.xml b/idp-conf/src/main/resources/system/conf/relying-party-system.xml
index 4d51c42..8eff3b2 100644
--- a/idp-conf/src/main/resources/system/conf/relying-party-system.xml
+++ b/idp-conf/src/main/resources/system/conf/relying-party-system.xml
@@ -92,6 +92,9 @@
     <util:constant id="SAML2.SSO.FEATURE_AUTHNCONTEXT"
         static-field="net.shibboleth.idp.saml.saml2.profile.config.BrowserSSOProfileConfiguration.FEATURE_AUTHNCONTEXT"/>
 
+    <util:constant id="SAML2.SSO.FEATURE_SCOPING"
+        static-field="net.shibboleth.idp.saml.saml2.profile.config.BrowserSSOProfileConfiguration.FEATURE_SCOPING"/>
+
     <bean id="SAML2.ECP" lazy-init="true"
         class="net.shibboleth.idp.saml.saml2.profile.config.ECPProfileConfiguration"
         p:inboundInterceptorFlows="security-policy/saml2-ecp"
diff --git a/idp-conf/src/main/resources/system/flows/authn/proxy/saml-proxy-beans.xml b/idp-conf/src/main/resources/system/flows/authn/proxy/saml-proxy-beans.xml
index cee5e27..d508137 100644
--- a/idp-conf/src/main/resources/system/flows/authn/proxy/saml-proxy-beans.xml
+++ b/idp-conf/src/main/resources/system/flows/authn/proxy/saml-proxy-beans.xml
@@ -143,7 +143,7 @@
                     p:noResultIsError="false">
                 <property name="activationCondition">
                     <bean class="net.shibboleth.idp.saml.profile.config.logic.SignRequestsPredicate"
-                        p:honorMetadata="%{idp.signing.honorWantAuthnRequestsSigned:true}" />
+                        p:honorMetadata="%{idp.saml.honorWantAuthnRequestsSigned:true}" />
                 </property>
             </bean>
         </constructor-arg>
diff --git a/idp-conf/src/main/resources/system/flows/saml/saml-abstract-beans.xml b/idp-conf/src/main/resources/system/flows/saml/saml-abstract-beans.xml
index 2ce289e..137d1a9 100644
--- a/idp-conf/src/main/resources/system/flows/saml/saml-abstract-beans.xml
+++ b/idp-conf/src/main/resources/system/flows/saml/saml-abstract-beans.xml
@@ -164,7 +164,7 @@
             <bean parent="shibboleth.Conditions.OR">
                 <constructor-arg>
                     <bean class="net.shibboleth.idp.saml.profile.config.logic.SignAssertionsPredicate"
-                        p:honorMetadata="%{idp.signing.honorWantAssertionsSigned:true}" />
+                        p:honorMetadata="%{idp.saml.honorWantAssertionsSigned:true}" />
                 </constructor-arg>
                 <constructor-arg ref="shibboleth.Conditions.IssuingDelegatedAssertion" />
             </bean>
@@ -228,7 +228,8 @@
         p:storageServices="#{ getObject('shibboleth.ClientStorageServices') ?: getObject('shibboleth.DefaultClientStorageServices') }" />
 
     <bean id="InitializeAuthenticationContext"
-        class="net.shibboleth.idp.saml.profile.impl.InitializeAuthenticationContext" scope="prototype" />
+        class="net.shibboleth.idp.saml.profile.impl.InitializeAuthenticationContext" scope="prototype"
+        p:honorAuthnRequest="%{idp.saml.honorAuthnRequest:true}" />
 
     <bean id="ResolveAttributes" class="net.shibboleth.idp.profile.impl.ResolveAttributes" scope="prototype"
         c:resolverService-ref="shibboleth.AttributeResolverService"
diff --git a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/saml1/profile/config/BrowserSSOProfileConfiguration.java b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/saml1/profile/config/BrowserSSOProfileConfiguration.java
index 4b6180e..90a32e6 100644
--- a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/saml1/profile/config/BrowserSSOProfileConfiguration.java
+++ b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/saml1/profile/config/BrowserSSOProfileConfiguration.java
@@ -34,6 +34,7 @@ import com.google.common.base.Predicates;
 
 import net.shibboleth.idp.authn.config.AuthenticationProfileConfiguration;
 import net.shibboleth.idp.saml.authn.principal.AuthenticationMethodPrincipal;
+import net.shibboleth.utilities.java.support.annotation.constraint.NonNegative;
 import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
 import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
 import net.shibboleth.utilities.java.support.annotation.constraint.NotLive;
@@ -70,7 +71,10 @@ public class BrowserSSOProfileConfiguration extends AbstractSAML1ArtifactAwarePr
 
     /** Lookup function to supply NameIdentifier formats. */
     @Nonnull private Function<ProfileRequestContext,Collection<String>> nameIDFormatPrecedenceLookupStrategy;
-    
+
+    /** Lookup function to supply proxyCount property. */
+    @Nonnull private Function<ProfileRequestContext,Integer> proxyCountLookupStrategy;
+
     /** Constructor. */
     public BrowserSSOProfileConfiguration() {
         this(PROFILE_ID);
@@ -91,6 +95,7 @@ public class BrowserSSOProfileConfiguration extends AbstractSAML1ArtifactAwarePr
         defaultAuthenticationMethodsLookupStrategy = FunctionSupport.constant(null);
         nameIDFormatPrecedenceLookupStrategy = FunctionSupport.constant(null);
         forceAuthnPredicate = Predicates.alwaysFalse();
+        proxyCountLookupStrategy = FunctionSupport.constant(null);
     }
 
     /**
@@ -325,4 +330,41 @@ public class BrowserSSOProfileConfiguration extends AbstractSAML1ArtifactAwarePr
         nameIDFormatPrecedenceLookupStrategy = Constraint.isNotNull(strategy, "Lookup strategy cannot be null");
     }
         
+    /** {@inheritDoc} */
+    @Nullable public Integer getProxyCount(@Nullable final ProfileRequestContext profileRequestContext) {
+        final Integer count = proxyCountLookupStrategy.apply(profileRequestContext);
+        if (count != null) {
+            Constraint.isGreaterThanOrEqual(0, count, "Proxy count must be greater than or equal to 0");
+        }
+        return count;
+    }
+
+    /**
+     * Sets the maximum number of times an assertion may be proxied outbound and/or
+     * the maximum number of hops between the relying party and a proxied authentication
+     * authority inbound.
+     * 
+     * @param count proxy count
+     * 
+     * @since 4.0.0
+     */
+    public void setProxyCount(@Nullable @NonNegative final Integer count) {
+        if (count != null) {
+            Constraint.isGreaterThanOrEqual(0, count, "Proxy count must be greater than or equal to 0");
+        }
+        proxyCountLookupStrategy = FunctionSupport.constant(count);
+    }
+
+    /**
+     * Set a lookup strategy for the maximum number of times an assertion may be proxied outbound and/or
+     * the maximum number of hops between the relying party and a proxied authentication authority inbound.
+     *
+     * @param strategy  lookup strategy
+     * 
+     * @since 4.0.0
+     */
+    public void setProxyCountLookupStrategy(@Nonnull final Function<ProfileRequestContext,Integer> strategy) {
+        proxyCountLookupStrategy = Constraint.isNotNull(strategy, "Lookup strategy cannot be null");
+    }
+
 }
\ No newline at end of file
diff --git a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/saml2/profile/config/BrowserSSOProfileConfiguration.java b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/saml2/profile/config/BrowserSSOProfileConfiguration.java
index 5f2f49d..8d9086e 100644
--- a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/saml2/profile/config/BrowserSSOProfileConfiguration.java
+++ b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/saml2/profile/config/BrowserSSOProfileConfiguration.java
@@ -62,6 +62,9 @@ public class BrowserSSOProfileConfiguration extends AbstractSAML2ArtifactAwarePr
         
     /** Bit constant for RequestedAuthnContext feature. */
     public static final int FEATURE_AUTHNCONTEXT = 0x1;
+
+    /** Bit constant for Scoping feature. */
+    public static final int FEATURE_SCOPING = 0x2;
     
     /** Whether attributes should be resolved in the course of the profile. */
     @Nonnull private Predicate<ProfileRequestContext> resolveAttributesPredicate;
diff --git a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/saml2/profile/config/navigate/ProxyCountLookupFunction.java b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/saml2/profile/config/navigate/ProxyCountLookupFunction.java
new file mode 100644
index 0000000..c2d1155
--- /dev/null
+++ b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/saml2/profile/config/navigate/ProxyCountLookupFunction.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.idp.saml.saml2.profile.config.navigate;
+
+import javax.annotation.Nullable;
+
+import net.shibboleth.idp.profile.config.ProfileConfiguration;
+import net.shibboleth.idp.profile.context.RelyingPartyContext;
+import net.shibboleth.idp.profile.context.navigate.AbstractRelyingPartyLookupFunction;
+import net.shibboleth.idp.saml.saml2.profile.config.SAML2ProfileConfiguration;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+
+/**
+ * A function that returns the allowable proxy count based on the result of
+ * {@link SAML2ProfileConfiguration#getProxyCount()}, if such a profile is available
+ * from a {@link RelyingPartyContext} obtained via a lookup function,
+ * by default a child of the {@link ProfileRequestContext}.
+ * 
+ * <p>If a specific setting is unavailable, a null is returned.</p>
+ */
+public class ProxyCountLookupFunction extends AbstractRelyingPartyLookupFunction<Integer> {
+
+    /** {@inheritDoc} */
+    @Nullable public Integer apply(@Nullable final ProfileRequestContext input) {
+        final RelyingPartyContext rpc = getRelyingPartyContextLookupStrategy().apply(input);
+        if (rpc != null) {
+            final ProfileConfiguration pc = rpc.getProfileConfig();
+            if (pc != null && pc instanceof SAML2ProfileConfiguration) {
+                return ((SAML2ProfileConfiguration) pc).getProxyCount(input);
+            }
+        }
+        
+        return null;
+    }
+
+}
\ No newline at end of file
diff --git a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/profile/impl/InitializeAuthenticationContext.java b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/profile/impl/InitializeAuthenticationContext.java
index d3add6e..44e95f4 100644
--- a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/profile/impl/InitializeAuthenticationContext.java
+++ b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/profile/impl/InitializeAuthenticationContext.java
@@ -17,8 +17,10 @@
 
 package net.shibboleth.idp.saml.profile.impl;
 
+import java.util.Set;
 import java.util.function.Function;
 import java.util.function.Predicate;
+import java.util.stream.Collectors;
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
@@ -26,13 +28,22 @@ import javax.annotation.Nullable;
 import net.shibboleth.idp.authn.config.navigate.ForceAuthnProfileConfigPredicate;
 import net.shibboleth.idp.authn.context.AuthenticationContext;
 import net.shibboleth.idp.profile.AbstractProfileAction;
+import net.shibboleth.idp.profile.context.RelyingPartyContext;
+import net.shibboleth.idp.saml.saml2.profile.config.BrowserSSOProfileConfiguration;
+import net.shibboleth.idp.saml.saml2.profile.config.navigate.ProxyCountLookupFunction;
 import net.shibboleth.utilities.java.support.component.ComponentSupport;
 import net.shibboleth.utilities.java.support.logic.Constraint;
 
+import org.opensaml.messaging.context.navigate.ChildContextLookup;
 import org.opensaml.messaging.context.navigate.MessageLookup;
+import org.opensaml.profile.action.ActionSupport;
+import org.opensaml.profile.action.EventIds;
 import org.opensaml.profile.context.ProfileRequestContext;
 import org.opensaml.profile.context.navigate.InboundMessageContextLookup;
 import org.opensaml.saml.saml2.core.AuthnRequest;
+import org.opensaml.saml.saml2.core.IDPEntry;
+import org.opensaml.saml.saml2.core.IDPList;
+import org.opensaml.saml.saml2.core.Scoping;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -40,30 +51,59 @@ import org.slf4j.LoggerFactory;
  * An action that creates an {@link AuthenticationContext} and attaches it to the current {@link ProfileRequestContext}.
  * 
  * <p>If the incoming message is a SAML 2.0 {@link AuthnRequest}, then basic authentication policy (IsPassive,
- * ForceAuthn) is copied into the context from the request.</p>
+ * ForceAuthn, Scoping) is copied into the context from the request.</p>
+ * 
+ * @event {@link EventIds#PROCEED_EVENT_ID}
+ * @event {@link EventIds#ACCESS_DENIED}
  * 
- * @event {@link org.opensaml.profile.action.EventIds#PROCEED_EVENT_ID}
  * @post ProfileRequestContext.getSubcontext(AuthenticationContext.class) != true
- * @post SAML 2.0 AuthnRequest policy flags are copied to the {@link AuthenticationContext}
+ * @post SAML 2.0 AuthnRequest policy flags are (optionally) copied to the {@link AuthenticationContext}
  */
 public class InitializeAuthenticationContext extends AbstractProfileAction {
 
     /** Class logger. */
     @Nonnull private final Logger log = LoggerFactory.getLogger(InitializeAuthenticationContext.class);
 
+    /** Strategy used to look up a {@link RelyingPartyContext} for configuration options. */
+    @Nonnull private Function<ProfileRequestContext,RelyingPartyContext> relyingPartyContextLookupStrategy;
+
     /** Extracts forceAuthn property from profile config. */
     @Nonnull private Predicate<ProfileRequestContext> forceAuthnPredicate;
     
+    /** Strategy used to determine proxy count from configuration. */
+    @Nullable private Function<ProfileRequestContext,Integer> proxyCountLookupStrategy;
+    
     /** Strategy used to locate the {@link AuthnRequest} to operate on, if any. */
     @Nonnull private Function<ProfileRequestContext,AuthnRequest> requestLookupStrategy;
     
+    /** Whether to honor various policy in an {@link AuthnRequest}. */
+    private boolean honorAuthnRequest;
+    
     /** Incoming SAML 2.0 request, if present. */
     @Nullable private AuthnRequest authnRequest;
 
     /** Constructor. */
     public InitializeAuthenticationContext() {
+        relyingPartyContextLookupStrategy = new ChildContextLookup<>(RelyingPartyContext.class);
         forceAuthnPredicate = new ForceAuthnProfileConfigPredicate();
+        proxyCountLookupStrategy = new ProxyCountLookupFunction();
         requestLookupStrategy = new MessageLookup<>(AuthnRequest.class).compose(new InboundMessageContextLookup());
+        honorAuthnRequest = true;
+    }
+    
+    /**
+     * Set the strategy used to return the {@link RelyingPartyContext} for configuration options.
+     * 
+     * @param strategy lookup strategy
+     * 
+     * @since 4.0.0
+     */
+    public void setRelyingPartyContextLookupStrategy(
+            @Nonnull final Function<ProfileRequestContext,RelyingPartyContext> strategy) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+
+        relyingPartyContextLookupStrategy =
+                Constraint.isNotNull(strategy, "RelyingPartyContext lookup strategy cannot be null");
     }
     
     /**
@@ -80,6 +120,19 @@ public class InitializeAuthenticationContext extends AbstractProfileAction {
     }
     
     /**
+     * Set the lookup function to apply to derive the proxy count from the configuration.
+     * 
+     * @param strategy lookup strategy
+     * 
+     * @since 4.0.0
+     */
+    public void setProxyCountLookupStrategy(@Nonnull final Function<ProfileRequestContext,Integer> strategy) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        
+        proxyCountLookupStrategy = Constraint.isNotNull(strategy, "Proxy count lookup strategy cannot be null");
+    }
+    
+    /**
      * Set the strategy used to locate the {@link AuthnRequest} to examine, if any.
      * 
      * @param strategy strategy used to locate the {@link AuthnRequest}
@@ -90,6 +143,22 @@ public class InitializeAuthenticationContext extends AbstractProfileAction {
         requestLookupStrategy = Constraint.isNotNull(strategy, "AuthnRequest lookup strategy cannot be null");
     }
     
+    /**
+     * Sets whether to honor various policy in an {@link AuthnRequest} such as IsPassive, ForceAuthn, and Scoping.
+     * 
+     * <p>Turning this off constitutes a standards violation and is provided for compatibility with garbage SAML
+     * implementations and incompetent deployers.</p>
+     * 
+     * @param flag flag to set
+     * 
+     * @since 4.0.0
+     */
+    public void setHonorAuthnRequest(final boolean flag) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        
+        honorAuthnRequest = flag;
+    }
+    
     /** {@inheritDoc} */
     @Override
     protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
@@ -98,11 +167,13 @@ public class InitializeAuthenticationContext extends AbstractProfileAction {
             return false;
         }
         
-        authnRequest = this.requestLookupStrategy.apply(profileRequestContext);
-        if (authnRequest == null) {
-            log.debug("{} No inbound AuthnRequest, passive flag will be off", getLogPrefix());
-        }
+        authnRequest = requestLookupStrategy.apply(profileRequestContext);
         
+        if (authnRequest != null && !honorAuthnRequest) {
+            log.warn("{} Ignoring incoming AuthnRequest policy content in violation of SAML standard",
+                    getLogPrefix());
+            authnRequest = null;
+        }
         return true;
     }
     
@@ -113,6 +184,9 @@ public class InitializeAuthenticationContext extends AbstractProfileAction {
         final AuthenticationContext authnCtx = new AuthenticationContext();
 
         if (authnRequest != null) {
+            if (!processScoping(profileRequestContext, authnCtx)) {
+                return;
+            }
             authnCtx.setForceAuthn(authnRequest.isForceAuthn());
             authnCtx.setIsPassive(authnRequest.isPassive());
         }
@@ -121,9 +195,74 @@ public class InitializeAuthenticationContext extends AbstractProfileAction {
             authnCtx.setForceAuthn(forceAuthnPredicate.test(profileRequestContext));
         }
         
+        // Merge requested and pre-configured proxy count.
+        
+        final Integer reqCount = authnCtx.getProxyCount();
+        Integer configCount = proxyCountLookupStrategy.apply(profileRequestContext);
+        if (configCount != null && configCount < 0) {
+            configCount = 0;
+        }
+        
+        if (reqCount != null) {
+            if (configCount != null) {
+                authnCtx.setProxyCount(Integer.min(configCount, reqCount));
+                log.debug("{} Combined requested and configured proxy count: {}", getLogPrefix(),
+                        authnCtx.getProxyCount());
+            }
+        } else {
+            authnCtx.setProxyCount(configCount);
+        }
+        
         profileRequestContext.addSubcontext(authnCtx, true);
 
         log.debug("{} Created authentication context: {}", getLogPrefix(), authnCtx);
     }
     
+    /**
+     * Check an inbound {@link AuthnRequest} for a {@link Scoping} element.
+     * 
+     * @param profileRequestContext current profile request context
+     * @param authenticationContext the context to populate
+     * 
+     * @return true iff processing should continue
+     */
+    private boolean processScoping(@Nonnull final ProfileRequestContext profileRequestContext,
+            @Nonnull final AuthenticationContext authenticationContext) {
+        
+        final Scoping scoping = authnRequest.getScoping();
+        if (scoping == null) {
+            log.debug("{} AuthnRequest did not contain Scoping, nothing to do", getLogPrefix());
+            return true;
+        }
+        
+        // Check if permitted.
+        final RelyingPartyContext rpContext = relyingPartyContextLookupStrategy.apply(profileRequestContext);
+        if (rpContext != null && rpContext.getProfileConfig() != null
+                && rpContext.getProfileConfig() instanceof BrowserSSOProfileConfiguration) {
+            if (((BrowserSSOProfileConfiguration) rpContext.getProfileConfig()).isFeatureDisallowed(
+                    profileRequestContext, BrowserSSOProfileConfiguration.FEATURE_SCOPING)) {
+                log.warn("{} Incoming Scoping disallowed by profile configuration", getLogPrefix());
+                ActionSupport.buildEvent(profileRequestContext, EventIds.ACCESS_DENIED);
+                return false;
+            }
+        }
+        
+        // The IDPList doesn't have mandatory semantics, other than disallowing removal.
+        
+        final IDPList idpList = scoping.getIDPList();
+        if (idpList != null && idpList.getIDPEntrys() != null) {
+            final Set<String> requestedAuthorities = idpList.getIDPEntrys()
+                    .stream()
+                    .map(IDPEntry::getProviderID)
+                    .filter(id -> id != null)
+                    .collect(Collectors.toUnmodifiableSet());
+            authenticationContext.getProxiableAuthorities().addAll(requestedAuthorities);
+        }
+        
+        if (scoping.getProxyCount() != null) {
+            authenticationContext.setProxyCount(Integer.max(0, scoping.getProxyCount()));
+        }
+        return true;
+    }
+    
 }
\ No newline at end of file
diff --git a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/saml2/profile/impl/AddAuthnRequest.java b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/saml2/profile/impl/AddAuthnRequest.java
index 185ff8b..8593ad0 100644
--- a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/saml2/profile/impl/AddAuthnRequest.java
+++ b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/saml2/profile/impl/AddAuthnRequest.java
@@ -101,11 +101,7 @@ public class AddAuthnRequest extends AbstractAuthenticationAction {
     /** Constructor. */
     public AddAuthnRequest() {
         // Default strategy is a 16-byte secure random source.
-        idGeneratorLookupStrategy = new Function<>() {
-            public IdentifierGenerationStrategy apply(final ProfileRequestContext input) {
-                return new SecureRandomIdentifierGenerationStrategy();
-            }
-        };
+        idGeneratorLookupStrategy = prc -> new SecureRandomIdentifierGenerationStrategy();
         
         // Fool the parent class into looking above instead of below the PRC for the context.
         setAuthenticationContextLookupStrategy(new ParentContextLookup<>(AuthenticationContext.class));
diff --git a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/saml2/profile/impl/ProcessRequestedAuthnContext.java b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/saml2/profile/impl/ProcessRequestedAuthnContext.java
index d56edb1..97414d8 100644
--- a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/saml2/profile/impl/ProcessRequestedAuthnContext.java
+++ b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/saml2/profile/impl/ProcessRequestedAuthnContext.java
@@ -29,7 +29,6 @@ import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 
 import net.shibboleth.idp.authn.AbstractAuthenticationAction;
-import net.shibboleth.idp.authn.AuthnEventIds;
 import net.shibboleth.idp.authn.context.AuthenticationContext;
 import net.shibboleth.idp.authn.context.RequestedPrincipalContext;
 import net.shibboleth.idp.profile.context.RelyingPartyContext;
@@ -67,8 +66,7 @@ import org.slf4j.LoggerFactory;
  * 
  * @event {@link EventIds#PROCEED_EVENT_ID}
  * @event {@link EventIds#INVALID_MSG_CTX}
- * 
- * @post ProfileRequestContext.
+ * @event {@link EventIds#ACCESS_DENIED}
  */
 public class ProcessRequestedAuthnContext extends AbstractAuthenticationAction {
 
@@ -206,7 +204,7 @@ public class ProcessRequestedAuthnContext extends AbstractAuthenticationAction {
             if (((BrowserSSOProfileConfiguration) rpContext.getProfileConfig()).isFeatureDisallowed(
                     profileRequestContext, BrowserSSOProfileConfiguration.FEATURE_AUTHNCONTEXT)) {
                 log.warn("{} Incoming RequestedAuthnContext disallowed by profile configuration", getLogPrefix());
-                ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.INVALID_AUTHN_CTX);
+                ActionSupport.buildEvent(profileRequestContext, EventIds.ACCESS_DENIED);
                 return;
             }
         }
diff --git a/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/transcoding/impl/SAML2StringAttributeTranscoderTest.java b/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/transcoding/impl/SAML2StringAttributeTranscoderTest.java
index 4babccc..685d8a2 100644
--- a/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/transcoding/impl/SAML2StringAttributeTranscoderTest.java
+++ b/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/transcoding/impl/SAML2StringAttributeTranscoderTest.java
@@ -17,7 +17,6 @@
 
 package net.shibboleth.idp.saml.attribute.transcoding.impl;
 
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
@@ -165,7 +164,7 @@ public class SAML2StringAttributeTranscoderTest extends OpenSAMLInitBaseTestCase
     @Test(expectedExceptions = {AttributeEncodingException.class,}) public void inappropriate() throws Exception {
         final int[] intArray = {1, 2, 3, 4};
         final List<IdPAttributeValue> values =
-                Arrays.asList(new ByteAttributeValue(new byte[] {1, 2, 3,}), new IdPAttributeValue() {
+                List.of(new ByteAttributeValue(new byte[] {1, 2, 3,}), new IdPAttributeValue() {
                     @Override
                     public Object getNativeValue() {
                         return intArray;
@@ -188,7 +187,7 @@ public class SAML2StringAttributeTranscoderTest extends OpenSAMLInitBaseTestCase
     
     @Test public void single() throws Exception {
         final List<IdPAttributeValue> values =
-                Arrays.asList(new ByteAttributeValue(new byte[] {1, 2, 3,}), new StringAttributeValue(STRING_1));
+                List.of(new ByteAttributeValue(new byte[] {1, 2, 3,}), new StringAttributeValue(STRING_1));
 
         final IdPAttribute inputAttribute = new IdPAttribute(ATTR_NAME);
         inputAttribute.setValues(values);
@@ -223,7 +222,7 @@ public class SAML2StringAttributeTranscoderTest extends OpenSAMLInitBaseTestCase
 
     @Test public void singleRequested() throws Exception {
         final List<IdPAttributeValue> values =
-                Arrays.asList(new ByteAttributeValue(new byte[] {1, 2, 3,}), new StringAttributeValue(STRING_1));
+                List.of(new ByteAttributeValue(new byte[] {1, 2, 3,}), new StringAttributeValue(STRING_1));
 
         final IdPRequestedAttribute inputAttribute = new IdPRequestedAttribute(ATTR_NAME);
         inputAttribute.setRequired(true);
@@ -307,7 +306,7 @@ public class SAML2StringAttributeTranscoderTest extends OpenSAMLInitBaseTestCase
     
     @Test public void multi() throws Exception {
         final List<IdPAttributeValue> values =
-                Arrays.asList(new ByteAttributeValue(new byte[] {1, 2, 3,}),
+                List.of(new ByteAttributeValue(new byte[] {1, 2, 3,}),
                         new StringAttributeValue(STRING_1),
                         new StringAttributeValue(STRING_2),
                         new ScopedStringAttributeValue(STRING_1, STRING_2));
diff --git a/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/transcoding/impl/SAML2XMLObjectAttributeTranscoderTest.java b/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/transcoding/impl/SAML2XMLObjectAttributeTranscoderTest.java
index d8bcb1c..9c7337f 100644
--- a/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/transcoding/impl/SAML2XMLObjectAttributeTranscoderTest.java
+++ b/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/attribute/transcoding/impl/SAML2XMLObjectAttributeTranscoderTest.java
@@ -17,7 +17,6 @@
 
 package net.shibboleth.idp.saml.attribute.transcoding.impl;
 
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
@@ -46,8 +45,8 @@ import net.shibboleth.utilities.java.support.component.ComponentInitializationEx
 import org.opensaml.core.OpenSAMLInitBaseTestCase;
 import org.opensaml.core.xml.XMLObject;
 import org.opensaml.core.xml.XMLObjectBuilder;
+import org.opensaml.core.xml.XMLObjectBuilderFactory;
 import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
-import org.opensaml.core.xml.schema.XSAny;
 import org.opensaml.core.xml.schema.XSString;
 import org.opensaml.saml.common.SAMLObjectBuilder;
 import org.opensaml.saml.saml2.core.Attribute;
@@ -63,7 +62,7 @@ public class SAML2XMLObjectAttributeTranscoderTest extends OpenSAMLInitBaseTestC
 
     private AttributeTranscoderRegistryImpl registry;
     
-    private XMLObjectBuilder<XSAny> anyBuilder;
+    private SAMLObjectBuilder<AttributeValue> anyBuilder;
     
     private XMLObjectBuilder<XSString> stringBuilder;
 
@@ -79,15 +78,13 @@ public class SAML2XMLObjectAttributeTranscoderTest extends OpenSAMLInitBaseTestC
 
     @BeforeClass public void setUp() throws ComponentInitializationException {
         
-        anyBuilder = XMLObjectProviderRegistrySupport.getBuilderFactory().<XSAny>getBuilderOrThrow(XSAny.TYPE_NAME);
-        stringBuilder = XMLObjectProviderRegistrySupport.getBuilderFactory().<XSString>getBuilderOrThrow(XSString.TYPE_NAME);
+        final XMLObjectBuilderFactory bf = XMLObjectProviderRegistrySupport.getBuilderFactory();
         
-        attributeBuilder = (SAMLObjectBuilder<Attribute>)
-                XMLObjectProviderRegistrySupport.getBuilderFactory().<Attribute>getBuilderOrThrow(
-                        Attribute.TYPE_NAME);
-        reqAttributeBuilder = (SAMLObjectBuilder<RequestedAttribute>)
-                XMLObjectProviderRegistrySupport.getBuilderFactory().<RequestedAttribute>getBuilderOrThrow(
-                        RequestedAttribute.TYPE_NAME);
+        anyBuilder = (SAMLObjectBuilder<AttributeValue>) bf.<AttributeValue>getBuilderOrThrow(AttributeValue.DEFAULT_ELEMENT_NAME);
+        stringBuilder = bf.<XSString>getBuilderOrThrow(XSString.TYPE_NAME);
+        
+        attributeBuilder = (SAMLObjectBuilder<Attribute>) bf.<Attribute>getBuilderOrThrow(Attribute.TYPE_NAME);
+        reqAttributeBuilder = (SAMLObjectBuilder<RequestedAttribute>) bf.<RequestedAttribute>getBuilderOrThrow(RequestedAttribute.TYPE_NAME);
         
         registry = new AttributeTranscoderRegistryImpl();
         registry.setId("test");
@@ -172,7 +169,7 @@ public class SAML2XMLObjectAttributeTranscoderTest extends OpenSAMLInitBaseTestC
     @Test(expectedExceptions = {AttributeEncodingException.class,}) public void inappropriate() throws Exception {
         final int[] intArray = {1, 2, 3, 4};
         final List<IdPAttributeValue> values =
-                Arrays.asList(new ByteAttributeValue(new byte[] {1, 2, 3,}), new IdPAttributeValue() {
+                List.of(new ByteAttributeValue(new byte[] {1, 2, 3,}), new IdPAttributeValue() {
                     @Override
                     public Object getNativeValue() {
                         return intArray;
@@ -195,7 +192,7 @@ public class SAML2XMLObjectAttributeTranscoderTest extends OpenSAMLInitBaseTestC
     
     @Test public void single() throws Exception {
         final List<IdPAttributeValue> values =
-                Arrays.asList(new ByteAttributeValue(new byte[] {1, 2, 3,}), objectFor(STRING_1));
+                List.of(new ByteAttributeValue(new byte[] {1, 2, 3,}), objectFor(STRING_1));
 
         final IdPAttribute inputAttribute = new IdPAttribute(ATTR_NAME);
         inputAttribute.setValues(values);
@@ -225,7 +222,7 @@ public class SAML2XMLObjectAttributeTranscoderTest extends OpenSAMLInitBaseTestC
 
     @Test public void singleRequested() throws Exception {
         final List<IdPAttributeValue> values =
-                Arrays.asList(new ByteAttributeValue(new byte[] {1, 2, 3,}), objectFor(STRING_1));
+                List.of(new ByteAttributeValue(new byte[] {1, 2, 3,}), objectFor(STRING_1));
 
         final IdPRequestedAttribute inputAttribute = new IdPRequestedAttribute(ATTR_NAME);
         inputAttribute.setRequired(true);
@@ -260,7 +257,7 @@ public class SAML2XMLObjectAttributeTranscoderTest extends OpenSAMLInitBaseTestC
         final XSString stringValue = stringBuilder.buildObject(new QName("Foo"));
         stringValue.setValue(STRING_1);
         
-        final XSAny attrValue = anyBuilder.buildObject(AttributeValue.DEFAULT_ELEMENT_NAME);
+        final AttributeValue attrValue = anyBuilder.buildObject();
         attrValue.getUnknownXMLObjects().add(stringValue);
         
         final Attribute samlAttribute = attributeBuilder.buildObject();
@@ -290,7 +287,7 @@ public class SAML2XMLObjectAttributeTranscoderTest extends OpenSAMLInitBaseTestC
         final XSString stringValue = stringBuilder.buildObject(new QName("Foo"));
         stringValue.setValue(STRING_1);
         
-        final XSAny attrValue = anyBuilder.buildObject(AttributeValue.DEFAULT_ELEMENT_NAME);
+        final AttributeValue attrValue = anyBuilder.buildObject();
         attrValue.getUnknownXMLObjects().add(stringValue);
         
         final RequestedAttribute samlAttribute = reqAttributeBuilder.buildObject();
@@ -317,7 +314,7 @@ public class SAML2XMLObjectAttributeTranscoderTest extends OpenSAMLInitBaseTestC
     
     @Test public void multi() throws Exception {
         final List<IdPAttributeValue> values =
-                Arrays.asList(objectFor(STRING_1), objectFor(STRING_2));
+                List.of(objectFor(STRING_1), objectFor(STRING_2));
 
         final IdPAttribute inputAttribute = new IdPAttribute(ATTR_NAME);
         inputAttribute.setValues(values);
@@ -355,13 +352,13 @@ public class SAML2XMLObjectAttributeTranscoderTest extends OpenSAMLInitBaseTestC
         final XSString stringValue = stringBuilder.buildObject(new QName("Foo"));
         stringValue.setValue(STRING_1);
         
-        final XSAny attrValue = anyBuilder.buildObject(AttributeValue.DEFAULT_ELEMENT_NAME);
+        final AttributeValue attrValue = anyBuilder.buildObject();
         attrValue.getUnknownXMLObjects().add(stringValue);
 
         final XSString stringValue2 = stringBuilder.buildObject(new QName("Bar"));
         stringValue2.setValue(STRING_2);
         
-        final XSAny attrValue2 = anyBuilder.buildObject(AttributeValue.DEFAULT_ELEMENT_NAME);
+        final AttributeValue attrValue2 = anyBuilder.buildObject();
         attrValue2.getUnknownXMLObjects().add(stringValue2);
         
         final Attribute samlAttribute = attributeBuilder.buildObject();
diff --git a/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/profile/impl/InitializeAuthenticationContextTest.java b/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/profile/impl/InitializeAuthenticationContextTest.java
index 558fca5..9388220 100644
--- a/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/profile/impl/InitializeAuthenticationContextTest.java
+++ b/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/profile/impl/InitializeAuthenticationContextTest.java
@@ -20,29 +20,45 @@ package net.shibboleth.idp.saml.profile.impl;
 import net.shibboleth.idp.authn.context.AuthenticationContext;
 import net.shibboleth.idp.profile.ActionTestingSupport;
 import net.shibboleth.idp.profile.RequestContextBuilder;
+import net.shibboleth.idp.profile.context.RelyingPartyContext;
 import net.shibboleth.idp.profile.context.navigate.WebflowRequestContextProfileRequestContextLookup;
+import net.shibboleth.idp.saml.saml2.profile.config.BrowserSSOProfileConfiguration;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+import net.shibboleth.utilities.java.support.logic.FunctionSupport;
 
-import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
+import java.util.Set;
+
+import org.opensaml.core.OpenSAMLInitBaseTestCase;
+import org.opensaml.profile.action.EventIds;
 import org.opensaml.profile.context.ProfileRequestContext;
-import org.opensaml.saml.common.SAMLObjectBuilder;
 import org.opensaml.saml.saml2.core.AuthnRequest;
+import org.opensaml.saml.saml2.core.Scoping;
+import org.opensaml.saml.saml2.profile.SAML2ActionTestingSupport;
 import org.springframework.webflow.execution.Event;
 import org.springframework.webflow.execution.RequestContext;
 import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
 /** {@link InitializeAuthenticationContext} unit test. */
-public class InitializeAuthenticationContextTest {
-
-    /** Test that the action functions properly if there is no inbound message context. */
-    @Test public void testNoInboundMessageContext() throws Exception {
+public class InitializeAuthenticationContextTest extends OpenSAMLInitBaseTestCase {
+
+    private InitializeAuthenticationContext action;
+    
+    @BeforeMethod
+    public void setUp() throws ComponentInitializationException {
+        action = new InitializeAuthenticationContext();
+        action.setProxyCountLookupStrategy(FunctionSupport.constant(1));
+        action.initialize();
+    }
+    
+    /** Test that the action functions properly if there is no inbound message context. 
+     * @throws ComponentInitializationException */
+    @Test public void testNoInboundMessageContext() throws ComponentInitializationException {
         final RequestContext requestCtx = new RequestContextBuilder().buildRequestContext();
         final ProfileRequestContext prc = new WebflowRequestContextProfileRequestContextLookup().apply(requestCtx);
         prc.setInboundMessageContext(null);
 
-        final InitializeAuthenticationContext action = new InitializeAuthenticationContext();
-        action.initialize();
-
         final Event event = action.execute(requestCtx);
         ActionTestingSupport.assertProceedEvent(event);
 
@@ -50,16 +66,14 @@ public class InitializeAuthenticationContextTest {
         Assert.assertNotNull(authnCtx);
         Assert.assertFalse(authnCtx.isForceAuthn());
         Assert.assertFalse(authnCtx.isPassive());
+        Assert.assertEquals(authnCtx.getProxyCount(), Integer.valueOf(1));
     }
 
     /** Test that the action functions properly if there is no inbound message. */
-    @Test public void testNoInboundMessage() throws Exception {
+    @Test public void testNoInboundMessage() throws ComponentInitializationException {
         final RequestContext requestCtx = new RequestContextBuilder().setInboundMessage(null).buildRequestContext();
         final ProfileRequestContext prc = new WebflowRequestContextProfileRequestContextLookup().apply(requestCtx);
 
-        final InitializeAuthenticationContext action = new InitializeAuthenticationContext();
-        action.initialize();
-
         final Event event = action.execute(requestCtx);
         ActionTestingSupport.assertProceedEvent(event);
 
@@ -67,19 +81,17 @@ public class InitializeAuthenticationContextTest {
         Assert.assertNotNull(authnCtx);
         Assert.assertFalse(authnCtx.isForceAuthn());
         Assert.assertFalse(authnCtx.isPassive());
+        Assert.assertEquals(authnCtx.getProxyCount(), Integer.valueOf(1));
     }
 
     /** Test that the action functions properly if the inbound message is not a SAML 2 AuthnRequest. */
-    @Test public void testSAML1AuthnRequest() throws Exception {
+    @Test public void testSAML1AuthnRequest() throws ComponentInitializationException {
         final RequestContext requestCtx =
                 new RequestContextBuilder().setInboundMessage(
                         new IdPInitiatedSSORequest("https://sp.example.org/sp", null, null, null)
                         ).buildRequestContext();
         final ProfileRequestContext prc = new WebflowRequestContextProfileRequestContextLookup().apply(requestCtx);
-
-        final InitializeAuthenticationContext action = new InitializeAuthenticationContext();
-        action.initialize();
-
+        
         final Event event = action.execute(requestCtx);
         ActionTestingSupport.assertProceedEvent(event);
 
@@ -87,14 +99,12 @@ public class InitializeAuthenticationContextTest {
         Assert.assertNotNull(authnCtx);
         Assert.assertFalse(authnCtx.isForceAuthn());
         Assert.assertFalse(authnCtx.isPassive());
+        Assert.assertEquals(authnCtx.getProxyCount(), Integer.valueOf(1));
     }
 
     /** Test that the action proceeds properly if the inbound message is a SAML2 AuthnRequest. */
-    @Test public void testCreateAuthenticationContext() throws Exception {
-        final SAMLObjectBuilder<AuthnRequest> builder = (SAMLObjectBuilder<AuthnRequest>)
-                XMLObjectProviderRegistrySupport.getBuilderFactory().<AuthnRequest>getBuilderOrThrow(
-                        AuthnRequest.DEFAULT_ELEMENT_NAME);        
-        final AuthnRequest authnRequest = builder.buildObject();
+    @Test public void testCreateAuthenticationContext() throws ComponentInitializationException {
+        final AuthnRequest authnRequest = SAML2ActionTestingSupport.buildAuthnRequest();
         authnRequest.setIsPassive(true);
         authnRequest.setForceAuthn(true);
 
@@ -102,9 +112,6 @@ public class InitializeAuthenticationContextTest {
                 new RequestContextBuilder().setInboundMessage(authnRequest).buildRequestContext();
         final ProfileRequestContext prc = new WebflowRequestContextProfileRequestContextLookup().apply(requestCtx);
 
-        final InitializeAuthenticationContext action = new InitializeAuthenticationContext();
-        action.initialize();
-
         final Event event = action.execute(requestCtx);
         ActionTestingSupport.assertProceedEvent(event);
         
@@ -112,6 +119,94 @@ public class InitializeAuthenticationContextTest {
         Assert.assertNotNull(authnCtx);
         Assert.assertTrue(authnCtx.isForceAuthn());
         Assert.assertTrue(authnCtx.isPassive());
+        Assert.assertEquals(authnCtx.getProxyCount(), Integer.valueOf(1));
+    }
+
+    @Test public void testAuthnRequestIgnored() throws ComponentInitializationException {
+        final AuthnRequest authnRequest = SAML2ActionTestingSupport.buildAuthnRequest();
+        authnRequest.setIsPassive(true);
+        authnRequest.setForceAuthn(true);
+
+        final RequestContext requestCtx =
+                new RequestContextBuilder().setInboundMessage(authnRequest).buildRequestContext();
+        final ProfileRequestContext prc = new WebflowRequestContextProfileRequestContextLookup().apply(requestCtx);
+
+        final Scoping scoping = SAML2ActionTestingSupport.buildScoping(0, null);
+        ((AuthnRequest) prc.getInboundMessageContext().getMessage()).setScoping(scoping);
+
+        action = new InitializeAuthenticationContext();
+        action.setProxyCountLookupStrategy(FunctionSupport.constant(1));
+        action.setHonorAuthnRequest(false);
+        action.initialize();
+        
+        final Event event = action.execute(requestCtx);
+        ActionTestingSupport.assertProceedEvent(event);
+        
+        final AuthenticationContext authnCtx = prc.getSubcontext(AuthenticationContext.class);
+        Assert.assertNotNull(authnCtx);
+        Assert.assertFalse(authnCtx.isForceAuthn());
+        Assert.assertFalse(authnCtx.isPassive());
+        Assert.assertEquals(authnCtx.getProxyCount(), Integer.valueOf(1));
+    }
+
+    @Test public void testScopingDisallowed() throws ComponentInitializationException {
+        final AuthnRequest authnRequest = SAML2ActionTestingSupport.buildAuthnRequest();
+        authnRequest.setIsPassive(true);
+        authnRequest.setForceAuthn(true);
+
+        final RequestContext requestCtx =
+                new RequestContextBuilder().setInboundMessage(authnRequest).buildRequestContext();
+        final ProfileRequestContext prc = new WebflowRequestContextProfileRequestContextLookup().apply(requestCtx);
+
+        final Scoping scoping = SAML2ActionTestingSupport.buildScoping(0, null);
+        ((AuthnRequest) prc.getInboundMessageContext().getMessage()).setScoping(scoping);
+        
+        final BrowserSSOProfileConfiguration config = new BrowserSSOProfileConfiguration();
+        config.setDisallowedFeatures(BrowserSSOProfileConfiguration.FEATURE_SCOPING);
+        
+        prc.getSubcontext(RelyingPartyContext.class, true).setProfileConfig(config);
+        
+        final Event event = action.execute(requestCtx);
+        ActionTestingSupport.assertEvent(event, EventIds.ACCESS_DENIED);
+        final AuthenticationContext authnCtx = prc.getSubcontext(AuthenticationContext.class);
+        Assert.assertNull(authnCtx);
+    }
+    
+    @Test public void testProxyCount() throws ComponentInitializationException {
+        final AuthnRequest authnRequest = SAML2ActionTestingSupport.buildAuthnRequest();
+        authnRequest.setIsPassive(true);
+        authnRequest.setForceAuthn(true);
+
+        final RequestContext requestCtx =
+                new RequestContextBuilder().setInboundMessage(authnRequest).buildRequestContext();
+        final ProfileRequestContext prc = new WebflowRequestContextProfileRequestContextLookup().apply(requestCtx);
+
+        final Scoping scoping = SAML2ActionTestingSupport.buildScoping(0, null);
+        ((AuthnRequest) prc.getInboundMessageContext().getMessage()).setScoping(scoping);
+        
+        final Event event = action.execute(requestCtx);
+        ActionTestingSupport.assertProceedEvent(event);
+        final AuthenticationContext authnCtx = prc.getSubcontext(AuthenticationContext.class);
+        Assert.assertEquals(authnCtx.getProxyCount(), Integer.valueOf(0));
+    }
+
+    @Test public void testProxyList() throws ComponentInitializationException {
+        final AuthnRequest authnRequest = SAML2ActionTestingSupport.buildAuthnRequest();
+        authnRequest.setIsPassive(true);
+        authnRequest.setForceAuthn(true);
+
+        final RequestContext requestCtx =
+                new RequestContextBuilder().setInboundMessage(authnRequest).buildRequestContext();
+        final ProfileRequestContext prc = new WebflowRequestContextProfileRequestContextLookup().apply(requestCtx);
+
+        final Scoping scoping = SAML2ActionTestingSupport.buildScoping(0, Set.of("foo", "bar"));
+        ((AuthnRequest) prc.getInboundMessageContext().getMessage()).setScoping(scoping);
+        
+        final Event event = action.execute(requestCtx);
+        ActionTestingSupport.assertProceedEvent(event);
+        final AuthenticationContext authnCtx = prc.getSubcontext(AuthenticationContext.class);
+        Assert.assertEquals(authnCtx.getProxyCount(), Integer.valueOf(0));
+        Assert.assertEquals(authnCtx.getProxiableAuthorities(), Set.of("foo", "bar"));
     }
 
 }
\ No newline at end of file
diff --git a/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/saml2/profile/impl/ProcessRequestedAuthnContextTest.java b/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/saml2/profile/impl/ProcessRequestedAuthnContextTest.java
index c643888..0c2394f 100644
--- a/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/saml2/profile/impl/ProcessRequestedAuthnContextTest.java
+++ b/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/saml2/profile/impl/ProcessRequestedAuthnContextTest.java
@@ -20,7 +20,6 @@ package net.shibboleth.idp.saml.saml2.profile.impl;
 import org.opensaml.profile.action.EventIds;
 import org.opensaml.profile.context.ProfileRequestContext;
 
-import net.shibboleth.idp.authn.AuthnEventIds;
 import net.shibboleth.idp.authn.context.AuthenticationContext;
 import net.shibboleth.idp.authn.context.RequestedPrincipalContext;
 import net.shibboleth.idp.profile.ActionTestingSupport;
@@ -121,7 +120,7 @@ public class ProcessRequestedAuthnContextTest extends OpenSAMLInitBaseTestCase {
                 BrowserSSOProfileConfiguration.FEATURE_AUTHNCONTEXT);
         
         final Event event = action.execute(src);
-        ActionTestingSupport.assertEvent(event, AuthnEventIds.INVALID_AUTHN_CTX);
+        ActionTestingSupport.assertEvent(event, EventIds.ACCESS_DENIED);
         Assert.assertNull(ac.getSubcontext(RequestedPrincipalContext.class));
     }
 

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the commits mailing list