[java-idp-oidc] 24/35: JOIDC-201 - Support for OAuth 2.0 Demonstrating Proof of Possession (DPoP)
Henri Mikkonen
henri.mikkonen at iki.fi
Thu Oct 24 14:01:49 UTC 2024
This is an automated email from the git hooks/post-receive script.
hjmikkon pushed a commit to branch dev/JOIDC-222
in repository java-idp-oidc.
View the commit online:
http://git.shibboleth.net/view/?p=java-idp-oidc.git;a=commit;h=08ec1b4a28e684ddb3ef34c1fd6ed946367bc08f
commit 08ec1b4a28e684ddb3ef34c1fd6ed946367bc08f
Author: Henri Mikkonen <henri.mikkonen at iki.fi>
AuthorDate: Sat Oct 12 16:02:28 2024 +0300
JOIDC-201 - Support for OAuth 2.0 Demonstrating Proof of Possession (DPoP)
https://shibboleth.atlassian.net/browse/JOIDC-201
- Switched DPoP nonces to be tied to the relying party
---
.../claims/impl/DPoPProofNonceClaimsValidator.java | 36 +++++++++++++++++-
.../impl/DefaultDPoPProofNonceGenerator.java | 30 +++++++++++++--
.../op/profile/flow/AbstractOidcApiFlowTest.java | 5 ++-
.../AbstractOidcClientAuthenticationFlowTest.java | 43 +++++++++++++++-------
.../op/profile/flow/IntrospectionFlowTest.java | 2 +-
.../op/profile/flow/PushedAuthorizeFlowTest.java | 4 +-
.../oidc/op/profile/flow/RevocationFlowTest.java | 2 +-
.../plugin/oidc/op/profile/flow/TokenFlowTest.java | 30 +++++++--------
.../plugin/oidc/op/profile/flow/UserInfoTest.java | 41 +++++++++++++++------
9 files changed, 141 insertions(+), 52 deletions(-)
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/security/jwt/claims/impl/DPoPProofNonceClaimsValidator.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/security/jwt/claims/impl/DPoPProofNonceClaimsValidator.java
index c97179e5..85fb559b 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/security/jwt/claims/impl/DPoPProofNonceClaimsValidator.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/security/jwt/claims/impl/DPoPProofNonceClaimsValidator.java
@@ -33,6 +33,7 @@ import com.nimbusds.jwt.JWTClaimsSet;
import net.shibboleth.oidc.jwt.claims.AbstractClaimsValidator;
import net.shibboleth.oidc.jwt.claims.ClaimsValidator;
import net.shibboleth.oidc.profile.config.navigate.DPoPProofNonceGeneratorLookupFunction;
+import net.shibboleth.profile.context.navigate.RelyingPartyIdLookupFunction;
import net.shibboleth.shared.annotation.ParameterName;
import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
import net.shibboleth.shared.annotation.constraint.ThreadSafeAfterInit;
@@ -67,6 +68,9 @@ public class DPoPProofNonceClaimsValidator extends AbstractClaimsValidator {
@Nonnull private Function<ProfileRequestContext, Function<ProfileRequestContext, String>>
nonceGeneratorLookupStrategy;
+ /** Strategy used to obtain the relying party ID. */
+ @Nonnull private Function<ProfileRequestContext,String> relyingPartyIdLookupStrategy;
+
/**
* Constructor.
*
@@ -75,6 +79,7 @@ public class DPoPProofNonceClaimsValidator extends AbstractClaimsValidator {
public DPoPProofNonceClaimsValidator(@Nonnull @ParameterName(name = "sealer") final DataSealer sealer) {
dataSealer = Constraint.isNotNull(sealer, "Data sealer cannot be null");
nonceGeneratorLookupStrategy = new DPoPProofNonceGeneratorLookupFunction();
+ relyingPartyIdLookupStrategy = new RelyingPartyIdLookupFunction();
}
/**
@@ -83,7 +88,7 @@ public class DPoPProofNonceClaimsValidator extends AbstractClaimsValidator {
* @param mapper object mapper
*/
public void setObjectMapper(@Nonnull final ObjectMapper mapper) {
- ifInitializedThrowUnmodifiabledComponentException();
+ checkSetterPreconditions();
objectMapper = Constraint.isNotNull(mapper, "Object mapper cannot be null");
}
@@ -95,9 +100,23 @@ public class DPoPProofNonceClaimsValidator extends AbstractClaimsValidator {
*/
public void setDpopProofNonceGeneratorLookupStrategy(
@Nonnull final Function<ProfileRequestContext,Function<ProfileRequestContext,String>> strategy) {
+ checkSetterPreconditions();
+
nonceGeneratorLookupStrategy = Constraint.isNotNull(strategy, "Lookup strategy cannot be null");
}
+ /**
+ * Set the strategy used to obtain the relying party ID.
+ *
+ * @param strategy lookup strategy
+ */
+ public void setRelyingPartyIdLookupStrategy(@Nonnull final Function<ProfileRequestContext,String> strategy) {
+ checkSetterPreconditions();
+
+ relyingPartyIdLookupStrategy = Constraint.isNotNull(strategy,
+ "Relying party ID lookup strategy cannot be null");
+ }
+
/** {@inheritDoc} */
protected void doInitialize() throws ComponentInitializationException {
super.doInitialize();
@@ -126,7 +145,20 @@ public class DPoPProofNonceClaimsValidator extends AbstractClaimsValidator {
final Map<String, Object> map = objectMapper.readValue(unwrapped, mapType);
final Instant nonceExp = Instant.ofEpochMilli(Long.parseLong((String) map.get("exp")));
if (Instant.now().isAfter(nonceExp)) {
- throw new DPoPProofNonceJWTValidationException("Value is expired");
+ throw new DPoPProofNonceJWTValidationException("Value is expired");
+ }
+ final String rpIdClaim = (String) map.get("rpId");
+ if (rpIdClaim == null) {
+ throw new DPoPProofNonceJWTValidationException("Relying party ID cannot be resolved from nonce");
+ }
+ final String rpId = relyingPartyIdLookupStrategy.apply(context);
+ if (rpId == null) {
+ log.error("Relying party ID cannot be resolved");
+ throw new DPoPProofNonceJWTValidationException("Relying party ID cannot be resolved");
+ }
+ if (!rpIdClaim.equals(rpId)) {
+ log.warn("Relying party IDs don't match: resolved '{}', nonce contained '{}'", rpId, rpIdClaim);
+ throw new DPoPProofNonceJWTValidationException("Relying party IDs don't match");
}
} catch (final DataSealerException e) {
log.trace("Could not unwrap the nonce data", e);
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/security/jwt/claims/impl/DefaultDPoPProofNonceGenerator.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/security/jwt/claims/impl/DefaultDPoPProofNonceGenerator.java
index 53e4aed9..917eab54 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/security/jwt/claims/impl/DefaultDPoPProofNonceGenerator.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/security/jwt/claims/impl/DefaultDPoPProofNonceGenerator.java
@@ -28,6 +28,7 @@ import org.slf4j.Logger;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
+import net.shibboleth.profile.context.navigate.RelyingPartyIdLookupFunction;
import net.shibboleth.shared.annotation.ParameterName;
import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
import net.shibboleth.shared.annotation.constraint.ThreadSafeAfterInit;
@@ -64,6 +65,9 @@ public class DefaultDPoPProofNonceGenerator extends AbstractIdentifiableInitiali
/** Lookup function to supply identifier generation strategy to use. */
@Nonnull private Function<ProfileRequestContext, IdentifierGenerationStrategy> idGeneratorLookupStrategy;
+ /** Strategy used to obtain the relying party ID. */
+ @Nonnull private Function<ProfileRequestContext,String> relyingPartyIdLookupStrategy;
+
/** Nonce lifetime. */
@Nonnull private Duration nonceLifetime;
@@ -79,6 +83,7 @@ public class DefaultDPoPProofNonceGenerator extends AbstractIdentifiableInitiali
assert fiveMins != null;
nonceLifetime = fiveMins;
idGeneratorLookupStrategy = FunctionSupport.constant(new SecureRandomIdentifierGenerationStrategy());
+ relyingPartyIdLookupStrategy = new RelyingPartyIdLookupFunction();
}
/**
@@ -87,7 +92,7 @@ public class DefaultDPoPProofNonceGenerator extends AbstractIdentifiableInitiali
* @param mapper object mapper
*/
public void setObjectMapper(@Nonnull final ObjectMapper mapper) {
- ifInitializedThrowUnmodifiabledComponentException();
+ checkSetterPreconditions();
objectMapper = Constraint.isNotNull(mapper, "Object mapper cannot be null");
}
@@ -98,7 +103,7 @@ public class DefaultDPoPProofNonceGenerator extends AbstractIdentifiableInitiali
* @param duration nonce lifetime
*/
public void setNonceLifetime(@Nonnull final Duration duration) {
- ifInitializedThrowUnmodifiabledComponentException();
+ checkSetterPreconditions();
Constraint.isTrue(!duration.isZero() && !duration.isNegative(), "Nonce lifetime must be greater than 0");
nonceLifetime = duration;
@@ -111,12 +116,24 @@ public class DefaultDPoPProofNonceGenerator extends AbstractIdentifiableInitiali
*/
public void setIdentifierGeneratorLookupStrategy(
@Nonnull final Function<ProfileRequestContext,IdentifierGenerationStrategy> strategy) {
- ifInitializedThrowUnmodifiabledComponentException();
+ checkSetterPreconditions();
idGeneratorLookupStrategy =
Constraint.isNotNull(strategy, "Identifier generation strategy cannot be null");
}
+ /**
+ * Set the strategy used to obtain the relying party ID.
+ *
+ * @param strategy lookup strategy
+ */
+ public void setRelyingPartyIdLookupStrategy(@Nonnull final Function<ProfileRequestContext,String> strategy) {
+ checkSetterPreconditions();
+
+ relyingPartyIdLookupStrategy = Constraint.isNotNull(strategy,
+ "Relying party ID lookup strategy cannot be null");
+ }
+
/** {@inheritDoc} */
protected void doInitialize() throws ComponentInitializationException {
super.doInitialize();
@@ -133,9 +150,14 @@ public class DefaultDPoPProofNonceGenerator extends AbstractIdentifiableInitiali
log.error("Could not resolve idGenerator");
return null;
}
+ final String rpId = relyingPartyIdLookupStrategy.apply(profileRequestContext);
+ if (rpId == null) {
+ log.error("Could not resolve relying party ID");
+ return null;
+ }
final String id = idGenerator.generateIdentifier(false);
final Instant nonceExp = Instant.now().plus(nonceLifetime);
- final Map<String, Object> map = Map.of("jti", id, "exp", "" + nonceExp.toEpochMilli());
+ final Map<String, Object> map = Map.of("jti", id, "exp", "" + nonceExp.toEpochMilli(), "rpId", rpId);
try {
final String raw = objectMapper.writeValueAsString(map);
if (raw != null) {
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/AbstractOidcApiFlowTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/AbstractOidcApiFlowTest.java
index 0d1b38b7..b938522d 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/AbstractOidcApiFlowTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/AbstractOidcApiFlowTest.java
@@ -203,10 +203,11 @@ public class AbstractOidcApiFlowTest extends AbstractOidcFlowTest {
throw new JOSEException("Unsupported algorithm " + jwsAlgorithm.getName());
}
- protected String createValidDPoPNonce() {
+ protected String createValidDPoPNonce(final String rpId) {
final Instant exp = Instant.now().plusSeconds(300);
try {
- return getDataSealer().wrap("{\"exp\":\"" + exp.toEpochMilli() + "\",\"jti\":\"mock\"}");
+ return getDataSealer().wrap("{\"exp\":\"" + exp.toEpochMilli() + "\",\"jti\":\"mock\",\"rpId\":\""
+ + rpId + "\"}");
} catch (DataSealerException e) {
Assert.fail("Could not create nonce", e);
}
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/AbstractOidcClientAuthenticationFlowTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/AbstractOidcClientAuthenticationFlowTest.java
index 96bf48ae..07687665 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/AbstractOidcClientAuthenticationFlowTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/AbstractOidcClientAuthenticationFlowTest.java
@@ -670,6 +670,21 @@ public abstract class AbstractOidcClientAuthenticationFlowTest extends AbstractO
}
}
+ @SuppressWarnings("null")
+ @Test
+ public void testInvalidDPoPProof_invalidNonceRpId() throws Exception {
+ for (final String id : clientIds) {
+ request.removeHeader("DPoP");
+ final SignedJWT jwt = createSecretJWT(validClaimsSet(id, "http://localhost/idp/profile/oidc/token"),
+ clientSecret, JWSAlgorithm.HS256);
+ request.addHeader("DPoP", buildDPoPProof(defaultDPoPProofKey(), JWSAlgorithm.ES256, "POST", jwtAud,
+ null, createValidDPoPNonce("wrong" + id)).serialize());
+ final FlowExecutionResult result = launchWithJwtAuthentication(jwt, null,
+ ClientAuthenticationMethod.CLIENT_SECRET_JWT);
+ assertErrorCode(result, OAuth2Error.USE_DPOP_NONCE_CODE);
+ }
+ }
+
@Test
public void testInvalidDPoPProof_unsignedProof() throws Exception {
for (final String id : clientIds) {
@@ -678,7 +693,7 @@ public abstract class AbstractOidcClientAuthenticationFlowTest extends AbstractO
clientSecret, JWSAlgorithm.HS256);
final ECKey jwk = defaultDPoPProofKey();
final PlainJWT dpopProof = buildUnsignedDPoPProof(jwk, "POST", jwtAud,
- null, createValidDPoPNonce());
+ null, createValidDPoPNonce(id));
final String headerValue = dpopProof.serialize(); assert headerValue != null;
request.addHeader("DPoP", headerValue);
final FlowExecutionResult result = launchWithJwtAuthentication(jwt, null,
@@ -702,7 +717,7 @@ public abstract class AbstractOidcClientAuthenticationFlowTest extends AbstractO
.jwtID(idGenerator.generateIdentifier())
.claim("htu", jwtAud)
.claim("htm", "POST")
- .claim("nonce", createValidDPoPNonce())
+ .claim("nonce", createValidDPoPNonce(id))
.issueTime(new Date())
.build();
final SignedJWT dpopProof = buildDPoPProof(header, claimsSet, jwk.toECPrivateKey());
@@ -729,7 +744,7 @@ public abstract class AbstractOidcClientAuthenticationFlowTest extends AbstractO
.jwtID(idGenerator.generateIdentifier())
.claim("htu", jwtAud)
.claim("htm", "POST")
- .claim("nonce", createValidDPoPNonce())
+ .claim("nonce", createValidDPoPNonce(id))
.build();
final SignedJWT dpopProof = buildDPoPProof(header, claimsSet, jwk.toECPrivateKey());
final String headerValue = dpopProof.serialize(); assert headerValue != null;
@@ -755,7 +770,7 @@ public abstract class AbstractOidcClientAuthenticationFlowTest extends AbstractO
.jwtID(idGenerator.generateIdentifier())
.claim("htu", jwtAud)
.claim("htm", "POST")
- .claim("nonce", createValidDPoPNonce())
+ .claim("nonce", createValidDPoPNonce(id))
.issueTime(Date.from(Instant.now().minusSeconds(600)))
.build();
final SignedJWT dpopProof = buildDPoPProof(header, claimsSet, jwk.toECPrivateKey());
@@ -782,7 +797,7 @@ public abstract class AbstractOidcClientAuthenticationFlowTest extends AbstractO
.jwtID(idGenerator.generateIdentifier())
.claim("htu", jwtAud)
.claim("htm", "POST")
- .claim("nonce", createValidDPoPNonce())
+ .claim("nonce", createValidDPoPNonce(id))
.issueTime(Date.from(Instant.now().plusSeconds(600)))
.build();
final SignedJWT dpopProof = buildDPoPProof(header, claimsSet, jwk.toECPrivateKey());
@@ -809,7 +824,7 @@ public abstract class AbstractOidcClientAuthenticationFlowTest extends AbstractO
.jwtID(idGenerator.generateIdentifier())
.claim("htu", jwtAud)
.claim("htm", "POST")
- .claim("nonce", createValidDPoPNonce())
+ .claim("nonce", createValidDPoPNonce(id))
.issueTime(Date.from(Instant.now()))
.build();
final SignedJWT dpopProof = buildDPoPProof(header, claimsSet, jwk.toECPrivateKey());
@@ -836,7 +851,7 @@ public abstract class AbstractOidcClientAuthenticationFlowTest extends AbstractO
final JWTClaimsSet claimsSet = new JWTClaimsSet.Builder()
.jwtID(idGenerator.generateIdentifier())
.claim("htm", "POST")
- .claim("nonce", createValidDPoPNonce())
+ .claim("nonce", createValidDPoPNonce(id))
.issueTime(Date.from(Instant.now()))
.build();
final SignedJWT dpopProof = buildDPoPProof(header, claimsSet, jwk.toECPrivateKey());
@@ -863,7 +878,7 @@ public abstract class AbstractOidcClientAuthenticationFlowTest extends AbstractO
.jwtID(idGenerator.generateIdentifier())
.claim("htu", jwtAud + "2")
.claim("htm", "POST")
- .claim("nonce", createValidDPoPNonce())
+ .claim("nonce", createValidDPoPNonce(id))
.issueTime(Date.from(Instant.now()))
.build();
final SignedJWT dpopProof = buildDPoPProof(header, claimsSet, jwk.toECPrivateKey());
@@ -889,7 +904,7 @@ public abstract class AbstractOidcClientAuthenticationFlowTest extends AbstractO
final JWTClaimsSet claimsSet = new JWTClaimsSet.Builder()
.jwtID(idGenerator.generateIdentifier())
.claim("htu", jwtAud)
- .claim("nonce", createValidDPoPNonce())
+ .claim("nonce", createValidDPoPNonce(id))
.issueTime(Date.from(Instant.now()))
.build();
final SignedJWT dpopProof = buildDPoPProof(header, claimsSet, jwk.toECPrivateKey());
@@ -916,7 +931,7 @@ public abstract class AbstractOidcClientAuthenticationFlowTest extends AbstractO
.jwtID(idGenerator.generateIdentifier())
.claim("htu", jwtAud)
.claim("htm", "GET")
- .claim("nonce", createValidDPoPNonce())
+ .claim("nonce", createValidDPoPNonce(id))
.issueTime(Date.from(Instant.now()))
.build();
final SignedJWT dpopProof = buildDPoPProof(header, claimsSet, jwk.toECPrivateKey());
@@ -942,7 +957,7 @@ public abstract class AbstractOidcClientAuthenticationFlowTest extends AbstractO
final JWTClaimsSet claimsSet = new JWTClaimsSet.Builder()
.claim("htu", jwtAud)
.claim("htm", "POST")
- .claim("nonce", createValidDPoPNonce())
+ .claim("nonce", createValidDPoPNonce(id))
.issueTime(Date.from(Instant.now()))
.build();
final SignedJWT dpopProof = buildDPoPProof(header, claimsSet, jwk.toECPrivateKey());
@@ -972,7 +987,7 @@ public abstract class AbstractOidcClientAuthenticationFlowTest extends AbstractO
.jwtID(idGenerator.generateIdentifier())
.claim("htu", jwtAud)
.claim("htm", "POST")
- .claim("nonce", createValidDPoPNonce())
+ .claim("nonce", createValidDPoPNonce(id))
.issueTime(Date.from(Instant.now()))
.build();
final SignedJWT dpopProof = buildDPoPProof(header, claimsSet, jwk.toECPrivateKey());
@@ -1003,7 +1018,7 @@ public abstract class AbstractOidcClientAuthenticationFlowTest extends AbstractO
.jwtID(idGenerator.generateIdentifier())
.claim("htu", jwtAud)
.claim("htm", "POST")
- .claim("nonce", createValidDPoPNonce())
+ .claim("nonce", createValidDPoPNonce(id))
.issueTime(Date.from(Instant.now()))
.claim("ath", "invalidContents")
.build();
@@ -1035,7 +1050,7 @@ public abstract class AbstractOidcClientAuthenticationFlowTest extends AbstractO
.jwtID(idGenerator.generateIdentifier())
.claim("htu", jwtAud)
.claim("htm", "POST")
- .claim("nonce", createValidDPoPNonce())
+ .claim("nonce", createValidDPoPNonce(id))
.issueTime(Date.from(Instant.now()))
.build();
final SignedJWT dpopProof = buildDPoPProof(header, claimsSet, jwk.toECPrivateKey());
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/IntrospectionFlowTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/IntrospectionFlowTest.java
index 518cdc4a..f727fe7a 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/IntrospectionFlowTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/IntrospectionFlowTest.java
@@ -264,7 +264,7 @@ public class IntrospectionFlowTest extends AbstractOidcClientAuthenticationFlowT
final DPoPAccessToken token = new DPoPAccessToken(claims.serialize(getDataSealer()));
final SignedJWT dpopProof = buildDPoPProof(dpopProofKey, JWSAlgorithm.ES256, "POST",
- "http://localhost/idp/profile/oauth2/introspection", token, createValidDPoPNonce());
+ "http://localhost/idp/profile/oauth2/introspection", token, createValidDPoPNonce(clientId));
request.addHeader("DPoP", dpopProof.serialize());
setBasicAuth(clientId, clientSecret);
storeMetadata(storageService, clientId, clientSecret, scope);
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/PushedAuthorizeFlowTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/PushedAuthorizeFlowTest.java
index a6e13577..966ad550 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/PushedAuthorizeFlowTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/PushedAuthorizeFlowTest.java
@@ -284,7 +284,7 @@ public class PushedAuthorizeFlowTest extends AbstractOidcClientAuthenticationFlo
storeMetadata(storageService, clientId, clientSecret, scope, "https://example.org/cb");
setBasicAuth(clientId, clientSecret);
request.addHeader("DPoP", buildDPoPProof("POST", "http://localhost/idp/profile/oauth2/pushed-authorization",
- createValidDPoPNonce()).serialize());
+ createValidDPoPNonce(clientId)).serialize());
Map<String, String> requestParameters = createRequestParameters(clientId);
requestParameters.put("dpop_jkt", "notMatching");
setHttpFormRequest("POST", requestParameters);
@@ -310,7 +310,7 @@ public class PushedAuthorizeFlowTest extends AbstractOidcClientAuthenticationFlo
storeMetadata(storageService, clientId, clientSecret, scope, "https://example.org/cb");
setBasicAuth(clientId, clientSecret);
request.addHeader("DPoP", buildDPoPProof("POST", "http://localhost/idp/profile/oauth2/pushed-authorization",
- createValidDPoPNonce()).serialize());
+ createValidDPoPNonce(clientId)).serialize());
setHttpFormRequest("POST", createRequestParameters(clientId));
final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
assertSuccessResponse(result, clientId);
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/RevocationFlowTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/RevocationFlowTest.java
index 20f12031..1a4a15a8 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/RevocationFlowTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/RevocationFlowTest.java
@@ -199,7 +199,7 @@ public class RevocationFlowTest extends AbstractOidcClientAuthenticationFlowTest
final DPoPAccessToken token = new DPoPAccessToken(claims.serialize(getDataSealer()));
final SignedJWT dpopProof = buildDPoPProof(dpopProofKey, JWSAlgorithm.ES256, "POST",
- "http://localhost/idp/profile/oauth2/revocation", token, createValidDPoPNonce());
+ "http://localhost/idp/profile/oauth2/revocation", token, createValidDPoPNonce(clientId));
request.addHeader("DPoP", dpopProof.serialize());
setBasicAuth(clientId, clientSecret);
storeMetadata(storageService, clientId, clientSecret, scope);
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/TokenFlowTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/TokenFlowTest.java
index 46321966..09c30c48 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/TokenFlowTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/TokenFlowTest.java
@@ -796,7 +796,7 @@ public class TokenFlowTest extends AbstractOidcClientAuthenticationFlowTest {
.setDpopProofJwkThumbprint("mockJkt");
final String authorizationCode = builder.build().serialize(getDataSealer());
request.addHeader("DPoP", buildDPoPProof("POST", "http://localhost/idp/profile/oidc/token",
- createValidDPoPNonce()).serialize());
+ createValidDPoPNonce(clientId)).serialize());
initializeGrantAndRequest(clientId, createRequestParameters(redirectUri,
"authorization_code",
@@ -810,7 +810,7 @@ public class TokenFlowTest extends AbstractOidcClientAuthenticationFlowTest {
public void testDPoPValidGrantThumbprintIncludedMatchingProof() throws Exception {
final AuthorizeCodeClaimsSet.Builder builder = new AuthorizeCodeClaimsSet.Builder();
final SignedJWT dpopProof = buildDPoPProof("POST", "http://localhost/idp/profile/oidc/token",
- createValidDPoPNonce());
+ createValidDPoPNonce(clientId));
builder.setJWTID(new SecureRandomIdentifierGenerationStrategy())
.setClientID(new ClientID(clientId))
.setIssuer("https://op.example.org")
@@ -842,7 +842,7 @@ public class TokenFlowTest extends AbstractOidcClientAuthenticationFlowTest {
final String clientId = clientIdAlwaysBearerAccessToken;
final AuthorizeCodeClaimsSet.Builder builder = new AuthorizeCodeClaimsSet.Builder();
final SignedJWT dpopProof = buildDPoPProof("POST", "http://localhost/idp/profile/oidc/token",
- createValidDPoPNonce());
+ createValidDPoPNonce(clientId));
final String jkt = dpopProof.getHeader().getJWK().computeThumbprint().toString();
builder.setJWTID(new SecureRandomIdentifierGenerationStrategy())
.setClientID(new ClientID(clientId))
@@ -883,7 +883,7 @@ public class TokenFlowTest extends AbstractOidcClientAuthenticationFlowTest {
final String clientId = clientIdPkcePlainPublic;
final AuthorizeCodeClaimsSet.Builder builder = new AuthorizeCodeClaimsSet.Builder();
final SignedJWT dpopProof = buildDPoPProof("POST", "http://localhost/idp/profile/oidc/token",
- createValidDPoPNonce());
+ createValidDPoPNonce(clientId));
builder.setJWTID(new SecureRandomIdentifierGenerationStrategy())
.setClientID(new ClientID(clientId))
.setIssuer("https://op.example.org")
@@ -916,7 +916,7 @@ public class TokenFlowTest extends AbstractOidcClientAuthenticationFlowTest {
final String clientId = clientIdPkcePlainPublic;
final RefreshTokenClaimsSet.Builder builder = new RefreshTokenClaimsSet.Builder();
final SignedJWT dpopProof = buildDPoPProof("POST", "http://localhost/idp/profile/oidc/token",
- createValidDPoPNonce());
+ createValidDPoPNonce(clientId));
builder.setJWTID(new SecureRandomIdentifierGenerationStrategy())
.setClientID(new ClientID(clientId))
.setIssuer("https://op.example.org")
@@ -947,7 +947,7 @@ public class TokenFlowTest extends AbstractOidcClientAuthenticationFlowTest {
final String clientId = clientIdPkcePlainPublic;
final RefreshTokenClaimsSet.Builder builder = new RefreshTokenClaimsSet.Builder();
final SignedJWT dpopProof = buildDPoPProof("POST", "http://localhost/idp/profile/oidc/token",
- createValidDPoPNonce());
+ createValidDPoPNonce(clientId));
builder.setJWTID(new SecureRandomIdentifierGenerationStrategy())
.setClientID(new ClientID(clientId))
.setIssuer("https://op.example.org")
@@ -973,7 +973,7 @@ public class TokenFlowTest extends AbstractOidcClientAuthenticationFlowTest {
public void testDPoPValidGrantThumbprintIncludedNonMatchingProof_confidentialClientRefresh() throws Exception {
final RefreshTokenClaimsSet.Builder builder = new RefreshTokenClaimsSet.Builder();
final SignedJWT dpopProof = buildDPoPProof("POST", "http://localhost/idp/profile/oidc/token",
- createValidDPoPNonce());
+ createValidDPoPNonce(clientId));
builder.setJWTID(new SecureRandomIdentifierGenerationStrategy())
.setClientID(new ClientID(clientId))
.setIssuer("https://op.example.org")
@@ -1014,7 +1014,7 @@ public class TokenFlowTest extends AbstractOidcClientAuthenticationFlowTest {
.setScope(scope);
final String authorizationCode = builder.build().serialize(getDataSealer());
request.addHeader("DPoP", buildDPoPProof("POST", "http://localhost/idp/profile/oidc/token",
- createValidDPoPNonce()).serialize());
+ createValidDPoPNonce(clientId)).serialize());
initializeGrantAndRequest(clientId, createRequestParameters(redirectUri,
"authorization_code",
@@ -1096,7 +1096,7 @@ public class TokenFlowTest extends AbstractOidcClientAuthenticationFlowTest {
.setDpopProofJwkThumbprint("mockJkt");
final String authorizationCode = builder.build().serialize(getDataSealer());
request.addHeader("DPoP", buildDPoPProof("POST", "http://localhost/idp/profile/oidc/token",
- createValidDPoPNonce()).serialize());
+ createValidDPoPNonce(clientId)).serialize());
initializeGrantAndRequest(clientId, createRequestParameters(redirectUri,
"authorization_code",
@@ -1111,7 +1111,7 @@ public class TokenFlowTest extends AbstractOidcClientAuthenticationFlowTest {
final String clientId = clientIdDPoPAccessToken;
final AuthorizeCodeClaimsSet.Builder builder = new AuthorizeCodeClaimsSet.Builder();
final SignedJWT dpopProof = buildDPoPProof("POST", "http://localhost/idp/profile/oidc/token",
- createValidDPoPNonce());
+ createValidDPoPNonce(clientId));
builder.setJWTID(new SecureRandomIdentifierGenerationStrategy())
.setClientID(new ClientID(clientId))
.setIssuer("https://op.example.org")
@@ -1148,7 +1148,7 @@ public class TokenFlowTest extends AbstractOidcClientAuthenticationFlowTest {
final String clientId = clientIdDPoPAccessToken;
final AuthorizeCodeClaimsSet.Builder builder = new AuthorizeCodeClaimsSet.Builder();
final SignedJWT dpopProof = buildDPoPProof("POST", "http://localhost/idp/profile/oidc/token",
- createValidDPoPNonce());
+ createValidDPoPNonce(clientId));
builder.setJWTID(new SecureRandomIdentifierGenerationStrategy())
.setClientID(new ClientID(clientId))
.setIssuer("https://op.example.org")
@@ -1219,7 +1219,7 @@ public class TokenFlowTest extends AbstractOidcClientAuthenticationFlowTest {
.setDpopProofJwkThumbprint("mockJkt");
final String authorizationCode = builder.build().serialize(getDataSealer());
request.addHeader("DPoP", buildDPoPProof("POST", "http://localhost/idp/profile/oidc/token",
- createValidDPoPNonce()).serialize());
+ createValidDPoPNonce(clientId)).serialize());
initializeGrantAndRequest(clientId, createRequestParameters(redirectUri,
"authorization_code",
@@ -1261,7 +1261,7 @@ public class TokenFlowTest extends AbstractOidcClientAuthenticationFlowTest {
final String clientId = clientIdDPoPJwtAccessToken;
final AuthorizeCodeClaimsSet.Builder builder = new AuthorizeCodeClaimsSet.Builder();
final SignedJWT dpopProof = buildDPoPProof("POST", "http://localhost/idp/profile/oidc/token",
- createValidDPoPNonce());
+ createValidDPoPNonce(clientId));
builder.setJWTID(new SecureRandomIdentifierGenerationStrategy())
.setClientID(new ClientID(clientId))
.setIssuer("https://op.example.org")
@@ -1298,7 +1298,7 @@ public class TokenFlowTest extends AbstractOidcClientAuthenticationFlowTest {
final String clientId = clientIdDPoPJwtBearerAccessToken;
final AuthorizeCodeClaimsSet.Builder builder = new AuthorizeCodeClaimsSet.Builder();
final SignedJWT dpopProof = buildDPoPProof("POST", "http://localhost/idp/profile/oidc/token",
- createValidDPoPNonce());
+ createValidDPoPNonce(clientId));
builder.setJWTID(new SecureRandomIdentifierGenerationStrategy())
.setClientID(new ClientID(clientId))
.setIssuer("https://op.example.org")
@@ -1337,7 +1337,7 @@ public class TokenFlowTest extends AbstractOidcClientAuthenticationFlowTest {
final String clientId = clientIdDPoPJwtAccessToken;
final AuthorizeCodeClaimsSet.Builder builder = new AuthorizeCodeClaimsSet.Builder();
final SignedJWT dpopProof = buildDPoPProof("POST", "http://localhost/idp/profile/oidc/token",
- createValidDPoPNonce());
+ createValidDPoPNonce(clientId));
builder.setJWTID(new SecureRandomIdentifierGenerationStrategy())
.setClientID(new ClientID(clientId))
.setIssuer("https://op.example.org")
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/UserInfoTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/UserInfoTest.java
index c44c4714..de743304 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/UserInfoTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/UserInfoTest.java
@@ -290,7 +290,7 @@ public class UserInfoTest extends AbstractOidcApiFlowTest {
final AccessTokenClaimsSet claims = buildDPoPAccessTokenClaimsSet("mockId");
final DPoPAccessToken token = new DPoPAccessToken(claims.serialize(getDataSealer()));
request.addHeader("DPoP", buildDPoPProof(request.getMethod(), "http://localhost/idp/profile/oidc/userinfo",
- createValidDPoPNonce()).serialize());
+ createValidDPoPNonce(clientId)).serialize());
storeMetadata(storageService, clientId, "mockSecret", scope);
request.addHeader("Authorization", getTokenHeaderValue(token));
@@ -306,7 +306,7 @@ public class UserInfoTest extends AbstractOidcApiFlowTest {
final AccessTokenClaimsSet claims = buildDPoPAccessTokenClaimsSet("mockId");
final DPoPAccessToken token = buildJWTDPoPToken(claims, signingKey.getPrivateKey(), "RS256");
request.addHeader("DPoP", buildDPoPProof(request.getMethod(), "http://localhost/idp/profile/oidc/userinfo",
- createValidDPoPNonce()).serialize());
+ createValidDPoPNonce(clientId)).serialize());
storeMetadata(storageService, clientId, "mockSecret", scope);
request.addHeader("Authorization", getTokenHeaderValue(token));
@@ -319,7 +319,7 @@ public class UserInfoTest extends AbstractOidcApiFlowTest {
public void testFailWithDPoP_noAth() throws URISyntaxException, NoSuchAlgorithmException, DataSealerException,
ComponentInitializationException, IOException, com.nimbusds.oauth2.sdk.ParseException, JOSEException {
final SignedJWT dpopProof = buildDPoPProof(request.getMethod(), "http://localhost/idp/profile/oidc/userinfo",
- createValidDPoPNonce());
+ createValidDPoPNonce(clientId));
final AccessTokenClaimsSet claims =
buildDPoPAccessTokenClaimsSet((dpopProof.getHeader().getJWK().computeThumbprint().toString()));
@@ -337,7 +337,7 @@ public class UserInfoTest extends AbstractOidcApiFlowTest {
public void testFailWithDPoP_nonMatchingAth() throws URISyntaxException, NoSuchAlgorithmException, DataSealerException,
ComponentInitializationException, IOException, com.nimbusds.oauth2.sdk.ParseException, JOSEException {
final SignedJWT dpopProof = buildDPoPProof(request.getMethod(), "http://localhost/idp/profile/oidc/userinfo",
- new DPoPAccessToken("mock"), createValidDPoPNonce());
+ new DPoPAccessToken("mock"), createValidDPoPNonce(clientId));
final AccessTokenClaimsSet claims =
buildDPoPAccessTokenClaimsSet((dpopProof.getHeader().getJWK().computeThumbprint().toString()));
@@ -369,6 +369,25 @@ public class UserInfoTest extends AbstractOidcApiFlowTest {
assertErrorCode(result, OAuth2Error.USE_DPOP_NONCE_CODE);
}
+ @SuppressWarnings("null")
+ @Test
+ public void testFailWithDPoP_invlidNonceRpId() throws URISyntaxException, NoSuchAlgorithmException, DataSealerException,
+ ComponentInitializationException, IOException, com.nimbusds.oauth2.sdk.ParseException, JOSEException {
+ final ECKey dpopProofKey = defaultDPoPProofKey();
+ final AccessTokenClaimsSet claims =
+ buildDPoPAccessTokenClaimsSet(dpopProofKey.computeThumbprint().toString());
+
+ final DPoPAccessToken token = new DPoPAccessToken(claims.serialize(getDataSealer()));
+ final SignedJWT dpopProof = buildDPoPProof(dpopProofKey, JWSAlgorithm.ES256, request.getMethod(),
+ "http://localhost/idp/profile/oidc/userinfo", token, createValidDPoPNonce("wrongId"));
+ request.addHeader("DPoP", dpopProof.serialize());
+
+ storeMetadata(storageService, clientId, "mockSecret", scope);
+ request.addHeader("Authorization", getTokenHeaderValue(token));
+ final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
+ assertErrorCode(result, OAuth2Error.USE_DPOP_NONCE_CODE);
+ }
+
@SuppressWarnings("null")
@Test
public void testSuccessOnlySubjectWithDPoP() throws URISyntaxException, NoSuchAlgorithmException, DataSealerException,
@@ -379,7 +398,7 @@ public class UserInfoTest extends AbstractOidcApiFlowTest {
final DPoPAccessToken token = new DPoPAccessToken(claims.serialize(getDataSealer()));
final SignedJWT dpopProof = buildDPoPProof(dpopProofKey, JWSAlgorithm.ES256, request.getMethod(),
- "http://localhost/idp/profile/oidc/userinfo", token, createValidDPoPNonce());
+ "http://localhost/idp/profile/oidc/userinfo", token, createValidDPoPNonce(clientId));
request.addHeader("DPoP", dpopProof.serialize());
storeMetadata(storageService, clientId, "mockSecret", scope);
@@ -411,7 +430,7 @@ public class UserInfoTest extends AbstractOidcApiFlowTest {
public void testFailWithJWTDPoP_noAth() throws URISyntaxException, NoSuchAlgorithmException, DataSealerException,
ComponentInitializationException, IOException, com.nimbusds.oauth2.sdk.ParseException, JOSEException {
final SignedJWT dpopProof = buildDPoPProof(request.getMethod(), "http://localhost/idp/profile/oidc/userinfo",
- createValidDPoPNonce());
+ createValidDPoPNonce(clientId));
final AccessTokenClaimsSet claims =
buildDPoPAccessTokenClaimsSet((dpopProof.getHeader().getJWK().computeThumbprint().toString()));
@@ -429,7 +448,7 @@ public class UserInfoTest extends AbstractOidcApiFlowTest {
public void testFailWithJWTDPoP_nonMatchingAth() throws URISyntaxException, NoSuchAlgorithmException, DataSealerException,
ComponentInitializationException, IOException, com.nimbusds.oauth2.sdk.ParseException, JOSEException {
final SignedJWT dpopProof = buildDPoPProof(request.getMethod(), "http://localhost/idp/profile/oidc/userinfo",
- new DPoPAccessToken("mock"), createValidDPoPNonce());
+ new DPoPAccessToken("mock"), createValidDPoPNonce(clientId));
final AccessTokenClaimsSet claims =
buildDPoPAccessTokenClaimsSet((dpopProof.getHeader().getJWK().computeThumbprint().toString()));
@@ -471,7 +490,7 @@ public class UserInfoTest extends AbstractOidcApiFlowTest {
final DPoPAccessToken token = buildJWTDPoPToken(claims, signingKey.getPrivateKey(), "RS256");
final SignedJWT dpopProof = buildDPoPProof(dpopProofKey, JWSAlgorithm.ES256, "POST",
- "http://localhost/idp/profile/oidc/userinfo", token, createValidDPoPNonce());
+ "http://localhost/idp/profile/oidc/userinfo", token, createValidDPoPNonce(clientId));
request.addHeader("DPoP", dpopProof.serialize());
storeMetadata(storageService, clientId, "mockSecret", scope);
@@ -492,7 +511,7 @@ public class UserInfoTest extends AbstractOidcApiFlowTest {
final DPoPAccessToken token = buildJWTDPoPToken(claims, signingKey.getPrivateKey(), "RS256");
final SignedJWT dpopProof = buildDPoPProof(dpopProofKey, JWSAlgorithm.ES256, "GET",
- "http://localhost/idp/profile/oidc/userinfo", token, createValidDPoPNonce());
+ "http://localhost/idp/profile/oidc/userinfo", token, createValidDPoPNonce(clientId));
request.addHeader("DPoP", dpopProof.serialize());
storeMetadata(storageService, clientId, "mockSecret", scope);
@@ -511,7 +530,7 @@ public class UserInfoTest extends AbstractOidcApiFlowTest {
final DPoPAccessToken token = buildJWTDPoPToken(claims, signingKey.getPrivateKey(), "RS256");
final SignedJWT dpopProof = buildDPoPProof(dpopProofKey, JWSAlgorithm.ES256, request.getMethod(),
- "http://localhost/idp/profile/oidc/userinfo", token, createValidDPoPNonce());
+ "http://localhost/idp/profile/oidc/userinfo", token, createValidDPoPNonce(clientId));
request.addHeader("DPoP", dpopProof.serialize());
storeMetadata(storageService, clientId, "mockSecret", scope);
@@ -538,7 +557,7 @@ public class UserInfoTest extends AbstractOidcApiFlowTest {
final DPoPAccessToken token = buildJWTDPoPToken(claims, signingKey.getPrivateKey(), "RS256");
final SignedJWT dpopProof = buildDPoPProof(dpopProofKey, JWSAlgorithm.ES256, request.getMethod(),
- "http://localhost/idp/profile/oidc/userinfo", token, createValidDPoPNonce());
+ "http://localhost/idp/profile/oidc/userinfo", token, createValidDPoPNonce(clientId));
request.addHeader("DPoP", dpopProof.serialize());
storeMetadata(storageService, clientId, "mockSecret", scope);
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list