1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package fr.paris.lutece.plugins.oauth2.web;
35
36 import java.io.IOException;
37 import java.io.Serializable;
38 import java.io.UnsupportedEncodingException;
39 import java.math.BigInteger;
40 import java.net.URLEncoder;
41 import java.security.NoSuchAlgorithmException;
42 import java.security.SecureRandom;
43 import javax.servlet.http.HttpServletRequest;
44 import javax.servlet.http.HttpServletResponse;
45 import javax.servlet.http.HttpSession;
46
47 import org.apache.commons.lang3.StringUtils;
48 import org.apache.log4j.Logger;
49
50 import fr.paris.lutece.plugins.oauth2.business.AuthClientConf;
51 import fr.paris.lutece.plugins.oauth2.business.AuthServerConf;
52 import fr.paris.lutece.plugins.oauth2.business.Token;
53 import fr.paris.lutece.plugins.oauth2.dataclient.DataClient;
54 import fr.paris.lutece.plugins.oauth2.jwt.JWTParser;
55 import fr.paris.lutece.plugins.oauth2.jwt.TokenValidationException;
56 import fr.paris.lutece.plugins.oauth2.service.DataClientService;
57 import fr.paris.lutece.plugins.oauth2.service.PkceUtil;
58 import fr.paris.lutece.plugins.oauth2.service.TokenService;
59 import fr.paris.lutece.portal.service.util.AppLogService;
60 import fr.paris.lutece.portal.service.util.AppPathService;
61 import fr.paris.lutece.portal.service.util.AppPropertiesService;
62 import fr.paris.lutece.util.http.SecurityUtil;
63 import fr.paris.lutece.util.httpaccess.HttpAccessException;
64 import fr.paris.lutece.util.url.UrlItem;
65
66
67
68
69 public class CallbackHandler implements Serializable
70 {
71 private static final String PROPERTY_ERROR_PAGE = "oauth2.error.page";
72 private static final long serialVersionUID = 1L;
73 private static Logger _logger = Logger.getLogger( Constants.LOGGER_OAUTH2 );
74 private String _handlerName;
75 private AuthServerConf _authServerConf;
76 private AuthClientConf _authClientConf;
77 private JWTParser _jWTParser;
78 private boolean _bDefault;
79
80
81
82
83 public AuthServerConf getAuthServerConf( )
84 {
85 return _authServerConf;
86 }
87
88
89
90
91
92 public void setAuthServerConf( AuthServerConf authServerConf )
93 {
94 _authServerConf = authServerConf;
95 }
96
97
98
99
100 public AuthClientConf getAuthClientConf( )
101 {
102 return _authClientConf;
103 }
104
105
106
107
108
109 public void setAuthClientConf( AuthClientConf authClientConf )
110 {
111 _authClientConf = authClientConf;
112 }
113
114
115
116
117
118
119
120
121
122 void handle( HttpServletRequest request, HttpServletResponse response )
123 {
124 String strError = request.getParameter( Constants.PARAMETER_ERROR );
125 String strCode = request.getParameter( Constants.PARAMETER_CODE );
126
127 if ( strError != null )
128 {
129 handleError( request, response, strError );
130 }
131 else
132 if ( strCode != null )
133 {
134 handleAuthorizationCodeResponse( request, response );
135 }
136 else
137 {
138 handleAuthorizationRequest( request, response );
139 }
140 }
141
142
143
144
145
146
147
148
149
150
151
152 private void handleError( HttpServletRequest request, HttpServletResponse response, String strError )
153 {
154 DataClient dataClient = DataClientService.instance( ).getClient( request );
155
156 if ( dataClient != null )
157 {
158
159 dataClient.handleError( request, response, strError );
160 }
161 else
162 {
163
164 try
165 {
166 UrlItem url = new UrlItem( AppPathService.getBaseUrl( request ) + AppPropertiesService.getProperty( PROPERTY_ERROR_PAGE ) );
167 url.addParameter( Constants.PARAMETER_ERROR, strError );
168 _logger.info( strError );
169 response.sendRedirect( url.getUrl( ) );
170 }
171 catch( IOException ex )
172 {
173
174 _logger.error( "Error redirecting to the error page : " + ex.getMessage( ), ex );
175 }
176 }
177 }
178
179
180
181
182
183
184
185
186
187 private void handleAuthorizationRequest( HttpServletRequest request, HttpServletResponse response )
188 {
189 try
190 {
191 HttpSession session = request.getSession( true );
192
193 DataClient dataClient = DataClientService.instance( ).getClient( request );
194
195 UrlItem url = new UrlItem( _authServerConf.getAuthorizationEndpointUri( ) );
196 url.addParameter( Constants.PARAMETER_CLIENT_ID, _authClientConf.getClientId( ) );
197 url.addParameter( Constants.PARAMETER_RESPONSE_TYPE, Constants.RESPONSE_TYPE_CODE );
198 url.addParameter( Constants.PARAMETER_REDIRECT_URI, URLEncoder.encode( generateRedirectUrl( request, dataClient ), "UTF-8" ) );
199 url.addParameter( Constants.PARAMETER_SCOPE, dataClient.getScopes( ) );
200 url.addParameter( Constants.PARAMETER_STATE, createState( session ) );
201 url.addParameter( Constants.PARAMETER_NONCE, createNonce( session ) );
202 if(_authClientConf.isPkce())
203 {
204 String strCodeVerifier=createCodeVerifier(session);
205 String strCodeChallenge=createCodeChallenge(strCodeVerifier, session);
206 url.addParameter(Constants.PARAMETER_CODE_CHALLENGE,strCodeChallenge);
207 url.addParameter(Constants.PARAMETER_CODE_CHALLENGE_METHOD,"S256");
208 }
209
210
211
212
213 addComplementaryParameters( url, request );
214 addBackPromptUrl(url, request);
215
216 String strAcrValues = dataClient.getAcrValues( );
217 if ( strAcrValues != null )
218 {
219 url.addParameter( Constants.PARAMETER_ACR_VALUES, strAcrValues );
220 }
221
222 String strUrl = url.getUrl( );
223 _logger.debug( "OAuth request : " + strUrl );
224 response.sendRedirect( strUrl );
225 }
226 catch( IOException ex )
227 {
228 String strError = "Error retrieving an authorization code : " + ex.getMessage( );
229 _logger.error( strError, ex );
230 handleError( request, response, Constants.ERROR_TYPE_RETRIEVING_AUTHORIZATION_CODE );
231 }
232 }
233
234
235
236
237
238
239
240
241
242 private void handleAuthorizationCodeResponse( HttpServletRequest request, HttpServletResponse response )
243 {
244 String strCode = request.getParameter( Constants.PARAMETER_CODE );
245
246 _logger.info( "OAuth Authorization code received : " + SecurityUtil.logForgingProtect( strCode ) );
247
248
249 if ( !checkState( request ) )
250 {
251 handleError( request, response, Constants.ERROR_TYPE_INVALID_STATE );
252
253 return;
254 }
255
256 try
257 {
258 HttpSession session = request.getSession( );
259 DataClient dataClient = DataClientService.instance( ).getClient( request );
260 String strRedirectUri = generateRedirectUrl( request, dataClient );
261 Token token = getToken( strRedirectUri, strCode, session );
262 dataClient.handleToken( token, request, response );
263 }
264 catch( IOException ex )
265 {
266 String strError = "Error retrieving token : " + ex.getMessage( );
267 _logger.error( strError, ex );
268 handleError( request, response, strError );
269 }
270 catch( HttpAccessException ex )
271 {
272 String strError = "Error retrieving token : " + ex.getMessage( );
273 _logger.error( strError, ex );
274 handleError( request, response, strError );
275 }
276 catch( TokenValidationException ex )
277 {
278 String strError = "Error retrieving token : " + ex.getMessage( );
279 _logger.error( strError, ex );
280 handleError( request, response, strError );
281 }
282 }
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301 private Token getToken( String strRedirectUri, String strAuthorizationCode, HttpSession session )
302 throws IOException, HttpAccessException, TokenValidationException
303 {
304
305 return TokenService.getService( ).getToken( strRedirectUri, _authClientConf, _authServerConf, strAuthorizationCode, session, _jWTParser,
306 getStoredNonce( session ),getStoredCodeVerifier(session) );
307
308 }
309
310
311
312
313
314
315
316
317
318
319
320 private String createNonce( HttpSession session )
321 {
322 String nonce = new BigInteger( 128, new SecureRandom( ) ).toString( 16 );
323 session.setAttribute( getNonceAttributeSessionName( ), nonce );
324
325 return nonce;
326 }
327
328
329
330
331
332
333
334
335
336 private String createCodeChallenge(String strCodeVerifier,HttpSession session )
337 {
338
339 String strChallenge=null;
340 try {
341 if(strCodeVerifier!=null)
342 {
343 strChallenge = PkceUtil.generateCodeChallenge(strCodeVerifier);
344 }
345 } catch (UnsupportedEncodingException|NoSuchAlgorithmException e) {
346 AppLogService.error(e);
347 }
348
349 session.setAttribute( getCodeChallengeAttributeSessionName(), strChallenge );
350
351 return strChallenge;
352 }
353
354
355
356
357
358
359
360
361 private String createCodeVerifier( HttpSession session )
362 {
363 String StrCodeVerifier=null;
364 try {
365 StrCodeVerifier = PkceUtil.generateCodeVerifier();
366 } catch (UnsupportedEncodingException e) {
367 AppLogService.error(e);
368 }
369 session.setAttribute( getCodeVerifierAttributeSessionName(), StrCodeVerifier );
370
371 return StrCodeVerifier;
372 }
373
374
375
376
377
378
379
380
381
382
383 private String getStoredCodeVerifier( HttpSession session )
384 {
385 return getStoredSessionString( session, getCodeVerifierAttributeSessionName() );
386 }
387
388
389
390
391
392
393
394
395
396 private String getStoredCodeChallenge( HttpSession session )
397 {
398 return getStoredSessionString( session, getCodeChallengeAttributeSessionName() );
399 }
400
401
402
403
404
405
406
407
408
409
410
411 private String getStoredNonce( HttpSession session )
412 {
413 return getStoredSessionString( session, getNonceAttributeSessionName( ) );
414 }
415
416
417
418
419
420
421
422 private String getNonceAttributeSessionName( )
423 {
424
425 return getHandlerName( ) == null ? Constants.NONCE_SESSION_VARIABLE : getHandlerName( ) + Constants.NONCE_SESSION_VARIABLE;
426 }
427
428
429
430
431
432 private String getCodeVerifierAttributeSessionName( )
433 {
434
435 return getHandlerName( ) == null ? Constants.CODE_VERIFIER_SESSION_VARIABLE : getHandlerName( ) + Constants.CODE_VERIFIER_SESSION_VARIABLE;
436 }
437
438
439
440
441
442
443 private String getCodeChallengeAttributeSessionName( )
444 {
445
446 return getHandlerName( ) == null ? Constants.CODE_CHALLENGE_SESSION_VARIABLE : getHandlerName( ) + Constants.CODE_CHALLENGE_SESSION_VARIABLE;
447 }
448
449
450
451
452
453
454
455
456
457
458
459 private boolean checkState( HttpServletRequest request )
460 {
461 String strState = request.getParameter( Constants.PARAMETER_STATE );
462 HttpSession session = request.getSession( );
463 String strStored = getStoredState( session );
464 boolean bCheck = ( ( strState == null ) || strState.equals( strStored ) );
465
466 if ( !bCheck )
467 {
468 _logger.debug( "Bad state returned by server : " + SecurityUtil.logForgingProtect( strState ) + " while expecting : " + strStored );
469 }
470
471 return bCheck;
472 }
473
474
475
476
477
478
479
480
481 private String createState( HttpSession session )
482 {
483 String strState = new BigInteger( 128, new SecureRandom( ) ).toString( 16 );
484 session.setAttribute( getStateAttributeSessionName( ), strState );
485
486 return strState;
487 }
488
489
490
491
492
493 private String getStateAttributeSessionName( )
494 {
495
496 return getHandlerName( ) == null ? Constants.STATE_SESSION_VARIABLE : getHandlerName( ) + Constants.STATE_SESSION_VARIABLE;
497 }
498
499
500
501
502
503
504
505
506 private String getStoredState( HttpSession session )
507 {
508 return getStoredSessionString( session, getStateAttributeSessionName( ) );
509 }
510
511
512
513
514
515
516
517
518
519
520 private static String getStoredSessionString( HttpSession session, String strKey )
521 {
522 Object object = session.getAttribute( strKey );
523
524 if ( ( object != null ) && object instanceof String )
525 {
526 return object.toString( );
527 }
528 else
529 {
530 return null;
531 }
532 }
533
534
535
536
537
538
539 public String getHandlerName( )
540 {
541 return _handlerName;
542 }
543
544
545
546
547
548
549
550 public void setHandlerName( String _handlerName )
551 {
552 this._handlerName = _handlerName;
553 }
554
555
556
557
558
559 public JWTParser getJWTParser( )
560 {
561 return _jWTParser;
562 }
563
564
565
566
567
568
569 public void setJWTParser( JWTParser jWTParser )
570 {
571 this._jWTParser = jWTParser;
572 }
573
574
575
576
577
578 public boolean isDefault( )
579 {
580 return _bDefault;
581 }
582
583
584
585
586
587
588 public void setDefault( boolean _bDefault )
589 {
590 this._bDefault = _bDefault;
591 }
592
593 private void addComplementaryParameters( UrlItem url, HttpServletRequest request )
594 {
595 String[] strComplementaryParams = request.getParameterValues( Constants.PARAMETER_COMPLEMENTARY_PARAMETER );
596 String strParamValue;
597 String strParamCode;
598
599 if ( strComplementaryParams!=null && strComplementaryParams.length > 0 )
600 {
601 for (int i = 0; i < strComplementaryParams.length; i++) {
602
603 if(strComplementaryParams[i].contains( "=" ))
604 {
605 strParamCode = strComplementaryParams[i].split( "=" ) [0];
606 strParamValue = strComplementaryParams[i].substring( strComplementaryParams[i].indexOf( "=" ) + 1, strComplementaryParams[i].length( ) );
607 try
608 {
609 strParamValue = URLEncoder.encode( strParamValue, "UTF-8" );
610 }
611 catch( UnsupportedEncodingException e )
612 {
613 _logger.error( "error during urlEncode of param" + strParamValue, e );
614 }
615 url.addParameter( strParamCode, strParamValue );
616 }
617
618 }
619
620 }
621
622 }
623
624
625
626 private void addBackPromptUrl( UrlItem url, HttpServletRequest request )
627 {
628 String strBackPromptUrl= request.getParameter( Constants.PARAMETER_BACK_PROMPT_URL);
629
630 if ( !StringUtils.isEmpty( strBackPromptUrl) )
631 {
632 try
633 {
634
635 url.addParameter( Constants.PARAMETER_BACK_PROMPT_URL, URLEncoder.encode( strBackPromptUrl, "UTF-8" ) );
636
637 }
638 catch( UnsupportedEncodingException e )
639 {
640 _logger.error( "error during urlEncode of param " +Constants.PARAMETER_BACK_PROMPT_URL + strBackPromptUrl, e );
641 }
642
643 }
644
645 }
646
647
648
649
650
651
652
653
654
655
656
657
658 private String generateRedirectUrl( HttpServletRequest request, DataClient dataClient )
659 {
660 String stRedirectUrl = _authClientConf.getRedirectUri( );
661 UrlItem url = new UrlItem(stRedirectUrl);
662
663 if ( stRedirectUrl == null )
664 {
665 stRedirectUrl = DataClientService.instance( ).getDataClientUrl( request, dataClient.getName( ), getHandlerName( ) );
666 url=new UrlItem(stRedirectUrl);
667
668 }
669 else
670 {
671
672
673 url.addParameter(Constants.PARAMETER_DATA_CLIENT, dataClient.getName( ));
674 if ( getHandlerName( ) != null )
675 {
676 url.addParameter(Constants.PARAMETER_HANDLER_NAME, getHandlerName( ));
677
678 }
679 }
680
681 String strBackPromptUrl= request.getParameter( Constants.PARAMETER_BACK_PROMPT_URL);
682
683 if ( !StringUtils.isEmpty( strBackPromptUrl) )
684 {
685
686 try {
687
688 url.addParameter(Constants.PARAMETER_BACK_PROMPT_URL,URLEncoder.encode(strBackPromptUrl, "UTF-8"));
689
690 } catch (UnsupportedEncodingException e) {
691 _logger.error(
692 "error during urlEncode of param " + Constants.PARAMETER_BACK_PROMPT_URL + strBackPromptUrl, e);
693 }
694
695 }
696
697 return url.getUrl();
698 }
699
700 }