[java-idp-oidc] branch main updated: JOIDC-64 - Refactor client authn on OAuth2 endpoints into a login flow

Scott Cantor cantor.2 at osu.edu
Tue Dec 21 19:41:21 UTC 2021


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

scantor pushed a commit to branch main
in repository java-idp-oidc.

View the commit online:
http://git.shibboleth.net/view/?p=java-idp-oidc.git;a=commit;h=23b56b9226c60e4e687cd5f01fc2cfd20a2ceac0

The following commit(s) were added to refs/heads/main by this push:
     new 23b56b92 JOIDC-64 - Refactor client authn on OAuth2 endpoints into a login flow
23b56b92 is described below

commit 23b56b9226c60e4e687cd5f01fc2cfd20a2ceac0
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Tue Dec 21 14:41:18 2021 -0500

    JOIDC-64 - Refactor client authn on OAuth2 endpoints into a login flow
    
    https://shibboleth.atlassian.net/browse/JOIDC-64
    
    New OAuth2Client login flow.
    Wire authn subflow into OAuth endpoints.
    Adjust error/status mappings and move to parent flow.
    Disable JWT tests for now.
---
 ...java => OIDCClientInfoCredentialValidator.java} |   6 +-
 .../META-INF/net.shibboleth.idp/postconfig.xml     |  26 ++-
 .../authn/OAuth2Client/OAuth2Client-beans.xml      | 140 +++++++++++++++
 .../flows/authn/OAuth2Client/OAuth2Client-flow.xml |  39 +++++
 .../oauth2/introspection/introspection-beans.xml   |  13 +-
 .../oauth2/introspection/introspection-flow.xml    |   7 +-
 .../flows/oauth2/revocation/revocation-beans.xml   |  16 +-
 .../flows/oauth2/revocation/revocation-flow.xml    |   7 +-
 .../oidc/abstract-api/oidc-abstract-api-beans.xml  |  34 ++++
 .../oidc/abstract-api/oidc-abstract-api-flow.xml   |  16 ++
 .../idp/flows/oidc/token/token-beans.xml           |  35 +---
 .../shibboleth/idp/flows/oidc/token/token-flow.xml |   9 +-
 .../idp/service/relying-party/postconfig.xml       |  51 +++---
 .../idp/plugin/oidc/op/conf/oidc.properties        |   8 +-
 ... => OIDCClientInfoCredentialValidatorTest.java} |   8 +-
 .../op/profile/flow/IntrospectionFlowTest.java     |   8 +-
 .../oidc/op/profile/flow/RevocationFlowTest.java   |  20 ++-
 .../plugin/oidc/op/profile/flow/TokenFlowTest.java |  31 ++--
 .../src/test/resources/conf/logback.xml            | 192 +++++++++++++++++++++
 19 files changed, 548 insertions(+), 118 deletions(-)

diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/authn/impl/OIDCClienInfoCredentialValidator.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/authn/impl/OIDCClientInfoCredentialValidator.java
similarity index 97%
rename from idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/authn/impl/OIDCClienInfoCredentialValidator.java
rename to idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/authn/impl/OIDCClientInfoCredentialValidator.java
index d124fb95..b14ff191 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/authn/impl/OIDCClienInfoCredentialValidator.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/authn/impl/OIDCClientInfoCredentialValidator.java
@@ -52,10 +52,10 @@ import com.nimbusds.openid.connect.sdk.rp.OIDCClientInformation;
  * via SAML metadata).
  */
 @ThreadSafeAfterInit
-public class OIDCClienInfoCredentialValidator extends AbstractUsernamePasswordCredentialValidator {
+public class OIDCClientInfoCredentialValidator extends AbstractUsernamePasswordCredentialValidator {
     
     /** Class logger. */
-    @Nonnull private final Logger log = LoggerFactory.getLogger(OIDCClienInfoCredentialValidator.class);
+    @Nonnull private final Logger log = LoggerFactory.getLogger(OIDCClientInfoCredentialValidator.class);
     
     /** Strategy that will return {@link OIDCMetadataContext}. */
     @Nonnull private Function<ProfileRequestContext,OIDCMetadataContext> oidcMetadataContextLookupStrategy;
@@ -64,7 +64,7 @@ public class OIDCClienInfoCredentialValidator extends AbstractUsernamePasswordCr
     @NonnullAfterInit private StringDigester digester;
     
     /** Constructor. */
-    public OIDCClienInfoCredentialValidator() {
+    public OIDCClientInfoCredentialValidator() {
         oidcMetadataContextLookupStrategy = new ChildContextLookup<>(OIDCMetadataContext.class).compose(
                 new InboundMessageContextLookup());
     }
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
index a4f6694a..51a73a33 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
@@ -29,19 +29,41 @@
     <bean parent="shibboleth.AuthnComparisonRegistration"
         c:key-ref="shibboleth.OIDCAuthnMethodExact" c:value-ref="shibboleth.ExactMatchFactory" />
 
+    <!-- OAuth2 login flow -->
+    <bean p:id="authn/OAuth2Client" parent="shibboleth.AuthenticationFlow"
+            p:order="%{idp.authn.OAuth2Client.order:1000}"
+            p:nonBrowserSupported="%{idp.authn.OAuth2Client.nonBrowserSupported:true}"
+            p:passiveAuthenticationSupported="%{idp.authn.OAuth2Client.passiveAuthenticationSupported:true}"
+            p:forcedAuthenticationSupported="%{idp.authn.OAuth2Client.forcedAuthenticationSupported:true}"
+            p:proxyRestrictionsEnforced="%{idp.authn.OAuth2Client.proxyRestrictionsEnforced:%{idp.authn.enforceProxyRestrictions:true}}"
+            p:proxyScopingEnforced="%{idp.authn.OAuth2Client.proxyScopingEnforced:false}"
+            p:discoveryRequired="%{idp.authn.OAuth2Client.discoveryRequired:false}"
+            p:lifetime="%{idp.authn.OAuth2Client.lifetime:%{idp.authn.defaultLifetime:PT60S}}"
+            p:inactivityTimeout="%{idp.authn.OAuth2Client.inactivityTimeout:%{idp.authn.defaultTimeout:PT60S}}"
+            p:reuseCondition-ref="#{'%{idp.authn.OAuth2Client.reuseCondition:shibboleth.Conditions.FALSE}'.trim()}"
+            p:activationCondition-ref="#{'%{idp.authn.OAuth2Client.activationCondition:shibboleth.Conditions.TRUE}'.trim()}"
+            p:subjectDecorator="#{getObject('%{idp.authn.OAuth2Client.subjectDecorator:}'.trim())}">
+        <property name="supportedPrincipalsByString">
+            <bean parent="shibboleth.CommaDelimStringArray"
+                c:_0="#{'%{idp.authn.OAuth2Client.supportedPrincipals:}'.trim()}" />
+        </property>
+    </bean>
+
+    <!-- Property-based definition of login flows for OAuth endpoints. -->    
+    <bean id="shibboleth.oidc.PotentialFlows" class="org.springframework.beans.factory.config.ListFactoryBean"
+        p:sourceList="#{getObject('shibboleth.AuthenticationFlowDescriptorManager').getComponents().?[id matches 'authn/(' + '%{idp.oauth2.authn.flows:OAuth2Client}'.trim() + ')']}" />
     
     <!-- Sealer used to protect confidentiality and integrity of authorize code, access token and refresh token. -->
     <bean id="shibboleth.oidc.TokenSealer" lazy-init="true"
         class="net.shibboleth.utilities.java.support.security.DataSealer"
         p:encoder-ref="base64Codec"
         p:decoder-ref="base64Codec"
-        p:keyStrategy-ref="#{ '%{idp.sealer.keyStrategy:shibboleth.DataSealerKeyStrategy}'.trim() }"
+        p:keyStrategy-ref="#{'%{idp.sealer.keyStrategy:shibboleth.DataSealerKeyStrategy}'.trim()}"
         p:lockedAtStartup="#{!environment.containsProperty('idp.sealer.keyStrategy') and (!environment.containsProperty('idp.sealer.storePassword') or !environment.containsProperty('idp.sealer.keyPassword')) }" />
 
     <bean id="base64Codec" class="org.apache.commons.codec.binary.Base64" c:lineLength="0"
         c:lineSeparator="#{new byte[] {10} }" c:urlSafe="true" />
 
-
     <!-- OIDC client information resolver service beans. -->
 
     <bean id="shibboleth.ClientInformationResolverService"
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OAuth2Client/OAuth2Client-beans.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OAuth2Client/OAuth2Client-beans.xml
new file mode 100644
index 00000000..e3c66b11
--- /dev/null
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OAuth2Client/OAuth2Client-beans.xml
@@ -0,0 +1,140 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:context="http://www.springframework.org/schema/context"
+       xmlns:util="http://www.springframework.org/schema/util"
+       xmlns:p="http://www.springframework.org/schema/p"
+       xmlns:c="http://www.springframework.org/schema/c"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
+                           http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
+                           
+       default-init-method="initialize"
+       default-destroy-method="destroy">
+       
+    <bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer"
+        p:placeholderPrefix="%{" p:placeholderSuffix="}" />
+
+    <bean class="net.shibboleth.ext.spring.config.IdentifiableBeanPostProcessor" />
+    <bean class="net.shibboleth.idp.profile.impl.ProfileActionBeanPostProcessor" />
+
+    <!-- Default message map. -->
+    <util:map id="shibboleth.authn.OAuth2Client.ClassifiedMessageMap">
+        <entry key="RequestUnsupported">
+            <list>
+                <value>RequestUnsupported</value>
+            </list>
+        </entry>
+    </util:map>
+
+    <import resource="conditional:%{idp.home}/conf/authn/oauth2client-authn-config.xml" />
+
+    <bean id="ExtractClientAuthenticationFromRequest"
+        class="net.shibboleth.idp.plugin.oidc.op.authn.impl.ExtractClientAuthenticationFromRequest" scope="prototype"
+        p:httpServletRequest-ref="shibboleth.HttpServletRequest" />
+
+    <bean id="ValidateClientAuthenticationType"
+        class="net.shibboleth.idp.plugin.oidc.op.authn.impl.ValidateClientAuthenticationType" scope="prototype" />
+
+    <bean id="DefaultCleanupHook"
+        class="net.shibboleth.idp.authn.impl.ValidateCredentials.UsernamePasswordCleanupHook" />
+    
+    <bean id="ValidateCredentials"
+        class="net.shibboleth.idp.authn.impl.ValidateCredentials" scope="prototype"
+        p:requireAll="%{idp.authn.OAuth2Client.requireAll:false}"
+        p:validators="#{getObject('shibboleth.authn.OAuth2Client.Validators') ?: getObject('DefaultOAuth2ClientValidators')}"
+        p:addDefaultPrincipals="%{idp.authn.OAuth2Client.addDefaultPrincipals:true}"
+        p:supportedPrincipals="#{getObject('shibboleth.authn.OAuth2Client.PrincipalOverride')}"
+        p:classifiedMessages="#{getObject('shibboleth.authn.OAuth2Client.ClassifiedMessageMap')}"
+        p:resultCachingPredicate="#{getObject('shibboleth.authn.OAuth2Client.resultCachingPredicate')}"
+        p:cleanupHook-ref="DefaultCleanupHook"
+        p:lockoutManager="#{getObject('shibboleth.authn.OAuth2Client.AccountLockoutManager')}" />
+    
+    <bean id="PopulateSubjectCanonicalizationContext"
+        class="net.shibboleth.idp.authn.impl.PopulateSubjectCanonicalizationContext" scope="prototype"
+        p:availableFlows-ref="shibboleth.PostLoginSubjectCanonicalizationFlows" />
+        
+    <!-- Default validators equivalent to previous versions. -->
+    
+    <util:list id="DefaultOAuth2ClientValidators">
+        <bean class="net.shibboleth.idp.plugin.oidc.op.authn.impl.OIDCClientInfoCredentialValidator"
+            p:id="oauth2-clientinfo" />
+        <!-- TODO: Add JWT Validators once implemented -->
+    </util:list>
+    
+    <!-- Validator parent beans -->
+    
+    <bean id="shibboleth.JAASValidator"
+        class="net.shibboleth.idp.authn.impl.JAASCredentialValidator" abstract="true"
+        p:id="oauth2-jaas" />
+
+    <bean id="shibboleth.KerberosValidator"
+        class="net.shibboleth.idp.authn.impl.KerberosCredentialValidator" abstract="true"
+        p:id="oauth2-krb5" />
+
+    <bean id="shibboleth.LDAPValidator"
+        class="net.shibboleth.idp.authn.impl.LDAPCredentialValidator" abstract="true"
+        p:id="oauth2-ldap"
+        p:authenticator-ref="shibboleth.authn.OAuth2Client.LDAP.authenticator" />
+
+    <bean id="shibboleth.HTPasswdValidator" abstract="true"
+        class="net.shibboleth.idp.authn.impl.HTPasswdCredentialValidator"
+        p:id="oauth2-htpasswd" />
+
+    <!-- Parent beans for custom ldaptive types. -->
+    
+    <bean id="shibboleth.authn.OAuth2Client.LDAP.authenticator" parent="shibboleth.LDAPAuthenticationFactory"
+        lazy-init="true" />
+
+    <bean id="shibboleth.X509ResourceCredentialConfig"
+        class="net.shibboleth.idp.authn.impl.X509ResourceCredentialConfig" abstract="true" /> 
+    <bean id="shibboleth.KeystoreResourceCredentialConfig"
+        class="net.shibboleth.idp.authn.impl.KeystoreResourceCredentialConfig" abstract="true" /> 
+    
+    <bean id="shibboleth.authn.OAuth2Client.LDAP.trustCertificates" parent="shibboleth.X509ResourceCredentialConfig"
+        p:trustCertificates="%{idp.authn.OAuth2Client.LDAP.trustCertificates:undefined}" />
+    <bean id="shibboleth.authn.OAuth2Client.LDAP.truststore" parent="shibboleth.KeystoreResourceCredentialConfig"
+        p:truststore="%{idp.authn.OAuth2Client.LDAP.trustStore:undefined}" />
+
+    <bean id="shibboleth.LDAPAuthenticationFactory" abstract="true"
+        class="net.shibboleth.idp.authn.config.LDAPAuthenticationFactoryBean"
+        p:authenticatorType="#{'%{idp.authn.OAuth2Client.LDAP.authenticator:anonSearchAuthenticator}'.trim()}"
+        p:trustType="#{'%{idp.authn.OAuth2Client.LDAP.sslConfig:certificateTrust}'.trim()}"
+        p:connectionStrategyType="#{'%{idp.authn.OAuth2Client.LDAP.connectionStrategy:ACTIVE_PASSIVE}'.trim()}"
+        p:ldapUrl="%{idp.authn.OAuth2Client.LDAP.ldapURL:ldap://localhost:10389}"
+        p:useStartTLS="%{idp.authn.OAuth2Client.LDAP.useStartTLS:true}"
+        p:connectTimeout="%{idp.authn.OAuth2Client.LDAP.connectTimeout:PT3S}"
+        p:responseTimeout="%{idp.authn.OAuth2Client.LDAP.responseTimeout:PT3S}"
+        p:trustCertificatesCredentialConfig-ref="shibboleth.authn.OAuth2Client.LDAP.trustCertificates"
+        p:truststoreCredentialConfig-ref="shibboleth.authn.OAuth2Client.LDAP.truststore"
+        p:disablePooling="%{idp.authn.OAuth2Client.LDAP.disablePooling:false}"
+        p:blockWaitTime="%{idp.pool.LDAP.blockWaitTime:PT3S}"
+        p:minPoolSize="%{idp.pool.LDAP.minSize:3}"
+        p:maxPoolSize="%{idp.pool.LDAP.maxSize:10}"
+        p:validateOnCheckout="%{idp.pool.LDAP.validateOnCheckout:false}"
+        p:validatePeriodically="%{idp.pool.LDAP.validatePeriodically:true}"
+        p:validatePeriod="%{idp.pool.LDAP.validatePeriod:PT5M}"
+        p:validateDn="#{'%{idp.pool.LDAP.validateDN:}'.trim()}"
+        p:validateFilter="#{'%{idp.pool.LDAP.validateFilter:(objectClass=*)}'.trim()}"
+        p:bindPoolPassivatorType="#{'%{idp.authn.OAuth2Client.LDAP.bindPoolPassivator:none}'.trim()}"
+        p:prunePeriod="%{idp.pool.LDAP.prunePeriod:PT5M}"
+        p:idleTime="%{idp.pool.LDAP.idleTime:PT10M}"
+        p:dnFormat="%{idp.authn.OAuth2Client.LDAP.dnFormat:undefined}"
+        p:baseDn="#{'%{idp.authn.OAuth2Client.LDAP.baseDN:undefined}'.trim()}"
+        p:userFilter="#{'%{idp.authn.OAuth2Client.LDAP.userFilter:undefined}'.trim()}"
+        p:subtreeSearch="%{idp.authn.OAuth2Client.LDAP.subtreeSearch:false}"
+        p:resolveEntryOnFailure="%{idp.authn.OAuth2Client.LDAP.resolveEntryOnFailure:false}"
+        p:resolveEntryWithBindDn="%{idp.authn.OAuth2Client.LDAP.resolveEntryWithBindDN:false}"
+        p:velocityEngine-ref="shibboleth.VelocityEngine"
+        p:bindDn="#{'%{idp.authn.OAuth2Client.LDAP.bindDN:undefined}'.trim()}"
+        p:bindDnCredential="%{idp.authn.OAuth2Client.LDAP.bindDNCredential:undefined}"
+        p:usePasswordPolicy="%{idp.authn.OAuth2Client.LDAP.usePasswordPolicy:false}"
+        p:usePasswordExpiration="%{idp.authn.OAuth2Client.LDAP.usePasswordExpiration:false}"
+        p:activeDirectory="%{idp.authn.OAuth2Client.LDAP.activeDirectory:false}"
+        p:freeIPA="%{idp.authn.OAuth2Client.LDAP.freeIPADirectory:false}"
+        p:EDirectory="%{idp.authn.OAuth2Client.LDAP.eDirectory:false}"
+        p:accountStateExpirationPeriod="%{idp.authn.OAuth2Client.LDAP.accountStateExpirationPeriod:#{null}}"
+        p:accountStateWarningPeriod="%{idp.authn.OAuth2Client.LDAP.accountStateWarningPeriod:#{null}}"
+        p:accountStateLoginFailures="%{idp.authn.OAuth2Client.LDAP.accountStateLoginFailures:0}" />
+
+</beans>
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OAuth2Client/OAuth2Client-flow.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OAuth2Client/OAuth2Client-flow.xml
new file mode 100644
index 00000000..73f28100
--- /dev/null
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OAuth2Client/OAuth2Client-flow.xml
@@ -0,0 +1,39 @@
+<flow xmlns="http://www.springframework.org/schema/webflow"
+      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+      xsi:schemaLocation="http://www.springframework.org/schema/webflow http://www.springframework.org/schema/webflow/spring-webflow.xsd"
+      parent="authn.abstract">
+
+    <!--
+    This is a login flow for handling OAuth2-defined client authentication mechanisms.
+    The current implementation relies on Nimbus APIs but could be replaced in the future if necessary.
+    -->
+
+    <action-state id="OAuth2Client">
+        <evaluate expression="ExtractClientAuthenticationFromRequest" />
+        <evaluate expression="ValidateClientAuthenticationType" />
+        <evaluate expression="ValidateCredentials" />
+        <evaluate expression="PopulateSubjectCanonicalizationContext" />
+        <evaluate expression="'proceed'" />
+        
+        <transition on="proceed" to="CallSubjectCanonicalization" />
+    </action-state>
+    
+    <!-- This runs a c14n step on the result of the authentication. -->
+    <subflow-state id="CallSubjectCanonicalization" subflow="c14n">
+        <input name="calledAsSubflow" value="true" />
+        <transition on="proceed" to="proceed" />
+        
+        <!-- This shouldn't generally happen, but if c14n fails, it's allowable to fall through. -->
+        <transition on="SubjectCanonicalizationError" to="ReselectFlow" />
+    </subflow-state>
+
+    <!-- As a "fall-through" method, remap selected events to select a different flow. -->
+    <global-transitions>
+        <transition on="NoCredentials" to="ReselectFlow" />
+        <transition on="InvalidCredentials" to="ReselectFlow" />
+        <transition on="RequestUnsupported" to="ReselectFlow" />
+    </global-transitions>
+
+    <bean-import resource="OAuth2Client-beans.xml" />
+
+</flow>
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oauth2/introspection/introspection-beans.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oauth2/introspection/introspection-beans.xml
index 3efd47e7..7b2ff74a 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oauth2/introspection/introspection-beans.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oauth2/introspection/introspection-beans.xml
@@ -43,10 +43,6 @@
         class="net.shibboleth.idp.plugin.oidc.op.oauth2.profile.impl.InitializeOutboundTokenIntrospectionResponseMessageContext"
         scope="prototype" />
 
-    <bean id="ValidateEndpointAuthentication"
-        class="net.shibboleth.idp.plugin.oidc.op.profile.impl.ValidateEndpointAuthentication" scope="prototype"
-        p:httpServletRequest-ref="shibboleth.HttpServletRequest" />
-
     <bean id="FormOutboundMessage"
         class="net.shibboleth.idp.plugin.oidc.op.oauth2.profile.impl.FormOutboundIntrospectionResponseMessage" scope="prototype"
         c:sealer-ref="#{'%{idp.oidc.tokenSealer:shibboleth.oidc.TokenSealer}'.trim()}"
@@ -54,16 +50,11 @@
 
     <bean id="BuildErrorResponseFromEvent"
         class="net.shibboleth.idp.plugin.oidc.op.oauth2.profile.impl.BuildIntrospectionErrorResponseFromEvent" scope="prototype"
-        p:httpServletResponse-ref="shibboleth.HttpServletResponse">
+            p:httpServletResponse-ref="shibboleth.HttpServletResponse"
+            p:mappedErrors-ref="shibboleth.oidc.ErrorMappings">
         <property name="eventContextLookupStrategy">
             <bean class="net.shibboleth.idp.profile.context.navigate.WebFlowCurrentEventLookupFunction" />
         </property>
-        <property name="mappedErrors">
-            <map value-type="com.nimbusds.oauth2.sdk.ErrorObject">
-                <entry key="#{T(org.opensaml.profile.action.EventIds).ACCESS_DENIED}"
-                    value="#{T(com.nimbusds.oauth2.sdk.OAuth2Error).INVALID_CLIENT}" />
-            </map>
-        </property>
     </bean>
 
 </beans>
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oauth2/introspection/introspection-flow.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oauth2/introspection/introspection-flow.xml
index cc907a13..2d56704c 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oauth2/introspection/introspection-flow.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oauth2/introspection/introspection-flow.xml
@@ -21,11 +21,12 @@
         <evaluate expression="SelectProfileConfiguration" />
         <evaluate expression="PostLookupPopulateAuditContext" />
         <evaluate expression="'proceed'" />
-        <transition on="proceed" to="OutboundContextsAndSecurityParameters" />
+        <transition on="proceed" to="AuthenticationSetup" />
     </action-state>
+    
+    <!-- Authentication subflow happens here. -->
 
-    <action-state id="OutboundContextsAndSecurityParameters">
-        <evaluate expression="ValidateEndpointAuthentication" />
+    <action-state id="ResumeAfterAuthentication">
         <evaluate expression="'proceed'" />
         <transition on="proceed" to="BuildResponseMessage" />
     </action-state>
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oauth2/revocation/revocation-beans.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oauth2/revocation/revocation-beans.xml
index c1f0c089..7abf14d0 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oauth2/revocation/revocation-beans.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oauth2/revocation/revocation-beans.xml
@@ -43,10 +43,6 @@
         class="net.shibboleth.idp.plugin.oidc.op.oauth2.profile.impl.InitializeOutboundRevokeTokenResponseMessageContext"
         scope="prototype" />
 
-    <bean id="ValidateEndpointAuthentication"
-        class="net.shibboleth.idp.plugin.oidc.op.profile.impl.ValidateEndpointAuthentication" scope="prototype"
-        p:httpServletRequest-ref="shibboleth.HttpServletRequest" />
-
     <bean id="RevokeToken" class="net.shibboleth.idp.plugin.oidc.op.oauth2.profile.impl.RevokeToken" scope="prototype"
         c:sealer-ref="#{'%{idp.oidc.tokenSealer:shibboleth.oidc.TokenSealer}'.trim()}"
         p:revocationCache-ref="shibboleth.RevocationCache" />
@@ -56,17 +52,13 @@
         scope="prototype" />
 
     <bean id="BuildErrorResponseFromEvent"
-        class="net.shibboleth.idp.plugin.oidc.op.oauth2.profile.impl.BuildRevokeTokenErrorResponseFromEvent" 
-        scope="prototype" p:httpServletResponse-ref="shibboleth.HttpServletResponse">
+            class="net.shibboleth.idp.plugin.oidc.op.oauth2.profile.impl.BuildRevokeTokenErrorResponseFromEvent" 
+            scope="prototype"
+            p:httpServletResponse-ref="shibboleth.HttpServletResponse"
+            p:mappedErrors-ref="shibboleth.oidc.ErrorMappings">
         <property name="eventContextLookupStrategy">
             <bean class="net.shibboleth.idp.profile.context.navigate.WebFlowCurrentEventLookupFunction" />
         </property>
-        <property name="mappedErrors">
-            <map value-type="com.nimbusds.oauth2.sdk.ErrorObject">
-                <entry key="#{T(org.opensaml.profile.action.EventIds).ACCESS_DENIED}"
-                    value="#{T(com.nimbusds.oauth2.sdk.OAuth2Error).ACCESS_DENIED}" />
-            </map>
-        </property>
     </bean>
 
 </beans>
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oauth2/revocation/revocation-flow.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oauth2/revocation/revocation-flow.xml
index 12f7e781..77ac1a52 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oauth2/revocation/revocation-flow.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oauth2/revocation/revocation-flow.xml
@@ -21,11 +21,12 @@
         <evaluate expression="SelectProfileConfiguration" />
         <evaluate expression="PostLookupPopulateAuditContext" />
         <evaluate expression="'proceed'" />
-        <transition on="proceed" to="OutboundContextsAndSecurityParameters" />
+        <transition on="proceed" to="AuthenticationSetup" />
     </action-state>
 
-    <action-state id="OutboundContextsAndSecurityParameters">
-        <evaluate expression="ValidateEndpointAuthentication" />
+    <!-- Authentication subflow happens here. -->
+
+    <action-state id="ResumeAfterAuthentication">
         <evaluate expression="'proceed'" />
         <transition on="proceed" to="RevokeToken" />
     </action-state>
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/abstract-api/oidc-abstract-api-beans.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/abstract-api/oidc-abstract-api-beans.xml
index 0d365430..78c7327f 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/abstract-api/oidc-abstract-api-beans.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/abstract-api/oidc-abstract-api-beans.xml
@@ -9,4 +9,38 @@
 
     <bean id="shibboleth.oidc.browserProfile" class="java.lang.Boolean" c:_0="false" />
 
+    <bean id="InitializeAuthenticationContext"
+        class="net.shibboleth.idp.saml.profile.impl.InitializeAuthenticationContext" scope="prototype" />
+
+    <util:map id="shibboleth.oidc.ErrorMappings" value-type="com.nimbusds.oauth2.sdk.ErrorObject">
+        <entry key="#{T(net.shibboleth.idp.authn.AuthnEventIds).AUTHN_EXCEPTION}"
+            value="#{T(com.nimbusds.oauth2.sdk.OAuth2Error).INVALID_CLIENT}" />
+        <entry key="#{T(net.shibboleth.idp.authn.AuthnEventIds).SUBJECT_C14N_ERROR}"
+            value="#{T(com.nimbusds.oauth2.sdk.OAuth2Error).INVALID_CLIENT}" />
+        <entry key="#{T(net.shibboleth.idp.authn.AuthnEventIds).UNKNOWN_USERNAME}"
+            value="#{T(com.nimbusds.oauth2.sdk.OAuth2Error).INVALID_CLIENT}" />
+        <entry key="#{T(net.shibboleth.idp.authn.AuthnEventIds).NO_CREDENTIALS}"
+            value="#{T(com.nimbusds.oauth2.sdk.OAuth2Error).INVALID_CLIENT}" />
+        <entry key="#{T(net.shibboleth.idp.authn.AuthnEventIds).INVALID_CREDENTIALS}"
+            value="#{T(com.nimbusds.oauth2.sdk.OAuth2Error).INVALID_CLIENT}" />
+        <entry key="#{T(net.shibboleth.idp.authn.AuthnEventIds).ACCOUNT_LOCKED}"
+            value="#{T(com.nimbusds.oauth2.sdk.OAuth2Error).INVALID_CLIENT}" />
+        <entry key="#{T(net.shibboleth.idp.authn.AuthnEventIds).ACCOUNT_ERROR}"
+            value="#{T(com.nimbusds.oauth2.sdk.OAuth2Error).INVALID_CLIENT}" />
+        <entry key="#{T(net.shibboleth.idp.authn.AuthnEventIds).RESELECT_FLOW}"
+            value="#{T(com.nimbusds.oauth2.sdk.OAuth2Error).INVALID_CLIENT}" />
+        <entry key="#{T(net.shibboleth.idp.authn.AuthnEventIds).NO_POTENTIAL_FLOW}"
+            value="#{T(com.nimbusds.oauth2.sdk.OAuth2Error).INVALID_CLIENT}" />
+
+        <entry key="#{T(net.shibboleth.idp.profile.IdPEventIds).INVALID_PROFILE_CONFIG}"
+            value="#{T(com.nimbusds.oauth2.sdk.OAuth2Error).ACCESS_DENIED}" />            
+        <entry key="#{T(org.opensaml.profile.action.EventIds).ACCESS_DENIED}"
+            value="#{T(com.nimbusds.oauth2.sdk.OAuth2Error).ACCESS_DENIED}" />            
+
+        <entry key="#{T(net.shibboleth.idp.plugin.oidc.op.profile.OidcEventIds).INVALID_GRANT}"
+            value="#{T(com.nimbusds.oauth2.sdk.OAuth2Error).INVALID_GRANT}" />
+        <entry key="#{T(net.shibboleth.idp.plugin.oidc.op.profile.OidcEventIds).INVALID_REDIRECT_URI}"
+            value="#{T(com.nimbusds.oauth2.sdk.OAuth2Error).INVALID_REQUEST}" />
+    </util:map>
+
 </beans>
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/abstract-api/oidc-abstract-api-flow.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/abstract-api/oidc-abstract-api-flow.xml
index a51f2c5a..a368ac53 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/abstract-api/oidc-abstract-api-flow.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/abstract-api/oidc-abstract-api-flow.xml
@@ -5,6 +5,22 @@
     abstract="true">
 
     <!--  Actions common to most OIDC profile backend flows. -->
+    
+    <action-state id="AuthenticationSetup">
+        <evaluate expression="InitializeAuthenticationContext" />
+        <evaluate expression="'proceed'" />
+        
+        <transition on="proceed" to="DoAuthenticationSubflow" />
+    </action-state>
+
+    <subflow-state id="DoAuthenticationSubflow" subflow="authn">
+        <input name="calledAsSubflow" value="true" />
+        <input name="bypassSessionActions" value="true" />
+        <input name="potentialFlows" value="getActiveFlow().getApplicationContext().getBean('shibboleth.oidc.PotentialFlows')" />
+        <transition on="proceed" to="ResumeAfterAuthentication" />
+        <transition on="RestartAuthentication" to="AuthenticationSetup" />
+    </subflow-state>
+    
     <action-state id="HandleError">
         <on-entry>
             <evaluate
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/token/token-beans.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/token/token-beans.xml
index 61b4987e..daa7c646 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/token/token-beans.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/token/token-beans.xml
@@ -28,16 +28,6 @@
         class="net.shibboleth.idp.plugin.oidc.op.profile.impl.InitializeOutboundTokenResponseMessageContext"
         scope="prototype" />
 
-    <bean id="ValidateEndpointAuthentication"
-	        class="net.shibboleth.idp.plugin.oidc.op.profile.impl.ValidateEndpointAuthentication" scope="prototype"
-	        p:httpServletRequest-ref="shibboleth.HttpServletRequest">
-        <property name="securityParametersLookupStrategy">
-            <bean parent="shibboleth.Functions.Compose"
-                c:g-ref="shibboleth.ChildLookup.SecurityParameters"
-                c:f-ref="shibboleth.ChildLookup.RelyingParty" />
-        </property>
-    </bean>
-
     <bean id="ValidateGrantType" class="net.shibboleth.idp.plugin.oidc.op.profile.impl.ValidateGrantType"
         scope="prototype" />
 
@@ -164,7 +154,7 @@
         scope="prototype" p:requestNonceLookupStrategy-ref="shibboleth.TokenRequestNonceLookupStrategy" />
 
     <bean id="AddAccessTokenHashToIDToken"
-        class="net.shibboleth.idp.plugin.oidc.op.profile.impl.AddAccessTokenHashToIDToken" scope="prototype">
+            class="net.shibboleth.idp.plugin.oidc.op.profile.impl.AddAccessTokenHashToIDToken" scope="prototype">
         <property name="securityParametersLookupStrategy">
             <bean parent="shibboleth.Functions.Compose"
                 c:g-ref="shibboleth.ChildLookup.SecurityParameters"
@@ -190,19 +180,12 @@
         class="net.shibboleth.idp.plugin.oidc.op.profile.impl.FormOutboundTokenResponseMessage" scope="prototype" />
 
     <bean id="BuildErrorResponseFromEvent"
-        class="net.shibboleth.idp.plugin.oidc.op.profile.impl.BuildTokenErrorResponseFromEvent" scope="prototype"
-        p:httpServletResponse-ref="shibboleth.HttpServletResponse">
+            class="net.shibboleth.idp.plugin.oidc.op.profile.impl.BuildTokenErrorResponseFromEvent" scope="prototype"
+            p:httpServletResponse-ref="shibboleth.HttpServletResponse"
+            p:mappedErrors-ref="shibboleth.oidc.ErrorMappings">
         <property name="eventContextLookupStrategy">
             <bean class="net.shibboleth.idp.profile.context.navigate.WebFlowCurrentEventLookupFunction" />
         </property>
-        <property name="mappedErrors">
-            <map value-type="com.nimbusds.oauth2.sdk.ErrorObject">
-                <entry key="#{T(net.shibboleth.idp.plugin.oidc.op.profile.OidcEventIds).INVALID_GRANT}"
-                    value="#{T(com.nimbusds.oauth2.sdk.OAuth2Error).INVALID_GRANT}" />
-                <entry key="#{T(net.shibboleth.idp.plugin.oidc.op.profile.OidcEventIds).INVALID_REDIRECT_URI}"
-                    value="#{T(com.nimbusds.oauth2.sdk.OAuth2Error).INVALID_REQUEST}" />
-            </map>
-        </property>
     </bean>
 
     <bean id="GrantTypeAuthorizationCode" parent="shibboleth.Conditions.Expression">
@@ -214,9 +197,9 @@
     </bean>
 
     <bean id="PopulateTokenEndpointJwtSignatureValidationParameters"
-        class="net.shibboleth.idp.plugin.oidc.op.profile.impl.PopulateOIDCSignatureSigningParameters " scope="prototype"
-        p:configurationLookupStrategy-ref="shibboleth.oidc.SignatureValidationConfigurationLookup"
-        p:signatureSigningParametersResolver-ref="shibboleth.oidc.TokenEndpointJwtSignatureValidationParametersResolver">
+            class="net.shibboleth.idp.plugin.oidc.op.profile.impl.PopulateOIDCSignatureSigningParameters " scope="prototype"
+            p:configurationLookupStrategy-ref="shibboleth.oidc.SignatureValidationConfigurationLookup"
+            p:signatureSigningParametersResolver-ref="shibboleth.oidc.TokenEndpointJwtSignatureValidationParametersResolver">
         <property name="securityParametersContextLookupStrategy">
             <bean parent="shibboleth.Functions.Compose"
                 c:g-ref="shibboleth.ChildLookupOrCreate.SecurityParameters"
@@ -239,8 +222,8 @@
         p:parameterType="#{T(net.shibboleth.idp.plugin.oidc.op.security.impl.OIDCClientInformationSignatureValidationParametersResolver.ParameterType).TOKEN_ENDPOINT_JWT_VALIDATION}" />
 
     <bean id="shibboleth.oidc.DefaultPostResponseAuditExtractorsForFlow"
-        parent="shibboleth.oidc.DefaultPostResponseAuditExtractors"
-        class="org.springframework.beans.factory.config.MapFactoryBean">
+            parent="shibboleth.oidc.DefaultPostResponseAuditExtractors"
+            class="org.springframework.beans.factory.config.MapFactoryBean">
         <property name="sourceMap">
             <map merge="true">
                 <entry>
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/token/token-flow.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/token/token-flow.xml
index 39ac40b2..5ee0e880 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/token/token-flow.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/token/token-flow.xml
@@ -23,13 +23,14 @@
         <evaluate expression="SelectRelyingPartyConfiguration" />        
         <evaluate expression="SelectProfileConfiguration" />
         <evaluate expression="PostLookupPopulateAuditContext" />
+        <evaluate expression="PopulateTokenEndpointJwtSignatureValidationParameters"/>
         <evaluate expression="'proceed'" />
-        <transition on="proceed" to="OutboundContextsAndSecurityParameters" />
+        <transition on="proceed" to="AuthenticationSetup" />
     </action-state>
 
-    <action-state id="OutboundContextsAndSecurityParameters">
-        <evaluate expression="PopulateTokenEndpointJwtSignatureValidationParameters"/>
-        <evaluate expression="ValidateEndpointAuthentication" />
+    <!-- Authentication subflow happens here. -->
+
+    <action-state id="ResumeAfterAuthentication">
         <evaluate expression="ValidateGrantType" />
         <evaluate expression="ValidateGrant" />
         <evaluate expression="ValidatePKCE" />
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/service/relying-party/postconfig.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/service/relying-party/postconfig.xml
index 1a68ea14..0f9df71c 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/service/relying-party/postconfig.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/service/relying-party/postconfig.xml
@@ -77,7 +77,31 @@
         </property>
     </bean>
     
-    <bean id="AbstractMDDrivenOIDCFlowAwareProfile" parent="AbstractMDDrivenOIDCProfile" abstract="true">
+    <bean id="AbstractMDDrivenOAuthClientAuthenticatableProfile" parent="AbstractMDDrivenOIDCProfile" abstract="true">
+        <property name="forceAuthnPredicate">
+            <bean class="net.shibboleth.utilities.java.support.logic.PredicateSupport" factory-method="fromFunction">
+                <constructor-arg>
+                    <bean parent="shibboleth.MDDrivenBoolProperty" p:propertyName="forceAuthn" />
+                </constructor-arg>
+                <constructor-arg value="false" />
+            </bean>
+        </property>
+        <property name="proxyCountLookupStrategy">
+            <bean parent="shibboleth.MDDrivenIntProperty" p:propertyName="proxyCount" />
+        </property>
+        <property name="authenticationFlowsLookupStrategy">
+            <bean parent="shibboleth.MDDrivenSetProperty" p:propertyName="authenticationFlows" />
+        </property>
+        <property name="postAuthenticationFlowsLookupStrategy">
+            <bean parent="shibboleth.MDDrivenListProperty" p:propertyName="postAuthenticationFlows" />
+        </property>
+        <property name="defaultAuthenticationMethodsLookupStrategy">
+            <bean parent="shibboleth.MDDrivenListProperty" p:propertyName="defaultAuthenticationMethods"
+                p:propertyType="#{T(net.shibboleth.oidc.authn.principal.AuthenticationContextClassReferencePrincipal)}" />
+        </property>
+    </bean>
+        
+    <bean id="AbstractMDDrivenOIDCFlowAwareProfile" parent="AbstractMDDrivenOAuthClientAuthenticatableProfile" abstract="true">
         <property name="authorizationCodeFlowEnabledPredicate">
             <bean class="net.shibboleth.utilities.java.support.logic.PredicateSupport" factory-method="fromFunction">
                 <constructor-arg>
@@ -134,27 +158,6 @@
                 <constructor-arg value="true" />
             </bean>
         </property>
-        <property name="forceAuthnPredicate">
-            <bean class="net.shibboleth.utilities.java.support.logic.PredicateSupport" factory-method="fromFunction">
-                <constructor-arg>
-                    <bean parent="shibboleth.MDDrivenBoolProperty" p:propertyName="forceAuthn" />
-                </constructor-arg>
-                <constructor-arg value="false" />
-            </bean>
-        </property>
-        <property name="proxyCountLookupStrategy">
-            <bean parent="shibboleth.MDDrivenIntProperty" p:propertyName="proxyCount" />
-        </property>
-        <property name="authenticationFlowsLookupStrategy">
-            <bean parent="shibboleth.MDDrivenSetProperty" p:propertyName="authenticationFlows" />
-        </property>
-        <property name="postAuthenticationFlowsLookupStrategy">
-            <bean parent="shibboleth.MDDrivenListProperty" p:propertyName="postAuthenticationFlows" />
-        </property>
-        <property name="defaultAuthenticationMethodsLookupStrategy">
-            <bean parent="shibboleth.MDDrivenListProperty" p:propertyName="defaultAuthenticationMethods"
-                p:propertyType="#{T(net.shibboleth.oidc.authn.principal.AuthenticationContextClassReferencePrincipal)}" />
-        </property>
         <property name="iDTokenLifetimeLookupStrategy">
             <bean parent="shibboleth.MDDrivenDurationProperty" p:propertyName="iDTokenLifetime"
                 p:defaultValue="%{idp.oidc.idToken.defaultLifetime:PT1H}" />
@@ -321,7 +324,7 @@
         </property>
     </bean>
             
-    <bean id="OAUTH2.Revocation.MDDriven" parent="AbstractMDDrivenOIDCProfile" lazy-init="true"
+    <bean id="OAUTH2.Revocation.MDDriven" parent="AbstractMDDrivenOAuthClientAuthenticatableProfile" lazy-init="true"
             class="net.shibboleth.oidc.profile.oauth2.config.OAuth2TokenRevocationConfiguration">
         <property name="tokenEndpointAuthMethodsLookupStrategy">
             <bean parent="shibboleth.MDDrivenSetProperty" p:propertyName="tokenEndpointAuthMethods">
@@ -335,7 +338,7 @@
         </property>
     </bean>
 
-    <bean id="OAUTH2.Introspection.MDDriven" parent="AbstractMDDrivenOIDCProfile" lazy-init="true"
+    <bean id="OAUTH2.Introspection.MDDriven" parent="AbstractMDDrivenOAuthClientAuthenticatableProfile" lazy-init="true"
             class="net.shibboleth.oidc.profile.oauth2.config.OAuth2TokenIntrospectionConfiguration">
         <property name="tokenEndpointAuthMethodsLookupStrategy">
             <bean parent="shibboleth.MDDrivenSetProperty" p:propertyName="tokenEndpointAuthMethods">
diff --git a/idp-oidc-extension-impl/src/main/resources/net/shibboleth/idp/plugin/oidc/op/conf/oidc.properties b/idp-oidc-extension-impl/src/main/resources/net/shibboleth/idp/plugin/oidc/op/conf/oidc.properties
index 990fcb3e..f3f23600 100644
--- a/idp-oidc-extension-impl/src/main/resources/net/shibboleth/idp/plugin/oidc/op/conf/oidc.properties
+++ b/idp-oidc-extension-impl/src/main/resources/net/shibboleth/idp/plugin/oidc/op/conf/oidc.properties
@@ -78,6 +78,10 @@ idp.oidc.subject.salt = this_too_should_be_ch4ng3d
 # Upgrade interval to the remote JWKs
 #idp.oidc.jwksuri.fetchInterval = PT30M
 
-# Lower bound on the next file refresh from the time calculated based on the previous attempt for OP's own metadata (configuration-flow).
+# Bounds on the next file refresh of the OP configuration resource
 #idp.oidc.config.minRefreshDelay = PT5M
-#idp.oidc.config.maxRefreshDelay = PT4H
\ No newline at end of file
+#idp.oidc.config.maxRefreshDelay = PT4H
+
+# Regular expression matching OAuth login flows to enable.
+# For most deployments, the default is sufficient to accomodate a variety of methods
+#idp.oauth2.authn.flows = OAuth2Client
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/authn/impl/OIDCClienInfoCredentialValidatorTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/authn/impl/OIDCClientInfoCredentialValidatorTest.java
similarity index 96%
rename from idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/authn/impl/OIDCClienInfoCredentialValidatorTest.java
rename to idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/authn/impl/OIDCClientInfoCredentialValidatorTest.java
index 44b9b786..93521921 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/authn/impl/OIDCClienInfoCredentialValidatorTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/authn/impl/OIDCClientInfoCredentialValidatorTest.java
@@ -49,13 +49,13 @@ import com.nimbusds.oauth2.sdk.id.ClientID;
 import com.nimbusds.openid.connect.sdk.rp.OIDCClientInformation;
 import com.nimbusds.openid.connect.sdk.rp.OIDCClientMetadata;
 
-/** Unit test for {@link OIDCClienInfoCredentialValidator}. */
-public class OIDCClienInfoCredentialValidatorTest extends BaseAuthenticationContextTest {
+/** Unit test for {@link OIDCClientInfoCredentialValidator}. */
+public class OIDCClientInfoCredentialValidatorTest extends BaseAuthenticationContextTest {
 
     private ClientID clientId;
     private Secret clientSecret;
     
-    private OIDCClienInfoCredentialValidator validator;
+    private OIDCClientInfoCredentialValidator validator;
     
     private ValidateCredentials action;
 
@@ -65,7 +65,7 @@ public class OIDCClienInfoCredentialValidatorTest extends BaseAuthenticationCont
         clientId = new ClientID("mockId");
         clientSecret = new Secret("secret1234567890secret1234567890secret1234567890");
         
-        validator = new OIDCClienInfoCredentialValidator();
+        validator = new OIDCClientInfoCredentialValidator();
         validator.setId("test");
         validator.initialize();
         
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/IntrospectionFlowTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/IntrospectionFlowTest.java
index b38ceca1..930ae64e 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/IntrospectionFlowTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/IntrospectionFlowTest.java
@@ -30,6 +30,7 @@ import org.testng.Assert;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
+import com.nimbusds.oauth2.sdk.OAuth2Error;
 import com.nimbusds.oauth2.sdk.Scope;
 import com.nimbusds.oauth2.sdk.TokenIntrospectionErrorResponse;
 import com.nimbusds.oauth2.sdk.TokenIntrospectionSuccessResponse;
@@ -69,8 +70,7 @@ public class IntrospectionFlowTest extends AbstractOidcApiFlowTest {
         setHttpFormRequest("POST", Collections.singletonMap("token",
                 super.buildToken(clientId, "sub", Scope.parse("openid")).toJSONObject().getAsString("access_token")));
         final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
-        assertErrorCode(result, "invalid_request");
-        assertErrorDescriptionContains(result, "InvalidProfileConfiguration");
+        assertErrorCode(result, OAuth2Error.ACCESS_DENIED_CODE);
     }
     
     @Test
@@ -81,7 +81,7 @@ public class IntrospectionFlowTest extends AbstractOidcApiFlowTest {
         setHttpFormRequest("POST", Collections.singletonMap("token_not",
                 super.buildToken(clientId, "sub", Scope.parse("openid")).toJSONObject().getAsString("access_token")));
         final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
-        assertErrorCode(result, "invalid_request");
+        assertErrorCode(result, OAuth2Error.INVALID_REQUEST_CODE);
     }
 
     @Test
@@ -146,6 +146,6 @@ public class IntrospectionFlowTest extends AbstractOidcApiFlowTest {
                 super.buildToken(clientId, "sub", Scope.parse("openid")).toJSONObject().getAsString("access_token")));
         final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
         TokenIntrospectionErrorResponse resp = (TokenIntrospectionErrorResponse) parseErrorResponse(result);
-        Assert.assertEquals(resp.getErrorObject().getCode(), "invalid_client");
+        Assert.assertEquals(resp.getErrorObject().getCode(), OAuth2Error.INVALID_CLIENT_CODE);
     }
 }
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/RevocationFlowTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/RevocationFlowTest.java
index 626e3bf4..6dffdb5f 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/RevocationFlowTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/RevocationFlowTest.java
@@ -26,11 +26,14 @@ import org.opensaml.storage.StorageService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.webflow.executor.FlowExecutionResult;
+import org.testng.Assert;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
+import com.nimbusds.oauth2.sdk.OAuth2Error;
 import com.nimbusds.oauth2.sdk.ParseException;
 import com.nimbusds.oauth2.sdk.Scope;
+import com.nimbusds.oauth2.sdk.TokenIntrospectionErrorResponse;
 
 import net.shibboleth.idp.plugin.oidc.op.oauth2.messaging.impl.OAuth2RevocationSuccessResponse;
 import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
@@ -66,8 +69,7 @@ public class RevocationFlowTest extends AbstractOidcApiFlowTest {
         setHttpFormRequest("POST", Collections.singletonMap("token", super.buildToken(clientId, "sub", 
                 Scope.parse("openid")).toJSONObject().getAsString("access_token")));
         final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
-        assertErrorCode(result, "invalid_request");
-        assertErrorDescriptionContains(result, "InvalidProfileConfiguration");
+        assertErrorCode(result, OAuth2Error.ACCESS_DENIED_CODE);
     }
 
     @Test
@@ -102,4 +104,16 @@ public class RevocationFlowTest extends AbstractOidcApiFlowTest {
         final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
         parseSuccessResponse(result, OAuth2RevocationSuccessResponse.class);
     }
-}
+
+    @Test
+    public void testFailedAuthentication() throws IOException, NoSuchAlgorithmException, URISyntaxException,
+            DataSealerException, ComponentInitializationException {
+        setBasicAuth(clientId, clientSecret + "X");
+        setHttpFormRequest("POST", Collections.singletonMap("token", super.buildLegacyToken(clientId, "sub",
+                Scope.parse("openid"), "mail").toJSONObject().getAsString("access_token")));
+        storeMetadata(storageService, clientId, clientSecret);
+        final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
+        assertErrorCode(result, OAuth2Error.INVALID_CLIENT_CODE);
+    }
+
+}
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/TokenFlowTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/TokenFlowTest.java
index b83c10ea..bc7cc61e 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/TokenFlowTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/TokenFlowTest.java
@@ -38,6 +38,7 @@ import com.nimbusds.jose.JOSEException;
 import com.nimbusds.jose.JWSAlgorithm;
 import com.nimbusds.jwt.JWT;
 import com.nimbusds.oauth2.sdk.AuthorizationCode;
+import com.nimbusds.oauth2.sdk.OAuth2Error;
 import com.nimbusds.oauth2.sdk.Scope;
 import com.nimbusds.oauth2.sdk.auth.ClientAuthenticationMethod;
 import com.nimbusds.oauth2.sdk.auth.ClientSecretJWT;
@@ -96,7 +97,7 @@ public class TokenFlowTest extends AbstractOidcFlowTest {
     public void testNoClientId() throws IOException, ParseException {
         setHttpFormRequest("POST", createRequestParameters(redirectUri, "authorization_code", "mockCode", null));
         final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
-        assertErrorCode(result, "invalid_request");
+        assertErrorCode(result, OAuth2Error.INVALID_REQUEST_CODE);
         assertErrorDescriptionContains(result, "UnableToDecode");
     }
 
@@ -104,7 +105,7 @@ public class TokenFlowTest extends AbstractOidcFlowTest {
     public void testNoGrantType() throws IOException, ParseException {
         setHttpFormRequest("POST", createRequestParameters(redirectUri, null, "mockCode", clientId));
         final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
-        assertErrorCode(result, "invalid_request");
+        assertErrorCode(result, OAuth2Error.INVALID_REQUEST_CODE);
         assertErrorDescriptionContains(result, "UnableToDecode");
     }
 
@@ -112,8 +113,7 @@ public class TokenFlowTest extends AbstractOidcFlowTest {
     public void testUntrustedClient() throws IOException, ParseException {
         setHttpFormRequest("POST", createRequestParameters(null, "authorization_code", "mockCode", clientId + "2"));
         final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
-        assertErrorCode(result, "invalid_request");
-        assertErrorDescriptionContains(result, "InvalidProfileConfiguration");
+        assertErrorCode(result, OAuth2Error.ACCESS_DENIED_CODE);
     }
     
     @Test
@@ -121,8 +121,7 @@ public class TokenFlowTest extends AbstractOidcFlowTest {
         setHttpFormRequest("POST", createRequestParameters(redirectUri, "authorization_code", "mockCode", clientId));
         storeMetadata(storageService, clientId, clientSecret);
         final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
-        assertErrorCode(result, "invalid_request");
-        assertErrorDescriptionContains(result, "AccessDenied");
+        assertErrorCode(result, OAuth2Error.INVALID_CLIENT_CODE);
     }
 
     @Test
@@ -131,7 +130,7 @@ public class TokenFlowTest extends AbstractOidcFlowTest {
         storeMetadata(storageService, clientId, clientSecret);
         setBasicAuth(clientId, clientSecret);
         final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
-        assertErrorCode(result, "invalid_grant");
+        assertErrorCode(result, OAuth2Error.INVALID_GRANT_CODE);
     }
     
     protected void initializeGrantAndRequest(String clientId, Map<String, String> requestParameters) 
@@ -238,10 +237,9 @@ public class TokenFlowTest extends AbstractOidcFlowTest {
                 consentedClaims);
         return new RefreshToken(BaseOIDCResponseActionTest.initializeDataSealer().wrap(json,
                 Instant.now().plusSeconds(30))).getValue();
-}
+    }
 
-    
-    @Test
+    @Test(enabled=false)
     public void testValidSecretJWT() throws ParseException, IOException, NoSuchAlgorithmException, URISyntaxException,
         DataSealerException, ComponentInitializationException, JOSEException {
         ClientSecretJWT clientAuth = buildSecretJwtAuth(clientSecret);
@@ -250,7 +248,7 @@ public class TokenFlowTest extends AbstractOidcFlowTest {
         Assert.assertNotNull(response.getTokens().getAccessToken());
     }
 
-    @Test
+    @Test(enabled=false)
     public void testValidSecretJWTNoAlg() throws ParseException, IOException, NoSuchAlgorithmException,
         URISyntaxException, DataSealerException, ComponentInitializationException, JOSEException {
         ClientSecretJWT clientAuth = buildSecretJwtAuth(clientSecret);
@@ -276,7 +274,7 @@ public class TokenFlowTest extends AbstractOidcFlowTest {
                 buildAuthorizationCode(clientIdPkcePlainUnforced, plainVerifier()), clientIdPkcePlainUnforced, null,
                 null, codeVerifier + "invalid"));
         final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
-        assertErrorCode(result, "invalid_request");
+        assertErrorCode(result, OAuth2Error.INVALID_REQUEST_CODE);
         assertErrorDescriptionContains(result, "MessageAuthenticationError");
     }
 
@@ -287,7 +285,7 @@ public class TokenFlowTest extends AbstractOidcFlowTest {
                 buildAuthorizationCode(clientIdPkcePlain, plainVerifier()), clientIdPkcePlain, null, null, 
                 codeVerifier + "invalid"));
         final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
-        assertErrorCode(result, "invalid_request");
+        assertErrorCode(result, OAuth2Error.INVALID_REQUEST_CODE);
         assertErrorDescriptionContains(result, "MessageAuthenticationError");
     }
 
@@ -330,7 +328,7 @@ public class TokenFlowTest extends AbstractOidcFlowTest {
                 buildAuthorizationCode(clientIdPkcePlainUnforced, s256Verifier()), clientIdPkcePlainUnforced, null, 
                 "S256", codeVerifier + "invalid"));
         final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
-        assertErrorCode(result, "invalid_request");
+        assertErrorCode(result, OAuth2Error.INVALID_REQUEST_CODE);
         assertErrorDescriptionContains(result, "MessageAuthenticationError");
     }
 
@@ -341,7 +339,7 @@ public class TokenFlowTest extends AbstractOidcFlowTest {
                 buildAuthorizationCode(clientIdPkceS256, s256Verifier()), clientIdPkceS256, null, "S256", 
                 codeVerifier + "invalid"));
         final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
-        assertErrorCode(result, "invalid_request");
+        assertErrorCode(result, OAuth2Error.INVALID_REQUEST_CODE);
         assertErrorDescriptionContains(result, "MessageAuthenticationError");
     }
 
@@ -371,8 +369,7 @@ public class TokenFlowTest extends AbstractOidcFlowTest {
         DataSealerException, ComponentInitializationException, JOSEException {
         ClientSecretJWT clientAuth = buildSecretJwtAuth(clientSecret + "invalid");
         final FlowExecutionResult result = launchWithJwtAuthentication(clientAuth, JWSAlgorithm.HS256);
-        assertErrorCode(result, "invalid_request");
-        assertErrorDescriptionContains(result, "AccessDenied");
+        assertErrorCode(result, OAuth2Error.INVALID_CLIENT_CODE);
     }
     
     @Test
diff --git a/idp-oidc-extension-impl/src/test/resources/conf/logback.xml b/idp-oidc-extension-impl/src/test/resources/conf/logback.xml
new file mode 100644
index 00000000..46c98e4a
--- /dev/null
+++ b/idp-oidc-extension-impl/src/test/resources/conf/logback.xml
@@ -0,0 +1,192 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+
+    <!--
+    Variables for simplifying logging configuration.
+    http://logback.qos.ch/manual/configuration.html#variableSubstitution
+    -->
+
+    <!--
+    If you want to use custom properties in this config file,
+    we load the main property file for you.
+    -->
+    <variable file="${idp.home}/conf/idp.properties" />
+
+    <!-- Location and retention. -->
+    
+    <variable name="idp.logfiles" value="${idp.logfiles:-${idp.home}/logs}" />
+    <variable name="idp.loghistory" value="${idp.loghistory:-180}" />
+    
+    <!-- Much higher performance if you operate on DEBUG. -->
+    <!-- <variable name="idp.process.appender" value="ASYNC_PROCESS" /> -->
+    
+    <!-- Logging level shortcuts. -->
+    <variable name="idp.loglevel.idp" value="${idp.loglevel.idp:-DEBUG}" />
+    <variable name="idp.loglevel.ldap" value="${idp.loglevel.ldap:-WARN}" />
+    <variable name="idp.loglevel.messages" value="${idp.loglevel.messages:-INFO}" />
+    <variable name="idp.loglevel.encryption" value="${idp.loglevel.encryption:-INFO}" />
+    <variable name="idp.loglevel.opensaml" value="${idp.loglevel.opensaml:-INFO}" />
+    <variable name="idp.loglevel.props" value="${idp.loglevel.props:-INFO}" />
+    <variable name="idp.loglevel.httpclient" value="${idp.loglevel.httpclient:-INFO}" />
+    
+    <!-- Don't turn these up unless you want a *lot* of noise. -->
+    <variable name="idp.loglevel.spring" value="${idp.loglevel.spring:-ERROR}" />
+    <variable name="idp.loglevel.container" value="${idp.loglevel.container:-ERROR}" />
+    <variable name="idp.loglevel.xmlsec" value="${idp.loglevel.xmlsec:-INFO}" />
+
+    <!-- =========================================================== -->
+    <!-- ============== Logging Categories and Levels ============== -->
+    <!-- =========================================================== -->
+
+    <!-- Logs IdP, but not OpenSAML, messages -->
+    <logger name="net.shibboleth.idp" level="${idp.loglevel.idp}"/>
+
+    <!-- Logs OpenSAML, but not IdP, messages -->
+    <logger name="org.opensaml.saml" level="${idp.loglevel.opensaml}"/>
+    
+    <!-- Logs LDAP related messages -->
+    <logger name="org.ldaptive" level="${idp.loglevel.ldap}"/>
+
+    <!-- Logs embedded HTTP client messages -->
+    <logger name="org.apache.http" level="${idp.loglevel.httpclient}"/>
+    
+    <!-- Logs inbound and outbound protocols messages at DEBUG level -->
+    <logger name="PROTOCOL_MESSAGE" level="${idp.loglevel.messages}" />
+
+    <!-- Logs unencrypted SAML at DEBUG level -->
+    <logger name="org.opensaml.saml.saml2.encryption.Encrypter" level="${idp.loglevel.encryption}" />
+    <logger name="org.opensaml.saml.saml2.encryption.Decrypter" level="${idp.loglevel.encryption}" />
+
+    <!-- Logs system properties during startup at DEBUG level -->
+    <logger name="net.shibboleth.idp.log.LogbackLoggingService" level="${idp.loglevel.props}" />
+
+    <!-- Especially chatty. -->
+    <logger name="org.apache.xml.security" level="${idp.loglevel.xmlsec}" />
+    <logger name="org.springframework" level="${idp.loglevel.spring}"/>
+    <logger name="org.apache.catalina" level="${idp.loglevel.container}"/>
+    <logger name="org.eclipse.jetty" level="${idp.loglevel.container}"/>
+
+
+    <!-- =========================================================== -->
+    <!-- ============== Low Level Details or Changes =============== -->
+    <!-- =========================================================== -->
+    
+    <!-- Process log. -->
+    <appender name="IDP_PROCESS" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <File>${idp.logfiles}/idp-process.log</File>
+        
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <fileNamePattern>${idp.logfiles}/idp-process-%d{yyyy-MM-dd}.log.gz</fileNamePattern>
+            <maxHistory>${idp.loghistory}</maxHistory>
+        </rollingPolicy>
+
+        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+            <charset>UTF-8</charset>
+            <Pattern>%date{ISO8601} - %mdc{idp.remote_addr} - %level [%logger:%line] - %msg%n%ex{short}</Pattern>
+        </encoder>
+
+        <!-- Ignore Velocity status page error. -->
+        <filter class="ch.qos.logback.core.filter.EvaluatorFilter">
+            <evaluator>
+                <matcher>
+                    <Name>VelocityStatusMatcher</Name>
+                    <regex>ResourceManager\s*: unable to find resource 'status\.vm' in any resource loader\.</regex>
+                </matcher>
+                <expression>VelocityStatusMatcher.matches(formattedMessage)</expression>
+            </evaluator>
+            <OnMatch>DENY</OnMatch>
+        </filter>
+    </appender>
+
+    <appender name="ASYNC_PROCESS" class="ch.qos.logback.classic.AsyncAppender">
+        <appender-ref ref="IDP_PROCESS" />
+        <discardingThreshold>0</discardingThreshold>
+    </appender>
+
+    <appender name="IDP_WARN" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <!-- Suppress anything below WARN. -->
+        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+            <level>WARN</level>
+        </filter>
+        
+        <File>${idp.logfiles}/idp-warn.log</File>
+        
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <fileNamePattern>${idp.logfiles}/idp-warn-%d{yyyy-MM-dd}.log.gz</fileNamePattern>
+            <maxHistory>${idp.loghistory}</maxHistory>
+        </rollingPolicy>
+        
+        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+            <charset>UTF-8</charset>
+            <Pattern>%date{ISO8601} - %mdc{idp.remote_addr} - %level [%logger:%line] - %msg%n%ex{full}</Pattern>
+        </encoder>
+        
+        <!-- Ignore Velocity status page error. -->
+        <filter class="ch.qos.logback.core.filter.EvaluatorFilter">
+            <evaluator>
+                <matcher>
+                    <Name>VelocityStatusMatcher</Name>
+                    <regex>ResourceManager\s*: unable to find resource 'status\.vm' in any resource loader\.</regex>
+                </matcher>
+                <expression>VelocityStatusMatcher.matches(formattedMessage)</expression>
+            </evaluator>
+            <OnMatch>DENY</OnMatch>
+        </filter>
+    </appender>
+    
+    <!-- Audit log. -->
+    <appender name="IDP_AUDIT" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <File>${idp.logfiles}/idp-audit.log</File>
+
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <fileNamePattern>${idp.logfiles}/idp-audit-%d{yyyy-MM-dd}.log.gz</fileNamePattern>
+            <maxHistory>${idp.loghistory}</maxHistory>
+        </rollingPolicy>
+
+        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+            <charset>UTF-8</charset>
+            <Pattern>%msg%n</Pattern>
+        </encoder>
+    </appender>
+    
+    <!-- Consent audit log. -->
+    <appender name="IDP_CONSENT_AUDIT" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <File>${idp.logfiles}/idp-consent-audit.log</File>
+
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <fileNamePattern>${idp.logfiles}/idp-consent-audit-%d{yyyy-MM-dd}.log.gz</fileNamePattern>
+            <maxHistory>${idp.loghistory}</maxHistory>
+        </rollingPolicy>
+
+        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+            <charset>UTF-8</charset>
+            <Pattern>%msg%n</Pattern>
+        </encoder>
+    </appender>
+
+    <!-- F-TICKS syslog destination. -->
+    <appender name="IDP_FTICKS" class="ch.qos.logback.classic.net.SyslogAppender">
+        <syslogHost>${idp.fticks.loghost:-localhost}</syslogHost>
+        <port>${idp.fticks.logport:-514}</port>
+        <facility>AUTH</facility>
+        <suffixPattern>[%thread] %logger %msg</suffixPattern>
+    </appender>
+
+    <logger name="Shibboleth-Audit" level="ALL">
+        <appender-ref ref="${idp.audit.appender:-IDP_AUDIT}"/>
+    </logger>
+
+    <logger name="Shibboleth-FTICKS" level="ALL" additivity="false">
+        <appender-ref ref="${idp.fticks.appender:-IDP_FTICKS}"/>
+    </logger>
+
+    <logger name="Shibboleth-Consent-Audit" level="ALL">
+        <appender-ref ref="${idp.consent.appender:-IDP_CONSENT_AUDIT}"/>
+    </logger>
+    
+    <root level="${idp.loglevel.root:-INFO}">
+        <appender-ref ref="${idp.process.appender:-IDP_PROCESS}"/>
+        <appender-ref ref="${idp.warn.appender:-IDP_WARN}" />
+    </root>
+
+</configuration>

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


More information about the commits mailing list