[java-idp-oidc] branch main updated: JOIDC-21 - Use token authentication for OIDC dynamic client registration
Henri Mikkonen
henri.mikkonen at iki.fi
Fri Mar 4 13:21:15 UTC 2022
This is an automated email from the git hooks/post-receive script.
hjmikkon pushed a commit to branch main
in repository java-idp-oidc.
View the commit online:
http://git.shibboleth.net/view/?p=java-idp-oidc.git;a=commit;h=d61acc04405e04e389cc4fbd58fba189a2ff8538
The following commit(s) were added to refs/heads/main by this push:
new d61acc04 JOIDC-21 - Use token authentication for OIDC dynamic client registration
d61acc04 is described below
commit d61acc04405e04e389cc4fbd58fba189a2ff8538
Author: Henri Mikkonen <henri.mikkonen at iki.fi>
AuthorDate: Fri Mar 4 15:18:58 2022 +0200
JOIDC-21 - Use token authentication for OIDC dynamic client registration
https://shibboleth.atlassian.net/browse/JOIDC-21
Support one-time flag for the access token: if enabled, the token gets revoked
after it has been used once.
---
.../op/token/support/RegistrationClaimsSet.java | 39 ++++++++++++++-
.../cli/IssueRegistrationAccessTokenArguments.java | 22 +++++++--
.../profile/impl/IssueRegistrationAccessToken.java | 17 +++++++
.../impl/ValidateRegistrationAccessToken.java | 14 ++++++
...tRegistrationTokenOnetimeUseLookupFunction.java | 56 ++++++++++++++++++++++
.../issue-registration-access-token-flow.xml | 1 +
6 files changed, 143 insertions(+), 6 deletions(-)
diff --git a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/token/support/RegistrationClaimsSet.java b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/token/support/RegistrationClaimsSet.java
index 2139be0b..172298ee 100644
--- a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/token/support/RegistrationClaimsSet.java
+++ b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/token/support/RegistrationClaimsSet.java
@@ -25,7 +25,6 @@ import javax.annotation.Nonnull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonGetter;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonSetter;
@@ -82,6 +81,10 @@ public final class RegistrationClaimsSet {
/** Relying party identifier. */
@JsonProperty("rp_id")
private String relyingPartyId;
+
+ /** Flag to signal one-time use of the token. */
+ @JsonProperty("onetime")
+ private Boolean onetime;
/**
* Constructor.
@@ -308,6 +311,24 @@ public final class RegistrationClaimsSet {
public void setRelyingPartyId(final String rpId) {
this.relyingPartyId = rpId;
}
+
+ /**
+ * Get the flag to signal one-time use of the token.
+ *
+ * @return The flag to signal one-time use of the token.
+ */
+ public boolean isOnetime() {
+ return onetime == null ? false : onetime.booleanValue();
+ }
+
+ /**
+ * Set the flag to signal one-time use of the token.
+ *
+ * @param flag What to set.
+ */
+ public void setOnetime(final Boolean flag) {
+ this.onetime = flag;
+ }
/**
* The builder for {@link RegistrationClaimsSet}.
@@ -343,7 +364,10 @@ public final class RegistrationClaimsSet {
/** Relying party identifier. */
private String relyingPartyId;
-
+
+ /** Flag to signal one-time use of the token. */
+ private Boolean onetime;
+
/**
* Constructor.
*
@@ -444,6 +468,16 @@ public final class RegistrationClaimsSet {
return this;
}
+ /**
+ * Set the flag to signal one-time use of the token.
+ * @param flag What to set.
+ * @return The builder instance.
+ */
+ public Builder withOnetime(final Boolean flag) {
+ this.onetime = flag;
+ return this;
+ }
+
/**
* Build the claims set object.
* @return The claims set object.
@@ -460,6 +494,7 @@ public final class RegistrationClaimsSet {
claimsSet.setAuthTime(authTime);
claimsSet.setMetadata(metadata);
claimsSet.setRelyingPartyId(relyingPartyId);
+ claimsSet.setOnetime(onetime);
return claimsSet;
}
}
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/cli/IssueRegistrationAccessTokenArguments.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/cli/IssueRegistrationAccessTokenArguments.java
index 7d42422f..b7580230 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/cli/IssueRegistrationAccessTokenArguments.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/cli/IssueRegistrationAccessTokenArguments.java
@@ -38,19 +38,32 @@ public class IssueRegistrationAccessTokenArguments extends AbstractCommandLineAr
/** The URL parameter name for the relying party identifier. */
public static final String URL_PARAM_RELYING_PARTY_ID = "relyingPartyId";
+
+ /** The URL parameter name for the one-time flag. */
+ public static final String URL_PARAM_ONE_TIME_TOKEN = "onetime";
/** Metadata policy for the requested OIDC dynamic client registration metadata. */
- @Parameter(names = {"-m", "--metadataPolicyLocation"}, required = false, description = "Metadata policy location")
+ @Parameter(names = {"-m", "--metadataPolicyLocation"}, required = true, description = "Metadata policy location")
@Nullable private String metadata;
/** Lifetime for the access token to be issued. */
- @Parameter(names = {"-l", "--lifetime"}, required = false, description = "Lifetime for the access token")
+ @Parameter(names = {"-l", "--lifetime"}, required = true, description = "Lifetime for the access token")
@Nullable private String lifetime;
/** Relying party identifier for the access token to be issued. */
- @Parameter(names = {"-i", "--relyingPartyId"}, required = false, description = "Relying party ID the access token")
+ @Parameter(names = {"-i", "--relyingPartyId"}, required = true, description = "Relying party ID the access token")
@Nullable private String relyingPartyId;
+ /** Flag to signal one-time use of the token. */
+ @Parameter(names = {"-o", "--onetime"}, required = false, description = "Flag to signal one-time use of the token")
+ @Nullable private boolean onetime;
+
+ /**
+ * Constructor.
+ */
+ public IssueRegistrationAccessTokenArguments() {
+ onetime = true;
+ }
/** {@inheritDoc} */
@Override
@@ -87,7 +100,8 @@ public class IssueRegistrationAccessTokenArguments extends AbstractCommandLineAr
.append("&" + URL_PARAM_METADATA_POLICY_LOCATION + "=")
.append(URLEncoder.encode(metadata, "UTF-8"))
.append("&" + URL_PARAM_RELYING_PARTY_ID + "=")
- .append(URLEncoder.encode(relyingPartyId, "UTF-8"));
+ .append(URLEncoder.encode(relyingPartyId, "UTF-8"))
+ .append("&" + URL_PARAM_ONE_TIME_TOKEN + "=" + onetime);
} catch (final UnsupportedEncodingException e) {
// UTF-8 is a required encoding.
throw new RuntimeException("URL encoding failed", e);
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/IssueRegistrationAccessToken.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/IssueRegistrationAccessToken.java
index c74eeaeb..0c51aa1c 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/IssueRegistrationAccessToken.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/IssueRegistrationAccessToken.java
@@ -41,6 +41,7 @@ import com.nimbusds.oauth2.sdk.token.BearerAccessToken;
import com.nimbusds.oauth2.sdk.token.Tokens;
import net.shibboleth.idp.plugin.oidc.op.profile.logic.DefaultRegistrationTokenLifetimeLookupFunction;
+import net.shibboleth.idp.plugin.oidc.op.profile.logic.DefaultRegistrationTokenOnetimeUseLookupFunction;
import net.shibboleth.idp.plugin.oidc.op.profile.logic.DefaultRegistrationTokenRelyingPartyIdLookupFunction;
import net.shibboleth.idp.plugin.oidc.op.token.support.RegistrationClaimsSet;
import net.shibboleth.idp.profile.AbstractProfileAction;
@@ -92,6 +93,9 @@ public class IssueRegistrationAccessToken extends AbstractProfileAction {
/** Lookup function for the relying party identifier. */
@Nonnull private Function<ProfileRequestContext, String> relyingPartyIdLookupStrategy;
+
+ /** Lookup function for the flag signaling one-time use of the token. */
+ @Nonnull private Function<ProfileRequestContext, Boolean> onetimeUseLookupStrategy;
/** The identifier generator to use. */
@Nullable private IdentifierGenerationStrategy idGenerator;
@@ -115,6 +119,7 @@ public class IssueRegistrationAccessToken extends AbstractProfileAction {
idGeneratorLookupStrategy = FunctionSupport.constant(new SecureRandomIdentifierGenerationStrategy());
tokenLifetimeLookupStrategy = new DefaultRegistrationTokenLifetimeLookupFunction();
relyingPartyIdLookupStrategy = new DefaultRegistrationTokenRelyingPartyIdLookupFunction();
+ onetimeUseLookupStrategy = new DefaultRegistrationTokenOnetimeUseLookupFunction();
}
/**
@@ -199,6 +204,17 @@ public class IssueRegistrationAccessToken extends AbstractProfileAction {
"Relying party ID lookup strategy cannot be null");
}
+ /**
+ * Set a lookup strategy for the flag signaling one-time use of the token.
+ *
+ * @param strategy lookup strategy
+ */
+ public void setOnetimeUseLookupStrategy(@Nonnull final Function<ProfileRequestContext, Boolean> strategy) {
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+
+ onetimeUseLookupStrategy = Constraint.isNotNull(strategy, "One-time use lookup strategy cannot be null");
+ }
+
/** {@inheritDoc} */
@Override
protected void doInitialize() throws ComponentInitializationException {
@@ -300,6 +316,7 @@ public class IssueRegistrationAccessToken extends AbstractProfileAction {
.withMetadata(metadataPolicy)
.withExpiration(exp)
.withRelyingPartyId(relyingPartyId)
+ .withOnetime(onetimeUseLookupStrategy.apply(profileRequestContext))
.build();
//TODO: possible end-user authentication claims:
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateRegistrationAccessToken.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateRegistrationAccessToken.java
index 2f7b0c6c..2f57507e 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateRegistrationAccessToken.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateRegistrationAccessToken.java
@@ -17,6 +17,7 @@
package net.shibboleth.idp.plugin.oidc.op.profile.impl;
+import java.time.Duration;
import java.time.Instant;
import java.util.function.Function;
@@ -235,6 +236,19 @@ public class ValidateRegistrationAccessToken extends AbstractOIDCRequestAction<O
return;
}
log.debug("{} registration access token {} successfully validated", getLogPrefix(), claimsSet.getJti());
+ if (claimsSet.isOnetime()) {
+ //TODO clockskew
+ final boolean revoked = revocationCache.revoke(RevocationCacheContexts.REGISTRATION_ACCESS_TOKEN,
+ claimsSet.getJti(), Duration.between(Instant.now(), claimsSet.getExpiration()));
+ if (revoked) {
+ log.debug("{} Successfully revoked the one-time token with jti {}", getLogPrefix(),
+ claimsSet.getJti());
+ } else {
+ log.error("{} Could not revoke a one-time token with jti {}", getLogPrefix(), claimsSet.getJti());
+ ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
+ return;
+ }
+ }
relyingPartyContext.setVerified(true);
relyingPartyContext.setRelyingPartyId(relyingPartyId);
registrationClaimsContext.setClaimsSet(claimsSet);
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/logic/DefaultRegistrationTokenOnetimeUseLookupFunction.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/logic/DefaultRegistrationTokenOnetimeUseLookupFunction.java
new file mode 100644
index 00000000..e55a2e37
--- /dev/null
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/logic/DefaultRegistrationTokenOnetimeUseLookupFunction.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements. See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You 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.idp.plugin.oidc.op.profile.logic;
+
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.springframework.webflow.execution.RequestContext;
+
+import net.shibboleth.idp.plugin.oidc.op.cli.IssueRegistrationAccessTokenArguments;
+import net.shibboleth.idp.profile.context.SpringRequestContext;
+
+/**
+ * A lookup function that fetches the flag signaling one-time use of the token from the SWF request parameters.
+ *
+ * The parameter key is {@link IssueRegistrationAccessTokenArguments#URL_PARAM_ONE_TIME_TOKEN}.
+ */
+public class DefaultRegistrationTokenOnetimeUseLookupFunction implements Function<ProfileRequestContext, Boolean> {
+
+ /** {@inheritDoc} */
+ @Override @Nullable
+ public Boolean apply(final @Nonnull ProfileRequestContext profileRequestContext) {
+ final SpringRequestContext springRequestContext =
+ profileRequestContext.getSubcontext(SpringRequestContext.class);
+ if (springRequestContext == null) {
+ return null;
+ }
+
+ final RequestContext requestContext = springRequestContext.getRequestContext();
+ if (requestContext == null) {
+ return null;
+ }
+
+ final String result = (String) requestContext.getFlowScope().get(
+ IssueRegistrationAccessTokenArguments.URL_PARAM_ONE_TIME_TOKEN);
+ return Boolean.valueOf(result);
+ }
+}
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/admin/oidc/issue-registration-access-token/issue-registration-access-token-flow.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/admin/oidc/issue-registration-access-token/issue-registration-access-token-flow.xml
index 42f2dcd7..4f07ff68 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/admin/oidc/issue-registration-access-token/issue-registration-access-token-flow.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/admin/oidc/issue-registration-access-token/issue-registration-access-token-flow.xml
@@ -10,6 +10,7 @@
<evaluate expression="T(net.shibboleth.utilities.java.support.primitive.StringSupport).trimOrNull(externalContext.getNativeRequest().getParameter('tokenLifetime'))" result="flowScope.tokenLifetime" />
<evaluate expression="T(net.shibboleth.utilities.java.support.primitive.StringSupport).trimOrNull(externalContext.getNativeRequest().getParameter('metadataPolicyLocation'))" result="flowScope.metadataPolicyLocation" />
<evaluate expression="T(net.shibboleth.utilities.java.support.primitive.StringSupport).trimOrNull(externalContext.getNativeRequest().getParameter('relyingPartyId'))" result="flowScope.relyingPartyId" />
+ <evaluate expression="T(net.shibboleth.utilities.java.support.primitive.StringSupport).trimOrNull(externalContext.getNativeRequest().getParameter('onetime'))" result="flowScope.onetime" />
</on-start>
<action-state id="InitializeProfileRequestContext">
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list