Skip to content

Commit 141f4da

Browse files
committed
added PKCE editing capabilities to UI
1 parent 650429a commit 141f4da

File tree

9 files changed

+109
-44
lines changed

9 files changed

+109
-44
lines changed

openid-connect-common/src/main/java/org/mitre/oauth2/model/RegisteredClientFields.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,5 +56,6 @@ public interface RegisteredClientFields {
5656
public String REDIRECT_URIS = "redirect_uris";
5757
public String CLIENT_SECRET = "client_secret";
5858
public String CLIENT_ID = "client_id";
59-
59+
public String CODE_CHALLENGE_METHOD = "code_challenge_method";
60+
6061
}

openid-connect-common/src/main/java/org/mitre/openid/connect/ClientDetailsEntityJsonProcessor.java

Lines changed: 37 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,36 @@
2020
package org.mitre.openid.connect;
2121

2222

23+
import static org.mitre.util.JsonUtils.getAsArray;
24+
import static org.mitre.util.JsonUtils.getAsDate;
25+
import static org.mitre.util.JsonUtils.getAsJweAlgorithm;
26+
import static org.mitre.util.JsonUtils.getAsJweEncryptionMethod;
27+
import static org.mitre.util.JsonUtils.getAsJwsAlgorithm;
28+
import static org.mitre.util.JsonUtils.getAsPkceAlgorithm;
29+
import static org.mitre.util.JsonUtils.getAsString;
30+
import static org.mitre.util.JsonUtils.getAsStringSet;
31+
32+
import java.text.ParseException;
33+
34+
import org.mitre.oauth2.model.ClientDetailsEntity;
35+
import org.mitre.oauth2.model.ClientDetailsEntity.AppType;
36+
import org.mitre.oauth2.model.ClientDetailsEntity.AuthMethod;
37+
import org.mitre.oauth2.model.ClientDetailsEntity.SubjectType;
38+
import org.mitre.oauth2.model.RegisteredClient;
39+
import org.slf4j.Logger;
40+
import org.slf4j.LoggerFactory;
41+
42+
import com.google.common.base.Joiner;
43+
import com.google.common.base.Splitter;
44+
import com.google.common.base.Strings;
45+
import com.google.common.collect.Sets;
46+
import com.google.gson.JsonElement;
47+
import com.google.gson.JsonObject;
48+
import com.google.gson.JsonParser;
49+
import com.nimbusds.jose.jwk.JWKSet;
50+
import com.nimbusds.jwt.JWT;
51+
import com.nimbusds.jwt.JWTParser;
52+
2353
import static org.mitre.oauth2.model.RegisteredClientFields.APPLICATION_TYPE;
2454
import static org.mitre.oauth2.model.RegisteredClientFields.CLAIMS_REDIRECT_URIS;
2555
import static org.mitre.oauth2.model.RegisteredClientFields.CLIENT_ID;
@@ -28,6 +58,7 @@
2858
import static org.mitre.oauth2.model.RegisteredClientFields.CLIENT_SECRET;
2959
import static org.mitre.oauth2.model.RegisteredClientFields.CLIENT_SECRET_EXPIRES_AT;
3060
import static org.mitre.oauth2.model.RegisteredClientFields.CLIENT_URI;
61+
import static org.mitre.oauth2.model.RegisteredClientFields.CODE_CHALLENGE_METHOD;
3162
import static org.mitre.oauth2.model.RegisteredClientFields.CONTACTS;
3263
import static org.mitre.oauth2.model.RegisteredClientFields.DEFAULT_ACR_VALUES;
3364
import static org.mitre.oauth2.model.RegisteredClientFields.DEFAULT_MAX_AGE;
@@ -59,37 +90,6 @@
5990
import static org.mitre.oauth2.model.RegisteredClientFields.USERINFO_ENCRYPTED_RESPONSE_ALG;
6091
import static org.mitre.oauth2.model.RegisteredClientFields.USERINFO_ENCRYPTED_RESPONSE_ENC;
6192
import static org.mitre.oauth2.model.RegisteredClientFields.USERINFO_SIGNED_RESPONSE_ALG;
62-
import static org.mitre.util.JsonUtils.getAsArray;
63-
import static org.mitre.util.JsonUtils.getAsDate;
64-
import static org.mitre.util.JsonUtils.getAsJweAlgorithm;
65-
import static org.mitre.util.JsonUtils.getAsJweEncryptionMethod;
66-
import static org.mitre.util.JsonUtils.getAsJwsAlgorithm;
67-
import static org.mitre.util.JsonUtils.getAsString;
68-
import static org.mitre.util.JsonUtils.getAsStringSet;
69-
70-
import java.text.ParseException;
71-
72-
import org.mitre.jwt.assertion.AssertionValidator;
73-
import org.mitre.oauth2.model.ClientDetailsEntity;
74-
import org.mitre.oauth2.model.ClientDetailsEntity.AppType;
75-
import org.mitre.oauth2.model.ClientDetailsEntity.AuthMethod;
76-
import org.mitre.oauth2.model.ClientDetailsEntity.SubjectType;
77-
import org.mitre.oauth2.model.RegisteredClient;
78-
import org.slf4j.Logger;
79-
import org.slf4j.LoggerFactory;
80-
import org.springframework.beans.factory.annotation.Autowired;
81-
import org.springframework.beans.factory.annotation.Qualifier;
82-
83-
import com.google.common.base.Joiner;
84-
import com.google.common.base.Splitter;
85-
import com.google.common.base.Strings;
86-
import com.google.common.collect.Sets;
87-
import com.google.gson.JsonElement;
88-
import com.google.gson.JsonObject;
89-
import com.google.gson.JsonParser;
90-
import com.nimbusds.jose.jwk.JWKSet;
91-
import com.nimbusds.jwt.JWT;
92-
import com.nimbusds.jwt.JWTParser;
9393

9494
/**
9595
* Utility class to handle the parsing and serialization of ClientDetails objects.
@@ -204,6 +204,9 @@ public static ClientDetailsEntity parse(JsonElement jsonEl) {
204204

205205
c.setClaimsRedirectUris(getAsStringSet(o, CLAIMS_REDIRECT_URIS));
206206

207+
c.setCodeChallengeMethod(getAsPkceAlgorithm(o, CODE_CHALLENGE_METHOD));
208+
209+
// note that this does not process or validate the software statement, that's handled in other components
207210
String softwareStatement = getAsString(o, SOFTWARE_STATEMENT);
208211
if (!Strings.isNullOrEmpty(softwareStatement)) {
209212
try {
@@ -216,6 +219,7 @@ public static ClientDetailsEntity parse(JsonElement jsonEl) {
216219
}
217220

218221

222+
219223
return c;
220224
} else {
221225
return null;
@@ -339,6 +343,8 @@ public static JsonObject serialize(RegisteredClient c) {
339343

340344
o.add(CLAIMS_REDIRECT_URIS, getAsArray(c.getClaimsRedirectUris()));
341345

346+
o.addProperty(CODE_CHALLENGE_METHOD, c.getCodeChallengeMethod() != null ? c.getCodeChallengeMethod().getName() : null);
347+
342348
if (c.getSoftwareStatement() != null) {
343349
o.addProperty(SOFTWARE_STATEMENT, c.getSoftwareStatement().serialize());
344350
}

openid-connect-common/src/main/java/org/mitre/util/JsonUtils.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import java.util.Map;
2929
import java.util.Set;
3030

31+
import org.mitre.oauth2.model.PKCEAlgorithm;
3132
import org.slf4j.Logger;
3233
import org.slf4j.LoggerFactory;
3334

@@ -138,6 +139,21 @@ public static JWSAlgorithm getAsJwsAlgorithm(JsonObject o, String member) {
138139
}
139140
}
140141

142+
/**
143+
* Gets the value of the given member as a PKCE Algorithm, null if it doesn't exist
144+
* @param o
145+
* @param member
146+
* @return
147+
*/
148+
public static PKCEAlgorithm getAsPkceAlgorithm(JsonObject o, String member) {
149+
String s = getAsString(o, member);
150+
if (s != null) {
151+
return PKCEAlgorithm.parse(s);
152+
} else {
153+
return null;
154+
}
155+
}
156+
141157
/**
142158
* Gets the value of the given member as a string, null if it doesn't exist
143159
*/

openid-connect-server-webapp/src/main/webapp/resources/js/client.js

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ var ClientModel = Backbone.Model.extend({
7777

7878
requestUris:[],
7979

80+
codeChallengeMethod:null,
81+
8082
authorities:[],
8183
accessTokenValiditySeconds: null,
8284
refreshTokenValiditySeconds: null,
@@ -957,7 +959,8 @@ var ClientFormView = Backbone.View.extend({
957959
idTokenSignedResponseAlg: this.defaultToNull($('#idTokenSignedResponseAlg select').val()),
958960
idTokenEncryptedResponseAlg: this.defaultToNull($('#idTokenEncryptedResponseAlg select').val()),
959961
idTokenEncryptedResponseEnc: this.defaultToNull($('#idTokenEncryptedResponseEnc select').val()),
960-
tokenEndpointAuthSigningAlg: this.defaultToNull($('#tokenEndpointAuthSigningAlg select').val())
962+
tokenEndpointAuthSigningAlg: this.defaultToNull($('#tokenEndpointAuthSigningAlg select').val()),
963+
codeChallengeMethod: this.defaultToNull($('#codeChallengeMethod select').val())
961964
};
962965

963966
// post-validate
@@ -1165,13 +1168,6 @@ var ClientFormView = Backbone.View.extend({
11651168
});
11661169

11671170

1168-
// add routes to the app
1169-
/*
1170-
"admin/clients":"listClients",
1171-
"admin/client/new":"newClient",
1172-
"admin/client/:id":"editClient",
1173-
*/
1174-
11751171
ui.routes.push({path: "admin/clients", name: "listClients", callback:
11761172
function () {
11771173

openid-connect-server-webapp/src/main/webapp/resources/js/dynreg.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ var DynRegClient = Backbone.Model.extend({
6161

6262
request_uris:[],
6363

64+
code_challenge_method:null,
65+
6466
registration_access_token:null,
6567
registration_client_uri:null
6668
},
@@ -435,7 +437,8 @@ var DynRegEditView = Backbone.View.extend({
435437
id_token_signed_response_alg: this.defaultToNull($('#idTokenSignedResponseAlg select').val()),
436438
id_token_encrypted_response_alg: this.defaultToNull($('#idTokenEncryptedResponseAlg select').val()),
437439
id_token_encrypted_response_enc: this.defaultToNull($('#idTokenEncryptedResponseEnc select').val()),
438-
token_endpoint_auth_signing_alg: this.defaultToNull($('#tokenEndpointAuthSigningAlg select').val())
440+
token_endpoint_auth_signing_alg: this.defaultToNull($('#tokenEndpointAuthSigningAlg select').val()),
441+
code_challenge_method: this.defaultToNull($('#codeChallengeMethod select').val())
439442
};
440443

441444
// set all empty strings to nulls

openid-connect-server-webapp/src/main/webapp/resources/template/client.html

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -744,6 +744,17 @@ <h1 data-i18n="client.client-form.edit"></h1>
744744
</div>
745745
</div>
746746

747+
<div class="control-group" id="codeChallengeMethod">
748+
<label class="control-label" data-i18n="client.client-form.code-challenge-method">Proof Key for Code Exchange (PKCE) Code Challenge Method</label>
749+
<div class="controls">
750+
<select>
751+
<option value="default" <%-client.codeChallengeMethod == null ? 'selected ' : ''%> data-i18n="client.client-form.code-challenge-none">No code challenge</option>
752+
<option value="plain" <%-client.codeChallengeMethod == "plain" ? 'selected' : ''%> data-i18n="client.client-form.code-challenge-plain">Plain code challenge</option>
753+
<option value="S256" <%-client.codeChallengeMethod == "S256" ? 'selected' : ''%> data-i18n="client.client-form.code-challenge-s256">SHA-256 hash algorithm</option>
754+
</select>
755+
</div>
756+
</div>
757+
747758
</div>
748759

749760
<div class="tab-pane" id="client-other-tab">

openid-connect-server-webapp/src/main/webapp/resources/template/dynreg.html

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,17 @@ <h1 data-i18n="client.client-form.edit"></h1>
512512
</select>
513513
</div>
514514
</div>
515+
516+
<div class="control-group" id="codeChallengeMethod">
517+
<label class="control-label" data-i18n="client.client-form.code-challenge-method">Proof Key for Code Exchange (PKCE) Code Challenge Method</label>
518+
<div class="controls">
519+
<select>
520+
<option value="default" <%-client.code_challenge_method == null ? 'selected ' : ''%> data-i18n="client.client-form.code-challenge-none">No code challenge</option>
521+
<option value="plain" <%-client.code_challenge_method == "plain" ? 'selected' : ''%> data-i18n="client.client-form.code-challenge-plain">Plain code challenge</option>
522+
<option value="S256" <%-client.code_challenge_method == "S256" ? 'selected' : ''%> data-i18n="client.client-form.code-challenge-s256">SHA-256 hash algorithm</option>
523+
</select>
524+
</div>
525+
</div>
515526
</div>
516527

517528
<div class="tab-pane" id="client-other-tab">

openid-connect-server/src/main/java/org/mitre/openid/connect/view/AbstractClientEntityView.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import javax.servlet.http.HttpServletRequest;
2828
import javax.servlet.http.HttpServletResponse;
2929

30+
import org.mitre.oauth2.model.PKCEAlgorithm;
3031
import org.slf4j.Logger;
3132
import org.slf4j.LoggerFactory;
3233
import org.springframework.http.HttpStatus;
@@ -36,11 +37,15 @@
3637
import com.google.gson.ExclusionStrategy;
3738
import com.google.gson.Gson;
3839
import com.google.gson.GsonBuilder;
40+
import com.google.gson.JsonDeserializationContext;
41+
import com.google.gson.JsonDeserializer;
3942
import com.google.gson.JsonElement;
43+
import com.google.gson.JsonParseException;
4044
import com.google.gson.JsonParser;
4145
import com.google.gson.JsonPrimitive;
4246
import com.google.gson.JsonSerializationContext;
4347
import com.google.gson.JsonSerializer;
48+
import com.nimbusds.jose.Algorithm;
4449
import com.nimbusds.jose.EncryptionMethod;
4550
import com.nimbusds.jose.JWEAlgorithm;
4651
import com.nimbusds.jose.JWSAlgorithm;
@@ -118,6 +123,15 @@ public JsonElement serialize(JWT src, Type typeOfSrc, JsonSerializationContext c
118123
}
119124

120125
})
126+
.registerTypeAdapter(PKCEAlgorithm.class, new JsonSerializer<PKCEAlgorithm>() {
127+
public JsonPrimitive serialize(PKCEAlgorithm src, Type typeOfSrc, JsonSerializationContext context) {
128+
if (src != null) {
129+
return new JsonPrimitive(src.getName());
130+
} else {
131+
return null;
132+
}
133+
}
134+
})
121135
.serializeNulls()
122136
.setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")
123137
.create();

openid-connect-server/src/main/java/org/mitre/openid/connect/web/ClientAPI.java

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import org.mitre.oauth2.model.ClientDetailsEntity.AppType;
3030
import org.mitre.oauth2.model.ClientDetailsEntity.AuthMethod;
3131
import org.mitre.oauth2.model.ClientDetailsEntity.SubjectType;
32+
import org.mitre.oauth2.model.PKCEAlgorithm;
3233
import org.mitre.oauth2.service.ClientDetailsEntityService;
3334
import org.mitre.oauth2.web.AuthenticationUtilities;
3435
import org.mitre.openid.connect.exception.ValidationException;
@@ -68,9 +69,6 @@
6869
import com.google.gson.JsonObject;
6970
import com.google.gson.JsonParseException;
7071
import com.google.gson.JsonParser;
71-
import com.google.gson.JsonPrimitive;
72-
import com.google.gson.JsonSerializationContext;
73-
import com.google.gson.JsonSerializer;
7472
import com.google.gson.JsonSyntaxException;
7573
import com.nimbusds.jose.Algorithm;
7674
import com.nimbusds.jose.EncryptionMethod;
@@ -203,6 +201,15 @@ public JWT deserialize(JsonElement json, Type typeOfT, JsonDeserializationContex
203201
}
204202
}
205203
})
204+
.registerTypeAdapter(PKCEAlgorithm.class, new JsonDeserializer<Algorithm>() {
205+
public PKCEAlgorithm deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
206+
if (json.isJsonPrimitive()) {
207+
return PKCEAlgorithm.parse(json.getAsString());
208+
} else {
209+
return null;
210+
}
211+
}
212+
})
206213
.setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")
207214
.create();
208215

0 commit comments

Comments
 (0)