[java-oidc-common] branch dev/JCOMOIDC-87 updated: JCOMOIDC-87 - Profile configuration for OIDC logout
Henri Mikkonen
henri.mikkonen at iki.fi
Tue Oct 24 13:32:14 UTC 2023
This is an automated email from the git hooks/post-receive script.
hjmikkon pushed a commit to branch dev/JCOMOIDC-87
in repository java-oidc-common.
View the commit online:
http://git.shibboleth.net/view/?p=java-oidc-common.git;a=commit;h=6e52b78f0390159cf04432536c9669d951d6cba0
The following commit(s) were added to refs/heads/dev/JCOMOIDC-87 by this push:
new 6e52b78 JCOMOIDC-87 - Profile configuration for OIDC logout
6e52b78 is described below
commit 6e52b78f0390159cf04432536c9669d951d6cba0
Author: Henri Mikkonen <henri.mikkonen at iki.fi>
AuthorDate: Tue Oct 24 12:31:36 2023 +0300
JCOMOIDC-87 - Profile configuration for OIDC logout
https://shibboleth.atlassian.net/browse/JCOMOIDC-87
New ClientInformationClientSecretCredentialResolver can be used for
resolving only client secret from OIDCClientInformation. This is
useful for signature validation configuration for id_token_hints.
---
...bstractClientInformationCredentialResolver.java | 96 ++++++++++++++
...tInformationClientSecretCredentialResolver.java | 44 +++++++
.../impl/ClientInformationCredentialResolver.java | 36 +----
...ormationClientSecretCredentialResolverTest.java | 145 +++++++++++++++++++++
.../shibboleth/oidc/profile/core/OidcEventIds.java | 7 +-
5 files changed, 293 insertions(+), 35 deletions(-)
diff --git a/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/credential/impl/AbstractClientInformationCredentialResolver.java b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/credential/impl/AbstractClientInformationCredentialResolver.java
new file mode 100644
index 0000000..c93a1e1
--- /dev/null
+++ b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/credential/impl/AbstractClientInformationCredentialResolver.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.oidc.security.credential.impl;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.security.credential.Credential;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.nimbusds.openid.connect.sdk.rp.OIDCClientInformation;
+
+import net.shibboleth.oidc.security.credential.ClientSecretCredential;
+import net.shibboleth.oidc.security.credential.DefaultClientSecretCredential;
+import net.shibboleth.oidc.security.jose.criterion.ClientInformationCriterion;
+import net.shibboleth.shared.component.ComponentInitializationException;
+import net.shibboleth.shared.component.InitializableComponent;
+import net.shibboleth.shared.logic.Constraint;
+import net.shibboleth.shared.resolver.CriteriaSet;
+import net.shibboleth.shared.resolver.ResolverException;
+
+/**
+ * Base class for resolving credentials from {@link OIDCClientInformation}.
+ */
+public abstract class AbstractClientInformationCredentialResolver extends BasicJOSEObjectCredentialResolver
+ implements InitializableComponent {
+
+ /** Class logger. */
+ @Nonnull private final Logger log = LoggerFactory.getLogger(AbstractClientInformationCredentialResolver.class);
+
+ /** Initialization flag. */
+ private boolean isInitialized;
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isInitialized() {
+ return isInitialized;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void initialize() throws ComponentInitializationException {
+ isInitialized = true;
+ }
+
+ /**
+ * Resolves client secret credential.
+ * @param criteriaSet the criteria set containing {@link ClientInformationCriterion}.
+ * @return the set containing {@link ClientSecretCredential} if secret was found from the metadata.
+ */
+ @Nonnull protected Collection<Credential> resolveSecretCredentials(@Nonnull final CriteriaSet criteriaSet) {
+
+ Constraint.isNotNull(criteriaSet, "CriteriaSet was null");
+
+ if (criteriaSet.contains(ClientInformationCriterion.class)) {
+ final OIDCClientInformation information =
+ criteriaSet.get(ClientInformationCriterion.class).getOidcClientInformation();
+
+ if (information.getSecret() != null) {
+ final LinkedHashSet<Credential> credentials = new LinkedHashSet<>(1);
+ try {
+ final ClientSecretCredential secretCred =
+ new DefaultClientSecretCredential(information.getSecret().getValue());
+ final Credential derivedCredential = deriveClientSecretCredential(secretCred, criteriaSet);
+ if (derivedCredential != null) {
+ credentials.add(derivedCredential);
+ }
+ } catch (final ResolverException e) {
+ log.warn("Unable to derive a client_secret based credential", e);
+ }
+ return credentials;
+ }
+
+ } else {
+ log.debug("Criteria did not contain a ClientInformationCriterion could not perform resolution");
+ }
+ return Collections.emptySet();
+ }
+
+}
diff --git a/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/credential/impl/ClientInformationClientSecretCredentialResolver.java b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/credential/impl/ClientInformationClientSecretCredentialResolver.java
new file mode 100644
index 0000000..0f129f9
--- /dev/null
+++ b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/credential/impl/ClientInformationClientSecretCredentialResolver.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.oidc.security.credential.impl;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.security.credential.Credential;
+import org.opensaml.security.credential.impl.AbstractCriteriaFilteringCredentialResolver;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import net.shibboleth.oidc.security.credential.ClientSecretCredential;
+import net.shibboleth.oidc.security.credential.JOSEObjectCredentialResolver;
+import net.shibboleth.shared.resolver.CriteriaSet;
+import net.shibboleth.shared.resolver.ResolverException;
+
+/**
+ * A {@link JOSEObjectCredentialResolver} that resolves client secret from of a ClientInformation. If the information
+ * contains client secret, it's converted into a {@link ClientSecretCredential}. Further filtering of credentials is
+ * provided by the {@link AbstractCriteriaFilteringCredentialResolver} parent class.
+ */
+public class ClientInformationClientSecretCredentialResolver extends AbstractClientInformationCredentialResolver {
+
+ /** Class logger. */
+ @Nonnull private final Logger log = LoggerFactory.getLogger(ClientInformationClientSecretCredentialResolver.class);
+
+ /** {@inheritDoc} */
+ @Override
+ protected Iterable<Credential> resolveFromSource(@Nonnull final CriteriaSet criteriaSet) throws ResolverException {
+ return resolveSecretCredentials(criteriaSet);
+ }
+}
diff --git a/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/credential/impl/ClientInformationCredentialResolver.java b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/credential/impl/ClientInformationCredentialResolver.java
index 538b70c..f2d03ab 100644
--- a/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/credential/impl/ClientInformationCredentialResolver.java
+++ b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/credential/impl/ClientInformationCredentialResolver.java
@@ -33,14 +33,10 @@ import com.nimbusds.openid.connect.sdk.rp.OIDCClientMetadata;
import net.shibboleth.oidc.jwk.RemoteJwkSetCache;
import net.shibboleth.oidc.security.credential.BasicJWKCredential;
-import net.shibboleth.oidc.security.credential.ClientSecretCredential;
-import net.shibboleth.oidc.security.credential.DefaultClientSecretCredential;
import net.shibboleth.oidc.security.credential.JOSEObjectCredentialResolver;
import net.shibboleth.oidc.security.jose.criterion.ClientInformationCriterion;
import net.shibboleth.shared.annotation.ParameterName;
import net.shibboleth.shared.annotation.constraint.Positive;
-import net.shibboleth.shared.component.ComponentInitializationException;
-import net.shibboleth.shared.component.InitializableComponent;
import net.shibboleth.shared.logic.Constraint;
import net.shibboleth.shared.primitive.StringSupport;
import net.shibboleth.shared.resolver.CriteriaSet;
@@ -55,15 +51,11 @@ import net.shibboleth.shared.resolver.ResolverException;
* <p>Note, only RSA or EC keys are resolved as these are the only key types which should be exposed in
* public JWKS documents.</p>
*/
-public class ClientInformationCredentialResolver extends BasicJOSEObjectCredentialResolver
- implements InitializableComponent {
+public class ClientInformationCredentialResolver extends AbstractClientInformationCredentialResolver {
/** Class logger. */
@Nonnull private final Logger log = LoggerFactory.getLogger(ClientInformationCredentialResolver.class);
- /** Initialization flag. */
- private boolean isInitialized;
-
/** The cache for remote JWK key sets. */
@Nonnull private final RemoteJwkSetCache remoteJwkSetCache;
@@ -95,18 +87,6 @@ public class ClientInformationCredentialResolver extends BasicJOSEObjectCredenti
keyFetchInterval = interval;
}
- /** {@inheritDoc} */
- @Override
- public boolean isInitialized() {
- return isInitialized;
- }
-
- /** {@inheritDoc} */
- @Override
- public void initialize() throws ComponentInitializationException {
- isInitialized = true;
- }
-
/** {@inheritDoc} */
@Override
protected Iterable<Credential> resolveFromSource(@Nonnull final CriteriaSet criteriaSet) throws ResolverException {
@@ -140,19 +120,7 @@ public class ClientInformationCredentialResolver extends BasicJOSEObjectCredenti
final LinkedHashSet<Credential> credentials = new LinkedHashSet<>(1);
final OIDCClientMetadata metadata = information.getOIDCMetadata();
- if (information.getSecret() != null) {
- try {
- final ClientSecretCredential secretCred =
- new DefaultClientSecretCredential(information.getSecret().getValue());
- final Credential derivedCredential = deriveClientSecretCredential(secretCred, criteriaSet);
- if (derivedCredential != null) {
- credentials.add(derivedCredential);
- }
- } catch (final ResolverException e) {
- log.warn("Unable to derive a client_secret based credential", e);
- }
- }
-
+ credentials.addAll(resolveSecretCredentials(criteriaSet));
final JWKSet keySet;
if (metadata.getJWKSetURI() != null) {
diff --git a/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/credential/impl/ClientInformationClientSecretCredentialResolverTest.java b/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/credential/impl/ClientInformationClientSecretCredentialResolverTest.java
new file mode 100644
index 0000000..79af86b
--- /dev/null
+++ b/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/credential/impl/ClientInformationClientSecretCredentialResolverTest.java
@@ -0,0 +1,145 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.oidc.security.credential.impl;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.fail;
+
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.annotation.Nonnull;
+
+import org.mockito.Mockito;
+import org.opensaml.security.credential.Credential;
+import org.opensaml.security.credential.UsageType;
+import org.opensaml.security.criteria.UsageCriterion;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.core.io.Resource;
+import org.springframework.util.FileCopyUtils;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.nimbusds.oauth2.sdk.util.JSONObjectUtils;
+import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
+import com.nimbusds.openid.connect.sdk.rp.OIDCClientInformation;
+
+import net.shibboleth.oidc.security.jose.criterion.ClientInformationCriterion;
+import net.shibboleth.oidc.security.jose.criterion.ProviderMetadataCriterion;
+import net.shibboleth.shared.component.InitializableComponent;
+import net.shibboleth.shared.resolver.CriteriaSet;
+
+/**
+ * Unit tests for {@link ClientInformationClientSecretCredentialResolver}.
+ */
+public class ClientInformationClientSecretCredentialResolverTest {
+
+ private static final ClassPathResource CLIENT_INFORMATION_REMOTE_KEYS =
+ new ClassPathResource("/metadata/test-resolver-client-information-remote-jwks.json");
+
+ private static final ClassPathResource CLIENT_INFORMATION_SECRET =
+ new ClassPathResource("/metadata/test-resolver-client-information-secret.json");
+
+ private ClientInformationClientSecretCredentialResolver resolver =
+ new ClientInformationClientSecretCredentialResolver();
+
+ private CriteriaSet criteria;
+
+ @BeforeMethod
+ protected CriteriaSet buildInitialCriteriaSet() throws Exception {
+ criteria = new CriteriaSet();
+
+ criteria.add(new ClientInformationCriterion(
+ OIDCClientInformation.parse(JSONObjectUtils.parse(readJsonFromFile(CLIENT_INFORMATION_REMOTE_KEYS)))));
+ return criteria;
+ }
+
+ @Test
+ public void testFail_IncorrectCriteria() throws Exception {
+ ((InitializableComponent) resolver).initialize();
+ // Needs ProviderMetadataCriterion or ClientInformationCriterion
+ criteria = new CriteriaSet();
+ criteria.add(new ProviderMetadataCriterion(Mockito.mock(OIDCProviderMetadata.class)));
+ final Iterable<Credential> creds = resolver.resolve(criteria);
+
+ assertNotNull(creds);
+ final List<Credential> credsList = new ArrayList<>();
+ creds.forEach(credsList::add);
+ // No keys
+ assertEquals(credsList.size(), 0);
+ }
+
+ @Test
+ public void testSuccess_Secret() throws Exception {
+ ((InitializableComponent) resolver).initialize();
+ criteria = new CriteriaSet();
+
+ criteria.add(new ClientInformationCriterion(
+ OIDCClientInformation.parse(JSONObjectUtils.parse(readJsonFromFile(CLIENT_INFORMATION_SECRET)))));
+
+ criteria.add(new UsageCriterion(UsageType.SIGNING));
+
+ final Iterable<Credential> creds = resolver.resolve(criteria);
+
+ assertNotNull(creds);
+ final List<Credential> credsList = new ArrayList<>();
+ creds.forEach(credsList::add);
+ // No keys
+ assertEquals(credsList.size(), 1);
+
+ }
+
+ @Test
+ public void testSuccess_Secret_noSecretCredentialWhenAsymmetricEncryption() throws Exception {
+ ((InitializableComponent) resolver).initialize();
+ criteria = new CriteriaSet();
+
+ criteria.add(new ClientInformationCriterion(
+ OIDCClientInformation.parse(JSONObjectUtils.parse(readJsonFromFile(CLIENT_INFORMATION_SECRET)))));
+
+ criteria.add(new UsageCriterion(UsageType.ENCRYPTION));
+ criteria.add(new KeyManagmentAlgorithmCriterion("RSA1_5"));
+ criteria.add(new DataEncryptionAlgorithmCriterion("A128GCW"));
+
+ final Iterable<Credential> creds = resolver.resolve(criteria);
+
+ assertNotNull(creds);
+ final List<Credential> credsList = new ArrayList<>();
+ creds.forEach(credsList::add);
+ // No keys nor secret
+ assertEquals(credsList.size(), 0);
+
+ }
+
+ /**
+ * Read a file into a string.
+ *
+ * @param location the location of the file to read
+ *
+ * @return the file as a string
+ */
+ protected String readJsonFromFile(@Nonnull final Resource location) {
+ try (Reader reader = new InputStreamReader(location.getInputStream(), StandardCharsets.UTF_8)) {
+ return FileCopyUtils.copyToString(reader);
+ } catch (final Exception ex) {
+ fail();
+ return null;
+ }
+ }
+}
diff --git a/oidc-common-profile-api/src/main/java/net/shibboleth/oidc/profile/core/OidcEventIds.java b/oidc-common-profile-api/src/main/java/net/shibboleth/oidc/profile/core/OidcEventIds.java
index 6b8821d..d5ddba8 100644
--- a/oidc-common-profile-api/src/main/java/net/shibboleth/oidc/profile/core/OidcEventIds.java
+++ b/oidc-common-profile-api/src/main/java/net/shibboleth/oidc/profile/core/OidcEventIds.java
@@ -98,7 +98,12 @@ public final class OidcEventIds {
* The id_token is invalid.
*/
@Nonnull @NotEmpty public static final String INVALID_ID_TOKEN = "InvalidIdToken";
-
+
+ /**
+ * The id_token_hint is invalid.
+ */
+ @Nonnull @NotEmpty public static final String INVALID_ID_TOKEN_HINT = "InvalidIdTokenHint";
+
/**
* The token is invalid.
*/
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list