[java-idp-plugin-oidc-rp] branch main updated: Flesh out a flow, integrate with new commons metadata provider
Phil Smart
philip.smart at jisc.ac.uk
Fri Oct 15 07:38:58 UTC 2021
This is an automated email from the git hooks/post-receive script.
philsmart pushed a commit to branch main
in repository java-idp-plugin-oidc-rp.
View the commit online:
http://git.shibboleth.net/view/?p=java-idp-plugin-oidc-rp.git;a=commit;h=b9b2af53b28190d1963ee9f9196a9bef1d4cb2d9
The following commit(s) were added to refs/heads/main by this push:
new b9b2af5 Flesh out a flow, integrate with new commons metadata provider
b9b2af5 is described below
commit b9b2af53b28190d1963ee9f9196a9bef1d4cb2d9
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Fri Oct 15 08:38:55 2021 +0100
Flesh out a flow, integrate with new commons metadata provider
---
idp-oidc-rp-impl/pom.xml | 68 ++++-
.../idp/plugin/authn/oidc/rp/OIDCRPModule.java | 1 +
.../oidc/rp/impl/AuthorizationController.java | 28 +-
.../authn/oidc/rp/impl/SetOIDCInformation.java | 7 +-
.../impl/DefaultIssuerIDLookupFunction.java | 30 +++
.../impl/OIDCProviderMetadataLookupHandler.java | 106 ++++++++
.../META-INF/net.shibboleth.idp/postconfig.xml | 47 +++-
...eans.xml => oidc-relying-party-authn-beans.xml} | 78 +++++-
.../oidc-relying-party-authn-flow.xml | 111 ++++++++
.../OIDCRelyingParty/oidc-relying-party-flow.xml | 55 ----
.../authn/providermetadata-resolver-system.xml | 69 +++++
.../plugin/authn/oidc/rp/impl/OIDCRPFlowTest.java | 203 +++++++++++++++
.../conf/authn/oidc-providermetadata-resolvers.xml | 29 +++
.../src/test/resources/logback-test.xml | 19 ++
.../resources/metadata/openid-configuration.json | 55 ++++
pom.xml | 285 +++++++++++----------
16 files changed, 980 insertions(+), 211 deletions(-)
diff --git a/idp-oidc-rp-impl/pom.xml b/idp-oidc-rp-impl/pom.xml
index 21c33e4..55f433a 100644
--- a/idp-oidc-rp-impl/pom.xml
+++ b/idp-oidc-rp-impl/pom.xml
@@ -9,28 +9,33 @@
</parent>
<artifactId>idp-plugin-oidc-rp-impl</artifactId>
- <packaging>jar</packaging>
+ <packaging>jar</packaging>
<name>Shibboleth IdP :: Plugins :: OIDC RP Login Flow Impl</name>
<description>IdP java-idp-oidc-rp plugin implementation.</description>
-
+
<properties>
<checkstyle.configLocation>${project.basedir}/../checkstyle.xml</checkstyle.configLocation>
<automatic.module.name>net.shibboleth.idp.plugin.authn.oidc.rp.impl</automatic.module.name>
</properties>
-
+
<dependencies>
-
- <!-- compile time intra project dependencies -->
+
+ <!-- compile time intra project dependencies -->
<dependency>
<groupId>net.shibboleth.idp.plugin.authn</groupId>
<artifactId>idp-plugin-oidc-rp-api</artifactId>
</dependency>
- <!-- provided dependencies -->
- <dependency>
+ <!-- provided dependencies -->
+ <dependency>
<groupId>com.nimbusds</groupId>
<artifactId>oauth2-oidc-sdk</artifactId>
<scope>provided</scope>
- </dependency>
+ </dependency>
+ <dependency>
+ <groupId>${slf4j.groupId}</groupId>
+ <artifactId>jcl-over-slf4j</artifactId>
+ <scope>provided</scope>
+ </dependency>
<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
@@ -46,7 +51,7 @@
<artifactId>httpcore</artifactId>
<scope>provided</scope>
</dependency>
- <dependency>
+ <dependency>
<groupId>com.nimbusds</groupId>
<artifactId>nimbus-jose-jwt</artifactId>
<scope>provided</scope>
@@ -60,12 +65,31 @@
<groupId>net.shibboleth.oidc</groupId>
<artifactId>oidc-common-crypto-api</artifactId>
<scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>net.shibboleth.oidc</groupId>
+ <artifactId>oidc-common-metadata-impl</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>net.shibboleth.oidc</groupId>
+ <artifactId>oidc-common-metadata-api</artifactId>
+ <scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
+ <!-- Tmp OP deps until things move to commons -->
+ <!-- <dependency>
+ <groupId>net.shibboleth.idp.plugin.oidc</groupId>
+ <artifactId>idp-plugin-oidc-op-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>net.shibboleth.idp.plugin.oidc</groupId>
+ <artifactId>idp-plugin-oidc-op-impl</artifactId>
+ </dependency> -->
<!-- Service API and Plugin Description dependency -->
<dependency>
<groupId>${idp.groupId}</groupId>
@@ -76,11 +100,33 @@
<groupId>${idp.groupId}</groupId>
<artifactId>idp-admin-impl</artifactId>
<scope>provided</scope>
- </dependency>
+ </dependency>
+ <!-- Test dependency -->
+ <dependency>
+ <groupId>net.shibboleth.idp.plugin.authn.test</groupId>
+ <artifactId>idp-webflow-execution-test</artifactId>
+ <version>0.0.1-SNAPSHOT</version>
+ <type>test-jar</type>
+ </dependency>
+ <dependency>
+ <groupId>${spring.groupId}</groupId>
+ <artifactId>spring-test</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>net.shibboleth.idp</groupId>
+ <artifactId>idp-conf</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>net.shibboleth.idp</groupId>
+ <artifactId>idp-conf-impl</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<build>
- <plugins>
+ <plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/OIDCRPModule.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/OIDCRPModule.java
index ecff9a9..90397e7 100644
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/OIDCRPModule.java
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/OIDCRPModule.java
@@ -2,6 +2,7 @@ package net.shibboleth.idp.plugin.authn.oidc.rp;
import java.io.IOException;
+import net.shibboleth.idp.module.IdPModule;
import net.shibboleth.idp.module.ModuleException;
import net.shibboleth.idp.module.impl.PluginIdPModule;
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AuthorizationController.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AuthorizationController.java
index bb9fd43..49fcd41 100644
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AuthorizationController.java
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AuthorizationController.java
@@ -53,9 +53,9 @@ import net.shibboleth.utilities.java.support.primitive.StringSupport;
* <p>Adds the {@link OpenIDConnectContext} to the {@link HttpSession} for extraction after the OAuth 2.0
* authorisation code response has been consumed, and control as returned to the IdP.</p>
*
- * <p>Servlet compatible with the @link {@link ExternalAuthentication} interface that consumes the HTTP Request from
+ * <p>Servlet also consumes the HTTP Request from
* an OpenID Connect provider (OAuth 2.0 authorization endpoint), which was issued in response to the Authorization
- * Code request made by the {@link OpenIDConnectStartServlet}. </p>
+ * Code request. </p>
* <p>
* The {@link HttpServletRequest} is placed inside the {@link OpenIDConnectContext} for interrogation later in the flow.
* </p>
@@ -84,6 +84,17 @@ public class AuthorizationController {
@Nonnull private final Logger log = LoggerFactory.getLogger(AuthorizationController.class);
+ /**
+ * Begin an authorization request to the configured OP.
+ *
+ * TODO FINISH
+ *
+ * @param httpRequest the servlet request.
+ * @param httpResponse the servlet response.
+ *
+ * @throws ServletException throw if there is an error constructing an authz request.
+ * @throws IOException throw if there is an error constructing an authz request.
+ */
@GetMapping("/auth")
public void authorizationRequest(@Nonnull final HttpServletRequest httpRequest,
@Nonnull final HttpServletResponse httpResponse) throws ServletException, IOException {
@@ -98,12 +109,12 @@ public class AuthorizationController {
throw new ExternalAuthenticationException("Could not access profileRequestContext from the request");
}
final AuthenticationContext authenticationContext =
- (AuthenticationContext) profileRequestContext.getSubcontext(AuthenticationContext.class);
+ profileRequestContext.getSubcontext(AuthenticationContext.class);
if (authenticationContext == null) {
throw new ExternalAuthenticationException("Could not get AuthenticationContext from the request");
}
final OpenIDConnectContext openIDConnectContext =
- (OpenIDConnectContext) authenticationContext
+ authenticationContext
.getSubcontext(OpenIDConnectContext.class);
if (openIDConnectContext == null) {
throw new ExternalAuthenticationException(
@@ -120,6 +131,15 @@ public class AuthorizationController {
}
+ /**
+ * Callback endpoint to accept the authorization request response.
+ *
+ * @param httpRequest the servlet request.
+ * @param httpResponse the servlet response.
+ *
+ * @throws ServletException throw if there is an error accepting the authz response.
+ * @throws IOException throw if there is an error accepting the authz response.
+ */
@GetMapping("/callback")
public void authorizationCallback(@Nonnull final HttpServletRequest httpRequest,
@Nonnull final HttpServletResponse httpResponse) throws ServletException, IOException {
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/SetOIDCInformation.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/SetOIDCInformation.java
index 7f10122..021e46a 100644
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/SetOIDCInformation.java
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/SetOIDCInformation.java
@@ -193,7 +193,7 @@ public class SetOIDCInformation extends AbstractAuthenticationAction {
* is set, the JWT will also be signed.
*
* <p>The key of the mapping is the name of the requested claim. The value of the requested claim
- * is either:
+ * is either:</p>
* <ul>
* <li>Null, if the claim is being requested in a default manor</li>
* <li><code>{"essential":true}</code> if the value is essential. The default is
@@ -201,10 +201,11 @@ public class SetOIDCInformation extends AbstractAuthenticationAction {
* <li>A specific value for the claim. This value must be a valid value for that claim.</li>
* <li>A specific set of values for the claim. These values must be valid value for that claim.</li>
* </ul>
- * </p>
+ *
* <p>
* If used, the OpenID Connect Provider must support it, as specified in the
* <code>request_parameter_supported</code> parameter of the Providers discovery metadata.
+ * </p>
*
* @param claims map of requested claims
*/
@@ -301,7 +302,7 @@ public class SetOIDCInformation extends AbstractAuthenticationAction {
}
/**
- * Setter for OpenId Scope values. New ones are be added to the {@value OIDCScopeValue#OPENID} scope.
+ * Setter for OpenId Scope values. New ones are be added to the {@code OIDCScopeValue#OPENID} scope.
*
* @param oidcScopes OpenID Connect Scope values, can be <code>null</code> and will be ignored.
*/
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/metadata/impl/DefaultIssuerIDLookupFunction.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/metadata/impl/DefaultIssuerIDLookupFunction.java
new file mode 100644
index 0000000..8791c87
--- /dev/null
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/metadata/impl/DefaultIssuerIDLookupFunction.java
@@ -0,0 +1,30 @@
+package net.shibboleth.idp.plugin.authn.oidc.rp.metadata.impl;
+
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.ThreadSafe;
+
+import org.opensaml.messaging.context.MessageContext;
+import org.opensaml.messaging.context.navigate.ContextDataLookupFunction;
+import org.opensaml.saml.common.messaging.context.SAMLPeerEntityContext;
+
+/**
+ * Strategy to pull out the ID of the OIDC proxy issuer/provider.
+ */
+//TODO is this where the OP ID should be? confusing
+//TODO should resolve a Nimbus issuer?
+ at ThreadSafe
+public class DefaultIssuerIDLookupFunction implements ContextDataLookupFunction<MessageContext, String> {
+
+ /** {@inheritDoc} */
+ @Nullable public String apply(@Nullable final MessageContext input) {
+ if (input == null) {
+ return null;
+ }
+
+ final SAMLPeerEntityContext context = input.getSubcontext(SAMLPeerEntityContext.class);
+ if (context == null) {
+ return null;
+ }
+ return context.getEntityId();
+ }
+}
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/metadata/impl/OIDCProviderMetadataLookupHandler.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/metadata/impl/OIDCProviderMetadataLookupHandler.java
new file mode 100644
index 0000000..369fbfa
--- /dev/null
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/metadata/impl/OIDCProviderMetadataLookupHandler.java
@@ -0,0 +1,106 @@
+package net.shibboleth.idp.plugin.authn.oidc.rp.metadata.impl;
+
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.messaging.context.MessageContext;
+import org.opensaml.messaging.handler.AbstractMessageHandler;
+import org.opensaml.messaging.handler.MessageHandlerException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.nimbusds.oauth2.sdk.id.Issuer;
+import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
+
+import net.shibboleth.oidc.metadata.ProviderMetadataResolver;
+import net.shibboleth.oidc.metadata.criterion.IssuerIDCriterion;
+import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+import net.shibboleth.utilities.java.support.component.ComponentSupport;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
+import net.shibboleth.utilities.java.support.resolver.ResolverException;
+
+
+/**
+ * Handler for inbound OIDC protocol messages that attempts to locate OIDC metadata for a OP (issuer), and attaches it with a
+ * {@link OIDCMetadataContext} as a child of a pre-existing instance of {@link MessageContext}.
+ */
+//TODO should this be conflated with the OIDCMetadataLookupHandler service
+public class OIDCProviderMetadataLookupHandler extends AbstractMessageHandler {
+
+ /** Logger. */
+ @Nonnull private final Logger log = LoggerFactory.getLogger(OIDCProviderMetadataLookupHandler.class);
+
+ /** Resolver used to look up OIDC client information. */
+ @NonnullAfterInit private ProviderMetadataResolver providerResolver;
+
+ /** Strategy used to obtain the issuer id value for the inbound message context. */
+ @Nonnull private Function<MessageContext,String> issuerIDLookupStrategy;
+
+ /**
+ * Constructor.
+ */
+ public OIDCProviderMetadataLookupHandler() {
+ issuerIDLookupStrategy = new DefaultIssuerIDLookupFunction();
+ }
+
+ /**
+ * Set the strategy used to locate the client id of the request.
+ *
+ * @param strategy lookup strategy
+ */
+ public void setIssuerIDLookupStrategy(@Nonnull final Function<MessageContext, String> strategy) {
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+ issuerIDLookupStrategy =
+ Constraint.isNotNull(strategy, "IssuerIDLookupStrategy lookup strategy cannot be null");
+ }
+
+ /**
+ * Set the {@link ProviderMetadataResolver} to use.
+ *
+ * @param resolver The resolver to use.
+ */
+ public void setProviderMetadataResolver(@Nonnull final ProviderMetadataResolver resolver) {
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+
+ providerResolver = Constraint.isNotNull(resolver, "IssuerMetadataResolver cannot be null");
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void doInitialize() throws ComponentInitializationException {
+ super.doInitialize();
+
+ if (providerResolver == null) {
+ throw new ComponentInitializationException("IssuerMetadataResolver cannot be null");
+ }
+ }
+
+ @Override
+ protected void doInvoke(MessageContext messageContext) throws MessageHandlerException {
+ ComponentSupport.ifNotInitializedThrowUninitializedComponentException(this);
+
+ // Resolve issuer id from inbound message
+ //TODO should resolve an Issuer directly here?
+ final String issuerId = issuerIDLookupStrategy.apply(messageContext);
+
+ final IssuerIDCriterion issuerCriterion = new IssuerIDCriterion(new Issuer(issuerId));
+ final CriteriaSet criteria = new CriteriaSet(issuerCriterion);
+ try {
+ final OIDCProviderMetadata issuerMetadata = providerResolver.resolveSingle(criteria);
+ if (issuerMetadata == null) {
+ log.debug("{} No provider metadata returned for {}",getLogPrefix(), issuerId);
+ return;
+ }
+ log.debug("{} Found provider metadata for '{}'", getLogPrefix(), issuerId);
+// final OIDCMetadataContext oidcCtx = new OIDCMetadataContext();
+// oidcCtx.setClientInformation(clientInformation);
+// messageContext.addSubcontext(oidcCtx);
+ } catch (final ResolverException e) {
+ log.error("{} ResolverException thrown during provider metadata lookup", getLogPrefix(), e);
+ }
+ }
+
+}
diff --git a/idp-oidc-rp-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml b/idp-oidc-rp-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
index 6bf6aee..883d26d 100644
--- a/idp-oidc-rp-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
+++ b/idp-oidc-rp-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
@@ -40,9 +40,54 @@
c:_0="#{'%{idp.authn.OIDC.RP.supportedPrincipals:}'.trim()}" />
</property>
</bean>
+
<!-- Controller implementation -->
- <bean id="shibboleth.OpenIDConnectStartServlet"
+ <bean id="shibboleth.oidc.rp.OpenIDConnectStartServlet"
class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.AuthorizationController" />
+
+ <!-- TODO these bean names need to change - otherwise they may class with the OP plugin if installed
+ at the same time -->
+
+ <!-- OIDC OP information resolver service beans. -->
+
+ <bean id="shibboleth.oidc.rp.ProviderMetadataResolver"
+ class="net.shibboleth.oidc.metadata.impl.ReloadingProviderMetadataProvider"
+ c:resolverService-ref="shibboleth.oidc.rp.ProviderMetadataResolverService" />
+
+ <!-- TODO add this back? depends-on="shibboleth.AttributeResolverService"-->
+
+ <bean id="shibboleth.oidc.rp.ProviderMetadataResolverService"
+ class="net.shibboleth.ext.spring.service.ReloadableSpringService"
+
+ p:serviceConfigurations-ref="ExtendedProviderMetadataResolverResources"
+ p:failFast="%{idp.service.providermetadata.failFast:%{idp.service.failFast:false}}"
+ p:reloadCheckDelay="%{idp.service.providermetadata.checkInterval:PT0S}"
+ p:beanPostProcessors-ref="shibboleth.IdentifiableBeanPostProcessor"
+ p:beanFactoryPostProcessors-ref="shibboleth.PropertySourcesPlaceholderConfigurer">
+ <constructor-arg name="claz"
+ value="net.shibboleth.oidc.metadata.ProviderMetadataResolver" />
+ <constructor-arg name="strategy">
+ <bean class="net.shibboleth.oidc.profile.spring.relyingparty.metadata.impl.ProviderMetadataResolverServiceStrategy" />
+ </constructor-arg>
+ </bean>
+ <!-- TODO ADD BACK THE CONDITION (GET TO WORK WITH TEST) <util:list id="shibboleth.DefaultProviderMetadataResolverResources">
+ <value>conditional:%{idp.home}/conf/oidc-providermetadata-resolvers.xml</value>
+ OR <value>classpath:/conf/oidc-providermetadata-resolvers.xml</value>
+ </util:list> -->
+ <util:list id="shibboleth.DefaultProviderMetadataResolverResources">
+ <value>%{idp.home}/conf/authn/oidc-providermetadata-resolvers.xml</value>
+ </util:list>
+ <!-- Auto-append system config file to resource set. -->
+ <bean id ="ExtendedProviderMetadataResolverResources" class="net.shibboleth.ext.spring.factory.CombiningListFactoryBean"
+ p:firstList="#{getObject('%{idp.service.providermetadata.resources:shibboleth.ProviderMetadataResolverResources}'.trim()) ?:
+ getObject('shibboleth.DefaultProviderMetadataResolverResources')}">
+ <property name="secondList">
+ <util:list >
+ <value>classpath:/net/shibboleth/idp/plugin/authn/oidc/rp/conf/authn/providermetadata-resolver-system.xml</value>
+ </util:list>
+ </property>
+ </bean>
+
</beans>
\ No newline at end of file
diff --git a/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-beans.xml b/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-authn-beans.xml
similarity index 71%
rename from idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-beans.xml
rename to idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-authn-beans.xml
index 953fd9d..29e4eaa 100644
--- a/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-beans.xml
+++ b/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-authn-beans.xml
@@ -18,11 +18,83 @@
<bean class="net.shibboleth.ext.spring.config.IdentifiableBeanPostProcessor" />
<bean class="net.shibboleth.idp.profile.impl.ProfileActionBeanPostProcessor" />
+
+ <!-- Initial discovery step -->
+ <bean id="PropertyDrivenDiscovery" parent="shibboleth.Functions.Constant"
+ c:target="#{'%{idp.authn.oidc.rp.proxyIssuer:}'.trim()}" />
+
+
+
<!-- Spring controller to start the authentication request and recieve the response -->
<bean id="shibboleth.authn.OpenIDConnect.externalAuthnPath" class="java.lang.String"
c:_0="servletRelative:/Authn/OIDC/RP/auth">
</bean>
+
+
+
+ <!-- Parent beans for indirecting into nested PRC. -->
+
+ <bean id="NestedWebFlowMessageHandlerAdaptor" abstract="true"
+ class="net.shibboleth.idp.profile.impl.WebFlowMessageHandlerAdaptor" scope="prototype"
+ p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext" />
+
+ <bean id="NestedWebFlowProfileActionAdaptor" abstract="true"
+ class="net.shibboleth.idp.profile.impl.WebFlowProfileActionAdaptor" scope="prototype"
+ p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext" />
+
+ <bean id="ParentAuthenticiationContextLookup" class="org.opensaml.messaging.context.navigate.ParentContextLookup"
+ c:type="net.shibboleth.idp.authn.context.AuthenticationContext" />
+
+ <!-- Action beans -->
+
+ <!-- Explicitly wrapped by a non-nested action adaptor. -->
+
+ <!-- FIXME (add this back) p:profileId="#{T(net.shibboleth.idp.plugin.oidc.op.config.OIDCCoreProtocolConfiguration).PROFILE_ID}"-->
+ <bean id="InitializeProxyProfileRequestContext"
+ class="net.shibboleth.idp.authn.proxy.impl.InitializeProxyProfileRequestContext"
+ p:profileId="http://shibboleth.net/ns/profiles/oidc/sso/browser"
+ p:loggingId="%{idp.service.logging.oidcsso:OIDC.SSO}"
+ p:browserProfile="true" />
+
+ <bean id="FlowStartPopulateAuditContext" parent="shibboleth.AbstractPopulateAuditContext"
+ p:fieldExtractors="#{getObject('shibboleth.FlowStartAuditExtractors') ?: getObject('shibboleth.DefaultFlowStartAuditExtractors')}" />
+
+
+ <bean id="PrepareInboundMessageContext"
+ class="net.shibboleth.idp.saml.session.impl.PrepareInboundMessageContext" scope="prototype"
+ p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext">
+ <property name="relyingPartyLookupStrategy">
+ <bean parent="shibboleth.Functions.Compose" c:f-ref="ParentAuthenticiationContextLookup">
+ <constructor-arg name="g">
+ <bean parent="shibboleth.Functions.Expression" c:expression="#input.getAuthenticatingAuthority()" />
+ </constructor-arg>
+ </bean>
+ </property>
+ </bean>
+
+ <bean id="OIDCMetadataLookup" parent="NestedWebFlowMessageHandlerAdaptor"
+ scope="prototype" c:executionDirection="INBOUND">
+ <constructor-arg name="messageHandler">
+ <bean class="net.shibboleth.idp.plugin.authn.oidc.rp.metadata.impl.OIDCProviderMetadataLookupHandler" scope="prototype">
+ <property name="providerMetadataResolver">
+ <ref bean="shibboleth.oidc.rp.ProviderMetadataResolver" />
+ </property>
+ <property name="issuerIDLookupStrategy">
+ <ref bean="shibboleth.oidc.rp.IssuerIDLookupStrategy" />
+ </property>
+ </bean>
+ </constructor-arg>
+ </bean>
+
+ <bean id="shibboleth.oidc.rp.IssuerIDLookupStrategy"
+ class="net.shibboleth.idp.plugin.authn.oidc.rp.metadata.impl.DefaultIssuerIDLookupFunction"
+ scope="prototype" />
+
+
+
+ <!-- OLD STUFF -->
+
<!-- TODO Add user import here
<import resource="openidconnect-authn-config.xml" /> -->
@@ -37,15 +109,15 @@
<!-- Populate RP UI info from metadata? -->
<util:constant id="shibboleth.authn.oidc.rp.populateUIInfo" static-field="java.lang.Boolean.FALSE" />
- <bean id="SetOIDCInformation" class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.SetOIDCInformation"
+ <!-- <bean id="SetOIDCInformation" class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.SetOIDCInformation"
p:redirectURI="%{idp.oidc.rp.redirectURI:}"
p:clientID="%{idp.oidc.rp.clientID:null}"
p:clientSecret="%{idp.oidc.rp.clientSecret:null}"
p:providerMetadataLocation="%{idp.oidc.rp.providerConfigurationDocument:null}"
p:scope="%{idp.oidc.rp.scope:null}">
- <!-- <property name="responseType" value="id_token token"/> -->
- </bean>
+ <property name="responseType" value="id_token token"/>
+ </bean> -->
<bean id="ValidateOIDCAuthenticationResponse"
class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.ValidateOIDCAuthenticationResponse"
diff --git a/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-authn-flow.xml b/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-authn-flow.xml
new file mode 100644
index 0000000..9d3ac58
--- /dev/null
+++ b/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-authn-flow.xml
@@ -0,0 +1,111 @@
+<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, authn/conditions">
+
+ <!-- This is a login flow for proxied authentication implemented via OIDC -->
+
+ <!-- OP discovery? -->
+ <on-start>
+ <!-- If installed, this can bypass discovery. -->
+ <evaluate expression="flowRequestContext.getActiveFlow().getApplicationContext().containsBean('shibboleth.authn.oidc.rp.discoveryFunction') ? flowRequestContext.getActiveFlow().getApplicationContext().getBean('shibboleth.authn.oidc.rp.discoveryFunction') : PropertyDrivenDiscovery" result="flowScope.customDiscovery" />
+ <evaluate expression="opensamlProfileRequestContext.getSubcontext(T(net.shibboleth.idp.authn.context.AuthenticationContext)).setAuthenticatingAuthority(customDiscovery.apply(opensamlProfileRequestContext))" />
+ </on-start>
+
+ <action-state id="Start">
+ <evaluate expression="'proceed'" />
+ <transition on="proceed" to="DoDiscovery" />
+ </action-state>
+
+ <action-state id="PostDiscovery">
+ <!-- Nest new PRC inside AC -->
+ <evaluate expression="InitializeProxyProfileRequestContext" />
+
+ <evaluate expression="FlowStartPopulateAuditContext" />
+
+ <!-- this is nice to get the rp out of the authenticating auth and setup a msg context
+ but do we need it for a SAMLPeerEntityContext? -->
+ <evaluate expression="PrepareInboundMessageContext" />
+
+ <!-- <evaluate expression="SAMLProtocolAndRole" /> maybe we need an OIDC role selector here, to say this is an
+ OP over the normal RP -->
+ <evaluate expression="OIDCMetadataLookup" />
+
+ <!--
+ <evaluate expression="SAMLMetadataLookup" />
+
+ <evaluate expression="InitializeRelyingPartyContextFromSAMLPeer" />
+ <evaluate expression="SelectRelyingPartyConfiguration" />
+
+ <evaluate expression="PostLookupPopulateAuditContext" />
+
+ <evaluate expression="SelectProfileConfiguration" />
+
+ <evaluate expression="InitializeOutboundMessageContext" />
+ <evaluate expression="InitializeMessageChannelSecurityContext" />
+ <evaluate expression="PopulateBindingAndEndpointContexts" />
+
+ <evaluate expression="PopulateRequestSignatureSigningParameters" />
+
+ <evaluate expression="AddAuthnRequest" />
+ <evaluate expression="PostRequestPopulateAuditContext" />
+ <evaluate expression="WriteAuditLog" /> -->
+ <evaluate expression="'proceed'" />
+
+ <transition on="proceed" to="AuthRequest" />
+ </action-state>
+
+
+
+ <!-- Discovery done -->
+<!--
+ <action-state id="SetRPUIInformation">
+ <evaluate expression="SetRPUIInformation" />
+ <evaluate expression="'proceed'" />
+ <transition on="proceed" to="SetOIDCInformation" />
+ </action-state>
+
+ <action-state id="SetOIDCInformation">
+ <evaluate expression="SetOIDCInformation" />
+ <evaluate expression="'proceed'" />
+ <transition on="proceed" to="ExternalTransfer" />
+ </action-state> -->
+
+ <view-state id="AuthRequest"
+ view="externalRedirect:#{T(net.shibboleth.idp.authn.ExternalAuthentication).getExternalRedirect(flowRequestContext.getActiveFlow().getApplicationContext().getBean('shibboleth.authn.OpenIDConnect.externalAuthnPath'), flowExecutionContext.getKey().toString())}">
+ <attribute name="csrf_excluded" value="true" type="boolean" />
+ <on-render>
+ <evaluate
+ expression="opensamlProfileRequestContext.getSubcontext(T(net.shibboleth.idp.authn.context.AuthenticationContext)).addSubcontext(new net.shibboleth.idp.authn.context.ExternalAuthenticationContext(new net.shibboleth.idp.authn.impl.ExternalAuthenticationImpl(false)), true).setFlowExecutionUrl(flowExecutionUrl + '&_eventId_proceed=1')" />
+ </on-render>
+ <transition to="ValidateResponse" />
+ </view-state>
+
+ <action-state id="ValidateResponse">
+ <evaluate expression="ValidateOIDCAuthenticationResponse" />
+ <evaluate expression="'proceed'" />
+ <transition on="proceed" to="GetToken" />
+ </action-state>
+
+ <action-state id="GetToken">
+ <evaluate expression="GetOIDCTokenResponse" />
+ <evaluate expression="'proceed'" />
+ <transition on="proceed" to="ValidateOIDCTokenResponse" />
+ </action-state>
+
+ <action-state id="ValidateOIDCTokenResponse">
+ <evaluate expression="ValidateIDTokenSignature" />
+ <evaluate expression="ValidateTokenClaims" />
+ <evaluate expression="'proceed'" />
+ <transition on="proceed" to="SetPrincipal" />
+ </action-state>
+
+
+ <action-state id="SetPrincipal">
+ <evaluate expression="ValidateOIDCAuthentication" />
+ <evaluate expression="'proceed'" />
+ <transition on="proceed" to="proceed" />
+ </action-state>
+
+ <bean-import resource="oidc-relying-party-authn-beans.xml" />
+
+</flow>
diff --git a/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-flow.xml b/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-flow.xml
deleted file mode 100644
index 76ec7df..0000000
--- a/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-flow.xml
+++ /dev/null
@@ -1,55 +0,0 @@
-<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, authn/conditions">
-
- <action-state id="SetRPUIInformation">
- <evaluate expression="SetRPUIInformation" />
- <evaluate expression="'proceed'" />
- <transition on="proceed" to="SetOIDCInformation" />
- </action-state>
-
- <action-state id="SetOIDCInformation">
- <evaluate expression="SetOIDCInformation" />
- <evaluate expression="'proceed'" />
- <transition on="proceed" to="ExternalTransfer" />
- </action-state>
-
- <view-state id="ExternalTransfer"
- view="externalRedirect:#{T(net.shibboleth.idp.authn.ExternalAuthentication).getExternalRedirect(flowRequestContext.getActiveFlow().getApplicationContext().getBean('shibboleth.authn.OpenIDConnect.externalAuthnPath'), flowExecutionContext.getKey().toString())}">
- <attribute name="csrf_excluded" value="true" type="boolean" />
- <on-render>
- <evaluate
- expression="opensamlProfileRequestContext.getSubcontext(T(net.shibboleth.idp.authn.context.AuthenticationContext)).addSubcontext(new net.shibboleth.idp.authn.context.ExternalAuthenticationContext(new net.shibboleth.idp.authn.impl.ExternalAuthenticationImpl(false)), true).setFlowExecutionUrl(flowExecutionUrl + '&_eventId_proceed=1')" />
- </on-render>
- <transition to="ValidateResponse" />
- </view-state>
-
- <action-state id="ValidateResponse">
- <evaluate expression="ValidateOIDCAuthenticationResponse" />
- <evaluate expression="'proceed'" />
- <transition on="proceed" to="GetToken" />
- </action-state>
-
- <action-state id="GetToken">
- <evaluate expression="GetOIDCTokenResponse" />
- <evaluate expression="'proceed'" />
- <transition on="proceed" to="ValidateOIDCTokenResponse" />
- </action-state>
-
- <action-state id="ValidateOIDCTokenResponse">
- <evaluate expression="ValidateIDTokenSignature" />
- <evaluate expression="ValidateTokenClaims" />
- <evaluate expression="'proceed'" />
- <transition on="proceed" to="SetPrincipal" />
- </action-state>
-
-
- <action-state id="SetPrincipal">
- <evaluate expression="ValidateOIDCAuthentication" />
- <evaluate expression="'proceed'" />
- <transition on="proceed" to="proceed" />
- </action-state>
-
- <bean-import resource="oidc-relying-party-beans.xml" />
-
-</flow>
diff --git a/idp-oidc-rp-impl/src/main/resources/net/shibboleth/idp/plugin/authn/oidc/rp/conf/authn/providermetadata-resolver-system.xml b/idp-oidc-rp-impl/src/main/resources/net/shibboleth/idp/plugin/authn/oidc/rp/conf/authn/providermetadata-resolver-system.xml
new file mode 100644
index 0000000..d3e71a9
--- /dev/null
+++ b/idp-oidc-rp-impl/src/main/resources/net/shibboleth/idp/plugin/authn/oidc/rp/conf/authn/providermetadata-resolver-system.xml
@@ -0,0 +1,69 @@
+<?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"
+ default-lazy-init="true">
+
+ <!-- Loaded by the postconfig.xml file as global beans -->
+
+ <bean id="shibboleth.oidc.rp.ProviderMetadataProvider" lazy-init="false"
+ class="net.shibboleth.oidc.metadata.ProviderMetadataProviderContainer"
+ p:embeddedResolver-ref="shibboleth.oidc.rp.ChainingProviderMetadataResolver">
+ </bean>
+
+ <bean id="shibboleth.oidc.rp.ChainingProviderMetadataResolver"
+ class="net.shibboleth.oidc.metadata.impl.ChainingProviderMetadataResolver"
+ p:id="InternalEmbeddedChainResolver"
+ p:resolvers="#{getObject('shibboleth.oidc.rp.ProviderMetadataResolvers')}"/>
+
+ <!-- abstract beans for the user space config to extend -->
+ <bean id="shibboleth.oidc.rp.FilesystemProviderMetadataResolver" abstract="true"
+ class="net.shibboleth.oidc.metadata.impl.FilesystemProviderMetadataResolver"/>
+
+ <bean id="shibboleth.oidc.rp.HTTPProviderMetadataResolver" abstract="true"
+ class="net.shibboleth.oidc.metadata.impl.DynamicOIDCProviderMetadataResolver"
+ c:metadataCache="#{getObject('shibboleth.oidc.rp.OIDCProviderMetadataCache')}"/>
+
+ <bean id="shibboleth.oidc.rp.DefaultHTTPProviderConfigurationMetadataFetchingStrategy"
+ class="net.shibboleth.oidc.metadata.impl.HTTPProviderConfigurationFetchingStrategy"
+ c:client-ref="shibboleth.InternalHttpClient"
+ c:handler-ref="shibboleth.oidc.rp.DefaultHTTProviderConfigurationMetadataResponseHandler"
+ />
+
+ <bean id="shibboleth.oidc.rp.DefaultHTTProviderConfigurationMetadataResponseHandler"
+ class="net.shibboleth.oidc.metadata.impl.HTTPProviderConfigurationFetchingStrategy.OIDCProviderMetadataResponseHandler"
+ />
+
+ <bean id="shibboleth.oidc.rp.OIDCProviderMetadataCache"
+ class="net.shibboleth.oidc.metadata.cache.impl.OIDCProviderMetadataCacheFactoryBean"
+ p:cacheOperationMode="#{T(net.shibboleth.oidc.metadata.cache.impl.MetadataCacheBuilder.CacheOperationMode).DYNAMIC}"
+ p:fetchStrategy="#{getObject('shibboleth.oidc.rp.DefaultHTTPProviderConfigurationMetadataFetchingStrategy')}"
+ p:criteriaToIdentifierStrategy="#{getObject('DefaultOIDCProviderMetadataCriteriaToIdentifierStrategy')}"
+ p:metadataExpirationTimeStrategy="#{getObject('DefaultMetadataExpirationTimeStrategy')}"
+ p:identifierExtractionStrategy="#{getObject('DefaultMetadataIdentifierExtractionStrategy')}"
+ />
+
+ <bean id="DefaultMetadataExpirationTimeStrategy"
+ class="net.shibboleth.oidc.metadata.cache.impl.DefaultOIDCProviderMetadataExpirationTimeStrategy"
+ c:duration="PT10M"/>
+
+ <bean id="DefaultMetadataIdentifierExtractionStrategy"
+ class="net.shibboleth.oidc.metadata.cache.impl.DefaultOIDCProviderMetadataIdentifierExtractionStrategy"/>
+
+ <bean id="DefaultOIDCProviderMetadataCriteriaToIdentifierStrategy"
+ class="net.shibboleth.oidc.metadata.cache.impl.DefaultOIDCProviderMetadataCriteriaToIdentifierStrategy"/>
+
+ <!-- Wildcard import hook for plugins. -->
+ <!-- TODO: Not sure we need this -->
+ <import resource="classpath*:/META-INF/net/shibboleth/idp/plugin/oidc/op/service/clientinfo/postconfig.xml" />
+
+</beans>
diff --git a/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCRPFlowTest.java b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCRPFlowTest.java
new file mode 100644
index 0000000..d389108
--- /dev/null
+++ b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCRPFlowTest.java
@@ -0,0 +1,203 @@
+package net.shibboleth.idp.plugin.authn.oidc.rp.impl;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.annotation.Nonnull;
+
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.ResponseHandler;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.protocol.HttpContext;
+import org.junit.Test;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.support.BeanDefinitionBuilder;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.webflow.core.collection.LocalAttributeMap;
+import org.springframework.webflow.engine.Flow;
+import org.springframework.webflow.execution.FlowExecution;
+import org.springframework.webflow.test.MockFlowBuilderContext;
+
+import com.nimbusds.oauth2.sdk.ParseException;
+import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
+
+import net.shibboleth.ext.spring.config.IdentifiableBeanPostProcessor;
+import net.shibboleth.idp.plugin.authn.test.flow.AbstractAuthnXmlFlowExecutionTests;
+import net.shibboleth.idp.plugin.authn.test.flow.mock.MockFlowBuilder;
+import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
+import net.shibboleth.utilities.java.support.annotation.constraint.Unmodifiable;
+
+/** Test the OIDC relying party flow.*/
+public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
+
+ private final String GOOD_PROVIDER_CONFIGURATION_INFO = "{\n"
+ + "\"issuer\": \"https://op.example.com\",\n"
+ + "\"authorization_endpoint\": \"https://example.oidc.op.org/o/oauth2/v2/auth\",\n"
+ + "\"device_authorization_endpoint\": \"https://oauth2.googleapis.com/device/code\",\n"
+ + "\"token_endpoint\": \"https://oauth2.googleapis.com/token\",\n"
+ + "\"userinfo_endpoint\": \"https://openidconnect.googleapis.com/v1/userinfo\",\n"
+ + "\"revocation_endpoint\": \"https://oauth2.googleapis.com/revoke\",\n"
+ + "\"jwks_uri\": \"https://www.googleapis.com/oauth2/v3/certs\",\n"
+ + "\"response_types_supported\": [\n"
+ + "\"code\",\n"
+ + "\"token\",\n"
+ + "\"id_token\",\n"
+ + "\"code token\",\n"
+ + "\"code id_token\",\n"
+ + "\"token id_token\",\n"
+ + "\"code token id_token\",\n"
+ + "\"none\"\n"
+ + "],\n"
+ + "\"subject_types_supported\": [\n"
+ + "\"public\"\n"
+ + "],\n"
+ + "\"id_token_signing_alg_values_supported\": [\n"
+ + "\"RS256\"\n"
+ + "],\n"
+ + "\"scopes_supported\": [\n"
+ + "\"openid\",\n"
+ + "\"email\",\n"
+ + "\"profile\"\n"
+ + "],\n"
+ + "\"token_endpoint_auth_methods_supported\": [\n"
+ + "\"client_secret_post\",\n"
+ + "\"client_secret_basic\"\n"
+ + "],\n"
+ + "\"claims_supported\": [\n"
+ + "\"aud\",\n"
+ + "\"email\",\n"
+ + "\"email_verified\",\n"
+ + "\"exp\",\n"
+ + "\"family_name\",\n"
+ + "\"given_name\",\n"
+ + "\"iat\",\n"
+ + "\"iss\",\n"
+ + "\"locale\",\n"
+ + "\"name\",\n"
+ + "\"picture\",\n"
+ + "\"sub\"\n"
+ + "],\n"
+ + "\"code_challenge_methods_supported\": [\n"
+ + "\"plain\",\n"
+ + "\"S256\"\n"
+ + "],\n"
+ + "\"grant_types_supported\": [\n"
+ + "\"authorization_code\",\n"
+ + "\"refresh_token\",\n"
+ + "\"urn:ietf:params:oauth:grant-type:device_code\",\n"
+ + "\"urn:ietf:params:oauth:grant-type:jwt-bearer\"\n"
+ + "]\n"
+ + "}";
+
+
+ /** Path to the flow to be tested.*/
+ @Nonnull private static final String FLOW =
+ "/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-authn-flow.xml";
+
+ /** Class logger. */
+ @Nonnull private final Logger log = LoggerFactory.getLogger(OIDCRPFlowTest.class);
+
+ /** List of mocked subflows.*/
+ @Nonnull @NonnullElements @Unmodifiable private final List<Flow> subflows =
+ List.of(MockFlowBuilder.MockNoOpFlow("c14n"));
+
+ /**
+ * Map of flow resources that support building the flow to test.
+ * These are only for parent flows, not subflows.
+ */
+ @Nonnull @NonnullElements @Unmodifiable private final Map<String,String> flowResources =
+ Map.of(
+ "classpath:/net/shibboleth/idp/flows/authn/authn-abstract-flow.xml","authn.abstract",
+ "classpath:/flows/authn/conditions/conditions-flow.xml","authn/conditions",
+ "classpath:/conf/authn/authn-events-flow.xml","authn.events");
+
+
+ @Override
+ protected void registerMockPropertySource(@Nonnull final MockFlowBuilderContext builderContext) {
+ super.registerMockPropertySource(builderContext);
+ }
+
+ @Override
+ protected void registerMockBeanDefinitions(@Nonnull final MockFlowBuilderContext builderContext) {
+ super.registerMockBeanDefinitions(builderContext);
+
+ addBeanDefinition(builderContext, "shibboleth.DefaultFlowStartAuditExtractors",BeanDefinitionBuilder.
+ genericBeanDefinition(HashMap.class).getBeanDefinition());
+
+ addBeanDefinition(builderContext, "shibboleth.AbstractPopulateAuditContext",BeanDefinitionBuilder.
+ genericBeanDefinition(net.shibboleth.idp.profile.audit.impl.PopulateAuditContext.class)
+ .setAbstract(true).getBeanDefinition());
+
+
+ addBeanDefinition(builderContext, "shibboleth.PropertySourcesPlaceholderConfigurer",BeanDefinitionBuilder.
+ genericBeanDefinition(org.springframework.context.support.PropertySourcesPlaceholderConfigurer.class)
+ .addPropertyValue("placeholderPrefix", "%{")
+ .addPropertyValue("placeholderSuffix", "}").getBeanDefinition());
+
+ addBeanDefinition(builderContext, "shibboleth.IdentifiableBeanPostProcessor",BeanDefinitionBuilder.
+ genericBeanDefinition(IdentifiableBeanPostProcessor.class).getBeanDefinition());
+
+
+ //create Mock HttpClient
+ final HttpClient httpClient = mock(HttpClient.class);
+ addBeanSingleton(builderContext, "shibboleth.InternalHttpClient", httpClient);
+
+ try {
+ when(httpClient.
+ execute(any(HttpUriRequest.class),any(ResponseHandler.class),any(HttpContext.class)))
+ .thenReturn(OIDCProviderMetadata.parse(GOOD_PROVIDER_CONFIGURATION_INFO));
+ } catch (ParseException | IOException e) {
+ log.error("Could not mock HTTP response",e);
+ }
+
+
+ loadBeanDefinitionsFromXmlFile(builderContext, new ClassPathResource("META-INF/net.shibboleth.idp/postconfig.xml"));
+
+
+ }
+
+ @Override
+ @Nonnull protected ProfileRequestContext buildProfileRequestContext(@Nonnull final String flowId,
+ @Nonnull final boolean forceAuthn,
+ @Nonnull final boolean addC14Context) {
+ return super.buildProfileRequestContext(flowId, forceAuthn, addC14Context);
+ }
+
+ @Test
+ public void testFlowToRedirect() {
+ setFlowPath(FLOW);
+ setFlowModelResources(flowResources);
+ setSubflows(subflows);
+
+ final Map<String,String> mockProperties = Map.of(
+ "idp.service.clientinfo.failFast","false",
+ "idp.oidc.rp.clientID","clientId",
+ "idp.oidc.rp.clientSecret","secret",
+ "idp.oidc.rp.providerConfigurationDocument","provider_location",
+ "idp.oidc.rp.redirectURI","https://localhost:8443/idp/profile/Authn/OIDC/RP/callback",
+ "idp.oidc.rp.scope","email",
+ "idp.authn.oidc.rp.proxyIssuer","https://op.example.com");
+
+ setMockProperties(mockProperties);
+
+ final LocalAttributeMap<Object> inputMap = new LocalAttributeMap<Object>();
+ inputMap.put("calledAsSubflow", true);
+
+ final FlowExecution flowExecution = getFlowExecutionFactory().createFlowExecution(getFlowDefinition());
+ flowExecution.getConversationScope()
+ .put("opensamlProfileRequestContext", buildProfileRequestContext("authn/OIDCRelyingParty",false,true));
+ updateFlowExecution(flowExecution);
+ flowExecution.start(inputMap, externalContext);
+ //TODO: only check it has ended, should check correct state - but it does not allow this
+ //find a way to check last state of an ended flow.
+ assertFlowExecutionEnded();
+ }
+}
diff --git a/idp-oidc-rp-impl/src/test/resources/conf/authn/oidc-providermetadata-resolvers.xml b/idp-oidc-rp-impl/src/test/resources/conf/authn/oidc-providermetadata-resolvers.xml
new file mode 100644
index 0000000..5b69c78
--- /dev/null
+++ b/idp-oidc-rp-impl/src/test/resources/conf/authn/oidc-providermetadata-resolvers.xml
@@ -0,0 +1,29 @@
+<?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">
+
+ <util:list id="shibboleth.oidc.rp.ProviderMetadataResolvers">
+ <ref bean="ExampleHTTPResolver" />
+ </util:list>
+
+
+ <bean id="ExampleFileResolver" parent="shibboleth.oidc.rp.FilesystemProviderMetadataResolver"
+ c:metadata="%{idp.home}/metadata/openid-configuration.json" />
+
+ <bean id="ExampleHTTPResolver" parent="shibboleth.oidc.rp.HTTPProviderMetadataResolver"
+ />
+
+
+
+
+</beans>
\ No newline at end of file
diff --git a/idp-oidc-rp-impl/src/test/resources/logback-test.xml b/idp-oidc-rp-impl/src/test/resources/logback-test.xml
new file mode 100644
index 0000000..3eabacc
--- /dev/null
+++ b/idp-oidc-rp-impl/src/test/resources/logback-test.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<configuration>
+ <logger name="net.shibboleth" level="DEBUG"/>
+ <logger name="org.springframework" level="DEBUG"/>
+
+ <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+ <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+ <pattern>%level [%logger:%line] - %msg%n</pattern>
+ <charset>UTF-8</charset>
+ </encoder>
+ </appender>
+
+ <root>
+ <level value="WARN" />
+ <appender-ref ref="STDOUT" />
+ </root>
+
+</configuration>
\ No newline at end of file
diff --git a/idp-oidc-rp-impl/src/test/resources/metadata/openid-configuration.json b/idp-oidc-rp-impl/src/test/resources/metadata/openid-configuration.json
new file mode 100644
index 0000000..912ff94
--- /dev/null
+++ b/idp-oidc-rp-impl/src/test/resources/metadata/openid-configuration.json
@@ -0,0 +1,55 @@
+{
+"issuer": "https://op.example.com",
+"authorization_endpoint": "https://op.example.com/o/oauth2/v2/auth",
+"token_endpoint": "https://oauth2.op.example.com/token",
+"userinfo_endpoint": "https://openidconnect.op.example.com/v1/userinfo",
+"revocation_endpoint": "https://oauth2.op.example.com/revoke",
+"jwks_uri": "https://op.example.com/oauth2/v3/certs",
+"response_types_supported": [
+"code",
+"token",
+"id_token",
+"code token",
+"code id_token",
+"token id_token",
+"code token id_token",
+"none"
+],
+"subject_types_supported": [
+"public"
+],
+"id_token_signing_alg_values_supported": [
+"RS256"
+],
+"scopes_supported": [
+"openid",
+"email",
+"profile"
+],
+"token_endpoint_auth_methods_supported": [
+"client_secret_post",
+"client_secret_basic"
+],
+"claims_supported": [
+"aud",
+"email",
+"email_verified",
+"exp",
+"family_name",
+"given_name",
+"iat",
+"iss",
+"locale",
+"name",
+"picture",
+"sub"
+],
+"code_challenge_methods_supported": [
+"plain",
+"S256"
+],
+"grant_types_supported": [
+"authorization_code",
+"refresh_token"
+]
+}
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 5d5a87d..22f6b7a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,145 +1,162 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <parent>
- <groupId>net.shibboleth</groupId>
- <artifactId>parent</artifactId>
- <version>11.2.2-SNAPSHOT</version>
- </parent>
- <groupId>net.shibboleth.idp.plugin.authn</groupId>
- <artifactId>idp-plugin-oidc-rp-parent</artifactId>
- <version>0.0.1-SNAPSHOT</version>
- <name>Shibboleth IdP :: Plugins :: OIDC RP Login Flow</name>
- <packaging>pom</packaging>
- <description>Java OIDC RP authentication plugin for the Shibboleth IdP.</description>
-
- <properties>
- <idp.groupId>net.shibboleth.idp</idp.groupId>
- <idp.version>4.1.0</idp.version>
- <opensaml.groupId>org.opensaml</opensaml.groupId>
- <opensaml.version>4.1.0</opensaml.version>
- <oidc.common.version>1.1.0-SNAPSHOT</oidc.common.version>
- <checkstyle.configLocation>${project.basedir}/checkstyle.xml</checkstyle.configLocation>
- </properties>
-
- <distributionManagement>
- <site>
- <id>site</id>
- <url>scp:${shibboleth.site.deploy.url}java-idp-plugin-oidc-rp/${project.version}/</url>
- </site>
- </distributionManagement>
-
- <repositories>
- <repository>
- <id>shib-release</id>
- <url>https://build.shibboleth.net/nexus/content/groups/public</url>
- <snapshots>
- <enabled>false</enabled>
- </snapshots>
- </repository>
- <repository>
- <id>shib-snapshot</id>
- <url>https://build.shibboleth.net/nexus/content/repositories/snapshots</url>
- <releases>
- <enabled>false</enabled>
- </releases>
- </repository>
- </repositories>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>net.shibboleth</groupId>
+ <artifactId>parent</artifactId>
+ <version>11.2.2-SNAPSHOT</version>
+ </parent>
+ <groupId>net.shibboleth.idp.plugin.authn</groupId>
+ <artifactId>idp-plugin-oidc-rp-parent</artifactId>
+ <version>0.0.1-SNAPSHOT</version>
+ <name>Shibboleth IdP :: Plugins :: OIDC RP Login Flow</name>
+ <packaging>pom</packaging>
+ <description>Java OIDC RP authentication plugin for the Shibboleth IdP.</description>
- <modules>
- <module>idp-oidc-rp-impl</module>
- <module>idp-oidc-rp-api</module>
- <!-- All DIST modules must come after the -api and -impl modules -->
- <module>idp-oidc-rp-dist</module>
- </modules>
+ <properties>
+ <idp.groupId>net.shibboleth.idp</idp.groupId>
+ <idp.version>4.1.0</idp.version>
+ <opensaml.groupId>org.opensaml</opensaml.groupId>
+ <opensaml.version>4.1.0</opensaml.version>
+ <oidc.common.version>1.1.1-SNAPSHOT</oidc.common.version>
+ <!-- This is tmp until certain classes move over to commons -->
+ <oidc.op.version>3.0.1</oidc.op.version>
+ <checkstyle.configLocation>${project.basedir}/checkstyle.xml</checkstyle.configLocation>
+ </properties>
- <scm>
- <connection>scm:git:...</connection>
- <developerConnection>scm:git:...</developerConnection>
- <url>...</url>
- </scm>
-
- <dependencies>
- <!-- Project wide test Dependencies -->
- <dependency>
- <groupId>org.testng</groupId>
- <artifactId>testng</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>${slf4j.groupId}</groupId>
- <artifactId>slf4j-api</artifactId>
- </dependency>
- <dependency>
- <groupId>org.mockito</groupId>
- <artifactId>mockito-core</artifactId>
- <scope>test</scope>
- </dependency>
- </dependencies>
- <dependencyManagement>
- <!-- java-idp-oidc-rp project dependencies -->
- <dependencies>
- <dependency>
- <groupId>net.shibboleth.idp.plugin.authn</groupId>
- <artifactId>idp-plugin-oidc-rp-api</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>net.shibboleth.idp.plugin.authn</groupId>
- <artifactId>idp-plugin-oidc-rp-impl</artifactId>
- <version>${project.version}</version>
- </dependency>
- <!-- OIDC Common BOM when importing OIDC dependencies -->
- <dependency>
+ <distributionManagement>
+ <site>
+ <id>site</id>
+ <url>scp:${shibboleth.site.deploy.url}java-idp-plugin-oidc-rp/${project.version}/</url>
+ </site>
+ </distributionManagement>
+
+ <repositories>
+ <repository>
+ <id>shib-release</id>
+ <url>https://build.shibboleth.net/nexus/content/groups/public</url>
+ <snapshots>
+ <enabled>false</enabled>
+ </snapshots>
+ </repository>
+ <repository>
+ <id>shib-snapshot</id>
+ <url>https://build.shibboleth.net/nexus/content/repositories/snapshots</url>
+ <releases>
+ <enabled>false</enabled>
+ </releases>
+ </repository>
+ </repositories>
+
+ <modules>
+ <module>idp-oidc-rp-impl</module>
+ <module>idp-oidc-rp-api</module>
+ <!-- All DIST modules must come after the -api and -impl modules -->
+ <module>idp-oidc-rp-dist</module>
+ </modules>
+
+ <scm>
+ <connection>scm:git:...</connection>
+ <developerConnection>scm:git:...</developerConnection>
+ <url>...</url>
+ </scm>
+
+ <dependencies>
+ <!-- Project wide test Dependencies -->
+ <dependency>
+ <groupId>org.testng</groupId>
+ <artifactId>testng</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-classic</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>${slf4j.groupId}</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-core</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+ <dependencyManagement>
+ <!-- java-idp-oidc-rp project dependencies -->
+ <dependencies>
+ <dependency>
+ <groupId>net.shibboleth.idp.plugin.authn</groupId>
+ <artifactId>idp-plugin-oidc-rp-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>net.shibboleth.idp.plugin.authn</groupId>
+ <artifactId>idp-plugin-oidc-rp-impl</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <!-- Tmp OP deps until things move to commons -->
+ <dependency>
+ <groupId>net.shibboleth.idp.plugin.oidc</groupId>
+ <artifactId>idp-plugin-oidc-op-api</artifactId>
+ <version>${oidc.op.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>net.shibboleth.idp.plugin.oidc</groupId>
+ <artifactId>idp-plugin-oidc-op-impl</artifactId>
+ <version>${oidc.op.version}</version>
+ </dependency>
+ <!-- OIDC Common BOM when importing OIDC dependencies -->
+ <dependency>
<groupId>net.shibboleth.oidc</groupId>
<artifactId>oidc-common-bom</artifactId>
<version>${oidc.common.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
- <!-- Shibboleth IdP BOM for importing IdP dependencies -->
- <dependency>
- <groupId>net.shibboleth.idp</groupId>
- <artifactId>idp-bom</artifactId>
- <version>${idp.version}</version>
- <type>pom</type>
- <scope>import</scope>
- </dependency>
- <!-- Provided shibboleth support dependencies -->
- <dependency>
- <groupId>net.shibboleth.utilities</groupId>
- <artifactId>java-support</artifactId>
- <version>${java-support.version}</version>
- <scope>provided</scope>
- </dependency>
- <!-- Test bom dependencies -->
- <dependency>
- <groupId>${idp.groupId}</groupId>
- <artifactId>idp-tests-bom</artifactId>
- <version>${idp.version}</version>
- <type>pom</type>
- <scope>import</scope>
- </dependency>
- </dependencies>
- </dependencyManagement>
- <build>
- <plugins>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-jar-plugin</artifactId>
- <configuration>
- <archive>
- <index>true</index>
- <manifestEntries>
- <Automatic-Module-Name>${automatic.module.name}</Automatic-Module-Name>
- <Sealed>true</Sealed>
- </manifestEntries>
- </archive>
- </configuration>
- </plugin>
- </plugins>
- </build>
+ <!-- Shibboleth IdP BOM for importing IdP dependencies -->
+ <dependency>
+ <groupId>net.shibboleth.idp</groupId>
+ <artifactId>idp-bom</artifactId>
+ <version>${idp.version}</version>
+ <type>pom</type>
+ <scope>import</scope>
+ </dependency>
+ <!-- Provided shibboleth support dependencies -->
+ <dependency>
+ <groupId>net.shibboleth.utilities</groupId>
+ <artifactId>java-support</artifactId>
+ <version>${java-support.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <!-- Test bom dependencies -->
+ <dependency>
+ <groupId>${idp.groupId}</groupId>
+ <artifactId>idp-tests-bom</artifactId>
+ <version>${idp.version}</version>
+ <type>pom</type>
+ <scope>import</scope>
+ </dependency>
+ </dependencies>
+ </dependencyManagement>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <configuration>
+ <archive>
+ <index>true</index>
+ <manifestEntries>
+ <Automatic-Module-Name>${automatic.module.name}</Automatic-Module-Name>
+ <Sealed>true</Sealed>
+ </manifestEntries>
+ </archive>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
</project>
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list