[java-idp-oidc] branch main updated: JOIDC-68 Request object (JWT) validation is incomplete
Scott Cantor
cantor.2 at osu.edu
Thu Dec 30 20:46:16 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=01f6ab2da1ee2be401025856431b807bb855a02d
The following commit(s) were added to refs/heads/main by this push:
new 01f6ab2d JOIDC-68 Request object (JWT) validation is incomplete
01f6ab2d is described below
commit 01f6ab2da1ee2be401025856431b807bb855a02d
Author: Henri Mikkonen <henri.mikkonen at iki.fi>
AuthorDate: Thu Dec 30 16:46:43 2021 +0200
JOIDC-68 Request object (JWT) validation is incomplete
https://shibboleth.atlassian.net/browse/JOIDC-68
Added ClaimValidators for verifying exp and nbf claims for all request objects - if
they're present in the JWT. For signed request objects: also iss and aud are required and
verified.
---
.../op/profile/impl/ValidateRequestObject.java | 66 +++++-
.../idp/flows/oidc/authorize/authorize-beans.xml | 51 ++++-
.../oidc/op/profile/flow/AuthorizeFlowTest.java | 124 +++++++++-
.../op/profile/impl/ValidateRequestObjectTest.java | 254 ++++++++++++++++++++-
4 files changed, 488 insertions(+), 7 deletions(-)
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateRequestObject.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateRequestObject.java
index 221b80ae..853e218c 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateRequestObject.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateRequestObject.java
@@ -31,14 +31,19 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.ResponseType;
import com.nimbusds.oauth2.sdk.id.ClientID;
import net.shibboleth.idp.plugin.oidc.op.profile.OidcEventIds;
+import net.shibboleth.oidc.jwt.claims.JWTClaimsValidation;
+import net.shibboleth.oidc.jwt.claims.JWTValidationException;
import net.shibboleth.oidc.security.impl.JWTSignatureValidationUtil;
import net.shibboleth.oidc.security.impl.OIDCSignatureValidationParameters;
+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;
@@ -61,6 +66,12 @@ public class ValidateRequestObject extends AbstractOIDCAuthenticationResponseAct
/** Request Object. */
@Nullable private JWT requestObject;
+ /** The claims validator to be applied for validating the signed request object. */
+ @NonnullAfterInit private JWTClaimsValidation signedClaimsValidation;
+
+ /** The claims validator to be applied for validating the plain/unsigned request object. */
+ @NonnullAfterInit private JWTClaimsValidation plainClaimsValidation;
+
/** Constructor. */
public ValidateRequestObject() {
securityParametersLookupStrategy = new ChildContextLookup<>(SecurityParametersContext.class);
@@ -79,6 +90,40 @@ public class ValidateRequestObject extends AbstractOIDCAuthenticationResponseAct
Constraint.isNotNull(strategy, "SecurityParameterContext lookup strategy cannot be null");
}
+ /**
+ * Set the claims validator used for validating the signed request object.
+ *
+ * @param validators What to set.
+ */
+ public void setSignedClaimsValidation(@Nonnull final JWTClaimsValidation validation) {
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+ signedClaimsValidation = Constraint.isNotNull(validation, "Signed claims validator cannot be null");
+ }
+
+ /**
+ * Set the claims validator used for validating the plain/unsigned request object.
+ *
+ * @param validators What to set.
+ */
+ public void setPlainClaimsValidation(@Nonnull final JWTClaimsValidation validation) {
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+ plainClaimsValidation = Constraint.isNotNull(validation, "Plain claims validator cannot be null");
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void doInitialize() throws ComponentInitializationException {
+ super.doInitialize();
+
+ if (signedClaimsValidation == null) {
+ throw new ComponentInitializationException("ClaimsValidation for signed requests cannot be null");
+ }
+
+ if (plainClaimsValidation == null) {
+ throw new ComponentInitializationException("ClaimsValidation for plain requests cannot be null");
+ }
+}
+
/** {@inheritDoc} */
@Override
protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
@@ -121,16 +166,18 @@ public class ValidateRequestObject extends AbstractOIDCAuthenticationResponseAct
}
}
+ final JWTClaimsSet claimsSet;
// Validate still client_id and response_type values
try {
- if (requestObject.getJWTClaimsSet().getClaims().containsKey("client_id")
+ claimsSet = requestObject.getJWTClaimsSet();
+ if (claimsSet.getClaims().containsKey("client_id")
&& !getAuthenticationRequest().getClientID()
- .equals(new ClientID((String) requestObject.getJWTClaimsSet().getClaim("client_id")))) {
+ .equals(new ClientID((String) claimsSet.getClaim("client_id")))) {
log.error("{} client_id in request object not matching client_id request parameter", getLogPrefix());
ActionSupport.buildEvent(profileRequestContext, OidcEventIds.INVALID_REQUEST_OBJECT);
return;
}
- if (requestObject.getJWTClaimsSet().getClaims().containsKey("response_type")
+ if (claimsSet.getClaims().containsKey("response_type")
&& !getAuthenticationRequest().getResponseType().equals(new ResponseType(
((String) requestObject.getJWTClaimsSet().getClaim("response_type")).split(" ")))) {
log.error("{} response_type in request object not matching response_type request parameter",
@@ -143,6 +190,19 @@ public class ValidateRequestObject extends AbstractOIDCAuthenticationResponseAct
ActionSupport.buildEvent(profileRequestContext, OidcEventIds.INVALID_REQUEST_OBJECT);
return;
}
+
+ try {
+ if (requestObject instanceof SignedJWT) {
+ signedClaimsValidation.validate(claimsSet, profileRequestContext);
+ } else {
+ plainClaimsValidation.validate(claimsSet, profileRequestContext);
+ }
+ } catch (final JWTValidationException e) {
+ log.warn("{} JWT validation failed: {}", getLogPrefix(), e.getMessage());
+ ActionSupport.buildEvent(profileRequestContext, OidcEventIds.INVALID_REQUEST_OBJECT);
+ return;
+ }
+
}
// Checkstyle: CyclomaticComplexity ON
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/authorize/authorize-beans.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/authorize/authorize-beans.xml
index ef383414..8738133e 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/authorize/authorize-beans.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/authorize/authorize-beans.xml
@@ -102,7 +102,9 @@
scope="prototype" />
<bean id="ValidateRequestObject" class="net.shibboleth.idp.plugin.oidc.op.profile.impl.ValidateRequestObject"
- scope="prototype">
+ scope="prototype"
+ p:plainClaimsValidation-ref="shibboleth.oidc.PlainRequestObjectClaimsValidation"
+ p:signedClaimsValidation-ref="shibboleth.oidc.SignedRequestObjectClaimsValidation">
<property name="securityParametersLookupStrategy">
<bean parent="shibboleth.Functions.Compose"
c:g-ref="shibboleth.ChildLookup.SecurityParameters"
@@ -110,6 +112,53 @@
</property>
</bean>
+ <bean id="shibboleth.oidc.PlainRequestObjectClaimsValidation"
+ class="net.shibboleth.oidc.security.jwt.claims.impl.ChainingJWTClaimsValidation"
+ p:claimValidators-ref="PlainClaimsValidators" />
+
+ <bean id="shibboleth.oidc.SignedRequestObjectClaimsValidation"
+ class="net.shibboleth.oidc.security.jwt.claims.impl.ChainingJWTClaimsValidation"
+ p:claimValidators-ref="SignedClaimsValidators" />
+
+ <bean id="ExpiryClaimsValidator"
+ class="net.shibboleth.oidc.security.jwt.claims.impl.ExpiryClaimsValidator"
+ p:clockSkew="%{idp.policy.clockSkew:PT1M}" />
+
+ <bean id="NotBeforeClaimsValidator"
+ class="net.shibboleth.oidc.security.jwt.claims.impl.NotBeforeClaimsValidator"
+ p:clockSkew="%{idp.policy.clockSkew:PT1M}" />
+
+ <bean id="IssuerClaimsValidator"
+ class="net.shibboleth.oidc.security.jwt.claims.impl.ExactMatchClaimsValidator"
+ p:claimName="iss">
+ <property name="valueToMatchLookupStrategy">
+ <bean parent="shibboleth.BiFunctions.Expression"
+ c:expression="#custom.apply(#input1.getInboundMessageContext()) == null ? null : #custom.apply(#input1.getInboundMessageContext()).toString()"
+ p:customObject-ref="shibboleth.ClientIDLookupStrategy" />
+ </property>
+ </bean>
+
+ <bean id="AudienceClaimsValidator"
+ class="net.shibboleth.oidc.security.jwt.claims.impl.AudienceClaimsValidator">
+ <property name="audienceLookupStrategy">
+ <bean parent="shibboleth.BiFunctions.Expression"
+ c:expression="#custom.apply(#input1)"
+ p:customObject-ref="shibboleth.ResponderIdLookup.Simple" />
+ </property>
+ </bean>
+
+ <util:list id="PlainClaimsValidators" value-type="net.shibboleth.oidc.jwt.claims.ClaimsValidator">
+ <ref bean="ExpiryClaimsValidator" />
+ <ref bean="NotBeforeClaimsValidator" />
+ </util:list>
+
+ <util:list id="SignedClaimsValidators" value-type="net.shibboleth.oidc.jwt.claims.ClaimsValidator">
+ <ref bean="ExpiryClaimsValidator" />
+ <ref bean="NotBeforeClaimsValidator" />
+ <ref bean="IssuerClaimsValidator" />
+ <ref bean="AudienceClaimsValidator" />
+ </util:list>
+
<bean id="ValidateRedirectURI" class="net.shibboleth.idp.plugin.oidc.op.profile.impl.ValidateRedirectURI"
scope="prototype" />
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 9aa2142c..441c60dc 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
@@ -18,6 +18,9 @@
package net.shibboleth.idp.plugin.oidc.op.profile.flow;
import java.io.IOException;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.Date;
import org.opensaml.storage.StorageService;
import org.springframework.beans.factory.annotation.Autowired;
@@ -28,6 +31,11 @@ import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
+import com.nimbusds.jose.JOSEException;
+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.ParseException;
import com.nimbusds.openid.connect.sdk.AuthenticationResponse;
import com.nimbusds.openid.connect.sdk.AuthenticationSuccessResponse;
@@ -43,7 +51,7 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
String redirectUri = "https://example.org/cb";
String clientId = "mockClientId";
- String clientSecret = "mockClientSecret";
+ String clientSecret = "mockClientSecretmockClientSecretmockClientSecretmockClientSecretmockClientSecret";
@Autowired
@Qualifier("shibboleth.StorageService")
@@ -144,6 +152,120 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
Assert.assertEquals(result.getOutcome().getId(), "ErrorView");
}
+ @Test
+ public void testWithPlainReqObjectExpired() throws IOException, ParseException, SessionException {
+ JWTClaimsSet ro = new JWTClaimsSet.Builder()
+ .expirationTime(Date.from(Instant.now().minus(Duration.ofMinutes(5))))
+ .build();
+ assertRequestObjectError(new PlainJWT(ro));
+ }
+
+ @Test
+ public void testWithPlainReqObjectNbfInFuture() throws IOException, ParseException, SessionException {
+ JWTClaimsSet ro = new JWTClaimsSet.Builder()
+ .notBeforeTime(Date.from(Instant.now().plus(Duration.ofMinutes(5))))
+ .build();
+ assertRequestObjectError(new PlainJWT(ro));
+ }
+
+ @Test
+ public void testWithPlainReqObjectOverwriteRedirectUri() throws IOException, ParseException,
+ SessionException {
+ JWTClaimsSet ro = new JWTClaimsSet.Builder()
+ .claim("redirect_uri", redirectUri)
+ .build();
+ PlainJWT requestObject = new PlainJWT(ro);
+ request.setMethod("GET");
+ request.setQueryString("client_id=mockClientId&response_type=code&scope=openid%20profile&redirect_uri="
+ + "https://invalid.org/cb&request=" + requestObject.serialize());
+ storeMetadata(storageService, clientId, clientSecret, redirectUri);
+
+ initializeThreadLocals();
+
+ FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
+ AuthenticationResponse responseMessage = parseSuccessResponse(result, AuthenticationResponse.class);
+ AuthenticationSuccessResponse successResponse = responseMessage.toSuccessResponse();
+ Assert.assertEquals(successResponse.getRedirectionURI().toString(), redirectUri);
+ Assert.assertNull(successResponse.getIDToken());
+ Assert.assertNull(successResponse.getAccessToken());
+ Assert.assertNotNull(successResponse.getAuthorizationCode());
+ }
+
+ @Test
+ public void testWithSignedReqObjectNoIssuer() throws IOException, ParseException, SessionException,
+ JOSEException {
+ JWTClaimsSet ro = new JWTClaimsSet.Builder()
+ .audience("https://op.example.org")
+ .build();
+ assertRequestObjectError(createSecretJWT(ro, clientSecret));
+ }
+
+ @Test
+ public void testWithSignedReqObjectNoAudience() throws IOException, ParseException, SessionException,
+ JOSEException {
+ JWTClaimsSet ro = new JWTClaimsSet.Builder()
+ .issuer(clientId)
+ .build();
+ assertRequestObjectError(createSecretJWT(ro, clientSecret));
+ }
+
+ @Test
+ public void testWithSignedReqObjectWrongIssuer() throws IOException, ParseException, SessionException,
+ JOSEException {
+ JWTClaimsSet ro = new JWTClaimsSet.Builder()
+ .audience("https://op.example.org")
+ .issuer("invalid")
+ .build();
+ assertRequestObjectError(createSecretJWT(ro, clientSecret));
+ }
+
+ @Test
+ public void testWithSignedReqObjectWrongAudience() throws IOException, ParseException, SessionException,
+ JOSEException {
+ JWTClaimsSet ro = new JWTClaimsSet.Builder()
+ .audience("https://invalid.org")
+ .issuer(clientId)
+ .build();
+ assertRequestObjectError(createSecretJWT(ro, clientSecret));
+ }
+
+ @Test
+ public void testWithSignedReqObjectOverwriteRedirectUri() throws IOException, ParseException,
+ SessionException, JOSEException {
+ JWTClaimsSet ro = new JWTClaimsSet.Builder()
+ .audience("https://op.example.org")
+ .issuer(clientId)
+ .claim("redirect_uri", redirectUri)
+ .build();
+ SignedJWT requestObject = createSecretJWT(ro, clientSecret);
+ request.setMethod("GET");
+ request.setQueryString("client_id=mockClientId&response_type=code&scope=openid%20profile&redirect_uri="
+ + "https://invalid.org/cb&request=" + requestObject.serialize());
+ storeMetadata(storageService, clientId, clientSecret, redirectUri);
+
+ initializeThreadLocals();
+
+ FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
+ AuthenticationResponse responseMessage = parseSuccessResponse(result, AuthenticationResponse.class);
+ AuthenticationSuccessResponse successResponse = responseMessage.toSuccessResponse();
+ Assert.assertEquals(successResponse.getRedirectionURI().toString(), redirectUri);
+ Assert.assertNull(successResponse.getIDToken());
+ Assert.assertNull(successResponse.getAccessToken());
+ Assert.assertNotNull(successResponse.getAuthorizationCode());
+ }
+
+ protected void assertRequestObjectError(final JWT requestObject) throws IOException {
+ request.setMethod("GET");
+ request.setQueryString("client_id=mockClientId&response_type=code&scope=openid%20profile&redirect_uri="
+ + redirectUri + "&request=" + requestObject.serialize());
+ storeMetadata(storageService, clientId, clientSecret, redirectUri);
+ initializeThreadLocals();
+
+ FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
+ assertFlowExecutionResult(result, FLOW_ID);
+ Assert.assertEquals(result.getOutcome().getId(), "ErrorView");
+ }
+
@AfterMethod
public void removeMetadata() throws IOException {
removeMetadata(storageService, clientId);
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateRequestObjectTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateRequestObjectTest.java
index cfe21b93..f5ad9190 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateRequestObjectTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateRequestObjectTest.java
@@ -20,12 +20,16 @@ package net.shibboleth.idp.plugin.oidc.op.profile.impl;
import net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCAuthenticationResponseContext;
import net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCMetadataContext;
import net.shibboleth.idp.plugin.oidc.op.profile.OidcEventIds;
-import net.shibboleth.idp.plugin.oidc.op.profile.impl.ValidateRequestObject;
import net.shibboleth.idp.profile.context.navigate.WebflowRequestContextProfileRequestContextLookup;
import net.shibboleth.idp.profile.testing.ActionTestingSupport;
import net.shibboleth.idp.profile.testing.RequestContextBuilder;
import net.shibboleth.oidc.security.credential.BasicJWKCredential;
import net.shibboleth.oidc.security.impl.OIDCSignatureValidationParameters;
+import net.shibboleth.oidc.security.jwt.claims.impl.AudienceClaimsValidator;
+import net.shibboleth.oidc.security.jwt.claims.impl.ChainingJWTClaimsValidation;
+import net.shibboleth.oidc.security.jwt.claims.impl.ExactMatchClaimsValidator;
+import net.shibboleth.oidc.security.jwt.claims.impl.ExpiryClaimsValidator;
+import net.shibboleth.oidc.security.jwt.claims.impl.NotBeforeClaimsValidator;
import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
import java.net.URI;
import java.net.URISyntaxException;
@@ -34,6 +38,10 @@ import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.ECPrivateKey;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.Date;
+import java.util.List;
import org.opensaml.profile.action.EventIds;
import org.opensaml.profile.context.ProfileRequestContext;
@@ -77,6 +85,8 @@ public class ValidateRequestObjectTest {
private OIDCAuthenticationResponseContext oidcRespCtx;
private KeyPair kp;
+
+ private String issuer;
@BeforeMethod
public void setup() throws ComponentInitializationException, NoSuchAlgorithmException {
@@ -85,6 +95,7 @@ public class ValidateRequestObjectTest {
oidcCtx = prc.getInboundMessageContext().getSubcontext(OIDCMetadataContext.class, true);
oidcRespCtx = new OIDCAuthenticationResponseContext();
prc.getOutboundMessageContext().addSubcontext(oidcRespCtx);
+ issuer = "https://op.example.org/";
OIDCClientMetadata metaData = new OIDCClientMetadata();
OIDCClientInformation information = new OIDCClientInformation(new ClientID("test"), null, metaData,
new Secret("ultimatetopsecretultimatetopsecret"), null, null);
@@ -107,6 +118,59 @@ public class ValidateRequestObjectTest {
params.setSignatureAlgorithm("RS256");
secCtx.setSignatureSigningParameters(params);
action = new ValidateRequestObject();
+ action.setPlainClaimsValidation(buildPlainClaimsValidation());
+ action.setSignedClaimsValidation(buildSignedClaimsValidation("000123"));
+ action.initialize();
+ }
+
+ protected ChainingJWTClaimsValidation buildSignedClaimsValidation(final String clientId) {
+ ChainingJWTClaimsValidation claimsValidation = new ChainingJWTClaimsValidation();
+ ExactMatchClaimsValidator issValidator = new ExactMatchClaimsValidator();
+ issValidator.setClaimName("iss");
+ issValidator.setValueToMatchLookupStrategy((prc, claimsSet) -> clientId);
+ AudienceClaimsValidator audValidator = new AudienceClaimsValidator();
+ audValidator.setAudienceLookupStrategy((prc, claimsSet) -> issuer);
+ claimsValidation.setClaimValidators(List.of(
+ new ExpiryClaimsValidator(),
+ new NotBeforeClaimsValidator(),
+ issValidator,
+ audValidator));
+ return claimsValidation;
+ }
+
+ protected ChainingJWTClaimsValidation buildPlainClaimsValidation() {
+ ChainingJWTClaimsValidation claimsValidation = new ChainingJWTClaimsValidation();
+ claimsValidation.setClaimValidators(List.of(
+ new ExpiryClaimsValidator(),
+ new NotBeforeClaimsValidator()));
+ return claimsValidation;
+ }
+
+ @Test(expectedExceptions = ComponentInitializationException.class)
+ public void testInitFailsNoValidators() throws ComponentInitializationException {
+ action = new ValidateRequestObject();
+ action.initialize();
+ }
+
+ @Test(expectedExceptions = ComponentInitializationException.class)
+ public void testInitFailsNoPlainValidator() throws ComponentInitializationException {
+ action = new ValidateRequestObject();
+ action.setSignedClaimsValidation(new ChainingJWTClaimsValidation());
+ action.initialize();
+ }
+
+ @Test(expectedExceptions = ComponentInitializationException.class)
+ public void testInitFailsNoSignedValidators() throws ComponentInitializationException {
+ action = new ValidateRequestObject();
+ action.setPlainClaimsValidation(new ChainingJWTClaimsValidation());
+ action.initialize();
+ }
+
+ @Test
+ public void testInitSuccess() throws ComponentInitializationException {
+ action = new ValidateRequestObject();
+ action.setPlainClaimsValidation(new ChainingJWTClaimsValidation());
+ action.setSignedClaimsValidation(new ChainingJWTClaimsValidation());
action.initialize();
}
@@ -225,6 +289,26 @@ public class ValidateRequestObjectTest {
ActionTestingSupport.assertProceedEvent(event);
}
+ @Test
+ public void testRequestObjectClientRespTypeMatchWithExpNbf()
+ throws NoSuchAlgorithmException, ComponentInitializationException, URISyntaxException {
+ JWTClaimsSet ro = new JWTClaimsSet.Builder()
+ .claim("client_id", "000123")
+ .claim("response_type", "code token")
+ .notBeforeTime(Date.from(Instant.now()))
+ .expirationTime(Date.from(Instant.now().plusSeconds(60))).build();
+ ResponseType rt = new ResponseType();
+ rt.add(ResponseType.Value.CODE);
+ rt.add(ResponseType.Value.TOKEN);
+ AuthenticationRequest req = new AuthenticationRequest.Builder(rt, new Scope("openid"), new ClientID("000123"),
+ URI.create("https://example.com/callback")).requestObject(new PlainJWT(ro)).nonce(new Nonce())
+ .state(new State()).build();
+ prc.getInboundMessageContext().setMessage(req);
+ oidcRespCtx.setRequestObject(req.getRequestObject());
+ final Event event = action.execute(requestCtx);
+ ActionTestingSupport.assertProceedEvent(event);
+ }
+
/**
* Test success case of RS256 signed request object.
*/
@@ -232,7 +316,29 @@ public class ValidateRequestObjectTest {
public void testRequestObjectSignedWithRS256() throws NoSuchAlgorithmException, ComponentInitializationException,
URISyntaxException, JOSEException, InvalidAlgorithmParameterException {
JWSSigner signer = new RSASSASigner(kp.getPrivate());
- JWTClaimsSet ro = new JWTClaimsSet.Builder().subject("alice").build();
+ JWTClaimsSet ro = new JWTClaimsSet.Builder()
+ .issuer("000123")
+ .audience(issuer).build();
+ SignedJWT signed = new SignedJWT(new JWSHeader(JWSAlgorithm.RS256), ro);
+ signed.sign(signer);
+ AuthenticationRequest req =
+ new AuthenticationRequest.Builder(new ResponseType("code"), new Scope("openid"), new ClientID("000123"),
+ URI.create("https://example.com/callback")).requestObject(signed).state(new State()).build();
+ prc.getInboundMessageContext().setMessage(req);
+ oidcRespCtx.setRequestObject(req.getRequestObject());
+ final Event event = action.execute(requestCtx);
+ ActionTestingSupport.assertProceedEvent(event);
+ }
+
+ @Test
+ public void testRequestObjectWithExpNbfSignedWithRS256() throws NoSuchAlgorithmException,
+ ComponentInitializationException, URISyntaxException, JOSEException, InvalidAlgorithmParameterException {
+ JWSSigner signer = new RSASSASigner(kp.getPrivate());
+ JWTClaimsSet ro = new JWTClaimsSet.Builder()
+ .issuer("000123")
+ .audience(issuer)
+ .notBeforeTime(Date.from(Instant.now()))
+ .expirationTime(Date.from(Instant.now().plusSeconds(60))).build();
SignedJWT signed = new SignedJWT(new JWSHeader(JWSAlgorithm.RS256), ro);
signed.sign(signer);
AuthenticationRequest req =
@@ -349,4 +455,148 @@ public class ValidateRequestObjectTest {
ActionTestingSupport.assertEvent(event, EventIds.INVALID_SEC_CFG);
}
+ @Test
+ public void testSignedExpiredRequestObject() throws NoSuchAlgorithmException, ComponentInitializationException,
+ URISyntaxException, JOSEException, InvalidAlgorithmParameterException {
+ JWSSigner signer = new RSASSASigner(kp.getPrivate());
+ JWTClaimsSet ro = new JWTClaimsSet.Builder()
+ .issuer("000123")
+ .audience(issuer)
+ .expirationTime(Date.from(Instant.now().minus(Duration.ofMinutes(5))))
+ .build();
+ SignedJWT signed = new SignedJWT(new JWSHeader(JWSAlgorithm.RS256), ro);
+ signed.sign(signer);
+ AuthenticationRequest req =
+ new AuthenticationRequest.Builder(new ResponseType("code"), new Scope("openid"), new ClientID("000123"),
+ URI.create("https://example.com/callback")).requestObject(signed).state(new State()).build();
+ prc.getInboundMessageContext().setMessage(req);
+ oidcRespCtx.setRequestObject(req.getRequestObject());
+ final Event event = action.execute(requestCtx);
+ ActionTestingSupport.assertEvent(event, OidcEventIds.INVALID_REQUEST_OBJECT);
+ }
+
+ @Test
+ public void testSignedNotBeforeInFutureRequestObject() throws NoSuchAlgorithmException, ComponentInitializationException,
+ URISyntaxException, JOSEException, InvalidAlgorithmParameterException {
+ JWSSigner signer = new RSASSASigner(kp.getPrivate());
+ JWTClaimsSet ro = new JWTClaimsSet.Builder()
+ .issuer("000123")
+ .audience(issuer)
+ .notBeforeTime(Date.from(Instant.now().plus(Duration.ofMinutes(5))))
+ .build();
+ SignedJWT signed = new SignedJWT(new JWSHeader(JWSAlgorithm.RS256), ro);
+ signed.sign(signer);
+ AuthenticationRequest req =
+ new AuthenticationRequest.Builder(new ResponseType("code"), new Scope("openid"), new ClientID("000123"),
+ URI.create("https://example.com/callback")).requestObject(signed).state(new State()).build();
+ prc.getInboundMessageContext().setMessage(req);
+ oidcRespCtx.setRequestObject(req.getRequestObject());
+ final Event event = action.execute(requestCtx);
+ ActionTestingSupport.assertEvent(event, OidcEventIds.INVALID_REQUEST_OBJECT);
+ }
+
+ @Test
+ public void testSignedNoIssuerRequstObject() throws NoSuchAlgorithmException, ComponentInitializationException,
+ URISyntaxException, JOSEException, InvalidAlgorithmParameterException {
+ JWSSigner signer = new RSASSASigner(kp.getPrivate());
+ JWTClaimsSet ro = new JWTClaimsSet.Builder()
+ .audience(issuer)
+ .build();
+ SignedJWT signed = new SignedJWT(new JWSHeader(JWSAlgorithm.RS256), ro);
+ signed.sign(signer);
+ AuthenticationRequest req =
+ new AuthenticationRequest.Builder(new ResponseType("code"), new Scope("openid"), new ClientID("000123"),
+ URI.create("https://example.com/callback")).requestObject(signed).state(new State()).build();
+ prc.getInboundMessageContext().setMessage(req);
+ oidcRespCtx.setRequestObject(req.getRequestObject());
+ final Event event = action.execute(requestCtx);
+ ActionTestingSupport.assertEvent(event, OidcEventIds.INVALID_REQUEST_OBJECT);
+ }
+
+ @Test
+ public void testSignedWrongIssuerRequstObject() throws NoSuchAlgorithmException, ComponentInitializationException,
+ URISyntaxException, JOSEException, InvalidAlgorithmParameterException {
+ JWSSigner signer = new RSASSASigner(kp.getPrivate());
+ JWTClaimsSet ro = new JWTClaimsSet.Builder()
+ .issuer("invalid")
+ .audience(issuer)
+ .build();
+ SignedJWT signed = new SignedJWT(new JWSHeader(JWSAlgorithm.RS256), ro);
+ signed.sign(signer);
+ AuthenticationRequest req =
+ new AuthenticationRequest.Builder(new ResponseType("code"), new Scope("openid"), new ClientID("000123"),
+ URI.create("https://example.com/callback")).requestObject(signed).state(new State()).build();
+ prc.getInboundMessageContext().setMessage(req);
+ oidcRespCtx.setRequestObject(req.getRequestObject());
+ final Event event = action.execute(requestCtx);
+ ActionTestingSupport.assertEvent(event, OidcEventIds.INVALID_REQUEST_OBJECT);
+ }
+
+ @Test
+ public void testSignedNoAudienceRequstObject() throws NoSuchAlgorithmException, ComponentInitializationException,
+ URISyntaxException, JOSEException, InvalidAlgorithmParameterException {
+ JWSSigner signer = new RSASSASigner(kp.getPrivate());
+ JWTClaimsSet ro = new JWTClaimsSet.Builder()
+ .issuer("000123")
+ .build();
+ SignedJWT signed = new SignedJWT(new JWSHeader(JWSAlgorithm.RS256), ro);
+ signed.sign(signer);
+ AuthenticationRequest req =
+ new AuthenticationRequest.Builder(new ResponseType("code"), new Scope("openid"), new ClientID("000123"),
+ URI.create("https://example.com/callback")).requestObject(signed).state(new State()).build();
+ prc.getInboundMessageContext().setMessage(req);
+ oidcRespCtx.setRequestObject(req.getRequestObject());
+ final Event event = action.execute(requestCtx);
+ ActionTestingSupport.assertEvent(event, OidcEventIds.INVALID_REQUEST_OBJECT);
+ }
+
+ @Test
+ public void testSignedWrongAudienceRequstObject() throws NoSuchAlgorithmException, ComponentInitializationException,
+ URISyntaxException, JOSEException, InvalidAlgorithmParameterException {
+ JWSSigner signer = new RSASSASigner(kp.getPrivate());
+ JWTClaimsSet ro = new JWTClaimsSet.Builder()
+ .issuer("000123")
+ .audience("invalid")
+ .build();
+ SignedJWT signed = new SignedJWT(new JWSHeader(JWSAlgorithm.RS256), ro);
+ signed.sign(signer);
+ AuthenticationRequest req =
+ new AuthenticationRequest.Builder(new ResponseType("code"), new Scope("openid"), new ClientID("000123"),
+ URI.create("https://example.com/callback")).requestObject(signed).state(new State()).build();
+ prc.getInboundMessageContext().setMessage(req);
+ oidcRespCtx.setRequestObject(req.getRequestObject());
+ final Event event = action.execute(requestCtx);
+ ActionTestingSupport.assertEvent(event, OidcEventIds.INVALID_REQUEST_OBJECT);
+ }
+
+ @Test
+ public void testPlainExpiredRequestObject() throws NoSuchAlgorithmException, ComponentInitializationException,
+ URISyntaxException, JOSEException, InvalidAlgorithmParameterException {
+ JWTClaimsSet ro = new JWTClaimsSet.Builder()
+ .expirationTime(Date.from(Instant.now().minus(Duration.ofMinutes(5))))
+ .build();
+ AuthenticationRequest req =
+ new AuthenticationRequest.Builder(new ResponseType("code"), new Scope("openid"), new ClientID("000123"),
+ URI.create("https://example.com/callback")).requestObject(new PlainJWT(ro)).state(new State()).build();
+ prc.getInboundMessageContext().setMessage(req);
+ oidcRespCtx.setRequestObject(req.getRequestObject());
+ final Event event = action.execute(requestCtx);
+ ActionTestingSupport.assertEvent(event, OidcEventIds.INVALID_REQUEST_OBJECT);
+ }
+
+ @Test
+ public void testPlainNotBeforeInFutureRequestObject() throws NoSuchAlgorithmException, ComponentInitializationException,
+ URISyntaxException, JOSEException, InvalidAlgorithmParameterException {
+ JWTClaimsSet ro = new JWTClaimsSet.Builder()
+ .notBeforeTime(Date.from(Instant.now().plus(Duration.ofMinutes(5))))
+ .build();
+ AuthenticationRequest req =
+ new AuthenticationRequest.Builder(new ResponseType("code"), new Scope("openid"), new ClientID("000123"),
+ URI.create("https://example.com/callback")).requestObject(new PlainJWT(ro)).state(new State()).build();
+ prc.getInboundMessageContext().setMessage(req);
+ oidcRespCtx.setRequestObject(req.getRequestObject());
+ final Event event = action.execute(requestCtx);
+ ActionTestingSupport.assertEvent(event, OidcEventIds.INVALID_REQUEST_OBJECT);
+ }
+
}
\ No newline at end of file
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list