10
10
import java .security .PrivateKey ;
11
11
import java .security .PublicKey ;
12
12
import java .security .Signature ;
13
+ import java .util .Arrays ;
13
14
import java .util .Enumeration ;
14
15
import java .util .HashMap ;
16
+ import java .util .List ;
15
17
import java .util .Map ;
16
18
import java .util .Random ;
17
19
21
23
import javax .servlet .http .HttpServletResponse ;
22
24
23
25
import org .apache .commons .codec .binary .Base64 ;
24
- import org .apache .commons .logging .Log ;
25
- import org .apache .commons .logging .LogFactory ;
26
26
import org .apache .http .auth .AuthScope ;
27
27
import org .apache .http .auth .UsernamePasswordCredentials ;
28
28
import org .apache .http .client .HttpClient ;
29
29
import org .apache .http .impl .client .DefaultHttpClient ;
30
30
import org .mitre .openid .connect .model .IdToken ;
31
31
import org .springframework .http .client .HttpComponentsClientHttpRequestFactory ;
32
+ import org .springframework .security .authentication .AuthenticationServiceException ;
32
33
import org .springframework .security .core .Authentication ;
33
34
import org .springframework .security .core .AuthenticationException ;
34
35
import org .springframework .security .web .authentication .AbstractAuthenticationProcessingFilter ;
85
86
public class OpenIdConnectAuthenticationFilter extends
86
87
AbstractAuthenticationProcessingFilter {
87
88
88
- private static Log logger = LogFactory
89
- .getLog (OpenIdConnectAuthenticationFilter .class );
90
-
91
89
private final static int HTTP_SOCKET_TIMEOUT = 30000 ;
92
90
private final static String SCOPE = "openid" ;
93
91
private final static int KEY_SIZE = 1024 ;
@@ -259,9 +257,18 @@ public void afterPropertiesSet() {
259
257
}
260
258
261
259
// prepend the spec necessary scope
262
- setScope (SCOPE + ((scope != null && !scope .isEmpty ()) ? " " + scope : "" ));
260
+ setScope ((scope != null && !scope .isEmpty ()) ? SCOPE + " " + scope
261
+ : SCOPE );
263
262
}
264
263
264
+ /*
265
+ * (non-Javadoc)
266
+ *
267
+ * @see org.springframework.security.web.authentication.
268
+ * AbstractAuthenticationProcessingFilter
269
+ * #attemptAuthentication(javax.servlet.http.HttpServletRequest,
270
+ * javax.servlet.http.HttpServletResponse)
271
+ */
265
272
/*
266
273
* (non-Javadoc)
267
274
*
@@ -275,6 +282,8 @@ public Authentication attemptAuthentication(HttpServletRequest request,
275
282
HttpServletResponse response ) throws AuthenticationException ,
276
283
IOException , ServletException {
277
284
285
+ final boolean debug = logger .isDebugEnabled ();
286
+
278
287
if (request .getParameter ("error" ) != null ) {
279
288
280
289
// Handle Authorization Endpoint error
@@ -313,10 +322,12 @@ public Authentication attemptAuthentication(HttpServletRequest request,
313
322
httpClient .getParams ().setParameter ("http.socket.timeout" ,
314
323
new Integer (httpSocketTimeout ));
315
324
316
- UsernamePasswordCredentials credentials = new UsernamePasswordCredentials (
317
- clientId , clientSecret );
318
- ((DefaultHttpClient ) httpClient ).getCredentialsProvider ()
319
- .setCredentials (AuthScope .ANY , credentials );
325
+ //
326
+ // TODO: basic auth is untested (it wasn't working last I tested)
327
+ // UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(
328
+ // clientId, clientSecret);
329
+ // ((DefaultHttpClient) httpClient).getCredentialsProvider()
330
+ // .setCredentials(AuthScope.ANY, credentials);
320
331
321
332
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory (
322
333
httpClient );
@@ -326,7 +337,18 @@ public Authentication attemptAuthentication(HttpServletRequest request,
326
337
MultiValueMap <String , String > form = new LinkedMultiValueMap <String , String >();
327
338
form .add ("grant_type" , "authorization_code" );
328
339
form .add ("code" , authorizationGrant );
329
- form .add ("redirect_uri" , buildRedirectURI (request ));
340
+ form .add ("redirect_uri" ,
341
+ buildRedirectURI (request , new String [] { "code" }));
342
+
343
+ // pass clientId and clientSecret in post of request
344
+ form .add ("client_id" , clientId );
345
+ form .add ("client_secret" , clientSecret );
346
+
347
+
348
+ if (debug ) {
349
+ logger .debug ("tokenEndpointURI = " + tokenEndpointURI );
350
+ logger .debug ("form = " + form );
351
+ }
330
352
331
353
String jsonString = null ;
332
354
@@ -341,7 +363,8 @@ public Authentication attemptAuthentication(HttpServletRequest request,
341
363
+ httpClientErrorException .getStatusText () + " : "
342
364
+ httpClientErrorException .getMessage ());
343
365
344
- return null ;
366
+ throw new AuthenticationServiceException (
367
+ "Unable to obtain Access Token." );
345
368
}
346
369
347
370
JsonElement jsonRoot = new JsonParser ().parse (jsonString );
@@ -355,7 +378,9 @@ public Authentication attemptAuthentication(HttpServletRequest request,
355
378
356
379
logger .error ("Token Endpoint returned: " + error );
357
380
358
- return null ;
381
+ throw new AuthenticationServiceException (
382
+ "Unable to obtain Access Token. Token Endpoint returned: "
383
+ + error );
359
384
360
385
} else {
361
386
@@ -376,7 +401,8 @@ public Authentication attemptAuthentication(HttpServletRequest request,
376
401
logger .error ("Problem parsing id_token: " + e );
377
402
// e.printStackTrace();
378
403
379
- return null ;
404
+ throw new AuthenticationServiceException (
405
+ "Problem parsing id_token return from Token endpoint: " + e );
380
406
}
381
407
382
408
} else {
@@ -385,7 +411,8 @@ public Authentication attemptAuthentication(HttpServletRequest request,
385
411
386
412
logger .error ("Token Endpoint did not return a token_id" );
387
413
388
- return null ;
414
+ throw new AuthenticationServiceException (
415
+ "Token Endpoint did not return a token_id" );
389
416
}
390
417
391
418
// Handle Check ID Endpoint interaction
@@ -418,7 +445,8 @@ public Authentication attemptAuthentication(HttpServletRequest request,
418
445
+ httpClientErrorException .getStatusText ()
419
446
+ " : " + httpClientErrorException .getMessage ());
420
447
421
- return null ;
448
+ throw new AuthenticationServiceException (
449
+ "Unable check token." );
422
450
}
423
451
424
452
jsonRoot = new JsonParser ().parse (jsonString );
@@ -453,29 +481,36 @@ public Authentication attemptAuthentication(HttpServletRequest request,
453
481
+ NONCE_SIGNATURE_COOKIE_NAME
454
482
+ " failed." );
455
483
456
- return null ;
484
+ throw new AuthenticationServiceException (
485
+ "Possible replay attack detected! "
486
+ + "The comparison of the nonce in the returned "
487
+ + "ID Token to the signed session "
488
+ + NONCE_SIGNATURE_COOKIE_NAME
489
+ + " failed." );
457
490
}
458
491
459
492
} else {
460
493
logger .error (NONCE_SIGNATURE_COOKIE_NAME
461
494
+ " was found, but was null or empty." );
462
495
463
- return null ;
496
+ throw new AuthenticationServiceException (NONCE_SIGNATURE_COOKIE_NAME
497
+ + " was found, but was null or empty." );
464
498
}
465
499
466
500
} else {
467
501
468
502
logger .error (NONCE_SIGNATURE_COOKIE_NAME
469
503
+ " cookie was not found." );
470
504
471
- return null ;
505
+ throw new AuthenticationServiceException (NONCE_SIGNATURE_COOKIE_NAME
506
+ + " cookie was not found." );
472
507
}
473
508
474
509
// Create an Authentication object for the token, and
475
510
// return.
476
511
477
512
OpenIdConnectAuthenticationToken token = new OpenIdConnectAuthenticationToken (
478
- idToken , userId );
513
+ userId , idToken );
479
514
480
515
Authentication authentication = this
481
516
.getAuthenticationManager ().authenticate (token );
@@ -495,7 +530,8 @@ public Authentication attemptAuthentication(HttpServletRequest request,
495
530
urlVariables .put ("response_type" , "code" );
496
531
urlVariables .put ("client_id" , clientId );
497
532
urlVariables .put ("scope" , scope );
498
- urlVariables .put ("redirect_uri" , buildRedirectURI (request ));
533
+ urlVariables .put ("redirect_uri" ,
534
+ buildRedirectURI (request , null ));
499
535
500
536
// Create a string value used to associate a user agent session
501
537
// with an ID Token to mitigate replay attacks. The value is
@@ -530,9 +566,15 @@ public Authentication attemptAuthentication(HttpServletRequest request,
530
566
*
531
567
* @param request
532
568
* the current request which is being processed by this filter
533
- * @return The redirect_uri.
569
+ * @param ingoreParameters
570
+ * an array of parameter names to ignore.
571
+ * @return
534
572
*/
535
- private String buildRedirectURI (HttpServletRequest request ) {
573
+ private String buildRedirectURI (HttpServletRequest request ,
574
+ String [] ingoreParameters ) {
575
+
576
+ List <String > ignore = (ingoreParameters != null ) ? Arrays
577
+ .asList (ingoreParameters ) : null ;
536
578
537
579
boolean isFirst = true ;
538
580
@@ -542,22 +584,25 @@ private String buildRedirectURI(HttpServletRequest request) {
542
584
.hasMoreElements ();) {
543
585
544
586
String name = (String ) e .nextElement ();
545
- // Assume for simplicity that there is only one value
546
- String value = request .getParameter (name );
547
587
548
- if (value == null ) {
549
- continue ;
550
- }
588
+ if (( ignore == null ) || (! ignore . contains ( name )) ) {
589
+ // Assume for simplicity that there is only one value
590
+ String value = request . getParameter ( name );
551
591
552
- if (isFirst ) {
553
- sb .append ("?" );
554
- isFirst = false ;
555
- }
592
+ if (value == null ) {
593
+ continue ;
594
+ }
556
595
557
- sb .append (name ).append ("=" ).append (value );
596
+ if (isFirst ) {
597
+ sb .append ("?" );
598
+ isFirst = false ;
599
+ }
558
600
559
- if (e .hasMoreElements ()) {
560
- sb .append ("&" );
601
+ sb .append (name ).append ("=" ).append (value );
602
+
603
+ if (e .hasMoreElements ()) {
604
+ sb .append ("&" );
605
+ }
561
606
}
562
607
}
563
608
0 commit comments