[java-idp-plugin-oidc-rp] branch main updated: Improve test construction and coverage
Phil Smart
philip.smart at jisc.ac.uk
Tue Feb 14 17:32:00 UTC 2023
This is an automated email from the git hooks/post-receive script.
philsmart pushed a commit to branch main
in repository java-idp-plugin-oidc-rp.
View the commit online:
http://git.shibboleth.net/view/?p=java-idp-plugin-oidc-rp.git;a=commit;h=4ecebdef09358d8a1a32ac06ff9717c2ec8b7308
The following commit(s) were added to refs/heads/main by this push:
new 4ecebde Improve test construction and coverage
4ecebde is described below
commit 4ecebdef09358d8a1a32ac06ff9717c2ec8b7308
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Tue Feb 14 17:31:55 2023 +0000
Improve test construction and coverage
---
.../OIDCRPFlowFromAuthenticationResponseTest.java | 138 ++++++---
.../oidc/rp/impl/OIDCRPFlowPreRedirectTest.java | 10 +-
.../plugin/authn/oidc/rp/impl/OIDCRPFlowTest.java | 37 ---
.../plugin/authn/oidc/rp/impl/TestTokenHelper.java | 343 +++++++++------------
4 files changed, 230 insertions(+), 298 deletions(-)
diff --git a/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCRPFlowFromAuthenticationResponseTest.java b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCRPFlowFromAuthenticationResponseTest.java
index c8a38b6..7bf0135 100644
--- a/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCRPFlowFromAuthenticationResponseTest.java
+++ b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCRPFlowFromAuthenticationResponseTest.java
@@ -8,13 +8,20 @@ import java.util.Map;
import org.junit.Test;
import org.opensaml.messaging.context.MessageContext;
import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.security.credential.BasicCredential;
import org.opensaml.security.credential.Credential;
import org.springframework.webflow.engine.impl.FlowExecutionImpl;
+import com.nimbusds.jose.EncryptionMethod;
import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.JWEAlgorithm;
+import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.JWSObject.State;
+import com.nimbusds.jose.jwk.Curve;
+import com.nimbusds.jose.jwk.KeyUse;
import com.nimbusds.jose.jwk.RSAKey;
+import com.nimbusds.jose.jwk.gen.ECKeyGenerator;
+import com.nimbusds.jose.jwk.gen.RSAKeyGenerator;
import com.nimbusds.jwt.SignedJWT;
import com.nimbusds.oauth2.sdk.ResponseType;
import com.nimbusds.oauth2.sdk.id.ClientID;
@@ -36,6 +43,7 @@ import net.shibboleth.oidc.profile.config.JSONSecurityConfiguration;
import net.shibboleth.oidc.profile.config.OIDCAuthorizationConfiguration;
import net.shibboleth.oidc.profile.core.OIDCAuthenticationRequest;
import net.shibboleth.oidc.security.credential.BasicJWKCredential;
+import net.shibboleth.oidc.security.credential.DefaultClientSecretCredential;
import net.shibboleth.oidc.security.credential.JOSEObjectCredentialResolver;
import net.shibboleth.oidc.security.credential.impl.BasicJOSEObjectCredentialResolver;
import net.shibboleth.oidc.security.credential.impl.ChainingJOSEObjectCredentialResolver;
@@ -43,7 +51,6 @@ import net.shibboleth.oidc.security.credential.impl.ClientSecretCriterionCredent
import net.shibboleth.oidc.security.impl.ExplicitKeySignedJWTTrustEngine;
import net.shibboleth.oidc.security.jose.impl.BasicDecryptionConfiguration;
import net.shibboleth.oidc.security.jose.impl.BasicSignatureValidationConfiguration;
-import net.shibboleth.utilities.java.support.collection.Pair;
import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
import net.shibboleth.utilities.java.support.resolver.ResolverException;
import okhttp3.mockwebserver.MockResponse;
@@ -62,7 +69,7 @@ public class OIDCRPFlowFromAuthenticationResponseTest extends OIDCRPFlowTest {
* @throws Exception on error.
*/
@Test
- public void testAuthnFlowFromAuthorizationCallback_Using_MACSignedIDToken_PlainUserInfo() throws Exception {
+ public void test_IDTokenHS256_PlainUserInfo() throws Exception {
setFlowPath(FLOW);
setFlowModelResources(flowResources);
@@ -78,12 +85,13 @@ public class OIDCRPFlowFromAuthenticationResponseTest extends OIDCRPFlowTest {
// First is token exchange
mockOPServer.enqueue(new MockResponse().setResponseCode(200)
.setHeader("content-type", "application/json")
- .setBody(TestTokenHelper.createAccessTokenResponseJSONWithHMACIDToken(
- OP_ISSUER_ID, CLIENT_ID, List.of(CLIENT_ID), CLIENT_SECRET)));
+ .setBody(TestTokenHelper.createAccessTokenResponseJSON(
+ OP_ISSUER_ID, CLIENT_ID, List.of(CLIENT_ID), JWSAlgorithm.HS256, null, null,
+ new DefaultClientSecretCredential(CLIENT_SECRET).toSigningCredential(), null)));
// Second is plain userInfo
mockOPServer.enqueue(new MockResponse().setResponseCode(200)
.setHeader("content-type", "application/json")
- .setBody(USERINFO_RESPONSE));
+ .setBody(TestTokenHelper.createPlainUserInfoResponseString(OP_ISSUER_ID, List.of(CLIENT_ID),"jdoe")));
mockOPServer.start(9918);
@@ -130,7 +138,7 @@ public class OIDCRPFlowFromAuthenticationResponseTest extends OIDCRPFlowTest {
* @throws Exception on error.
*/
@Test
- public void testAuthnFlowFromAuthorizationCallback_Using_MACSignedIDToken_InvalidPlainUserInfo() throws Exception {
+ public void test_IDTokenHS256_InvalidPlainUserInfo() throws Exception {
setFlowPath(FLOW);
setFlowModelResources(flowResources);
@@ -146,12 +154,13 @@ public class OIDCRPFlowFromAuthenticationResponseTest extends OIDCRPFlowTest {
// First is token exchange
mockOPServer.enqueue(new MockResponse().setResponseCode(200)
.setHeader("content-type", "application/json")
- .setBody(TestTokenHelper.createAccessTokenResponseJSONWithHMACIDToken(
- OP_ISSUER_ID, CLIENT_ID, List.of(CLIENT_ID), CLIENT_SECRET)));
+ .setBody(TestTokenHelper.createAccessTokenResponseJSON(
+ OP_ISSUER_ID, CLIENT_ID, List.of(CLIENT_ID), JWSAlgorithm.HS256, null, null,
+ new DefaultClientSecretCredential(CLIENT_SECRET).toSigningCredential(), null)));
// Second is plain userInfo
mockOPServer.enqueue(new MockResponse().setResponseCode(200)
.setHeader("content-type", "application/json")
- .setBody(USERINFO_RESPONSE_NO_SUB));
+ .setBody(TestTokenHelper.createPlainUserInfoResponseString(OP_ISSUER_ID, List.of(CLIENT_ID), null)));
mockOPServer.start(9918);
@@ -183,12 +192,10 @@ public class OIDCRPFlowFromAuthenticationResponseTest extends OIDCRPFlowTest {
// Should have failed to produce a result
assertFlowEndedInErrorConditions(prc, "InvalidUserInfoClaims");
-
}
-
@Test
- public void testAuthnFlowFromAuthorizationCallback_Using_HMAC_UserInfo_And_IDToken_Response() throws Exception {
+ public void test_IDTokenHS256_UserInfoHS256() throws Exception {
setFlowPath(FLOW);
setFlowModelResources(flowResources);
@@ -204,11 +211,14 @@ public class OIDCRPFlowFromAuthenticationResponseTest extends OIDCRPFlowTest {
// First is token exchange
mockOPServer.enqueue(new MockResponse().setResponseCode(200)
.setHeader("content-type", "application/json")
- .setBody(TestTokenHelper.createAccessTokenResponseJSONWithHMACIDToken(
- OP_ISSUER_ID, CLIENT_ID, List.of(CLIENT_ID), CLIENT_SECRET)));
- // Second is userInfo
- final var userInfoToken = TestTokenHelper.createHMACSignedUserInfoJWTResponseJSON(
- OP_ISSUER_ID,CLIENT_ID, List.of(CLIENT_ID), CLIENT_SECRET);
+ .setBody(TestTokenHelper.createAccessTokenResponseJSON(
+ OP_ISSUER_ID, CLIENT_ID, List.of(CLIENT_ID), JWSAlgorithm.HS256, null, null,
+ new DefaultClientSecretCredential(CLIENT_SECRET).toSigningCredential(), null)));
+ // Second is userInfo
+ final var userInfoToken = TestTokenHelper.createJWTUserInfoResponse(
+ OP_ISSUER_ID, List.of(CLIENT_ID), "jdoe", JWSAlgorithm.HS256, null, null,
+ new DefaultClientSecretCredential(CLIENT_SECRET).toSigningCredential(), null);
+
mockOPServer.enqueue(new MockResponse().setResponseCode(200)
.setHeader("content-type", "application/jwt")
.setBody(userInfoToken.serialize()));
@@ -255,7 +265,7 @@ public class OIDCRPFlowFromAuthenticationResponseTest extends OIDCRPFlowTest {
}
@Test
- public void testAuthnFlowFromAuthorizationCallback_Using_SymetricSignedIDToken_And_AsymetricSignedAndEncryptedUserInfoResponse()
+ public void test_IDTokenHS256_DirA128CBC_HS256_UserInfoES256_RSA_OAEP_256A256GCM()
throws Exception {
setFlowPath(FLOW);
@@ -271,17 +281,32 @@ public class OIDCRPFlowFromAuthenticationResponseTest extends OIDCRPFlowTest {
final MockWebServer mockOPServer = createSimpleServer();
// First is token exchange
+
+ final var accessTokenResponseJson = TestTokenHelper.createAccessTokenResponseJSON(
+ OP_ISSUER_ID, CLIENT_ID, List.of(CLIENT_ID), JWSAlgorithm.HS256, JWEAlgorithm.DIR,
+ EncryptionMethod.A128CBC_HS256,
+ new DefaultClientSecretCredential(CLIENT_SECRET).toSigningCredential(),
+ new DefaultClientSecretCredential(CLIENT_SECRET).toEncryptionCredential(
+ JWEAlgorithm.DIR, EncryptionMethod.A128CBC_HS256));
+
mockOPServer.enqueue(new MockResponse().setResponseCode(200)
.setHeader("content-type", "application/json")
- .setBody(TestTokenHelper.createAccessTokenResponseJSONIDTokenSignedAndDirEncrypted(
- OP_ISSUER_ID, CLIENT_ID, List.of(CLIENT_ID), CLIENT_SECRET)));
- // Second is userInfo
- final var userInfoTokenAndKey =
- TestTokenHelper.createAsymetricSignedAndAsymetricEncryptedUserInfoJWTResponse(
- OP_ISSUER_ID,List.of(CLIENT_ID), CLIENT_ID);
+ .setBody(accessTokenResponseJson));
+ // Second is userInfo
+ final var sigKey = new ECKeyGenerator(Curve.P_256).keyID("123").generate();
+ final RSAKey encKey = new RSAKeyGenerator(2048)
+ .keyID("1")
+ .keyUse(KeyUse.ENCRYPTION)
+ .generate();
+ final var userInfoToken = TestTokenHelper.createJWTUserInfoResponse(
+ OP_ISSUER_ID, List.of(CLIENT_ID), "jdoe", JWSAlgorithm.ES256, JWEAlgorithm.RSA_OAEP_256,
+ EncryptionMethod.A256GCM,
+ new BasicCredential(sigKey.toPublicKey(), sigKey.toPrivateKey()),
+ new BasicCredential(encKey.toPublicKey(), encKey.toPrivateKey()));
+
mockOPServer.enqueue(new MockResponse().setResponseCode(200)
.setHeader("content-type", "application/jwt")
- .setBody(userInfoTokenAndKey.getSecond().serialize()));
+ .setBody(userInfoToken.serialize()));
mockOPServer.start(9918);
@@ -315,10 +340,10 @@ public class OIDCRPFlowFromAuthenticationResponseTest extends OIDCRPFlowTest {
public Credential resolveSingle(final CriteriaSet criteria) throws ResolverException {
final BasicJWKCredential jwkCredential = new BasicJWKCredential();
jwkCredential.setAlgorithm(JWEAlgorithm.RSA_OAEP_256);
- jwkCredential.setKid(userInfoTokenAndKey.getFirst().getSecond().getKeyID());
+ jwkCredential.setKid(encKey.getKeyID());
try {
- jwkCredential.setPrivateKey(userInfoTokenAndKey.getFirst().getSecond().toPrivateKey());
- jwkCredential.setPublicKey(userInfoTokenAndKey.getFirst().getSecond().toPublicKey());
+ jwkCredential.setPrivateKey(encKey.toPrivateKey());
+ jwkCredential.setPublicKey(encKey.toPublicKey());
} catch (final JOSEException e) {
fail();
}
@@ -344,10 +369,10 @@ public class OIDCRPFlowFromAuthenticationResponseTest extends OIDCRPFlowTest {
public Credential resolveSingle(final CriteriaSet criteria) throws ResolverException {
final BasicJWKCredential jwkCredential = new BasicJWKCredential();
jwkCredential.setAlgorithm(JWEAlgorithm.RSA_OAEP_256);
- jwkCredential.setKid(userInfoTokenAndKey.getFirst().getFirst().getKeyID());
+ jwkCredential.setKid(sigKey.getKeyID());
try {
- jwkCredential.setPrivateKey(userInfoTokenAndKey.getFirst().getFirst().toPrivateKey());
- jwkCredential.setPublicKey(userInfoTokenAndKey.getFirst().getFirst().toPublicKey());
+ jwkCredential.setPrivateKey(sigKey.toPrivateKey());
+ jwkCredential.setPublicKey(sigKey.toPublicKey());
} catch (final JOSEException e) {
fail();
}
@@ -403,7 +428,7 @@ public class OIDCRPFlowFromAuthenticationResponseTest extends OIDCRPFlowTest {
* @throws Exception on error.
*/
@Test
- public void testAuthnFlowFromAuthorizationCallback_Using_SymetricSigned_And_AsymetricEncryptedIDToken()
+ public void test_IDTokenHS256_RSA_OAEP_256_A256GCM_PlainUserInfo()
throws Exception {
setFlowPath(FLOW);
@@ -417,18 +442,26 @@ public class OIDCRPFlowFromAuthenticationResponseTest extends OIDCRPFlowTest {
setMockProperties(mockProperties);
- final Pair<String, RSAKey> accessTokenAndKey =
- TestTokenHelper.createAccessTokenResponseJSONWithSignedAndAsymmetricEncryptedIDToken(
- OP_ISSUER_ID, CLIENT_ID, List.of(CLIENT_ID), CLIENT_SECRET);
+ final RSAKey encKey = new RSAKeyGenerator(2048)
+ .keyID("1")
+ .keyUse(KeyUse.ENCRYPTION)
+ .generate();
+ final var accessTokenResponseJson = TestTokenHelper.createAccessTokenResponseJSON(
+ OP_ISSUER_ID, CLIENT_ID, List.of(CLIENT_ID), JWSAlgorithm.HS256, JWEAlgorithm.RSA_OAEP_256,
+ EncryptionMethod.A256GCM,
+ new DefaultClientSecretCredential(CLIENT_SECRET).toSigningCredential(),
+ new BasicCredential(encKey.toPublicKey(), encKey.toPrivateKey()));
+
+
final MockWebServer mockOPServer = createSimpleServer();
// First is token exchange
mockOPServer.enqueue(new MockResponse().setResponseCode(200)
.setHeader("content-type", "application/json")
- .setBody(accessTokenAndKey.getFirst()));
+ .setBody(accessTokenResponseJson));
// Second is userInfo
mockOPServer.enqueue(new MockResponse().setResponseCode(200)
.setHeader("content-type", "application/json")
- .setBody(USERINFO_RESPONSE));
+ .setBody(TestTokenHelper.createPlainUserInfoResponseString(OP_ISSUER_ID, List.of(CLIENT_ID), "jdoe")));
mockOPServer.start(9918);
@@ -460,10 +493,10 @@ public class OIDCRPFlowFromAuthenticationResponseTest extends OIDCRPFlowTest {
public Credential resolveSingle(final CriteriaSet criteria) throws ResolverException {
final BasicJWKCredential jwkCredential = new BasicJWKCredential();
jwkCredential.setAlgorithm(JWEAlgorithm.RSA_OAEP_256);
- jwkCredential.setKid(accessTokenAndKey.getSecond().getKeyID());
+ jwkCredential.setKid(encKey.getKeyID());
try {
- jwkCredential.setPrivateKey(accessTokenAndKey.getSecond().toPrivateKey());
- jwkCredential.setPublicKey(accessTokenAndKey.getSecond().toPublicKey());
+ jwkCredential.setPrivateKey(encKey.toPrivateKey());
+ jwkCredential.setPublicKey(encKey.toPublicKey());
} catch (final JOSEException e) {
fail();
}
@@ -517,7 +550,7 @@ public class OIDCRPFlowFromAuthenticationResponseTest extends OIDCRPFlowTest {
* @throws Exception on error.
*/
@Test
- public void testAuthnFlowFromAuthorizationCallback_UsingPlainJSONObjectUserInfoResponse()
+ public void test_IDTokenHS256_PlainJWTUserInfoResponse()
throws Exception {
setFlowPath(FLOW);
@@ -535,12 +568,13 @@ public class OIDCRPFlowFromAuthenticationResponseTest extends OIDCRPFlowTest {
// First is token exchange
mockOPServer.enqueue(new MockResponse().setResponseCode(200)
.setHeader("content-type", "application/json")
- .setBody(TestTokenHelper.createAccessTokenResponseJSONWithHMACIDToken(
- OP_ISSUER_ID, CLIENT_ID, List.of(CLIENT_ID), CLIENT_SECRET)));
+ .setBody(TestTokenHelper.createAccessTokenResponseJSON(
+ OP_ISSUER_ID, CLIENT_ID, List.of(CLIENT_ID), JWSAlgorithm.HS256, null, null,
+ new DefaultClientSecretCredential(CLIENT_SECRET).toSigningCredential(), null)));
// Second is userInfo
mockOPServer.enqueue(new MockResponse().setResponseCode(200)
.setHeader("content-type", "application/jwt")
- .setBody(TestTokenHelper.createPlainUserInfoJWTResponseJSON(OP_ISSUER_ID,CLIENT_ID)
+ .setBody(TestTokenHelper.createPlainUserInfoResponseJSON(OP_ISSUER_ID,CLIENT_ID)
.serialize()));
mockOPServer.start(9918);
@@ -563,7 +597,7 @@ public class OIDCRPFlowFromAuthenticationResponseTest extends OIDCRPFlowTest {
}
@Test
- public void testAuthnFlowFromAuthorizationCallback_Using_HMAC_UserInfo_And_IDToken_Response_WithACRAMRTranslation()
+ public void test_IDTokenHS256_UserInfoHS256_WithACRAMRTranslation()
throws Exception {
setFlowPath(FLOW);
@@ -580,11 +614,13 @@ public class OIDCRPFlowFromAuthenticationResponseTest extends OIDCRPFlowTest {
// First is token exchange
mockOPServer.enqueue(new MockResponse().setResponseCode(200)
.setHeader("content-type", "application/json")
- .setBody(TestTokenHelper.createAccessTokenResponseJSONWithHMACIDToken(
- OP_ISSUER_ID, CLIENT_ID, List.of(CLIENT_ID), CLIENT_SECRET)));
+ .setBody(TestTokenHelper.createAccessTokenResponseJSON(
+ OP_ISSUER_ID, CLIENT_ID, List.of(CLIENT_ID), JWSAlgorithm.HS256, null, null,
+ new DefaultClientSecretCredential(CLIENT_SECRET).toSigningCredential(), null)));
// Second is userInfo
- final var userInfoToken = TestTokenHelper.createHMACSignedUserInfoJWTResponseJSON(
- OP_ISSUER_ID,CLIENT_ID, List.of(CLIENT_ID), CLIENT_SECRET);
+ final var userInfoToken = TestTokenHelper.createJWTUserInfoResponse(
+ OP_ISSUER_ID, List.of(CLIENT_ID), "jdoe", JWSAlgorithm.HS256, null, null,
+ new DefaultClientSecretCredential(CLIENT_SECRET).toSigningCredential(), null);
mockOPServer.enqueue(new MockResponse().setResponseCode(200)
.setHeader("content-type", "application/jwt")
.setBody(userInfoToken.serialize()));
@@ -623,7 +659,7 @@ public class OIDCRPFlowFromAuthenticationResponseTest extends OIDCRPFlowTest {
* @throws Exception on error.
*/
@Test
- public void testAuthnFlowFromAuthorizationCallback_UnsupportedOIDCFlow()
+ public void testUnsupportedOIDCFlow()
throws Exception {
setFlowPath(FLOW);
@@ -670,7 +706,7 @@ public class OIDCRPFlowFromAuthenticationResponseTest extends OIDCRPFlowTest {
* @throws Exception on error.
*/
@Test
- public void testAuthnFlowFromAuthorizationCallback_ErrorAuthenticationResponse()
+ public void testErrorAuthenticationResponse()
throws Exception {
setFlowPath(FLOW);
diff --git a/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCRPFlowPreRedirectTest.java b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCRPFlowPreRedirectTest.java
index c3f6932..763b084 100644
--- a/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCRPFlowPreRedirectTest.java
+++ b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCRPFlowPreRedirectTest.java
@@ -93,7 +93,7 @@ public class OIDCRPFlowPreRedirectTest extends OIDCRPFlowTest {
* @throws Exception on error.
*/
@Test
- public void testFlowToAuthorizationRedirect_UsingRequestObject() throws Exception {
+ public void test_UsingRequestObject() throws Exception {
setFlowPath(FLOW);
setFlowModelResources(flowResources);
setSubflows(subflows);
@@ -145,7 +145,7 @@ public class OIDCRPFlowPreRedirectTest extends OIDCRPFlowTest {
* @throws Exception on error.
*/
@Test
- public void testFlowToAuthorizationRedirect_UsingRequestObject_WithEncryption() throws Exception {
+ public void test_UsingRequestObject_WithEncryption() throws Exception {
setFlowPath(FLOW);
setFlowModelResources(flowResources);
setSubflows(subflows);
@@ -219,7 +219,7 @@ public class OIDCRPFlowPreRedirectTest extends OIDCRPFlowTest {
* @throws Exception on error.
*/
@Test
- public void testFlowToAuthorizationRedirect_UsingRequestObject_RSA256Signature() throws Exception {
+ public void test_UsingRequestObject_RSA256Signature() throws Exception {
setFlowPath(FLOW);
setFlowModelResources(flowResources);
setSubflows(subflows);
@@ -281,7 +281,7 @@ public class OIDCRPFlowPreRedirectTest extends OIDCRPFlowTest {
* @throws Exception on error.
*/
@Test
- public void testFlowToAuthorizationRedirect_Fail_UsingRequestObject_UnsupportedSignatureAlgorithm()
+ public void test_Fail_UsingRequestObject_UnsupportedSignatureAlgorithm()
throws Exception {
setFlowPath(FLOW);
setFlowModelResources(flowResources);
@@ -343,7 +343,7 @@ public class OIDCRPFlowPreRedirectTest extends OIDCRPFlowTest {
* @throws Exception on error.
*/
@Test
- public void testFlowToAuthorizationRedirect_WithACRs() throws Exception {
+ public void testWithACRs() throws Exception {
setFlowPath(FLOW);
setFlowModelResources(flowResources);
setSubflows(subflows);
diff --git a/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCRPFlowTest.java b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCRPFlowTest.java
index f02bf00..8a71389 100644
--- a/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCRPFlowTest.java
+++ b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCRPFlowTest.java
@@ -88,7 +88,6 @@ import net.shibboleth.oidc.security.impl.ExplicitKeySignedJWTTrustEngine;
import net.shibboleth.oidc.security.jose.impl.BasicDecryptionConfiguration;
import net.shibboleth.oidc.security.jose.impl.BasicSignatureValidationConfiguration;
import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
-import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
import net.shibboleth.utilities.java.support.annotation.constraint.Unmodifiable;
import okhttp3.mockwebserver.MockWebServer;
import okhttp3.tls.HandshakeCertificates;
@@ -167,42 +166,6 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
protected static final ClassPathResource GOOD_PROVIDER_CONFIGURATION_INFO_USE_REQUEST_OBJECT_HS512_SIG =
new ClassPathResource("/metadata/test-provider-requestobject-HS512-only-sig.json");
-
- /** Mock JSON Object response from the UserInfo endpoint.*/
- @Nonnull @NotEmpty
- protected static final String USERINFO_RESPONSE ="{\n"
- + " \"sub\": \"jdoe\",\n"
- + " \"website\": \"https://openid.net/\",\n"
- + " \"zoneinfo\": \"America/Los_Angeles\",\n"
- + " \"birthdate\": \"2000-02-03\",\n"
- + " \"gender\": \"female\",\n"
- + " \"preferred_username\": \"d.tu\",\n"
- + " \"given_name\": \"Demo\",\n"
- + " \"middle_name\": \"Theresa\",\n"
- + " \"locale\": \"en-US\",\n"
- + " \"updated_at\": 1580000000,\n"
- + " \"name\": \"Demo T. User\",\n"
- + " \"nickname\": \"Dee\",\n"
- + " \"family_name\": \"User\"\n"
- + "}";
-
- /** Mock JSON Object response from the UserInfo endpoint.*/
- @Nonnull @NotEmpty
- protected static final String USERINFO_RESPONSE_NO_SUB ="{\n"
- + " \"website\": \"https://openid.net/\",\n"
- + " \"zoneinfo\": \"America/Los_Angeles\",\n"
- + " \"birthdate\": \"2000-02-03\",\n"
- + " \"gender\": \"female\",\n"
- + " \"preferred_username\": \"d.tu\",\n"
- + " \"given_name\": \"Demo\",\n"
- + " \"middle_name\": \"Theresa\",\n"
- + " \"locale\": \"en-US\",\n"
- + " \"updated_at\": 1580000000,\n"
- + " \"name\": \"Demo T. User\",\n"
- + " \"nickname\": \"Dee\",\n"
- + " \"family_name\": \"User\"\n"
- + "}";
-
/** Path to the flow to be tested.*/
@Nonnull protected static final String FLOW =
diff --git a/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/TestTokenHelper.java b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/TestTokenHelper.java
index 21ac7ba..7271d6c 100644
--- a/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/TestTokenHelper.java
+++ b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/TestTokenHelper.java
@@ -18,10 +18,21 @@
package net.shibboleth.idp.plugin.authn.oidc.rp.impl;
+import java.security.interfaces.ECPrivateKey;
+import java.security.interfaces.ECPublicKey;
+import java.security.interfaces.RSAPublicKey;
+import java.text.ParseException;
import java.time.Instant;
import java.util.Date;
import java.util.List;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.security.credential.Credential;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
import com.nimbusds.jose.EncryptionMethod;
import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.JOSEObjectType;
@@ -30,27 +41,20 @@ import com.nimbusds.jose.JWEHeader;
import com.nimbusds.jose.JWEObject;
import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.JWSHeader;
-import com.nimbusds.jose.KeyLengthException;
import com.nimbusds.jose.Payload;
+import com.nimbusds.jose.crypto.AESEncrypter;
import com.nimbusds.jose.crypto.DirectEncrypter;
+import com.nimbusds.jose.crypto.ECDHEncrypter;
import com.nimbusds.jose.crypto.ECDSASigner;
import com.nimbusds.jose.crypto.MACSigner;
import com.nimbusds.jose.crypto.RSAEncrypter;
-import com.nimbusds.jose.jwk.Curve;
-import com.nimbusds.jose.jwk.ECKey;
-import com.nimbusds.jose.jwk.KeyUse;
-import com.nimbusds.jose.jwk.RSAKey;
-import com.nimbusds.jose.jwk.gen.ECKeyGenerator;
-import com.nimbusds.jose.jwk.gen.RSAKeyGenerator;
+import com.nimbusds.jose.crypto.RSASSASigner;
import com.nimbusds.jwt.EncryptedJWT;
+import com.nimbusds.jwt.JWT;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.PlainJWT;
import com.nimbusds.jwt.SignedJWT;
-import net.shibboleth.oidc.security.credential.JWKCredential;
-import net.shibboleth.oidc.security.credential.JWKCredentialSupport;
-import net.shibboleth.utilities.java.support.collection.Pair;
-
/** A helper class for generating tokens for testing.*/
public final class TestTokenHelper {
@@ -62,9 +66,6 @@ public final class TestTokenHelper {
/**
* Build a basic {@link JWTClaimsSet} from the supplied parameters.
*
- * @param subject the subject
- * @param nonce the nonce
- * @param name the name
* @param opIssuerId the OP's issuer ID
* @param clientId the client_id
* @param audience the list of audiences
@@ -95,231 +96,170 @@ public final class TestTokenHelper {
}
/**
- * Create an OAuth access token with a runtime constructed id_token. This allows the
- * expiry to be current. The token is signed with the client_secret using HS256.
- *
- * @param opIssuerId the OP's issuer ID
- * @param clientId the client_id
- * @param audience the list of audiences for the id_token token
- * @param clientSecret the client_secret used to sign the id_token
- *
+ * Build a basic {@link JWTClaimsSet} from the supplied parameters to mock a UserInfo response.
*
- * @return a serialized access token response.
+ * @param subject the subject
+ * @param opIssuerId the OP's issuer ID *
+ * @param audience the list of audiences
+ * @param sub the subject
*
- * @throws Exception on error.
+ * @return the JWT claims set.
*/
- public static String createAccessTokenResponseJSONWithHMACIDToken(
- final String opIssuerId, final String clientId,
- final List<String> audience, final String clientSecret) throws Exception {
+ public static JWTClaimsSet createBasicUserInfoClaims(
+ final String opIssuerId, final List<String> audience, final String sub) {
- final var header = new JWSHeader.Builder(JWSAlgorithm.HS256)
- .type(JOSEObjectType.JWT)
+ return new JWTClaimsSet.Builder()
+ .issuer(opIssuerId)
+ .audience(audience)
+ .subject(sub)
+ .claim("preferred_username", "d.tu")
+ .claim("given_name", "Demo")
+ .claim("family_name", "User")
+ .claim("nonce", "abadnonce")
+ .claim("nickname", "Dee")
+ .claim("name","Demo T. User")
.build();
-
- final var payload = createBasicClaims(opIssuerId, clientId, audience);
-
- final var signedJWT = new SignedJWT(header,payload);
- signedJWT.sign(new MACSigner(clientSecret));
-
- return buildTemplateAccessTokenJSONResponse(signedJWT.serialize());
}
/**
- * Create an OAuth access token with a runtime constructed id_token. This allows the
- * expiry to be current. The token is signed using HS256 and encrypted using the 'Direct Encryption' (dir)
- * management mode i.e. no key wrapping. Both use the shared client_secret.
+ * Create a JWT from the given payload. The JWT can either be plain, or signed and encrypted. If encrypted, it must
+ * be signed.
*
- * @param opIssuerId the OP's issuer ID
- * @param clientId the client_id
- * @param audience the list of audiences for the id_token token
- * @param clientSecret the client_secret used to sign the id_token
+ * @param payload the payload to create the JWT from
+ * @param sigAlg the signature alg to use. {@code Null} if the JWT is not going to be signed and/or encrypted.
+ * @param jweAlg the encryption alg to use. {@code Null} if the JWT is not going to be encrypted. Note, if encrypted
+ * the signature algorithm must also be supplied, otherwise a plain JWT will be returned.
+ * @param enc the content encryption algorithm to use.
+ * @param sigKey the key to use for signing
+ * @param encKey the key to use for encryption
*
- * @return a serialized access token response.
+ * @return and plain JWT, JWS, or JWE.
*
- * @throws Exception on error.
+ * @throws JOSEException on error.
+ * @throws ParseException on error.
*/
- public static String createAccessTokenResponseJSONIDTokenSignedAndDirEncrypted(final String opIssuerId,
- final String clientId, final List<String> audience, final String clientSecret) throws Exception {
-
- final var header = new JWSHeader.Builder(JWSAlgorithm.HS256)
- .type(JOSEObjectType.JWT)
- .build();
-
- //client_secret needs to be derived
- final JWKCredential derviedCred = JWKCredentialSupport.deriveSymmetricKeyForAlgAndEnc(
- TestCredentialHelper.createClientSecretCredential(clientSecret).toSigningCredential(),
- JWEAlgorithm.DIR, EncryptionMethod.A256GCM);
-
- final var payload = createBasicClaims(opIssuerId, clientId, audience);
- final var signedJWT = new SignedJWT(header,payload);
- // sign off the client_secret octets directly
- signedJWT.sign(new MACSigner(clientSecret));
-
- final JWEObject jweObject =
- new JWEObject(new JWEHeader.Builder(JWEAlgorithm.DIR, EncryptionMethod.A256GCM)
- .contentType("JWT")
- .build(),
- new Payload(signedJWT));
- jweObject.encrypt(new DirectEncrypter(derviedCred.getSecretKey()));
- final EncryptedJWT jwe = EncryptedJWT.parse(jweObject.serialize());
+ public static JWT createJWT(@Nonnull final JWTClaimsSet payload, @Nullable final JWSAlgorithm sigAlg,
+ @Nullable final JWEAlgorithm jweAlg, @Nullable final EncryptionMethod enc,
+ @Nullable final Credential sigKey, @Nullable final Credential encKey) throws JOSEException, ParseException {
- return buildTemplateAccessTokenJSONResponse(jwe.serialize());
+ // Sign first.
+ if (sigAlg != null) {
+ final var header = new JWSHeader.Builder(sigAlg)
+ .type(JOSEObjectType.JWT)
+ .build();
+ final var signedJWT = new SignedJWT(header,payload);
+
+ if (JWSAlgorithm.Family.HMAC_SHA.contains(sigAlg)){
+ signedJWT.sign(new MACSigner(sigKey.getSecretKey()));
+ } else if (JWSAlgorithm.Family.RSA.contains(sigAlg)) {
+ signedJWT.sign(new RSASSASigner(sigKey.getPrivateKey()));
+ } else if (JWSAlgorithm.Family.EC.contains(sigAlg)) {
+ signedJWT.sign(new ECDSASigner((ECPrivateKey)sigKey.getPrivateKey()));
+ }
+
+ if (jweAlg != null) {
+ final JWEObject jweObject =
+ new JWEObject(new JWEHeader.Builder(jweAlg, enc)
+ .contentType("JWT")
+ .build(),
+ new Payload(signedJWT));
+
+ if (JWEAlgorithm.Family.RSA.contains(jweAlg)) {
+ jweObject.encrypt(new RSAEncrypter((RSAPublicKey)encKey.getPublicKey()));
+ return EncryptedJWT.parse(jweObject.serialize());
+
+ } else if (JWEAlgorithm.Family.AES_KW.contains(jweAlg)
+ || JWEAlgorithm.Family.AES_GCM_KW.contains(jweAlg)) {
+
+ jweObject.encrypt(new AESEncrypter(encKey.getSecretKey()));
+ return EncryptedJWT.parse(jweObject.serialize());
+
+ } else if (JWEAlgorithm.Family.ECDH_ES.contains(jweAlg)) {
+ jweObject.encrypt(new ECDHEncrypter((ECPublicKey) encKey.getPublicKey()));
+ return EncryptedJWT.parse(jweObject.serialize());
+ } else if (JWEAlgorithm.DIR == jweAlg) {
+ jweObject.encrypt(new DirectEncrypter(encKey.getSecretKey()));
+ return EncryptedJWT.parse(jweObject.serialize());
+ }
+ } else {
+ return signedJWT;
+ }
+ }
+ return new PlainJWT(payload);
}
/**
- * Create an OAuth access token with a runtime constructed id_token. This allows the
- * expiry to be current. The token is signed using HS256 (MAC) and encrypted using a key encryption
- * management mode.
+ * Create a JWT UserInfo response. Can be a JWS or JWE depending on the input values.
*
- * @param opIssuerId the OP's issuer ID
- * @param clientId the client_id
- * @param audience the list of audiences for the id_token token
- * @param clientSecret the client_secret used to sign the id_token
- *
- * @return a serialized access token response and the RSAKey used to encrypt the CEK.
- *
- * @throws Exception on error.
- */
- public static Pair<String, RSAKey> createAccessTokenResponseJSONWithSignedAndAsymmetricEncryptedIDToken(
- final String opIssuerId,
- final String clientId, final List<String> audience, final String clientSecret) throws Exception {
-
- final var header = new JWSHeader.Builder(JWSAlgorithm.HS256)
- .type(JOSEObjectType.JWT)
- .build();
-
- final var payload = createBasicClaims(opIssuerId, clientId, audience);
- final var signedJWT = new SignedJWT(header,payload);
- signedJWT.sign(new MACSigner(clientSecret));
-
-
- final RSAKey keyRecipient = new RSAKeyGenerator(2048)
- .keyID("1")
- .keyUse(KeyUse.ENCRYPTION)
- .generate();
-
- final JWEObject jweObject =
- new JWEObject(new JWEHeader.Builder(JWEAlgorithm.RSA_OAEP_256, EncryptionMethod.A256GCM)
- .contentType("JWT")
- .build(),
- new Payload(signedJWT));
- jweObject.encrypt(new RSAEncrypter(keyRecipient.toPublicJWK()));
- final EncryptedJWT jwe = EncryptedJWT.parse(jweObject.serialize());
-
- return new Pair<>(buildTemplateAccessTokenJSONResponse(jwe.serialize()), keyRecipient);
- }
-
- /**
- * Create an RSA asymetrically signed and encrypted UserInfo response JWT. Return both signature and encryption
- * keys alongside the EncryptedJWT in the response.
- *
- * @param issuer the issuer
+ * @param opIssuerId the issuer
* @param audience the audience
- * @param clientId the client_id
- * @return the signed JWT alongside the pair of signature and encryption keys
- * @throws Exception
- *
- * @throws Exception on error
+ * @param sub the subject
+ * @param sigAlg the signature algorithm
+ * @param jweAlg the encryption algorithm
+ * @param enc the content encryption algorithm
+ * @param sigKey the signing key
+ * @param encKey the encryption key
+ * @return
+ * @throws JOSEException on error
+ * @throws ParseException on error
*/
- public static Pair<Pair<ECKey, RSAKey>, EncryptedJWT> createAsymetricSignedAndAsymetricEncryptedUserInfoJWTResponse(
- final String issuer, final List<String> audience, final String clientId) throws Exception {
-
- final RSAKey keyRecipient = new RSAKeyGenerator(2048)
- .keyID("2")
- .keyUse(KeyUse.ENCRYPTION)
- .generate();
+ public static JWT createJWTUserInfoResponse(final String opIssuerId, final List<String> audience,
+ final String sub, @Nullable final JWSAlgorithm sigAlg,
+ @Nullable final JWEAlgorithm jweAlg, @Nullable final EncryptionMethod enc,
+ @Nullable final Credential sigKey, @Nullable final Credential encKey) throws JOSEException, ParseException {
- final var keySignedJWTPair = createECDSASignedUserInfoJWTResponseJSON(issuer, clientId, audience);
+ final var payload = createBasicUserInfoClaims(opIssuerId, audience, sub);
+ return createJWT(payload, sigAlg, jweAlg, enc, sigKey, encKey);
- final JWEObject jweObject =
- new JWEObject(new JWEHeader.Builder(JWEAlgorithm.RSA_OAEP_256, EncryptionMethod.A256GCM)
- .contentType("JWT")
- .build(),
- new Payload(keySignedJWTPair.getSecond()));
- jweObject.encrypt(new RSAEncrypter(keyRecipient.toPublicJWK()));
-
- return new Pair<>(
- new Pair<>(keySignedJWTPair.getFirst(),keyRecipient),
- EncryptedJWT.parse(jweObject.serialize()));
}
/**
- * Create an ECDSA signed UserInfo response JWT.
+ * Create an access token response JSON object and build a suitable id_token to include in the response.
*
* @param opIssuerId the issuer
* @param clientId the client_id
* @param audience the audience
+ * @param sigAlg the signature algorithm
+ * @param jweAlg the encryption algorithm
+ * @param enc the content encryption algorithm
+ * @param sigKey the signing key
+ * @param encKey the encryption key
*
- * @return the signed JWT
+ * @return an access token response JSON object
*
- * @throws JOSEException on error
+ * @throws Exception on error.
*/
- public static Pair<ECKey, SignedJWT> createECDSASignedUserInfoJWTResponseJSON(
- final String opIssuerId, final String clientId, final List<String> audience)
- throws JOSEException {
+ public static String createAccessTokenResponseJSON(
+ final String opIssuerId, final String clientId,
+ final List<String> audience, @Nullable final JWSAlgorithm sigAlg,
+ @Nullable final JWEAlgorithm jweAlg, @Nullable final EncryptionMethod enc,
+ @Nullable final Credential sigKey, @Nullable final Credential encKey) throws Exception {
- final var key = new ECKeyGenerator(Curve.P_256).keyID("123").generate();
-
- final var header = new JWSHeader.Builder(JWSAlgorithm.ES256)
- .type(JOSEObjectType.JWT)
- .keyID(key.getKeyID())
- .build();
final var payload = createBasicClaims(opIssuerId, clientId, audience);
+ final JWT idToken = createJWT(payload, sigAlg, jweAlg, enc, sigKey, encKey);
- final var signedJWT = new SignedJWT(header, payload);
- signedJWT.sign(new ECDSASigner(key.toECPrivateKey()));
- return new Pair<>(key,signedJWT);
+ return buildTemplateAccessTokenJSONResponse(idToken.serialize());
}
/**
- * Create a HMAC signed UserInfo response JWT.
+ * Create a plain JSON object based UserInfo response.
*
* @param opIssuerId the issuer
- * @param clientId the client_id
* @param audience the audience
- * @param clientSecret the client_secret
+ * @param sub the subject.
+ * @return a plain JSON object representing the UserInfo response
*
- * @return the signed JWT
- *
- * @throws JOSEException on error
+ * @throws JsonProcessingException on error
*/
- public static SignedJWT createHMACSignedUserInfoJWTResponseJSON(
- final String opIssuerId, final String clientId, final List<String> audience, final String clientSecret)
- throws JOSEException {
-
- final var header = new JWSHeader.Builder(JWSAlgorithm.HS256)
- .type(JOSEObjectType.JWT)
- .build();
-
- final var payload = createBasicClaims(opIssuerId, clientId, audience);
-
- final var signedJWT = new SignedJWT(header, payload);
- signedJWT.sign(new MACSigner(clientSecret));
- return signedJWT;
- }
-
- /**
- * Create a MAC signed JWT from the given JWT claims set, using the supplied secret.
- *
- * @param payload the payload to sign
- * @param secret the secret used to create the MAC
- *
- * @return the signed JWT
- *
- * @throws KeyLengthException on error
- * @throws JOSEException on error
- */
- public static SignedJWT createHMACSignedJWT(final JWTClaimsSet payload, final String secret)
- throws KeyLengthException, JOSEException {
- final var header = new JWSHeader.Builder(JWSAlgorithm.HS256)
- .type(JOSEObjectType.JWT)
- .build();
- final var signedJWT = new SignedJWT(header, payload);
- signedJWT.sign(new MACSigner(secret));
- return signedJWT;
+ public static String createPlainUserInfoResponseString(final String opIssuerId, final List<String> audience,
+ final String sub)
+ throws JsonProcessingException {
+ final JWTClaimsSet claims = createBasicUserInfoClaims(opIssuerId, audience, sub);
+ return new ObjectMapper().writeValueAsString(claims.toJSONObject());
}
+
/**
* Create a Plain UserInfo response JWT.
*
@@ -328,20 +268,13 @@ public final class TestTokenHelper {
* @return the signed JWT
* @throws JOSEException on error
*/
- public static PlainJWT createPlainUserInfoJWTResponseJSON(final String issuer, final String audience)
- throws JOSEException {
-
-
- final var payload = new JWTClaimsSet.Builder()
- .issuer(issuer)
- .audience(audience)
- .subject("jdoe")
- .claim("preferred_username", "jdoe")
- .claim("name", "J Doe")
- .build();
+ public static PlainJWT createPlainUserInfoResponseJSON(final String issuer, final String audience)
+ throws JOSEException {
+ final var payload = createBasicUserInfoClaims(issuer, List.of(audience), "jdoe");
return new PlainJWT(payload);
}
+
/**
* Build a simple OAuth2.0/OIDC Access Token JSON response using the serialized JWT.
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list