[java-idp-oidc] branch main updated: JOIDC-99 - Support OAuth 2.0 Authorization Server Issuer Identification as per RFC9207
Henri Mikkonen
henri.mikkonen at iki.fi
Fri May 20 13:28:01 UTC 2022
This is an automated email from the git hooks/post-receive script.
hjmikkon 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=ab3f4406c6e474fe63b285ec2804fedc5ec8c1e4
The following commit(s) were added to refs/heads/main by this push:
new ab3f4406 JOIDC-99 - Support OAuth 2.0 Authorization Server Issuer Identification as per RFC9207
ab3f4406 is described below
commit ab3f4406c6e474fe63b285ec2804fedc5ec8c1e4
Author: Henri Mikkonen <henri.mikkonen at iki.fi>
AuthorDate: Fri May 20 16:26:26 2022 +0300
JOIDC-99 - Support OAuth 2.0 Authorization Server Issuer Identification as per RFC9207
https://shibboleth.atlassian.net/browse/JOIDC-99
Wired the new profile configuration property into use when building success or
error authentication response.
---
.../BuildAuthenticationErrorResponseFromEvent.java | 81 ++++++++++-
.../FormOutboundAuthenticationResponseMessage.java | 74 +++++++++-
.../oidc/op/profile/flow/AuthorizeFlowTest.java | 150 ++++++++++++++++++++-
.../src/test/resources/conf/relying-party.xml | 7 +
4 files changed, 307 insertions(+), 5 deletions(-)
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/BuildAuthenticationErrorResponseFromEvent.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/BuildAuthenticationErrorResponseFromEvent.java
index 14ead389..240956d6 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/BuildAuthenticationErrorResponseFromEvent.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/BuildAuthenticationErrorResponseFromEvent.java
@@ -18,18 +18,33 @@
package net.shibboleth.idp.plugin.oidc.op.profile.impl;
import java.net.URI;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import javax.annotation.Nonnull;
+
+import org.opensaml.profile.action.ActionSupport;
import org.opensaml.profile.context.EventContext;
import org.opensaml.profile.context.ProfileRequestContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
import com.nimbusds.oauth2.sdk.ErrorObject;
import com.nimbusds.oauth2.sdk.ResponseMode;
import com.nimbusds.oauth2.sdk.ResponseType;
+import com.nimbusds.oauth2.sdk.id.Issuer;
import com.nimbusds.openid.connect.sdk.AuthenticationErrorResponse;
import net.shibboleth.idp.plugin.oidc.op.profile.context.navigate.DefaultRequestResponseModeLookupFunction;
import net.shibboleth.idp.plugin.oidc.op.profile.context.navigate.DefaultRequestResponseTypeLookupFunction;
import net.shibboleth.idp.plugin.oidc.op.profile.context.navigate.DefaultRequestStateLookupFunction;
import net.shibboleth.idp.plugin.oidc.op.profile.context.navigate.ValidatedRedirectURILookupFunction;
+import net.shibboleth.idp.profile.IdPEventIds;
+import net.shibboleth.idp.profile.context.navigate.ResponderIdLookupFunction;
+import net.shibboleth.oidc.profile.config.logic.IncludeIssuerInAuthenticationResponsePredicate;
+import net.shibboleth.utilities.java.support.component.ComponentSupport;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+import net.shibboleth.utilities.java.support.primitive.StringSupport;
/**
* This action reads an event from the configured {@link EventContext} lookup strategy, constructs an OIDC
@@ -38,6 +53,70 @@ import net.shibboleth.idp.plugin.oidc.op.profile.context.navigate.ValidatedRedir
public class BuildAuthenticationErrorResponseFromEvent
extends AbstractBuildErrorResponseFromEvent<AuthenticationErrorResponse> {
+ /** Class logger. */
+ @Nonnull private Logger log = LoggerFactory.getLogger(BuildAuthenticationErrorResponseFromEvent.class);
+
+ /** Strategy used to obtain the response issuer value. */
+ @Nonnull private Function<ProfileRequestContext, String> issuerLookupStrategy;
+
+ /** Predicate to signal whether or not to include iss-parameter to the response. */
+ @Nonnull private Predicate<ProfileRequestContext> includeIssuerInResponsePredicate;
+
+ /** Issuer value to included in the response message, if configured to be included. */
+ private Issuer issuer;
+
+ /**
+ * Constructor.
+ */
+ public BuildAuthenticationErrorResponseFromEvent() {
+ issuerLookupStrategy = new ResponderIdLookupFunction();
+ includeIssuerInResponsePredicate = new IncludeIssuerInAuthenticationResponsePredicate();
+ }
+
+ /**
+ * Set the strategy used to locate the issuer value to use.
+ *
+ * @param strategy what to set
+ */
+ public void setIssuerLookupStrategy(@Nonnull final Function<ProfileRequestContext, String> strategy) {
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+
+ issuerLookupStrategy = Constraint.isNotNull(strategy, "IssuerLookupStrategy lookup strategy cannot be null");
+ }
+
+ /**
+ * Set the predicate to signal whether or not to include iss-parameter to the response.
+ *
+ * @param predicate what to set
+ */
+ public void setIncludeIssuerInResponsePredicate(@Nonnull final Predicate<ProfileRequestContext> predicate) {
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+
+ includeIssuerInResponsePredicate = Constraint.isNotNull(predicate,
+ "Include issuer in response predicate cannot be null");
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
+ if (!super.doPreExecute(profileRequestContext)) {
+ return false;
+ }
+
+ if (includeIssuerInResponsePredicate.test(profileRequestContext)) {
+ final String issValue = issuerLookupStrategy.apply(profileRequestContext);
+ if (StringSupport.trimOrNull(issValue) == null) {
+ log.error("{} Could not resolve value for issuer even though it's required", getLogPrefix());
+ ActionSupport.buildEvent(profileRequestContext, IdPEventIds.INVALID_RELYING_PARTY_CTX);
+ return false;
+ }
+ issuer = new Issuer(issValue);
+ } else {
+ issuer = null;
+ }
+ return true;
+ }
+
/** {@inheritDoc} */
@Override
protected AuthenticationErrorResponse buildErrorResponse(final ErrorObject error,
@@ -58,7 +137,7 @@ public class BuildAuthenticationErrorResponseFromEvent
responseMode = defaultResponseMode;
}
return new AuthenticationErrorResponse(redirectURI, error,
- new DefaultRequestStateLookupFunction().apply(profileRequestContext), responseMode);
+ new DefaultRequestStateLookupFunction().apply(profileRequestContext), issuer, responseMode);
}
}
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/FormOutboundAuthenticationResponseMessage.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/FormOutboundAuthenticationResponseMessage.java
index 28029451..cd425f9a 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/FormOutboundAuthenticationResponseMessage.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/FormOutboundAuthenticationResponseMessage.java
@@ -17,6 +17,9 @@
package net.shibboleth.idp.plugin.oidc.op.profile.impl;
+import java.util.function.Function;
+import java.util.function.Predicate;
+
import javax.annotation.Nonnull;
import org.opensaml.profile.action.ActionSupport;
@@ -24,11 +27,19 @@ import org.opensaml.profile.action.EventIds;
import org.opensaml.profile.context.ProfileRequestContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+
+import com.nimbusds.oauth2.sdk.id.Issuer;
import com.nimbusds.openid.connect.sdk.AuthenticationResponse;
import com.nimbusds.openid.connect.sdk.AuthenticationSuccessResponse;
import net.shibboleth.idp.plugin.oidc.op.profile.context.navigate.DefaultRequestResponseModeLookupFunction;
import net.shibboleth.idp.plugin.oidc.op.profile.context.navigate.DefaultRequestStateLookupFunction;
+import net.shibboleth.idp.profile.IdPEventIds;
+import net.shibboleth.idp.profile.context.navigate.ResponderIdLookupFunction;
+import net.shibboleth.oidc.profile.config.logic.IncludeIssuerInAuthenticationResponsePredicate;
+import net.shibboleth.utilities.java.support.component.ComponentSupport;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+import net.shibboleth.utilities.java.support.primitive.StringSupport;
/**
* Action that forms outbound message based on request and response context. Formed message is set to
@@ -39,6 +50,67 @@ public class FormOutboundAuthenticationResponseMessage extends AbstractOIDCAuthe
/** Class logger. */
@Nonnull private Logger log = LoggerFactory.getLogger(FormOutboundAuthenticationResponseMessage.class);
+ /** Strategy used to obtain the response issuer value. */
+ @Nonnull private Function<ProfileRequestContext, String> issuerLookupStrategy;
+
+ /** Predicate to signal whether or not to include iss-parameter to the response. */
+ @Nonnull private Predicate<ProfileRequestContext> includeIssuerInResponsePredicate;
+
+ /** Issuer value to included in the response message, if configured to be included. */
+ private Issuer issuer;
+
+ /**
+ * Constructor.
+ */
+ public FormOutboundAuthenticationResponseMessage() {
+ issuerLookupStrategy = new ResponderIdLookupFunction();
+ includeIssuerInResponsePredicate = new IncludeIssuerInAuthenticationResponsePredicate();
+ }
+
+ /**
+ * Set the strategy used to locate the issuer value to use.
+ *
+ * @param strategy what to set
+ */
+ public void setIssuerLookupStrategy(@Nonnull final Function<ProfileRequestContext, String> strategy) {
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+
+ issuerLookupStrategy = Constraint.isNotNull(strategy, "IssuerLookupStrategy lookup strategy cannot be null");
+ }
+
+ /**
+ * Set the predicate to signal whether or not to include iss-parameter to the response.
+ *
+ * @param predicate what to set
+ */
+ public void setIncludeIssuerInResponsePredicate(@Nonnull final Predicate<ProfileRequestContext> predicate) {
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+
+ includeIssuerInResponsePredicate = Constraint.isNotNull(predicate,
+ "Include issuer in response predicate cannot be null");
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
+ if (!super.doPreExecute(profileRequestContext)) {
+ return false;
+ }
+
+ if (includeIssuerInResponsePredicate.test(profileRequestContext)) {
+ final String issValue = issuerLookupStrategy.apply(profileRequestContext);
+ if (StringSupport.trimOrNull(issValue) == null) {
+ log.error("{} Could not resolve value for issuer even though it's required", getLogPrefix());
+ ActionSupport.buildEvent(profileRequestContext, IdPEventIds.INVALID_RELYING_PARTY_CTX);
+ return false;
+ }
+ issuer = new Issuer(issValue);
+ } else {
+ issuer = null;
+ }
+ return true;
+ }
+
/** {@inheritDoc} */
@Override
protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
@@ -51,7 +123,7 @@ public class FormOutboundAuthenticationResponseMessage extends AbstractOIDCAuthe
final AuthenticationResponse resp = new AuthenticationSuccessResponse(getOidcResponseContext().getRedirectURI(),
getOidcResponseContext().getAuthorizationCode(), getOidcResponseContext().getProcessedToken(),
getOidcResponseContext().getAccessToken(),
- new DefaultRequestStateLookupFunction().apply(profileRequestContext), null,
+ new DefaultRequestStateLookupFunction().apply(profileRequestContext), null, issuer,
new DefaultRequestResponseModeLookupFunction().apply(profileRequestContext));
profileRequestContext.getOutboundMessageContext().setMessage(resp);
}
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/AuthorizeFlowTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/AuthorizeFlowTest.java
index 13fb2f9d..89abe9c7 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/AuthorizeFlowTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/AuthorizeFlowTest.java
@@ -39,7 +39,10 @@ import com.nimbusds.jwt.JWT;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.PlainJWT;
import com.nimbusds.jwt.SignedJWT;
+import com.nimbusds.oauth2.sdk.ErrorResponse;
+import com.nimbusds.oauth2.sdk.Response;
import com.nimbusds.oauth2.sdk.Scope;
+import com.nimbusds.openid.connect.sdk.AuthenticationErrorResponse;
import com.nimbusds.openid.connect.sdk.AuthenticationResponse;
import com.nimbusds.openid.connect.sdk.AuthenticationSuccessResponse;
import com.nimbusds.openid.connect.sdk.claims.ClaimRequirement;
@@ -62,6 +65,7 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
String issuer = "https://op.example.org";
String redirectUri = "https://example.org/cb";
String clientId = "mockClientId";
+ String clientIdIssInResponse = "mockClientIdIssInResponse";
String clientSecret = "mockClientSecretmockClientSecretmockClientSecretmockClientSecretmockClientSecret";
Scope scope = Scope.parse("openid profile email");
@@ -94,6 +98,7 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
Assert.assertNull(successResponse.getIDToken());
Assert.assertNull(successResponse.getAccessToken());
Assert.assertNotNull(successResponse.getAuthorizationCode());
+ Assert.assertNull(successResponse.getIssuer());
}
@Test
@@ -112,8 +117,29 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
Assert.assertNull(successResponse.getIDToken());
Assert.assertNull(successResponse.getAccessToken());
Assert.assertNotNull(successResponse.getAuthorizationCode());
+ Assert.assertNull(successResponse.getIssuer());
}
-
+
+ @Test
+ public void testWithAuthorizationCodeFlowIssInResponse() throws IOException, SessionException {
+ request.setMethod("GET");
+ request.setQueryString("client_id=mockClientIdIssInResponse&response_type=code&scope=openid%20profile&redirect_uri="
+ + redirectUri);
+ storeMetadata(storageService, clientIdIssInResponse, clientSecret, scope, redirectUri);
+
+ initializeThreadLocals();
+
+ final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
+ final AuthenticationResponse responseMessage = parseSuccessResponse(result, AuthenticationResponse.class);
+ final AuthenticationSuccessResponse successResponse = responseMessage.toSuccessResponse();
+ Assert.assertEquals(successResponse.getRedirectionURI().toString(), redirectUri);
+ Assert.assertNull(successResponse.getIDToken());
+ Assert.assertNull(successResponse.getAccessToken());
+ Assert.assertNotNull(successResponse.getAuthorizationCode());
+ Assert.assertNotNull(successResponse.getIssuer());
+ Assert.assertEquals(successResponse.getIssuer().getValue(), issuer);
+ }
+
@Test
public void testWithAuthorizationCodeFlowNoOpenid() throws IOException, SessionException {
request.setMethod("GET");
@@ -156,6 +182,27 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
Assert.assertNotNull(successResponse.getIDToken());
Assert.assertNull(successResponse.getAccessToken());
Assert.assertNull(successResponse.getAuthorizationCode());
+ Assert.assertNull(successResponse.getIssuer());
+ }
+
+ @Test
+ public void testWithImplicitFlowIssInResponse() throws IOException, SessionException {
+ request.setMethod("GET");
+ request.setQueryString("client_id=mockClientIdIssInResponse&response_type=id_token&scope=openid%20profile&redirect_uri="
+ + redirectUri + "&nonce=idhas3h23hi13h1o2i32");
+ storeMetadata(storageService, clientIdIssInResponse, clientSecret, scope, redirectUri);
+
+ initializeThreadLocals();
+
+ final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
+ final AuthenticationResponse responseMessage = parseSuccessResponse(result, AuthenticationResponse.class);
+ final AuthenticationSuccessResponse successResponse = responseMessage.toSuccessResponse();
+ Assert.assertEquals(successResponse.getRedirectionURI().toString(), redirectUri);
+ Assert.assertNotNull(successResponse.getIDToken());
+ Assert.assertNull(successResponse.getAccessToken());
+ Assert.assertNull(successResponse.getAuthorizationCode());
+ Assert.assertNotNull(successResponse.getIssuer());
+ Assert.assertEquals(successResponse.getIssuer().getValue(), issuer);
}
@Test
@@ -174,6 +221,7 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
Assert.assertNotNull(successResponse.getIDToken());
Assert.assertNull(successResponse.getAccessToken());
Assert.assertNull(successResponse.getAuthorizationCode());
+ Assert.assertNull(successResponse.getIssuer());
}
@Test
@@ -197,6 +245,32 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
AccessTokenClaimsSet.parse(successResponse.getAccessToken().getValue(), getDataSealer());
Assert.assertEquals(token.getAudience(), Collections.singletonList(issuer));
Assert.assertNull(token.getClaimsSet().getStringClaim("eduPersonScopedAffiliation"));
+ Assert.assertNull(successResponse.getIssuer());
+ }
+
+ @Test
+ public void testWithImplicitTokenFlowIssInResponse() throws IOException, SessionException, DataSealerException, ParseException {
+ request.setMethod("GET");
+ request.setQueryString("client_id=mockClientIdIssInResponse&response_type=id_token+token&scope=openid%20profile"
+ + "&redirect_uri="+ redirectUri + "&nonce=idhas3h23hi13h1o2i32");
+ storeMetadata(storageService, clientIdIssInResponse, clientSecret, scope, redirectUri);
+
+ initializeThreadLocals();
+
+ final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
+ final AuthenticationResponse responseMessage = parseSuccessResponse(result, AuthenticationResponse.class);
+ final AuthenticationSuccessResponse successResponse = responseMessage.toSuccessResponse();
+ Assert.assertEquals(successResponse.getRedirectionURI().toString(), redirectUri);
+ Assert.assertNotNull(successResponse.getIDToken());
+ Assert.assertNotNull(successResponse.getAccessToken());
+ Assert.assertNull(successResponse.getAuthorizationCode());
+
+ final AccessTokenClaimsSet token =
+ AccessTokenClaimsSet.parse(successResponse.getAccessToken().getValue(), getDataSealer());
+ Assert.assertEquals(token.getAudience(), Collections.singletonList(issuer));
+ Assert.assertNull(token.getClaimsSet().getStringClaim("eduPersonScopedAffiliation"));
+ Assert.assertNotNull(successResponse.getIssuer());
+ Assert.assertEquals(successResponse.getIssuer().getValue(), issuer);
}
@Test
@@ -215,6 +289,7 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
Assert.assertNotNull(successResponse.getIDToken());
Assert.assertNotNull(successResponse.getAccessToken());
Assert.assertNull(successResponse.getAuthorizationCode());
+ Assert.assertNull(successResponse.getIssuer());
final JWTClaimsSet token = SignedJWT.parse(successResponse.getAccessToken().getValue()).getJWTClaimsSet();
Assert.assertEquals(token.getAudience(), List.of(resource, issuer));
@@ -263,6 +338,27 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
Assert.assertNotNull(successResponse.getIDToken());
Assert.assertNull(successResponse.getAccessToken());
Assert.assertNotNull(successResponse.getAuthorizationCode());
+ Assert.assertNull(successResponse.getIssuer());
+ }
+
+ @Test
+ public void testWithHybridIdTokenFlowIssInResponse() throws IOException, SessionException {
+ request.setMethod("GET");
+ request.setQueryString("client_id=mockClientIdIssInResponse&response_type=code+id_token&scope=openid%20profile"
+ + "&redirect_uri=" + redirectUri + "&nonce=idhas3h23hi13h1o2i32");
+ storeMetadata(storageService, clientIdIssInResponse, clientSecret, scope, redirectUri);
+
+ initializeThreadLocals();
+
+ final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
+ final AuthenticationResponse responseMessage = parseSuccessResponse(result, AuthenticationResponse.class);
+ final AuthenticationSuccessResponse successResponse = responseMessage.toSuccessResponse();
+ Assert.assertEquals(successResponse.getRedirectionURI().toString(), redirectUri);
+ Assert.assertNotNull(successResponse.getIDToken());
+ Assert.assertNull(successResponse.getAccessToken());
+ Assert.assertNotNull(successResponse.getAuthorizationCode());
+ Assert.assertNotNull(successResponse.getIssuer());
+ Assert.assertEquals(successResponse.getIssuer().getValue(), issuer);
}
@Test
@@ -281,6 +377,7 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
Assert.assertNotNull(successResponse.getIDToken());
Assert.assertNull(successResponse.getAccessToken());
Assert.assertNotNull(successResponse.getAuthorizationCode());
+ Assert.assertNull(successResponse.getIssuer());
}
@Test
@@ -318,6 +415,7 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
AccessTokenClaimsSet.parse(successResponse.getAccessToken().getValue(), getDataSealer());
Assert.assertEquals(token.getAudience(), Collections.singletonList(issuer));
Assert.assertNull(token.getClaimsSet().getStringClaim("eduPersonScopedAffiliation"));
+ Assert.assertNull(successResponse.getIssuer());
}
@Test
@@ -337,6 +435,7 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
Assert.assertNull(successResponse.getIDToken());
Assert.assertNotNull(successResponse.getAccessToken());
Assert.assertNotNull(successResponse.getAuthorizationCode());
+ Assert.assertNull(successResponse.getIssuer());
final JWTClaimsSet token = SignedJWT.parse(successResponse.getAccessToken().getValue()).getJWTClaimsSet();
Assert.assertEquals(token.getAudience(), List.of(resource, issuer));
@@ -359,6 +458,7 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
Assert.assertNotNull(successResponse.getIDToken());
Assert.assertNotNull(successResponse.getAccessToken());
Assert.assertNotNull(successResponse.getAuthorizationCode());
+ Assert.assertNull(successResponse.getIssuer());
final AccessTokenClaimsSet token =
AccessTokenClaimsSet.parse(successResponse.getAccessToken().getValue(), getDataSealer());
@@ -382,6 +482,7 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
Assert.assertNotNull(successResponse.getIDToken());
Assert.assertNotNull(successResponse.getAccessToken());
Assert.assertNotNull(successResponse.getAuthorizationCode());
+ Assert.assertNull(successResponse.getIssuer());
final JWTClaimsSet token = SignedJWT.parse(successResponse.getAccessToken().getValue()).getJWTClaimsSet();
Assert.assertEquals(token.getAudience(), List.of(resource, issuer));
@@ -525,8 +626,26 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
assertErrorCode(result, "invalid_request");
assertErrorDescriptionContains(result, "InvalidSubject");
+ assertErrorResponseWithNoIssuer(result);
}
-
+
+ @Test
+ public void testWithAuthorizationCodeFlowNoScopesIssInResponse() throws IOException, SessionException {
+ request.setMethod("GET");
+ request.setQueryString("client_id=mockClientIdIssInResponse&response_type=code&scope=openid%20profile&redirect_uri="
+ + redirectUri);
+ storeMetadata(storageService, clientIdIssInResponse, clientSecret, null, redirectUri);
+
+ initializeThreadLocals();
+
+ // TODO: Speculation this should fail more explicitly if openid scope isn't valid.
+
+ final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
+ assertErrorCode(result, "invalid_request");
+ assertErrorDescriptionContains(result, "InvalidSubject");
+ assertErrorResponseWithIssuer(result);
+ }
+
@Test
public void testWithAuthorizationCodeFlowWithIDTokenClaims() throws IOException, SessionException, DataSealerException, ParseException {
request.setMethod("GET");
@@ -544,7 +663,8 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
Assert.assertNull(successResponse.getIDToken());
Assert.assertNull(successResponse.getAccessToken());
Assert.assertNotNull(successResponse.getAuthorizationCode());
-
+ Assert.assertNull(successResponse.getIssuer());
+
final AuthorizeCodeClaimsSet code =
AuthorizeCodeClaimsSet.parse(successResponse.getAuthorizationCode().getValue(), getDataSealer());
Assert.assertNotNull(code.getClaimsRequest());
@@ -573,6 +693,7 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
Assert.assertNull(successResponse.getIDToken());
Assert.assertNull(successResponse.getAccessToken());
Assert.assertNotNull(successResponse.getAuthorizationCode());
+ Assert.assertNull(successResponse.getIssuer());
final AuthorizeCodeClaimsSet code =
AuthorizeCodeClaimsSet.parse(successResponse.getAuthorizationCode().getValue(), getDataSealer());
@@ -601,6 +722,7 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
Assert.assertNull(successResponse.getIDToken());
Assert.assertNull(successResponse.getAccessToken());
Assert.assertNotNull(successResponse.getAuthorizationCode());
+ Assert.assertNull(successResponse.getIssuer());
}
@Test
@@ -651,6 +773,7 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
Assert.assertNull(successResponse.getIDToken());
Assert.assertNull(successResponse.getAccessToken());
Assert.assertNotNull(successResponse.getAuthorizationCode());
+ Assert.assertNull(successResponse.getIssuer());
}
@Test
@@ -698,6 +821,7 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
Assert.assertNull(successResponse.getIDToken());
Assert.assertNull(successResponse.getAccessToken());
Assert.assertNotNull(successResponse.getAuthorizationCode());
+ Assert.assertNull(successResponse.getIssuer());
final AuthorizeCodeClaimsSet code =
AuthorizeCodeClaimsSet.parse(successResponse.getAuthorizationCode().getValue(), getDataSealer());
@@ -779,6 +903,7 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
Assert.assertNull(successResponse.getIDToken());
Assert.assertNull(successResponse.getAccessToken());
Assert.assertNotNull(successResponse.getAuthorizationCode());
+ Assert.assertNull(successResponse.getIssuer());
}
@Test
@@ -825,6 +950,7 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
Assert.assertNull(successResponse.getIDToken());
Assert.assertNull(successResponse.getAccessToken());
Assert.assertNotNull(successResponse.getAuthorizationCode());
+ Assert.assertNull(successResponse.getIssuer());
final AuthorizeCodeClaimsSet code =
AuthorizeCodeClaimsSet.parse(successResponse.getAuthorizationCode().getValue(), getDataSealer());
@@ -858,12 +984,30 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
Assert.assertEquals(result.getOutcome().getId(), "ErrorView");
}
+ protected void assertErrorResponseWithNoIssuer(final FlowExecutionResult result) {
+ final Response response = parseResponse(result);
+ Assert.assertFalse(response.indicatesSuccess());
+ Assert.assertTrue(response instanceof AuthenticationErrorResponse);
+ final AuthenticationErrorResponse errorResponse = (AuthenticationErrorResponse) response;
+ Assert.assertNull(errorResponse.getIssuer());
+ }
+
+ protected void assertErrorResponseWithIssuer(final FlowExecutionResult result) {
+ final Response response = parseResponse(result);
+ Assert.assertFalse(response.indicatesSuccess());
+ Assert.assertTrue(response instanceof AuthenticationErrorResponse);
+ final AuthenticationErrorResponse errorResponse = (AuthenticationErrorResponse) response;
+ Assert.assertNotNull(errorResponse.getIssuer());
+ Assert.assertEquals(errorResponse.getIssuer().getValue(), issuer);
+ }
+
@AfterMethod
public void removeMetadata() throws IOException {
removeMetadata(storageService, clientId);
removeMetadata(storageService, "mockClientIdPKCEPlainUnforced");
removeMetadata(storageService, "mockClientIdPKCEPlain");
removeMetadata(storageService, "mockClientIdPKCES256");
+ removeMetadata(storageService, clientIdIssInResponse);
}
}
diff --git a/idp-oidc-extension-impl/src/test/resources/conf/relying-party.xml b/idp-oidc-extension-impl/src/test/resources/conf/relying-party.xml
index 5339dd47..5f040a41 100644
--- a/idp-oidc-extension-impl/src/test/resources/conf/relying-party.xml
+++ b/idp-oidc-extension-impl/src/test/resources/conf/relying-party.xml
@@ -58,6 +58,13 @@
</bean>
<util:list id="shibboleth.RelyingPartyOverrides">
+ <bean parent="RelyingPartyByName" c:relyingPartyIds="mockClientIdIssInResponse">
+ <property name="profileConfigurations">
+ <list>
+ <bean parent="OIDC.SSO.MDDriven" p:includeIssuerInResponse="true"/>
+ </list>
+ </property>
+ </bean>
<bean parent="RelyingPartyByName" c:relyingPartyIds="mockClientIdNoRefreshTokensInSSOProfile">
<property name="profileConfigurations">
<list>
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list