[java-idp-oidc] branch main updated: Allow client_id claim to pass through access token into reg flow.
Scott Cantor
cantor.2 at osu.edu
Wed Mar 9 22:05:45 UTC 2022
This is an automated email from the git hooks/post-receive script.
scantor pushed a commit to branch main
in repository java-idp-oidc.
View the commit online:
http://git.shibboleth.net/view/?p=java-idp-oidc.git;a=commit;h=08e580665a09fa36d9f41cdea9a3068d8feefcd9
The following commit(s) were added to refs/heads/main by this push:
new 08e58066 Allow client_id claim to pass through access token into reg flow.
08e58066 is described below
commit 08e580665a09fa36d9f41cdea9a3068d8feefcd9
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Wed Mar 9 17:04:14 2022 -0500
Allow client_id claim to pass through access token into reg flow.
---
.../OIDCClientRegistrationTokenClaimsContext.java | 25 +--
...gistrationTokenClaimsContextLookupFunction.java | 2 +-
...istrationTokenMetadataPolicyLookupFunction.java | 10 +-
.../op/token/support/RegistrationClaimsSet.java | 231 +++++++++++++--------
.../oidc/op/profile/impl/GenerateClientID.java | 76 +++++--
...nitializeRegistrationMetadataPolicyContext.java | 11 +-
.../impl/ValidateRegistrationAccessToken.java | 41 ++--
.../idp/flows/oidc/register/register-beans.xml | 37 ++--
.../oidc/op/profile/flow/RegistrationFlowTest.java | 110 ++++++----
9 files changed, 328 insertions(+), 215 deletions(-)
diff --git a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/messaging/context/OIDCClientRegistrationTokenClaimsContext.java b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/messaging/context/OIDCClientRegistrationTokenClaimsContext.java
index 62e94440..db8383e6 100644
--- a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/messaging/context/OIDCClientRegistrationTokenClaimsContext.java
+++ b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/messaging/context/OIDCClientRegistrationTokenClaimsContext.java
@@ -17,9 +17,11 @@
package net.shibboleth.idp.plugin.oidc.op.messaging.context;
+import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.opensaml.messaging.context.BaseContext;
+import org.opensaml.messaging.context.MessageContext;
import net.shibboleth.idp.plugin.oidc.op.token.support.RegistrationClaimsSet;
@@ -27,29 +29,27 @@ import net.shibboleth.idp.plugin.oidc.op.token.support.RegistrationClaimsSet;
* Subcontext carrying information on the claims included in the initial access token used for accessing the dynamic
* client registration endpoint.
*
- * This context appears as a subcontext of the (inbound) {@link org.opensaml.messaging.context.MessageContext}.
+ * This context appears as a subcontext of the (inbound) {@link MessageContext}.
*
* @since 3.1.0
*/
-public class OIDCClientRegistrationTokenClaimsContext extends BaseContext {
+public final class OIDCClientRegistrationTokenClaimsContext extends BaseContext {
/** The claims set in the initial access token. */
@Nullable private RegistrationClaimsSet claimsSet;
- /**
- * Constructor.
- */
- public OIDCClientRegistrationTokenClaimsContext() {
- // no op
- }
-
/**
* Set the claims set in the initial access token.
*
- * @param set What to set.
+ * @param set What to set
+ *
+ * @return this context
*/
- public void setClaimsSet(@Nullable final RegistrationClaimsSet set) {
+ @Nonnull public OIDCClientRegistrationTokenClaimsContext setClaimsSet(
+ @Nullable final RegistrationClaimsSet set) {
claimsSet = set;
+
+ return this;
}
/**
@@ -60,4 +60,5 @@ public class OIDCClientRegistrationTokenClaimsContext extends BaseContext {
@Nullable public RegistrationClaimsSet getClaimsSet() {
return claimsSet;
}
-}
+
+}
\ No newline at end of file
diff --git a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/DefaultOIDCClientRegistrationTokenClaimsContextLookupFunction.java b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/DefaultOIDCClientRegistrationTokenClaimsContextLookupFunction.java
index 382b7eaa..85ed056d 100644
--- a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/DefaultOIDCClientRegistrationTokenClaimsContextLookupFunction.java
+++ b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/DefaultOIDCClientRegistrationTokenClaimsContextLookupFunction.java
@@ -37,6 +37,6 @@ public class DefaultOIDCClientRegistrationTokenClaimsContextLookupFunction
if (input == null || input.getInboundMessageContext() == null) {
return null;
}
- return input.getInboundMessageContext().getSubcontext(OIDCClientRegistrationTokenClaimsContext.class, false);
+ return input.getInboundMessageContext().getSubcontext(OIDCClientRegistrationTokenClaimsContext.class);
}
}
diff --git a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/DefaultOIDCClientRegistrationTokenMetadataPolicyLookupFunction.java b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/DefaultOIDCClientRegistrationTokenMetadataPolicyLookupFunction.java
index d41bc52f..ed800b32 100644
--- a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/DefaultOIDCClientRegistrationTokenMetadataPolicyLookupFunction.java
+++ b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/DefaultOIDCClientRegistrationTokenMetadataPolicyLookupFunction.java
@@ -18,7 +18,6 @@
package net.shibboleth.idp.plugin.oidc.op.profile.context.navigate;
import java.util.Map;
-import java.util.function.Function;
import javax.annotation.Nullable;
@@ -37,15 +36,14 @@ public class DefaultOIDCClientRegistrationTokenMetadataPolicyLookupFunction impl
ContextDataLookupFunction<ProfileRequestContext, Map<String, MetadataPolicy>> {
/** {@inheritDoc} */
- @Override @Nullable
- public Map<String, MetadataPolicy> apply(@Nullable ProfileRequestContext profileRequestContext) {
- final Function<ProfileRequestContext, OIDCClientRegistrationTokenClaimsContext> regTokenCtxLookup =
- new DefaultOIDCClientRegistrationTokenClaimsContextLookupFunction();
- final OIDCClientRegistrationTokenClaimsContext regTokenCtx = regTokenCtxLookup.apply(profileRequestContext);
+ @Nullable public Map<String, MetadataPolicy> apply(@Nullable final ProfileRequestContext profileRequestContext) {
+ final OIDCClientRegistrationTokenClaimsContext regTokenCtx =
+ new DefaultOIDCClientRegistrationTokenClaimsContextLookupFunction().apply(profileRequestContext);
if (regTokenCtx == null) {
return null;
}
final RegistrationClaimsSet claimsSet = regTokenCtx.getClaimsSet();
return claimsSet == null ? null : claimsSet.getMetadata();
}
+
}
\ No newline at end of file
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 172298ee..2c732127 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
@@ -21,6 +21,7 @@ import java.time.Instant;
import java.util.Map;
import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -30,6 +31,12 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonSetter;
import net.shibboleth.oidc.metadata.policy.MetadataPolicy;
+import net.shibboleth.utilities.java.support.annotation.constraint.NonNegative;
+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.NotLive;
+import net.shibboleth.utilities.java.support.annotation.constraint.Unmodifiable;
+import net.shibboleth.utilities.java.support.primitive.StringSupport;
/**
* Class wrapping claims for the initial registration access token.
@@ -44,47 +51,51 @@ public final class RegistrationClaimsSet {
/** Identifier for the token. */
@JsonProperty("jti")
- private String jti;
+ @Nullable @NotEmpty private String jti;
/** Type of the token. */
@JsonProperty("type")
- private String keyType;
+ @Nullable @NotEmpty private String keyType;
/** OP issuer. */
@JsonProperty("iss")
- private String issuer;
+ @Nullable @NotEmpty private String issuer;
/** Expiration time of the token. */
@JsonProperty("exp")
- private Instant expiration;
+ @Nullable private Instant expiration;
/** Issuance time of the token. */
@JsonProperty("iat")
- private Instant issuedAt;
+ @Nullable private Instant issuedAt;
/** The principal who issued the token. */
@JsonProperty("prncpl")
- private String principal;
+ @Nullable @NotEmpty private String principal;
/** Authentication context class reference value of the performed authentication. */
@JsonProperty("acr")
- private String authContext;
+ @Nullable @NotEmpty private String authContext;
/** Authentication time of the performed authentication. */
@JsonProperty("auth_time")
- private Instant authTime;
+ @Nullable private Instant authTime;
/** Allowed metadata values to be issued with the token. */
@JsonProperty("metadata")
- private Map<String, MetadataPolicy> metadata;
+ @Nullable @NonnullElements private Map<String,MetadataPolicy> metadata;
/** Relying party identifier. */
@JsonProperty("rp_id")
- private String relyingPartyId;
-
+ @Nullable @NotEmpty private String relyingPartyId;
+
+ /** Client identifier. */
+ @JsonProperty("client_id")
+ @Nullable @NotEmpty private String clientId;
+
/** Flag to signal one-time use of the token. */
@JsonProperty("onetime")
- private Boolean onetime;
+ @Nullable private Boolean onetime;
/**
* Constructor.
@@ -97,7 +108,7 @@ public final class RegistrationClaimsSet {
*
* @return The identifier for the token.
*/
- public String getJti() {
+ @Nullable @NotEmpty public String getJti() {
return jti;
}
@@ -106,8 +117,8 @@ public final class RegistrationClaimsSet {
*
* @param id What to set.
*/
- public void setJti(final String id) {
- this.jti = id;
+ public void setJti(@Nullable @NotEmpty final String id) {
+ jti = StringSupport.trimOrNull(id);
}
/**
@@ -115,7 +126,7 @@ public final class RegistrationClaimsSet {
*
* @return The type of the token.
*/
- public String getKeyType() {
+ @Nullable @NotEmpty public String getKeyType() {
return keyType;
}
@@ -124,8 +135,8 @@ public final class RegistrationClaimsSet {
*
* @param type What to set.
*/
- public void setKeyType(final String type) {
- this.keyType = type;
+ public void setKeyType(@Nullable @NotEmpty final String type) {
+ keyType = type;
}
/**
@@ -133,7 +144,7 @@ public final class RegistrationClaimsSet {
*
* @return The issuer of the token.
*/
- public String getIssuer() {
+ @Nullable @NotEmpty public String getIssuer() {
return issuer;
}
@@ -142,8 +153,8 @@ public final class RegistrationClaimsSet {
*
* @param iss What to set.
*/
- public void setIssuer(final String iss) {
- this.issuer = iss;
+ public void setIssuer(@Nullable @NotEmpty final String iss) {
+ issuer = StringSupport.trimOrNull(iss);
}
/**
@@ -151,7 +162,7 @@ public final class RegistrationClaimsSet {
*
* @return The expiration time of the token.
*/
- public Instant getExpiration() {
+ @Nullable public Instant getExpiration() {
return expiration;
}
@@ -160,8 +171,8 @@ public final class RegistrationClaimsSet {
*
* @param exp What to set.
*/
- public void setExpiration(final Instant exp) {
- this.expiration = exp;
+ public void setExpiration(@Nullable final Instant exp) {
+ expiration = exp;
}
/**
@@ -170,8 +181,8 @@ public final class RegistrationClaimsSet {
* @return The expiration time of the token using epoch seconds.
*/
@JsonGetter("exp")
- public long getExpirationEpoch() {
- return expiration.getEpochSecond();
+ @NonNegative public long getExpirationEpoch() {
+ return expiration != null ? expiration.getEpochSecond() : 0;
}
/**
@@ -180,8 +191,8 @@ public final class RegistrationClaimsSet {
* @param exp What to set.
*/
@JsonSetter("exp")
- public void setExpirationEpoch(final long exp) {
- this.expiration = Instant.ofEpochSecond(exp);
+ public void setExpirationEpoch(@NonNegative final long exp) {
+ expiration = Instant.ofEpochSecond(exp);
}
/**
@@ -189,7 +200,7 @@ public final class RegistrationClaimsSet {
*
* @return The issuance time of the token.
*/
- public Instant getIssuedAt() {
+ @Nullable public Instant getIssuedAt() {
return issuedAt;
}
@@ -198,8 +209,8 @@ public final class RegistrationClaimsSet {
*
* @param iat What to set.
*/
- public void setIssuedAt(final Instant iat) {
- this.issuedAt = iat;
+ public void setIssuedAt(@Nullable final Instant iat) {
+ issuedAt = iat;
}
/**
@@ -208,8 +219,8 @@ public final class RegistrationClaimsSet {
* @param iat What to set.
*/
@JsonSetter("iat")
- public void setIssuedAtEpoch(final long iat) {
- this.issuedAt = Instant.ofEpochSecond(iat);
+ public void setIssuedAtEpoch(@NonNegative final long iat) {
+ issuedAt = Instant.ofEpochSecond(iat);
}
/**
@@ -218,8 +229,8 @@ public final class RegistrationClaimsSet {
* @return The issuance time of the token using epoch seconds.
*/
@JsonGetter("iat")
- public long getIssuedAtEpoch() {
- return issuedAt.getEpochSecond();
+ @NonNegative public long getIssuedAtEpoch() {
+ return issuedAt != null ? issuedAt.getEpochSecond() : 0;
}
/**
@@ -227,17 +238,17 @@ public final class RegistrationClaimsSet {
*
* @return The principal who issued the token.
*/
- public String getPrincipal() {
+ @Nullable @NotEmpty public String getPrincipal() {
return principal;
}
/**
* Set the principal who issued the token.
*
- * @param prncpl What to set.
+ * @param prin What to set.
*/
- public void setPrincipal(final String prncpl) {
- this.principal = prncpl;
+ public void setPrincipal(@Nullable @NotEmpty final String prin) {
+ principal = prin;
}
/**
@@ -245,7 +256,7 @@ public final class RegistrationClaimsSet {
*
* @return The authentication context class reference value of the performed authentication.
*/
- public String getAuthContext() {
+ @Nullable @NotEmpty public String getAuthContext() {
return authContext;
}
@@ -254,8 +265,8 @@ public final class RegistrationClaimsSet {
*
* @param acr What to set.
*/
- public void setAuthContext(final String acr) {
- this.authContext = acr;
+ public void setAuthContext(@Nullable @NotEmpty final String acr) {
+ authContext = StringSupport.trimOrNull(acr);
}
/**
@@ -263,7 +274,7 @@ public final class RegistrationClaimsSet {
*
* @return The authentication time of the performed authentication.
*/
- public Instant getAuthTime() {
+ @Nullable public Instant getAuthTime() {
return authTime;
}
@@ -272,8 +283,8 @@ public final class RegistrationClaimsSet {
*
* @param time What to set.
*/
- public void setAuthTime(final Instant time) {
- this.authTime = time;
+ public void setAuthTime(@Nullable final Instant time) {
+ authTime = time;
}
/**
@@ -281,7 +292,7 @@ public final class RegistrationClaimsSet {
*
* @return The allowed metadata values to be issued with the token.
*/
- public Map<String, MetadataPolicy> getMetadata() {
+ @Nullable @NonnullElements @NotLive @Unmodifiable public Map<String, MetadataPolicy> getMetadata() {
return metadata;
}
@@ -290,8 +301,8 @@ public final class RegistrationClaimsSet {
*
* @param data What to set.
*/
- public void setMetadata(final Map<String, MetadataPolicy> data) {
- this.metadata = data;
+ public void setMetadata(@Nullable @NonnullElements final Map<String, MetadataPolicy> data) {
+ metadata = data != null ? Map.copyOf(data) : null;
}
/**
@@ -299,19 +310,41 @@ public final class RegistrationClaimsSet {
*
* @return The relying party identifier.
*/
- public String getRelyingPartyId() {
+ @Nullable @NotEmpty public String getRelyingPartyId() {
return relyingPartyId;
}
/**
* Set the relying party identifier.
*
- * @param rpId What to set.
+ * @param id id to set
+ */
+ public void setRelyingPartyId(@Nullable @NotEmpty final String id) {
+ relyingPartyId = StringSupport.trimOrNull(id);
+ }
+
+ /**
+ * Get the client identifier.
+ *
+ * <p>This is may or may not be the same as the relying party identifier,
+ * and if set is a signal to the registration flow to use this value rather than
+ * a generated one.</p>
+ *
+ * @return client identifier.
*/
- public void setRelyingPartyId(final String rpId) {
- this.relyingPartyId = rpId;
+ @Nullable @NotEmpty public String getClientId() {
+ return clientId;
}
+ /**
+ * Set the client identifier.
+ *
+ * @param id id to set
+ */
+ public void setClientId(@Nullable @NotEmpty final String id) {
+ clientId = StringSupport.trimOrNull(id);
+ }
+
/**
* Get the flag to signal one-time use of the token.
*
@@ -326,8 +359,8 @@ public final class RegistrationClaimsSet {
*
* @param flag What to set.
*/
- public void setOnetime(final Boolean flag) {
- this.onetime = flag;
+ public void setOnetime(@Nullable final Boolean flag) {
+ onetime = flag;
}
/**
@@ -336,46 +369,49 @@ public final class RegistrationClaimsSet {
public static class Builder {
/** Identifier for the token. */
- private String jti;
+ @Nullable @NotEmpty private String jti;
/** Type of the token. */
- private String keyType;
+ @Nullable @NotEmpty private String keyType;
/** Issuer of the token. */
- private String issuer;
+ @Nullable @NotEmpty private String issuer;
/** Expiration time of the token. */
- private Instant expiration;
+ @Nullable private Instant expiration;
/** Issuance time of the token. */
- private Instant issuedAt;
+ @Nullable private Instant issuedAt;
/** The principal who issued the token. */
- private String principal;
+ @Nullable @NotEmpty private String principal;
/** The authentication context class reference value of the performed authentication. */
- private String authContext;
+ @Nullable @NotEmpty private String authContext;
/** The authentication time of the performed authentication. */
- private Instant authTime;
+ @Nullable private Instant authTime;
/** Allowed metadata values to be issued with the token. */
- private Map<String, MetadataPolicy> metadata;
+ @Nullable @NonnullElements private Map<String,MetadataPolicy> metadata;
/** Relying party identifier. */
- private String relyingPartyId;
+ @Nullable @NotEmpty private String relyingPartyId;
+
+ /** Client identifier. */
+ @Nullable @NotEmpty private String clientId;
/** Flag to signal one-time use of the token. */
- private Boolean onetime;
+ @Nullable private Boolean onetime;
/**
* Constructor.
*
* @param id The identifier for the token.
*/
- public Builder(final String id) {
- this.jti = id;
- this.keyType = VALUE_TYPE_RT;
+ public Builder(@Nullable @NotEmpty final String id) {
+ jti = id;
+ keyType = VALUE_TYPE_RT;
}
/**
@@ -383,8 +419,8 @@ public final class RegistrationClaimsSet {
* @param type What to set.
* @return The builder instance.
*/
- public Builder withType(final String type) {
- this.keyType = type;
+ public Builder withType(@Nullable @NotEmpty final String type) {
+ keyType = type;
return this;
}
@@ -393,8 +429,8 @@ public final class RegistrationClaimsSet {
* @param iss What to set.
* @return The builder instance.
*/
- public Builder withIssuer(final String iss) {
- this.issuer = iss;
+ public Builder withIssuer(@Nullable @NotEmpty final String iss) {
+ issuer = iss;
return this;
}
@@ -403,8 +439,8 @@ public final class RegistrationClaimsSet {
* @param exp What to set.
* @return The builder instance.
*/
- public Builder withExpiration(final Instant exp) {
- this.expiration = exp;
+ public Builder withExpiration(@Nullable final Instant exp) {
+ expiration = exp;
return this;
}
@@ -413,8 +449,8 @@ public final class RegistrationClaimsSet {
* @param iat What to set.
* @return The builder instance.
*/
- public Builder withIssuedAt(final Instant iat) {
- this.issuedAt = iat;
+ public Builder withIssuedAt(@Nullable final Instant iat) {
+ issuedAt = iat;
return this;
}
@@ -423,8 +459,8 @@ public final class RegistrationClaimsSet {
* @param prncpl What to set.
* @return The builder instance.
*/
- public Builder withPrincipal(final String prncpl) {
- this.principal = prncpl;
+ public Builder withPrincipal(@Nullable @NotEmpty final String prncpl) {
+ principal = prncpl;
return this;
}
@@ -433,8 +469,8 @@ public final class RegistrationClaimsSet {
* @param acr What to set.
* @return The builder instance.
*/
- public Builder withAcr(final String acr) {
- this.authContext = acr;
+ public Builder withAcr(@Nullable @NotEmpty final String acr) {
+ authContext = acr;
return this;
}
@@ -443,8 +479,8 @@ public final class RegistrationClaimsSet {
* @param time What to set.
* @return The builder instance.
*/
- public Builder withAuthTime(final Instant time) {
- this.authTime = time;
+ public Builder withAuthTime(@Nullable final Instant time) {
+ authTime = time;
return this;
}
@@ -453,28 +489,38 @@ public final class RegistrationClaimsSet {
* @param data What to set.
* @return The builder instance.
*/
- public Builder withMetadata(final Map<String, MetadataPolicy> data) {
- this.metadata = data;
+ public Builder withMetadata(@Nullable @NonnullElements final Map<String, MetadataPolicy> data) {
+ metadata = data != null ? Map.copyOf(data) : null;
return this;
}
/**
* Set the relying party identifier.
- * @param rpId What to set.
+ * @param id What to set.
* @return The builder instance.
*/
- public Builder withRelyingPartyId(final String rpId) {
- this.relyingPartyId = rpId;
+ public Builder withRelyingPartyId(@Nullable @NotEmpty final String id) {
+ relyingPartyId = id;
return this;
}
-
+
+ /**
+ * Set the client identifier.
+ * @param id What to set
+ * @return The builder instance.
+ */
+ public Builder withClientId(@Nullable @NotEmpty final String id) {
+ clientId = id;
+ 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;
+ public Builder withOnetime(@Nullable final Boolean flag) {
+ onetime = flag;
return this;
}
@@ -482,7 +528,7 @@ public final class RegistrationClaimsSet {
* Build the claims set object.
* @return The claims set object.
*/
- public RegistrationClaimsSet build() {
+ @Nonnull public RegistrationClaimsSet build() {
final RegistrationClaimsSet claimsSet = new RegistrationClaimsSet();
claimsSet.setJti(jti);
claimsSet.setKeyType(keyType);
@@ -494,9 +540,10 @@ public final class RegistrationClaimsSet {
claimsSet.setAuthTime(authTime);
claimsSet.setMetadata(metadata);
claimsSet.setRelyingPartyId(relyingPartyId);
+ claimsSet.setClientId(clientId);
claimsSet.setOnetime(onetime);
return claimsSet;
}
}
-}
+}
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/GenerateClientID.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/GenerateClientID.java
index c8c30563..081e553f 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/GenerateClientID.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/GenerateClientID.java
@@ -27,10 +27,13 @@ import org.opensaml.messaging.context.navigate.ChildContextLookup;
import org.opensaml.profile.action.ActionSupport;
import org.opensaml.profile.action.EventIds;
import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.profile.context.navigate.OutboundMessageContextLookup;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCClientRegistrationResponseContext;
+import net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCClientRegistrationTokenClaimsContext;
+import net.shibboleth.idp.plugin.oidc.op.profile.context.navigate.DefaultOIDCClientRegistrationTokenClaimsContextLookupFunction;
import net.shibboleth.idp.profile.AbstractProfileAction;
import net.shibboleth.idp.profile.IdPEventIds;
import net.shibboleth.idp.profile.config.ProfileConfiguration;
@@ -40,36 +43,46 @@ import net.shibboleth.utilities.java.support.logic.Constraint;
import net.shibboleth.utilities.java.support.security.IdentifierGenerationStrategy;
/**
- * Creates a new client ID with the {@link IdentifierGenerationStrategy} attached to the profile configuration. The
- * client ID is included to the {@link OIDCClientRegistrationResponseContext}.
+ * Creates the client ID for the registration.
+ *
+ * <p>If an access token was used, it may contain the client_id to use. Otherwise uses
+ * the {@link IdentifierGenerationStrategy} attached to the profile configuration. The
+ * client ID is included to the {@link OIDCClientRegistrationResponseContext}.</p>
*/
public class GenerateClientID extends AbstractProfileAction {
/** Class logger. */
- @Nonnull
- private final Logger log = LoggerFactory.getLogger(GenerateClientID.class);
+ @Nonnull private final Logger log = LoggerFactory.getLogger(GenerateClientID.class);
/**
* Strategy used to locate the {@link RelyingPartyContext} associated with a given {@link ProfileRequestContext}.
*/
@Nonnull private Function<ProfileRequestContext,RelyingPartyContext> relyingPartyContextLookupStrategy;
- /**
- * Strategy used to locate the {@link OIDCClientRegistrationResponseContext} associated with a given
- * {@link MessageContext}.
- */
- @Nonnull private Function<MessageContext,OIDCClientRegistrationResponseContext> oidcResponseContextLookupStrategy;
+ /** Strategy used to locate the {@link OIDCClientRegistrationResponseContext} associated with a given request. */
+ @Nonnull private Function<ProfileRequestContext,OIDCClientRegistrationResponseContext>
+ oidcResponseContextLookupStrategy;
+ /** Strategy used to locate the {@link OIDCClientRegistrationTokenClaimsContext} associated with the request. */
+ @Nonnull private Function<ProfileRequestContext,OIDCClientRegistrationTokenClaimsContext>
+ registrationTokenContextLookupStrategy;
+
/** The RelyingPartyContext to operate on. */
@Nullable private RelyingPartyContext rpCtx;
-
+
/** The OIDCClientRegistrationResponseContext to create the client ID to. */
@Nullable private OIDCClientRegistrationResponseContext oidcResponseCtx;
+
+ /** The OIDCClientRegistrationTokenClaimsContext from which to optionally obtain client ID. */
+ @Nullable private OIDCClientRegistrationTokenClaimsContext registrationTokenCtx;
/** Constructor. */
public GenerateClientID() {
relyingPartyContextLookupStrategy = new ChildContextLookup<>(RelyingPartyContext.class);
- oidcResponseContextLookupStrategy = new ChildContextLookup<>(OIDCClientRegistrationResponseContext.class);
+ oidcResponseContextLookupStrategy = new ChildContextLookup<>(
+ OIDCClientRegistrationResponseContext.class).compose(
+ new OutboundMessageContextLookup());
+ registrationTokenContextLookupStrategy = new DefaultOIDCClientRegistrationTokenClaimsContextLookupFunction();
}
/**
@@ -95,13 +108,28 @@ public class GenerateClientID extends AbstractProfileAction {
* given {@link MessageContext}
*/
public void setOidcResponseContextLookupStrategy(
- @Nonnull final Function<MessageContext,OIDCClientRegistrationResponseContext> strategy) {
+ @Nonnull final Function<ProfileRequestContext,OIDCClientRegistrationResponseContext> strategy) {
ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
oidcResponseContextLookupStrategy = Constraint.isNotNull(strategy,
"OIDCClientRegistrationResponseContext lookup strategy cannot be null");
}
+
+ /**
+ * Set the strategy used to locate the {@link OIDCClientRegistrationTokenClaimsContext} associated with a given
+ * request.
+ *
+ * @param strategy lookup strategy
+ */
+ public void setRegistrationTokenContextLookupStrategy(
+ @Nonnull final Function<ProfileRequestContext,OIDCClientRegistrationTokenClaimsContext> strategy) {
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+
+ registrationTokenContextLookupStrategy = Constraint.isNotNull(strategy,
+ "OIDCClientRegistrationTokenClaimsContext lookup strategy cannot be null");
+ }
+
/** {@inheritDoc} */
@Override
protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
@@ -129,13 +157,18 @@ public class GenerateClientID extends AbstractProfileAction {
return false;
}
- oidcResponseCtx = oidcResponseContextLookupStrategy.apply(profileRequestContext.getOutboundMessageContext());
+ oidcResponseCtx = oidcResponseContextLookupStrategy.apply(profileRequestContext);
if (oidcResponseCtx == null) {
log.debug("{} No OIDC client registration response context associated with this profile request",
getLogPrefix());
ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MSG_CTX);
return false;
}
+
+ registrationTokenCtx = registrationTokenContextLookupStrategy.apply(profileRequestContext);
+ if (registrationTokenCtx != null && registrationTokenCtx.getClaimsSet() == null) {
+ registrationTokenCtx = null;
+ }
return true;
}
@@ -147,8 +180,19 @@ public class GenerateClientID extends AbstractProfileAction {
final IdentifierGenerationStrategy idGenerator =
profileConfig.getSecurityConfiguration(profileRequestContext).getIdGenerator();
- final String clientId = idGenerator.generateIdentifier();
+ String clientId = null;
+ if (registrationTokenCtx != null) {
+ clientId = registrationTokenCtx.getClaimsSet().getClientId();
+ }
+
+ if (clientId != null) {
+ log.debug("{} Using client_id supplied by access token: {}", getLogPrefix(), clientId);
+ } else {
+ clientId = idGenerator.generateIdentifier();
+ log.debug("{} Created a new client ID: {}", getLogPrefix(), clientId);
+ }
+
oidcResponseCtx.setClientId(clientId);
- log.debug("{} Created a new client ID {}", getLogPrefix(), clientId);
}
-}
+
+}
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/InitializeRegistrationMetadataPolicyContext.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/InitializeRegistrationMetadataPolicyContext.java
index a6b15be9..e16faf96 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/InitializeRegistrationMetadataPolicyContext.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/InitializeRegistrationMetadataPolicyContext.java
@@ -28,6 +28,7 @@ import org.opensaml.messaging.context.navigate.ChildContextLookup;
import org.opensaml.profile.action.ActionSupport;
import org.opensaml.profile.action.EventIds;
import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.profile.context.navigate.InboundMessageContextLookup;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -62,7 +63,7 @@ public class InitializeRegistrationMetadataPolicyContext extends AbstractProfile
tokenMetadataPolicyLookupStrategy;
/** The strategy used to create or locate the metadata policy context. */
- @NonnullAfterInit private Function<MessageContext, OIDCClientRegistrationMetadataPolicyContext>
+ @NonnullAfterInit private Function<ProfileRequestContext,OIDCClientRegistrationMetadataPolicyContext>
registrationPolicyContextCreationStrategy;
/** The strategy used for merging profile and token based metadata policies. */
@@ -82,7 +83,8 @@ public class InitializeRegistrationMetadataPolicyContext extends AbstractProfile
profileMetadataPolicyLookupStrategy = new RegistrationMetadataPolicyLookupFunction();
tokenMetadataPolicyLookupStrategy = new DefaultOIDCClientRegistrationTokenMetadataPolicyLookupFunction();
registrationPolicyContextCreationStrategy =
- new ChildContextLookup<>(OIDCClientRegistrationMetadataPolicyContext.class, true);
+ new ChildContextLookup<>(OIDCClientRegistrationMetadataPolicyContext.class, true).compose(
+ new InboundMessageContextLookup());
metadataPolicyMergingStrategy = new DefaultMetadataPolicyMergingStrategy();
metadataPolicyValidationStrategy = new DefaultMetadataPolicyValidator();
}
@@ -119,7 +121,7 @@ public class InitializeRegistrationMetadataPolicyContext extends AbstractProfile
* @param strategy What to set.
*/
public void setRegistrationPolicyContextCreationStrategy(
- @Nonnull final Function<MessageContext, OIDCClientRegistrationMetadataPolicyContext> strategy) {
+ @Nonnull final Function<ProfileRequestContext,OIDCClientRegistrationMetadataPolicyContext> strategy) {
ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
registrationPolicyContextCreationStrategy = Constraint.isNotNull(strategy,
@@ -158,8 +160,7 @@ public class InitializeRegistrationMetadataPolicyContext extends AbstractProfile
return false;
}
- metadataPolicyContext =
- registrationPolicyContextCreationStrategy.apply(profileRequestContext.getInboundMessageContext());
+ metadataPolicyContext = registrationPolicyContextCreationStrategy.apply(profileRequestContext);
if (metadataPolicyContext == null) {
log.error("{} The registration metadata policy context could not be created, invalid profile context",
getLogPrefix());
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 2f57507e..083f908a 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
@@ -23,11 +23,11 @@ import java.util.function.Function;
import javax.annotation.Nonnull;
-import org.opensaml.messaging.context.MessageContext;
import org.opensaml.messaging.context.navigate.ChildContextLookup;
import org.opensaml.profile.action.ActionSupport;
import org.opensaml.profile.action.EventIds;
import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.profile.context.navigate.InboundMessageContextLookup;
import org.opensaml.storage.RevocationCache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -45,7 +45,6 @@ import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterI
import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
import net.shibboleth.utilities.java.support.component.ComponentSupport;
import net.shibboleth.utilities.java.support.logic.Constraint;
-import net.shibboleth.utilities.java.support.primitive.StringSupport;
import net.shibboleth.utilities.java.support.security.DataSealer;
import net.shibboleth.utilities.java.support.security.DataSealerException;
@@ -60,10 +59,10 @@ public class ValidateRegistrationAccessToken extends AbstractOIDCRequestAction<O
@Nonnull private Logger log = LoggerFactory.getLogger(ValidateRegistrationAccessToken.class);
/** Strategy that will return a {@link RelyingPartyContext}. */
- @Nonnull private Function<ProfileRequestContext, RelyingPartyContext> relyingPartyContextLookupStrategy;
+ @Nonnull private Function<ProfileRequestContext,RelyingPartyContext> relyingPartyContextLookupStrategy;
/** Strategy to create or return a {@link OIDCClientRegistrationTokenClaimsContext}. */
- @Nonnull private Function<MessageContext, OIDCClientRegistrationTokenClaimsContext>
+ @Nonnull private Function<ProfileRequestContext,OIDCClientRegistrationTokenClaimsContext>
registrationClaimsContextCreationStrategy;
/** Data sealer for unwrapping authorization code. */
@@ -77,9 +76,6 @@ public class ValidateRegistrationAccessToken extends AbstractOIDCRequestAction<O
/** The relying party context to operate on. */
private RelyingPartyContext relyingPartyContext;
-
- /** The registration token claims context to operate on. */
- private OIDCClientRegistrationTokenClaimsContext registrationClaimsContext;
/** The registration access token to be validated. */
private String accessToken;
@@ -88,9 +84,10 @@ public class ValidateRegistrationAccessToken extends AbstractOIDCRequestAction<O
* Constructor.
*/
public ValidateRegistrationAccessToken() {
- relyingPartyContextLookupStrategy = new ChildContextLookup<>(RelyingPartyContext.class, false);
+ relyingPartyContextLookupStrategy = new ChildContextLookup<>(RelyingPartyContext.class);
registrationClaimsContextCreationStrategy =
- new ChildContextLookup<>(OIDCClientRegistrationTokenClaimsContext.class, true);
+ new ChildContextLookup<>(OIDCClientRegistrationTokenClaimsContext.class, true).compose(
+ new InboundMessageContextLookup());
}
/**
@@ -112,7 +109,7 @@ public class ValidateRegistrationAccessToken extends AbstractOIDCRequestAction<O
* @param strategy creation strategy
*/
public void setRegistrationClaimsContextCreationStrategy(
- @Nonnull final Function<MessageContext, OIDCClientRegistrationTokenClaimsContext> strategy) {
+ @Nonnull final Function<ProfileRequestContext,OIDCClientRegistrationTokenClaimsContext> strategy) {
ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
registrationClaimsContextCreationStrategy = Constraint.isNotNull(strategy,
@@ -190,14 +187,6 @@ public class ValidateRegistrationAccessToken extends AbstractOIDCRequestAction<O
}
accessToken = getRequest().getAccessToken().getValue();
- registrationClaimsContext =
- registrationClaimsContextCreationStrategy.apply(profileRequestContext.getInboundMessageContext());
- if (registrationClaimsContext == null) {
- log.error("{} The registration token claims context could not be created, invalid profile context",
- getLogPrefix());
- ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
- return false;
- }
return true;
}
@@ -228,7 +217,7 @@ public class ValidateRegistrationAccessToken extends AbstractOIDCRequestAction<O
ActionSupport.buildEvent(profileRequestContext, OidcEventIds.INVALID_GRANT);
return;
}
- final String relyingPartyId = StringSupport.trimOrNull(claimsSet.getRelyingPartyId());
+ final String relyingPartyId = claimsSet.getRelyingPartyId();
if (relyingPartyId == null) {
log.error("{} registration access token {} didn't contain relying party identifier", getLogPrefix(),
claimsSet.getJti());
@@ -249,8 +238,20 @@ public class ValidateRegistrationAccessToken extends AbstractOIDCRequestAction<O
return;
}
}
+
+ final OIDCClientRegistrationTokenClaimsContext registrationClaimsContext =
+ registrationClaimsContextCreationStrategy.apply(profileRequestContext);
+ if (registrationClaimsContext == null) {
+ log.error("{} The registration token claims context could not be created, invalid profile context",
+ getLogPrefix());
+ ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
+ return;
+ }
+
+ registrationClaimsContext.setClaimsSet(claimsSet);
+
relyingPartyContext.setVerified(true);
relyingPartyContext.setRelyingPartyId(relyingPartyId);
- registrationClaimsContext.setClaimsSet(claimsSet);
}
+
}
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/register/register-beans.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/register/register-beans.xml
index 754a5013..0058a9e2 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/register/register-beans.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/register/register-beans.xml
@@ -6,18 +6,22 @@
xmlns:util="http://www.springframework.org/schema/util"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
- http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
- http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
+ http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
+ http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
default-init-method="initialize" default-destroy-method="destroy">
<bean id="shibboleth.oidc.profileId" class="java.lang.String"
c:_0="#{T(net.shibboleth.oidc.profile.config.OIDCDynamicRegistrationConfiguration).PROFILE_ID}" />
- <bean id="shibboleth.oidc.loggingId" class="java.lang.String" c:_0="%{idp.service.logging.oidcdynreg:OIDC.Registration}" />
+ <bean id="shibboleth.oidc.loggingId" class="java.lang.String"
+ c:_0="%{idp.service.logging.oidcdynreg:OIDC.Registration}" />
- <bean id="DecodeMessage"
- class="org.opensaml.profile.action.impl.DecodeMessage"
+ <bean id="InitializeOutboundMessageContext"
+ class="net.shibboleth.idp.plugin.oidc.op.profile.impl.InitializeOutboundRegistrationResponseMessageContext"
scope="prototype">
+ </bean>
+
+ <bean id="DecodeMessage" class="org.opensaml.profile.action.impl.DecodeMessage" scope="prototype">
<constructor-arg>
<bean
class="net.shibboleth.idp.plugin.oidc.op.decoding.impl.OIDCClientRegistrationRequestDecoder"
@@ -31,7 +35,14 @@
p:revocationCache-ref="shibboleth.oidc.RevocationCache"
p:sealer-ref="#{'%{idp.oidc.dynreg.tokenSealer:shibboleth.oidc.TokenSealer}'.trim()}"
p:objectMapper-ref="shibboleth.oidc.JSONObjectMapper" />
-
+
+ <bean id="SelectRelyingPartyConfiguration"
+ class="net.shibboleth.idp.profile.impl.SelectRelyingPartyConfiguration" scope="prototype"
+ p:relyingPartyConfigurationResolver-ref="shibboleth.RelyingPartyConfigurationResolver" />
+
+ <bean id="SelectProfileConfiguration"
+ class="net.shibboleth.idp.profile.impl.SelectProfileConfiguration" scope="prototype" />
+
<bean id="ValidateRegistrationRequestMetadata"
class="net.shibboleth.idp.plugin.oidc.op.profile.impl.ValidateRegistrationRequestMetadata" scope="prototype" />
@@ -154,20 +165,6 @@
</property>
</bean>
- <bean id="SelectRelyingPartyConfiguration"
- class="net.shibboleth.idp.profile.impl.SelectRelyingPartyConfiguration"
- scope="prototype"
- p:relyingPartyConfigurationResolver-ref="shibboleth.RelyingPartyConfigurationResolver" />
-
- <bean id="SelectProfileConfiguration"
- class="net.shibboleth.idp.profile.impl.SelectProfileConfiguration"
- scope="prototype" />
-
- <bean id="InitializeOutboundMessageContext"
- class="net.shibboleth.idp.plugin.oidc.op.profile.impl.InitializeOutboundRegistrationResponseMessageContext"
- scope="prototype">
- </bean>
-
<bean id="BuildErrorResponseFromEvent"
class="net.shibboleth.idp.plugin.oidc.op.profile.impl.BuildRegistrationErrorResponseFromEvent"
scope="prototype"
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/RegistrationFlowTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/RegistrationFlowTest.java
index c3472714..69e5292a 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/RegistrationFlowTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/RegistrationFlowTest.java
@@ -18,24 +18,22 @@
package net.shibboleth.idp.plugin.oidc.op.profile.flow;
import java.io.IOException;
-import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
-import java.security.NoSuchAlgorithmException;
import java.time.Instant;
import org.opensaml.storage.StorageService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
-import org.springframework.webflow.core.FlowException;
import org.springframework.webflow.executor.FlowExecutionResult;
import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import com.nimbusds.langtag.LangTag;
import com.nimbusds.langtag.LangTagException;
import com.nimbusds.oauth2.sdk.GrantType;
-import com.nimbusds.oauth2.sdk.ParseException;
import com.nimbusds.oauth2.sdk.token.BearerAccessToken;
import com.nimbusds.openid.connect.sdk.rp.OIDCClientInformation;
import com.nimbusds.openid.connect.sdk.rp.OIDCClientInformationResponse;
@@ -45,8 +43,6 @@ import net.minidev.json.JSONObject;
import net.minidev.json.parser.JSONParser;
import net.shibboleth.idp.plugin.oidc.op.profile.impl.BaseOIDCResponseActionTest;
import net.shibboleth.oidc.metadata.impl.BaseStorageServiceClientInformationComponent;
-import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
-import net.shibboleth.utilities.java.support.security.DataSealerException;
/**
* Some tests for the dynamic registration flow.
@@ -55,7 +51,10 @@ public class RegistrationFlowTest extends AbstractOidcFlowTest {
public static final String FLOW_ID = "oidc/register";
- String redirectUri = "https://example.org/cb";
+ private final String redirectUri = "https://example.org/cb";
+
+ private String rpId;
+ private String clientId;
@Autowired
@Qualifier("shibboleth.StorageService")
@@ -64,9 +63,22 @@ public class RegistrationFlowTest extends AbstractOidcFlowTest {
public RegistrationFlowTest() {
super(FLOW_ID);
}
+
+ @BeforeMethod
+ public void setUp() {
+ rpId = "mockRpId";
+ clientId = null;
+ }
+
+ @AfterMethod
+ public void tearDown() throws IOException {
+ if (clientId != null) {
+ storageService.delete(BaseStorageServiceClientInformationComponent.CONTEXT_NAME, clientId);
+ }
+ }
@Test
- public void testInvalidMessage() throws UnsupportedEncodingException, ParseException {
+ public void testInvalidMessage() throws Exception {
setJsonRequest("POST", "{ \"test\":not_json");
final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
assertErrorCode(result, "invalid_client_metadata");
@@ -87,17 +99,18 @@ public class RegistrationFlowTest extends AbstractOidcFlowTest {
}
@Test
- public void testUnauthenticated_success() throws ParseException, IOException, net.minidev.json.parser.ParseException {
+ public void testUnauthenticated_success() throws Exception {
setJsonRequest("POST", "{ \"redirect_uris\":[\"" + redirectUri + "\"] }");
final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
- OIDCClientInformationResponse parsedResponse = parseSuccessResponse(result, OIDCClientInformationResponse.class);
- OIDCClientInformation clientInfo = parsedResponse.getOIDCClientInformation();
- OIDCClientMetadata metadata = clientInfo.getOIDCMetadata();
- String record = storageService.read(BaseStorageServiceClientInformationComponent.CONTEXT_NAME,
+ final OIDCClientInformationResponse parsedResponse =
+ parseSuccessResponse(result, OIDCClientInformationResponse.class);
+ final OIDCClientInformation clientInfo = parsedResponse.getOIDCClientInformation();
+ final OIDCClientMetadata metadata = clientInfo.getOIDCMetadata();
+ final String record = storageService.read(BaseStorageServiceClientInformationComponent.CONTEXT_NAME,
clientInfo.getID().toString()).getValue();
Assert.assertNotNull(record);
- JSONParser parser = new JSONParser(JSONParser.DEFAULT_PERMISSIVE_MODE);
- OIDCClientInformation storedInfo = OIDCClientInformation.parse((JSONObject) parser.parse(record));
+ final JSONParser parser = new JSONParser(JSONParser.DEFAULT_PERMISSIVE_MODE);
+ final OIDCClientInformation storedInfo = OIDCClientInformation.parse((JSONObject) parser.parse(record));
Assert.assertEquals(storedInfo.getID(), clientInfo.getID());
Assert.assertEquals(storedInfo.getSecret(), clientInfo.getSecret());
Assert.assertEquals(storedInfo.getOIDCMetadata().getRedirectionURIStrings(), metadata.getRedirectionURIStrings());
@@ -105,7 +118,7 @@ public class RegistrationFlowTest extends AbstractOidcFlowTest {
}
@Test
- public void testAccessToken_nonCompliantWithProfilePolicy1() throws NoSuchAlgorithmException, DataSealerException, ComponentInitializationException {
+ public void testAccessToken_nonCompliantWithProfilePolicy1() throws Exception {
setJsonRequest("POST", "{ \"redirect_uris\":[\"" + redirectUri + "\"] }");
request.addHeader("Authorization", buildRegistrationAccessToken("[\"https://invalid.domain.org/cb\"]")
.toAuthorizationHeader());
@@ -114,7 +127,7 @@ public class RegistrationFlowTest extends AbstractOidcFlowTest {
}
@Test
- public void testAccessToken_nonCompliantWithProfilePolicy2() throws NoSuchAlgorithmException, DataSealerException, ComponentInitializationException {
+ public void testAccessToken_nonCompliantWithProfilePolicy2() throws Exception {
setJsonRequest("POST", "{ \"redirect_uris\":[\"" + redirectUri + "\"], \"id_token_signed_response_alg\":\"HS256\" }");
request.addHeader("Authorization", buildRegistrationAccessToken("[\"https://example.org/cb\"]")
.toAuthorizationHeader());
@@ -123,77 +136,91 @@ public class RegistrationFlowTest extends AbstractOidcFlowTest {
}
@Test
- public void testAccessToken_success() throws ParseException, IOException, net.minidev.json.parser.ParseException, NoSuchAlgorithmException, DataSealerException, ComponentInitializationException, FlowException {
+ public void testAccessToken_success() throws Exception {
setJsonRequest("POST", buildRequestMessage(redirectUri));
request.addHeader("Authorization", buildRegistrationAccessToken("[\"https://example.org/cb\"]")
.toAuthorizationHeader());
- assertSuccessfulResponse(flowExecutor.launchExecution(FLOW_ID, null, externalContext));
+ assertSuccessfulResponse(flowExecutor.launchExecution(FLOW_ID, null, externalContext), null);
}
@Test
- public void testAccessToken_successCustomClaimIgnored() throws ParseException, IOException, net.minidev.json.parser.ParseException, NoSuchAlgorithmException, DataSealerException, ComponentInitializationException, FlowException {
+ public void testAccessToken_success_withClientID() throws Exception {
+ setJsonRequest("POST", buildRequestMessage(redirectUri));
+ clientId = "https://example.org";
+ request.addHeader("Authorization", buildRegistrationAccessToken("[\"https://example.org/cb\"]")
+ .toAuthorizationHeader());
+ assertSuccessfulResponse(flowExecutor.launchExecution(FLOW_ID, null, externalContext), clientId);
+ }
+
+ @Test
+ public void testAccessToken_successCustomClaimIgnored() throws Exception {
setJsonRequest("POST", buildRequestMessage(redirectUri, "\"customClaim\":\"customValue\""));
request.addHeader("Authorization", buildRegistrationAccessToken("[\"https://example.org/cb\"]")
.toAuthorizationHeader());
final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
- assertSuccessfulResponse(result);
+ assertSuccessfulResponse(result, null);
final OIDCClientInformationResponse parsedResponse =
parseSuccessResponse(result, OIDCClientInformationResponse.class);
Assert.assertNull(parsedResponse.getOIDCClientInformation().getOIDCMetadata().getCustomField("customClaim"));
}
@Test
- public void testAccessToken_successCustomClaimInPolicyAdded() throws ParseException, IOException, net.minidev.json.parser.ParseException, NoSuchAlgorithmException, DataSealerException, ComponentInitializationException, FlowException {
+ public void testAccessToken_successCustomClaimInPolicyAdded() throws Exception {
setJsonRequest("POST", buildRequestMessage(redirectUri, "\"customClaim\":\"customValue\""));
request.addHeader("Authorization", buildRegistrationAccessToken("[\"https://example.org/cb\"]", new String[] {"customClaim"})
.toAuthorizationHeader());
final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
- assertSuccessfulResponse(result);
+ assertSuccessfulResponse(result, null);
final OIDCClientInformationResponse parsedResponse =
parseSuccessResponse(result, OIDCClientInformationResponse.class);
Assert.assertEquals(parsedResponse.getOIDCClientInformation().getOIDCMetadata().getCustomField("customClaim"), "customValue");
}
@Test
- public void testAccessToken_noPolicyNoRedirectUri() throws NoSuchAlgorithmException, DataSealerException,
- ComponentInitializationException {
+ public void testAccessToken_noPolicyNoRedirectUri() throws Exception {
setJsonRequest("POST", "{ \"test\":false }");
- request.addHeader("Authorization", buildRegistrationAccessToken("mockDynRegClientNoProfilePolicy",
- (String) null, (String[]) null).toAuthorizationHeader());
+ rpId = "mockDynRegClientNoProfilePolicy";
+ request.addHeader("Authorization", buildRegistrationAccessToken((String) null,
+ (String[]) null).toAuthorizationHeader());
final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
assertErrorCode(result, "invalid_redirect_uri");
}
@Test
- public void testAccessToken_nonDefaultPolicyActive_failsWhenIncompatibleRequest()
- throws NoSuchAlgorithmException, DataSealerException, ComponentInitializationException {
+ public void testAccessToken_nonDefaultPolicyActive_failsWhenIncompatibleRequest() throws Exception {
// the policy for mockDynRegClientAnotherProfilePolicy shouldn't accept other grant_types than implicit
setJsonRequest("POST", buildRequestMessage(redirectUri, "\"grant_types\":[\"authorization_code\"]"));
- request.addHeader("Authorization", buildRegistrationAccessToken("mockDynRegClientAnotherProfilePolicy",
- (String) null, (String[]) null).toAuthorizationHeader());
+ rpId = "mockDynRegClientAnotherProfilePolicy";
+ request.addHeader("Authorization", buildRegistrationAccessToken((String) null,
+ (String[]) null).toAuthorizationHeader());
final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
assertErrorCode(result, "invalid_client_metadata");
}
@Test
- public void testAccessToken_nonDefaultPolicyActive_successWithCompatibleRequest()
- throws NoSuchAlgorithmException, DataSealerException, ComponentInitializationException, ParseException, IOException, net.minidev.json.parser.ParseException {
+ public void testAccessToken_nonDefaultPolicyActive_successWithCompatibleRequest() throws Exception {
setJsonRequest("POST", buildRequestMessage(redirectUri, "\"grant_types\":[\"implicit\"]"));
- request.addHeader("Authorization", buildRegistrationAccessToken("mockDynRegClientAnotherProfilePolicy",
- (String) null, (String[]) null).toAuthorizationHeader());
+ rpId = "mockDynRegClientAnotherProfilePolicy";
+ request.addHeader("Authorization", buildRegistrationAccessToken((String) null,
+ (String[]) null).toAuthorizationHeader());
final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
- assertSuccessfulResponse(result);
+ assertSuccessfulResponse(result, null);
final OIDCClientInformationResponse parsedResponse =
parseSuccessResponse(result, OIDCClientInformationResponse.class);
Assert.assertTrue(parsedResponse.getOIDCClientInformation().getOIDCMetadata().getGrantTypes().contains(GrantType.IMPLICIT));
}
- protected void assertSuccessfulResponse(final FlowExecutionResult result) throws IOException, ParseException, net.minidev.json.parser.ParseException {
+ protected void assertSuccessfulResponse(final FlowExecutionResult result, final String clientId) throws Exception {
final OIDCClientInformationResponse parsedResponse =
parseSuccessResponse(result, OIDCClientInformationResponse.class);
final OIDCClientInformation clientInfo = OIDCClientInformation.parse(
parsedResponse.getOIDCClientInformation().toJSONObject());
final OIDCClientMetadata metadata = clientInfo.getOIDCMetadata();
+ if (clientId != null) {
+ Assert.assertEquals(clientId, clientInfo.getID().getValue());
+ } else {
+ Assert.assertNotEquals(clientId, clientInfo.getID().getValue());
+ }
final String record = storageService.read(BaseStorageServiceClientInformationComponent.CONTEXT_NAME,
clientInfo.getID().toString()).getValue();
Assert.assertNotNull(record);
@@ -208,17 +235,13 @@ public class RegistrationFlowTest extends AbstractOidcFlowTest {
try {
Assert.assertEquals(metadata.getPolicyURI(LangTag.parse("fi")), new URI("https://policy.org/finnish"));
Assert.assertEquals(metadata.getPolicyURI(LangTag.parse("en")), new URI("https://policy.org/english"));
- } catch (LangTagException | URISyntaxException e) {
+ } catch (final LangTagException | URISyntaxException e) {
Assert.fail();
}
Assert.assertEquals(storedMetadata.getPolicyURIEntries(), metadata.getPolicyURIEntries());
}
- protected BearerAccessToken buildRegistrationAccessToken(final String redirectUriSubset, final String... additionalPolicyClaims) throws NoSuchAlgorithmException, DataSealerException, ComponentInitializationException {
- return buildRegistrationAccessToken("mockRpId", redirectUriSubset, additionalPolicyClaims);
- }
-
- protected BearerAccessToken buildRegistrationAccessToken(final String rpId, final String redirectUriSubset, final String... additionalPolicyClaims) throws NoSuchAlgorithmException, DataSealerException, ComponentInitializationException {
+ protected BearerAccessToken buildRegistrationAccessToken(final String redirectUriSubset, final String... additionalPolicyClaims) throws Exception {
final StringBuilder metadata = new StringBuilder();
if (additionalPolicyClaims != null) {
for (int i = 0; i < additionalPolicyClaims.length; i++) {
@@ -242,6 +265,7 @@ public class RegistrationFlowTest extends AbstractOidcFlowTest {
"\"iat\":" + Instant.now().getEpochSecond() + "," +
"\"jti\":\"" + idGenerator.generateIdentifier() + "\"," +
"\"rp_id\":\"" + rpId + "\"," +
+ (clientId != null ? ("\"client_id\":\"" + clientId + "\",") : "") +
"\"metadata\":" + (metadata.length() == 0 ? "null" : "{" + metadata.toString()) + "}" +
"}";
return new BearerAccessToken(BaseOIDCResponseActionTest.initializeDataSealer().wrap(json,
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list