added PKCE editing capabilities to UI
parent
650429a2de
commit
141f4da7f1
|
@ -56,5 +56,6 @@ public interface RegisteredClientFields {
|
|||
public String REDIRECT_URIS = "redirect_uris";
|
||||
public String CLIENT_SECRET = "client_secret";
|
||||
public String CLIENT_ID = "client_id";
|
||||
|
||||
public String CODE_CHALLENGE_METHOD = "code_challenge_method";
|
||||
|
||||
}
|
||||
|
|
|
@ -20,6 +20,36 @@
|
|||
package org.mitre.openid.connect;
|
||||
|
||||
|
||||
import static org.mitre.util.JsonUtils.getAsArray;
|
||||
import static org.mitre.util.JsonUtils.getAsDate;
|
||||
import static org.mitre.util.JsonUtils.getAsJweAlgorithm;
|
||||
import static org.mitre.util.JsonUtils.getAsJweEncryptionMethod;
|
||||
import static org.mitre.util.JsonUtils.getAsJwsAlgorithm;
|
||||
import static org.mitre.util.JsonUtils.getAsPkceAlgorithm;
|
||||
import static org.mitre.util.JsonUtils.getAsString;
|
||||
import static org.mitre.util.JsonUtils.getAsStringSet;
|
||||
|
||||
import java.text.ParseException;
|
||||
|
||||
import org.mitre.oauth2.model.ClientDetailsEntity;
|
||||
import org.mitre.oauth2.model.ClientDetailsEntity.AppType;
|
||||
import org.mitre.oauth2.model.ClientDetailsEntity.AuthMethod;
|
||||
import org.mitre.oauth2.model.ClientDetailsEntity.SubjectType;
|
||||
import org.mitre.oauth2.model.RegisteredClient;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import com.nimbusds.jose.jwk.JWKSet;
|
||||
import com.nimbusds.jwt.JWT;
|
||||
import com.nimbusds.jwt.JWTParser;
|
||||
|
||||
import static org.mitre.oauth2.model.RegisteredClientFields.APPLICATION_TYPE;
|
||||
import static org.mitre.oauth2.model.RegisteredClientFields.CLAIMS_REDIRECT_URIS;
|
||||
import static org.mitre.oauth2.model.RegisteredClientFields.CLIENT_ID;
|
||||
|
@ -28,6 +58,7 @@ import static org.mitre.oauth2.model.RegisteredClientFields.CLIENT_NAME;
|
|||
import static org.mitre.oauth2.model.RegisteredClientFields.CLIENT_SECRET;
|
||||
import static org.mitre.oauth2.model.RegisteredClientFields.CLIENT_SECRET_EXPIRES_AT;
|
||||
import static org.mitre.oauth2.model.RegisteredClientFields.CLIENT_URI;
|
||||
import static org.mitre.oauth2.model.RegisteredClientFields.CODE_CHALLENGE_METHOD;
|
||||
import static org.mitre.oauth2.model.RegisteredClientFields.CONTACTS;
|
||||
import static org.mitre.oauth2.model.RegisteredClientFields.DEFAULT_ACR_VALUES;
|
||||
import static org.mitre.oauth2.model.RegisteredClientFields.DEFAULT_MAX_AGE;
|
||||
|
@ -59,37 +90,6 @@ import static org.mitre.oauth2.model.RegisteredClientFields.TOS_URI;
|
|||
import static org.mitre.oauth2.model.RegisteredClientFields.USERINFO_ENCRYPTED_RESPONSE_ALG;
|
||||
import static org.mitre.oauth2.model.RegisteredClientFields.USERINFO_ENCRYPTED_RESPONSE_ENC;
|
||||
import static org.mitre.oauth2.model.RegisteredClientFields.USERINFO_SIGNED_RESPONSE_ALG;
|
||||
import static org.mitre.util.JsonUtils.getAsArray;
|
||||
import static org.mitre.util.JsonUtils.getAsDate;
|
||||
import static org.mitre.util.JsonUtils.getAsJweAlgorithm;
|
||||
import static org.mitre.util.JsonUtils.getAsJweEncryptionMethod;
|
||||
import static org.mitre.util.JsonUtils.getAsJwsAlgorithm;
|
||||
import static org.mitre.util.JsonUtils.getAsString;
|
||||
import static org.mitre.util.JsonUtils.getAsStringSet;
|
||||
|
||||
import java.text.ParseException;
|
||||
|
||||
import org.mitre.jwt.assertion.AssertionValidator;
|
||||
import org.mitre.oauth2.model.ClientDetailsEntity;
|
||||
import org.mitre.oauth2.model.ClientDetailsEntity.AppType;
|
||||
import org.mitre.oauth2.model.ClientDetailsEntity.AuthMethod;
|
||||
import org.mitre.oauth2.model.ClientDetailsEntity.SubjectType;
|
||||
import org.mitre.oauth2.model.RegisteredClient;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import com.nimbusds.jose.jwk.JWKSet;
|
||||
import com.nimbusds.jwt.JWT;
|
||||
import com.nimbusds.jwt.JWTParser;
|
||||
|
||||
/**
|
||||
* Utility class to handle the parsing and serialization of ClientDetails objects.
|
||||
|
@ -204,6 +204,9 @@ public class ClientDetailsEntityJsonProcessor {
|
|||
|
||||
c.setClaimsRedirectUris(getAsStringSet(o, CLAIMS_REDIRECT_URIS));
|
||||
|
||||
c.setCodeChallengeMethod(getAsPkceAlgorithm(o, CODE_CHALLENGE_METHOD));
|
||||
|
||||
// note that this does not process or validate the software statement, that's handled in other components
|
||||
String softwareStatement = getAsString(o, SOFTWARE_STATEMENT);
|
||||
if (!Strings.isNullOrEmpty(softwareStatement)) {
|
||||
try {
|
||||
|
@ -216,6 +219,7 @@ public class ClientDetailsEntityJsonProcessor {
|
|||
}
|
||||
|
||||
|
||||
|
||||
return c;
|
||||
} else {
|
||||
return null;
|
||||
|
@ -339,6 +343,8 @@ public class ClientDetailsEntityJsonProcessor {
|
|||
|
||||
o.add(CLAIMS_REDIRECT_URIS, getAsArray(c.getClaimsRedirectUris()));
|
||||
|
||||
o.addProperty(CODE_CHALLENGE_METHOD, c.getCodeChallengeMethod() != null ? c.getCodeChallengeMethod().getName() : null);
|
||||
|
||||
if (c.getSoftwareStatement() != null) {
|
||||
o.addProperty(SOFTWARE_STATEMENT, c.getSoftwareStatement().serialize());
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.mitre.oauth2.model.PKCEAlgorithm;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
@ -138,6 +139,21 @@ public class JsonUtils {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the given member as a PKCE Algorithm, null if it doesn't exist
|
||||
* @param o
|
||||
* @param member
|
||||
* @return
|
||||
*/
|
||||
public static PKCEAlgorithm getAsPkceAlgorithm(JsonObject o, String member) {
|
||||
String s = getAsString(o, member);
|
||||
if (s != null) {
|
||||
return PKCEAlgorithm.parse(s);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the given member as a string, null if it doesn't exist
|
||||
*/
|
||||
|
|
|
@ -77,6 +77,8 @@ var ClientModel = Backbone.Model.extend({
|
|||
|
||||
requestUris:[],
|
||||
|
||||
codeChallengeMethod:null,
|
||||
|
||||
authorities:[],
|
||||
accessTokenValiditySeconds: null,
|
||||
refreshTokenValiditySeconds: null,
|
||||
|
@ -957,7 +959,8 @@ var ClientFormView = Backbone.View.extend({
|
|||
idTokenSignedResponseAlg: this.defaultToNull($('#idTokenSignedResponseAlg select').val()),
|
||||
idTokenEncryptedResponseAlg: this.defaultToNull($('#idTokenEncryptedResponseAlg select').val()),
|
||||
idTokenEncryptedResponseEnc: this.defaultToNull($('#idTokenEncryptedResponseEnc select').val()),
|
||||
tokenEndpointAuthSigningAlg: this.defaultToNull($('#tokenEndpointAuthSigningAlg select').val())
|
||||
tokenEndpointAuthSigningAlg: this.defaultToNull($('#tokenEndpointAuthSigningAlg select').val()),
|
||||
codeChallengeMethod: this.defaultToNull($('#codeChallengeMethod select').val())
|
||||
};
|
||||
|
||||
// post-validate
|
||||
|
@ -1165,13 +1168,6 @@ var ClientFormView = Backbone.View.extend({
|
|||
});
|
||||
|
||||
|
||||
// add routes to the app
|
||||
/*
|
||||
"admin/clients":"listClients",
|
||||
"admin/client/new":"newClient",
|
||||
"admin/client/:id":"editClient",
|
||||
*/
|
||||
|
||||
ui.routes.push({path: "admin/clients", name: "listClients", callback:
|
||||
function () {
|
||||
|
||||
|
|
|
@ -61,6 +61,8 @@ var DynRegClient = Backbone.Model.extend({
|
|||
|
||||
request_uris:[],
|
||||
|
||||
code_challenge_method:null,
|
||||
|
||||
registration_access_token:null,
|
||||
registration_client_uri:null
|
||||
},
|
||||
|
@ -435,7 +437,8 @@ var DynRegEditView = Backbone.View.extend({
|
|||
id_token_signed_response_alg: this.defaultToNull($('#idTokenSignedResponseAlg select').val()),
|
||||
id_token_encrypted_response_alg: this.defaultToNull($('#idTokenEncryptedResponseAlg select').val()),
|
||||
id_token_encrypted_response_enc: this.defaultToNull($('#idTokenEncryptedResponseEnc select').val()),
|
||||
token_endpoint_auth_signing_alg: this.defaultToNull($('#tokenEndpointAuthSigningAlg select').val())
|
||||
token_endpoint_auth_signing_alg: this.defaultToNull($('#tokenEndpointAuthSigningAlg select').val()),
|
||||
code_challenge_method: this.defaultToNull($('#codeChallengeMethod select').val())
|
||||
};
|
||||
|
||||
// set all empty strings to nulls
|
||||
|
|
|
@ -744,6 +744,17 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group" id="codeChallengeMethod">
|
||||
<label class="control-label" data-i18n="client.client-form.code-challenge-method">Proof Key for Code Exchange (PKCE) Code Challenge Method</label>
|
||||
<div class="controls">
|
||||
<select>
|
||||
<option value="default" <%-client.codeChallengeMethod == null ? 'selected ' : ''%> data-i18n="client.client-form.code-challenge-none">No code challenge</option>
|
||||
<option value="plain" <%-client.codeChallengeMethod == "plain" ? 'selected' : ''%> data-i18n="client.client-form.code-challenge-plain">Plain code challenge</option>
|
||||
<option value="S256" <%-client.codeChallengeMethod == "S256" ? 'selected' : ''%> data-i18n="client.client-form.code-challenge-s256">SHA-256 hash algorithm</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="tab-pane" id="client-other-tab">
|
||||
|
|
|
@ -512,6 +512,17 @@
|
|||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group" id="codeChallengeMethod">
|
||||
<label class="control-label" data-i18n="client.client-form.code-challenge-method">Proof Key for Code Exchange (PKCE) Code Challenge Method</label>
|
||||
<div class="controls">
|
||||
<select>
|
||||
<option value="default" <%-client.code_challenge_method == null ? 'selected ' : ''%> data-i18n="client.client-form.code-challenge-none">No code challenge</option>
|
||||
<option value="plain" <%-client.code_challenge_method == "plain" ? 'selected' : ''%> data-i18n="client.client-form.code-challenge-plain">Plain code challenge</option>
|
||||
<option value="S256" <%-client.code_challenge_method == "S256" ? 'selected' : ''%> data-i18n="client.client-form.code-challenge-s256">SHA-256 hash algorithm</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane" id="client-other-tab">
|
||||
|
|
|
@ -27,6 +27,7 @@ import java.util.Map;
|
|||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.mitre.oauth2.model.PKCEAlgorithm;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
@ -36,11 +37,15 @@ import org.springframework.web.servlet.view.AbstractView;
|
|||
import com.google.gson.ExclusionStrategy;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.JsonParser;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import com.google.gson.JsonSerializationContext;
|
||||
import com.google.gson.JsonSerializer;
|
||||
import com.nimbusds.jose.Algorithm;
|
||||
import com.nimbusds.jose.EncryptionMethod;
|
||||
import com.nimbusds.jose.JWEAlgorithm;
|
||||
import com.nimbusds.jose.JWSAlgorithm;
|
||||
|
@ -118,6 +123,15 @@ public abstract class AbstractClientEntityView extends AbstractView {
|
|||
}
|
||||
|
||||
})
|
||||
.registerTypeAdapter(PKCEAlgorithm.class, new JsonSerializer<PKCEAlgorithm>() {
|
||||
public JsonPrimitive serialize(PKCEAlgorithm src, Type typeOfSrc, JsonSerializationContext context) {
|
||||
if (src != null) {
|
||||
return new JsonPrimitive(src.getName());
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
})
|
||||
.serializeNulls()
|
||||
.setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")
|
||||
.create();
|
||||
|
|
|
@ -29,6 +29,7 @@ import org.mitre.oauth2.model.ClientDetailsEntity;
|
|||
import org.mitre.oauth2.model.ClientDetailsEntity.AppType;
|
||||
import org.mitre.oauth2.model.ClientDetailsEntity.AuthMethod;
|
||||
import org.mitre.oauth2.model.ClientDetailsEntity.SubjectType;
|
||||
import org.mitre.oauth2.model.PKCEAlgorithm;
|
||||
import org.mitre.oauth2.service.ClientDetailsEntityService;
|
||||
import org.mitre.oauth2.web.AuthenticationUtilities;
|
||||
import org.mitre.openid.connect.exception.ValidationException;
|
||||
|
@ -68,9 +69,6 @@ import com.google.gson.JsonElement;
|
|||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.JsonParser;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import com.google.gson.JsonSerializationContext;
|
||||
import com.google.gson.JsonSerializer;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import com.nimbusds.jose.Algorithm;
|
||||
import com.nimbusds.jose.EncryptionMethod;
|
||||
|
@ -203,6 +201,15 @@ public class ClientAPI {
|
|||
}
|
||||
}
|
||||
})
|
||||
.registerTypeAdapter(PKCEAlgorithm.class, new JsonDeserializer<Algorithm>() {
|
||||
public PKCEAlgorithm deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
|
||||
if (json.isJsonPrimitive()) {
|
||||
return PKCEAlgorithm.parse(json.getAsString());
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
})
|
||||
.setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")
|
||||
.create();
|
||||
|
||||
|
|
Loading…
Reference in New Issue