28
28
import java .util .concurrent .TimeUnit ;
29
29
30
30
import org .apache .commons .codec .binary .Base64 ;
31
+ import org .apache .http .MethodNotSupportedException ;
31
32
import org .apache .http .client .HttpClient ;
32
33
import org .apache .http .impl .client .HttpClientBuilder ;
33
34
import org .mitre .oauth2 .model .ClientDetailsEntity ;
34
35
import org .mitre .oauth2 .model .SystemScope ;
36
+ import org .mitre .oauth2 .model .ClientDetailsEntity .AuthMethod ;
35
37
import org .mitre .oauth2 .repository .OAuth2ClientRepository ;
36
38
import org .mitre .oauth2 .repository .OAuth2TokenRepository ;
37
39
import org .mitre .oauth2 .service .ClientDetailsEntityService ;
@@ -121,12 +123,15 @@ public ClientDetailsEntity saveNewClient(ClientDetailsEntity client) {
121
123
if (Strings .isNullOrEmpty (client .getClientId ())) {
122
124
client = generateClientId (client );
123
125
}
124
-
126
+
125
127
// make sure that clients with the "refresh_token" grant type have the "offline_access" scope, and vice versa
126
128
ensureRefreshTokenConsistency (client );
127
129
128
130
// make sure we don't have both a JWKS and a JWKS URI
129
131
ensureKeyConsistency (client );
132
+
133
+ // check consistency when using HEART mode
134
+ checkHeartMode (client );
130
135
131
136
// timestamp this to right now
132
137
client .setCreatedAt (new Date ());
@@ -146,6 +151,7 @@ public ClientDetailsEntity saveNewClient(ClientDetailsEntity client) {
146
151
}
147
152
148
153
/**
154
+ * Make sure the client has only one type of key registered
149
155
* @param client
150
156
*/
151
157
private void ensureKeyConsistency (ClientDetailsEntity client ) {
@@ -155,6 +161,9 @@ private void ensureKeyConsistency(ClientDetailsEntity client) {
155
161
}
156
162
}
157
163
164
+ /**
165
+ * Make sure the client doesn't request any system reserved scopes
166
+ */
158
167
private void ensureNoReservedScopes (ClientDetailsEntity client ) {
159
168
// make sure a client doesn't get any special system scopes
160
169
Set <SystemScope > requestedScope = scopeService .fromStrings (client .getScope ());
@@ -164,6 +173,10 @@ private void ensureNoReservedScopes(ClientDetailsEntity client) {
164
173
client .setScope (scopeService .toStrings (requestedScope ));
165
174
}
166
175
176
+ /**
177
+ * Load the sector identifier URI if it exists and check the redirect URIs against it
178
+ * @param client
179
+ */
167
180
private void checkSectorIdentifierUri (ClientDetailsEntity client ) {
168
181
if (!Strings .isNullOrEmpty (client .getSectorIdentifierUri ())) {
169
182
try {
@@ -183,6 +196,10 @@ private void checkSectorIdentifierUri(ClientDetailsEntity client) {
183
196
}
184
197
}
185
198
199
+ /**
200
+ * Make sure the client has the appropriate scope and grant type.
201
+ * @param client
202
+ */
186
203
private void ensureRefreshTokenConsistency (ClientDetailsEntity client ) {
187
204
if (client .getAuthorizedGrantTypes ().contains ("refresh_token" )
188
205
|| client .getScope ().contains (SystemScopeService .OFFLINE_ACCESS )) {
@@ -191,6 +208,82 @@ private void ensureRefreshTokenConsistency(ClientDetailsEntity client) {
191
208
}
192
209
}
193
210
211
+ /**
212
+ * If HEART mode is enabled, make sure the client meets the requirements:
213
+ * - Only one of authorization_code, implicit, or client_credentials can be used at a time
214
+ * - A redirect_uri must be registered with either authorization_code or implicit
215
+ * - A key must be registered
216
+ * - A client secret must not be generated
217
+ * - authorization_code and client_credentials must use the private_key authorization method
218
+ * @param client
219
+ */
220
+ private void checkHeartMode (ClientDetailsEntity client ) {
221
+ if (config .isHeartMode ()) {
222
+ if (client .getGrantTypes ().contains ("authorization_code" )) {
223
+ // make sure we don't have incompatible grant types
224
+ if (client .getGrantTypes ().contains ("implicit" ) || client .getGrantTypes ().contains ("client_credentials" )) {
225
+ throw new IllegalArgumentException ("[HEART mode] Incompatible grant types" );
226
+ }
227
+
228
+ // make sure we've got the right authentication method
229
+ if (client .getTokenEndpointAuthMethod () == null || client .getTokenEndpointAuthMethod ().equals (AuthMethod .PRIVATE_KEY )) {
230
+ throw new IllegalArgumentException ("[HEART mode] Authorization code clients must use the private_key authentication method" );
231
+ }
232
+
233
+ // make sure we've got a redirect URI
234
+ if (client .getRedirectUris ().isEmpty ()) {
235
+ throw new IllegalArgumentException ("[HEART mode] Authorization code clients must register at least one redirect URI" );
236
+ }
237
+ }
238
+
239
+ if (client .getGrantTypes ().contains ("implicit" )) {
240
+ // make sure we don't have incompatible grant types
241
+ if (client .getGrantTypes ().contains ("authorization_code" ) || client .getGrantTypes ().contains ("client_credentials" ) || client .getGrantTypes ().contains ("refresh_token" )) {
242
+ throw new IllegalArgumentException ("[HEART mode] Incompatible grant types" );
243
+ }
244
+
245
+ // make sure we've got the right authentication method
246
+ if (client .getTokenEndpointAuthMethod () == null || client .getTokenEndpointAuthMethod ().equals (AuthMethod .NONE )) {
247
+ throw new IllegalArgumentException ("[HEART mode] Implicit clients must use the none authentication method" );
248
+ }
249
+
250
+ // make sure we've got a redirect URI
251
+ if (client .getRedirectUris ().isEmpty ()) {
252
+ throw new IllegalArgumentException ("[HEART mode] Implicit clients must register at least one redirect URI" );
253
+ }
254
+ }
255
+
256
+ if (client .getGrantTypes ().contains ("client_credentials" )) {
257
+ // make sure we don't have incompatible grant types
258
+ if (client .getGrantTypes ().contains ("authorization_code" ) || client .getGrantTypes ().contains ("implicit" ) || client .getGrantTypes ().contains ("refresh_token" )) {
259
+ throw new IllegalArgumentException ("[HEART mode] Incompatible grant types" );
260
+ }
261
+
262
+ // make sure we've got the right authentication method
263
+ if (client .getTokenEndpointAuthMethod () == null || client .getTokenEndpointAuthMethod ().equals (AuthMethod .PRIVATE_KEY )) {
264
+ throw new IllegalArgumentException ("[HEART mode] Client credentials clients must use the private_key authentication method" );
265
+ }
266
+
267
+ // make sure we've got a redirect URI
268
+ if (!client .getRedirectUris ().isEmpty ()) {
269
+ throw new IllegalArgumentException ("[HEART mode] Client credentials clients must not register a redirect URI" );
270
+ }
271
+
272
+ }
273
+
274
+ // make sure we don't have a client secret
275
+ if (!Strings .isNullOrEmpty (client .getClientSecret ())) {
276
+ throw new IllegalArgumentException ("[HEART mode] Client secrets are not allowed" );
277
+ }
278
+
279
+ // make sure we've got a key registered
280
+ if (client .getJwks () == null && Strings .isNullOrEmpty (client .getJwksUri ())) {
281
+ throw new IllegalArgumentException ("[HEART mode] All clients must have a key registered" );
282
+ }
283
+
284
+ }
285
+ }
286
+
194
287
/**
195
288
* Get the client by its internal ID
196
289
*/
@@ -283,6 +376,9 @@ public ClientDetailsEntity updateClient(ClientDetailsEntity oldClient, ClientDet
283
376
284
377
// make sure we don't have both a JWKS and a JWKS URI
285
378
ensureKeyConsistency (newClient );
379
+
380
+ // check consistency when using HEART mode
381
+ checkHeartMode (newClient );
286
382
287
383
// check the sector URI
288
384
checkSectorIdentifierUri (newClient );
@@ -317,7 +413,12 @@ public ClientDetailsEntity generateClientId(ClientDetailsEntity client) {
317
413
*/
318
414
@ Override
319
415
public ClientDetailsEntity generateClientSecret (ClientDetailsEntity client ) {
320
- client .setClientSecret (Base64 .encodeBase64URLSafeString (new BigInteger (512 , new SecureRandom ()).toByteArray ()).replace ("=" , "" ));
416
+ if (config .isHeartMode ()) {
417
+ logger .error ("[HEART mode] Can't generate a client secret, skipping step; client won't be saved due to invalid configuration" );
418
+ client .setClientSecret (null );
419
+ } else {
420
+ client .setClientSecret (Base64 .encodeBase64URLSafeString (new BigInteger (512 , new SecureRandom ()).toByteArray ()).replace ("=" , "" ));
421
+ }
321
422
return client ;
322
423
}
323
424
0 commit comments