1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package edu.internet2.middleware.shibboleth.idp.authn;
18
19 import java.io.IOException;
20 import java.security.GeneralSecurityException;
21 import java.security.MessageDigest;
22 import java.security.NoSuchAlgorithmException;
23 import java.security.Principal;
24 import java.util.ArrayList;
25 import java.util.HashMap;
26 import java.util.HashSet;
27 import java.util.Iterator;
28 import java.util.Map;
29 import java.util.Set;
30 import java.util.Map.Entry;
31
32 import javax.security.auth.Subject;
33 import javax.servlet.RequestDispatcher;
34 import javax.servlet.ServletConfig;
35 import javax.servlet.ServletException;
36 import javax.servlet.http.Cookie;
37 import javax.servlet.http.HttpServlet;
38 import javax.servlet.http.HttpServletRequest;
39 import javax.servlet.http.HttpServletResponse;
40
41 import org.joda.time.DateTime;
42 import org.opensaml.common.IdentifierGenerator;
43 import org.opensaml.common.impl.SecureRandomIdentifierGenerator;
44 import org.opensaml.saml2.core.AuthnContext;
45 import org.opensaml.util.storage.StorageService;
46 import org.opensaml.ws.transport.http.HTTPTransportUtils;
47 import org.opensaml.xml.util.Base64;
48 import org.opensaml.xml.util.DatatypeHelper;
49 import org.slf4j.Logger;
50 import org.slf4j.LoggerFactory;
51
52 import edu.internet2.middleware.shibboleth.common.session.SessionManager;
53 import edu.internet2.middleware.shibboleth.common.util.HttpHelper;
54 import edu.internet2.middleware.shibboleth.idp.profile.IdPProfileHandlerManager;
55 import edu.internet2.middleware.shibboleth.idp.session.AuthenticationMethodInformation;
56 import edu.internet2.middleware.shibboleth.idp.session.ServiceInformation;
57 import edu.internet2.middleware.shibboleth.idp.session.Session;
58 import edu.internet2.middleware.shibboleth.idp.session.impl.AuthenticationMethodInformationImpl;
59 import edu.internet2.middleware.shibboleth.idp.session.impl.ServiceInformationImpl;
60
61
62 public class AuthenticationEngine extends HttpServlet {
63
64
65 public static final String LOGIN_CONTEXT_PARTITION_NAME_INIT_PARAM_NAME = "loginContextPartitionName";
66
67
68 public static final String LOGIN_CONTEXT_LIFETIME_INIT_PARAM_NAME = "loginContextEntryLifetime";
69
70
71 public static final String IDP_SESSION_COOKIE_NAME = "_idp_session";
72
73
74 public static final String LOGIN_CONTEXT_KEY_NAME = "_idp_authn_lc_key";
75
76
77 private static final long serialVersionUID = -8479060989001890156L;
78
79
80 private static final Logger LOG = LoggerFactory.getLogger(AuthenticationEngine.class);
81
82
83 private static StorageService<String, LoginContextEntry> storageService;
84
85
86 private static String loginContextPartitionName;
87
88
89 private static long loginContextEntryLifetime;
90
91
92 private static IdentifierGenerator idGen;
93
94
95 private IdPProfileHandlerManager handlerManager;
96
97
98 private SessionManager<Session> sessionManager;
99
100
101 public void init(ServletConfig config) throws ServletException {
102 super.init(config);
103
104 String handlerManagerId = config.getInitParameter("handlerManagerId");
105 if (DatatypeHelper.isEmpty(handlerManagerId)) {
106 handlerManagerId = "shibboleth.HandlerManager";
107 }
108 handlerManager = (IdPProfileHandlerManager) getServletContext().getAttribute(handlerManagerId);
109
110 String sessionManagerId = config.getInitParameter("sessionManagedId");
111 if (DatatypeHelper.isEmpty(sessionManagerId)) {
112 sessionManagerId = "shibboleth.SessionManager";
113 }
114 sessionManager = (SessionManager<Session>) getServletContext().getAttribute(sessionManagerId);
115
116 String storageServiceId = config.getInitParameter("storageServiceId");
117 if (DatatypeHelper.isEmpty(storageServiceId)) {
118 storageServiceId = "shibboleth.StorageService";
119 }
120 storageService = (StorageService<String, LoginContextEntry>) getServletContext().getAttribute(storageServiceId);
121
122 String partitionName = DatatypeHelper.safeTrimOrNullString(config
123 .getInitParameter(LOGIN_CONTEXT_PARTITION_NAME_INIT_PARAM_NAME));
124 if (partitionName != null) {
125 loginContextPartitionName = partitionName;
126 } else {
127 loginContextPartitionName = "loginContexts";
128 }
129
130 String lifetime = DatatypeHelper.safeTrimOrNullString(config
131 .getInitParameter(LOGIN_CONTEXT_LIFETIME_INIT_PARAM_NAME));
132 if (lifetime != null) {
133 loginContextEntryLifetime = Long.parseLong(lifetime);
134 } else {
135 loginContextEntryLifetime = 1000 * 60 * 30;
136 }
137
138 try {
139 idGen = new SecureRandomIdentifierGenerator();
140 } catch (NoSuchAlgorithmException e) {
141 throw new ServletException("Error create random number generator", e);
142 }
143 }
144
145
146
147
148
149
150
151
152
153
154 protected static LoginContext retrieveLoginContext(HttpServletRequest httpRequest, boolean removeFromStorageService) {
155
156
157
158
159 LoginContext loginContext = (LoginContext) httpRequest.getAttribute(LoginContext.LOGIN_CONTEXT_KEY);
160 if (loginContext != null) {
161 LOG.trace("Login context retrieved from HTTP request attribute");
162 return loginContext;
163 }
164
165 String contextId = DatatypeHelper.safeTrimOrNullString((String) httpRequest
166 .getAttribute(LOGIN_CONTEXT_KEY_NAME));
167
168 if (contextId == null) {
169 Cookie[] requestCookies = httpRequest.getCookies();
170 if (requestCookies != null) {
171 for (Cookie requestCookie : requestCookies) {
172 if (DatatypeHelper.safeEquals(requestCookie.getName(), LOGIN_CONTEXT_KEY_NAME)) {
173 LOG.trace("Located cookie with login context key");
174 contextId = requestCookie.getValue();
175 break;
176 }
177 }
178 }
179 }
180
181 LOG.trace("Using login context key {} to look up login context", contextId);
182 LoginContextEntry entry;
183 if (removeFromStorageService) {
184 entry = storageService.remove(loginContextPartitionName, contextId);
185 } else {
186 entry = storageService.get(loginContextPartitionName, contextId);
187 }
188 if (entry == null) {
189 LOG.trace("No entry for login context found in storage service.");
190 return null;
191 } else if (entry.isExpired()) {
192 LOG.trace("Login context entry found in storage service but it was expired.");
193 return null;
194 } else {
195 LOG.trace("Login context entry found in storage service.");
196 return entry.getLoginContext();
197 }
198 }
199
200
201
202
203
204
205
206 public static void returnToAuthenticationEngine(HttpServletRequest httpRequest, HttpServletResponse httpResponse) {
207 LOG.debug("Returning control to authentication engine");
208 LoginContext loginContext = retrieveLoginContext(httpRequest, false);
209 if (loginContext == null) {
210 LOG.error("No login context available, unable to return to authentication engine");
211 forwardRequest("/idp-error.jsp", httpRequest, httpResponse);
212 } else {
213 forwardRequest(loginContext.getAuthenticationEngineURL(), httpRequest, httpResponse);
214 }
215 }
216
217
218
219
220
221
222
223
224 public static void returnToProfileHandler(LoginContext loginContext, HttpServletRequest httpRequest,
225 HttpServletResponse httpResponse) {
226 LOG.debug("Returning control to profile handler at: {}", loginContext.getProfileHandlerURL());
227 httpRequest.setAttribute(LoginContext.LOGIN_CONTEXT_KEY, loginContext);
228
229
230 Cookie lcKeyCookie = new Cookie(LOGIN_CONTEXT_KEY_NAME, "");
231 lcKeyCookie.setMaxAge(0);
232 httpResponse.addCookie(lcKeyCookie);
233
234 forwardRequest(loginContext.getProfileHandlerURL(), httpRequest, httpResponse);
235 }
236
237
238
239
240
241
242
243
244 protected static void forwardRequest(String forwardPath, HttpServletRequest httpRequest,
245 HttpServletResponse httpResponse) {
246 try {
247 RequestDispatcher dispatcher = httpRequest.getRequestDispatcher(forwardPath);
248 dispatcher.forward(httpRequest, httpResponse);
249 return;
250 } catch (IOException e) {
251 LOG.error("Unable to return control back to authentication engine", e);
252 } catch (ServletException e) {
253 LOG.error("Unable to return control back to authentication engine", e);
254 }
255 }
256
257
258 @SuppressWarnings("unchecked")
259 protected void service(HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws ServletException,
260 IOException {
261 LOG.debug("Processing incoming request");
262
263 if (httpResponse.isCommitted()) {
264 LOG.error("HTTP Response already committed");
265 }
266
267 LoginContext loginContext = retrieveLoginContext(httpRequest, true);
268 if (loginContext == null) {
269 LOG.error("Incoming request does not have attached login context");
270 throw new ServletException("Incoming request does not have attached login context");
271 }
272
273 if (!loginContext.getAuthenticationAttempted()) {
274 startUserAuthentication(loginContext, httpRequest, httpResponse);
275 } else {
276 completeAuthentication(loginContext, httpRequest, httpResponse);
277 }
278 }
279
280
281
282
283
284
285
286
287
288
289 protected void startUserAuthentication(LoginContext loginContext, HttpServletRequest httpRequest,
290 HttpServletResponse httpResponse) {
291 LOG.debug("Beginning user authentication process");
292 try {
293 Session idpSession = (Session) httpRequest.getAttribute(Session.HTTP_SESSION_BINDING_ATTRIBUTE);
294 if (idpSession != null) {
295 LOG.debug("Existing IdP session available for principal {}", idpSession.getPrincipalName());
296 }
297
298 Map<String, LoginHandler> possibleLoginHandlers = determinePossibleLoginHandlers(loginContext);
299 LOG.debug("Possible authentication handlers for this request: {}", possibleLoginHandlers);
300
301
302 if (loginContext.isForceAuthRequired()) {
303 filterByForceAuthentication(idpSession, loginContext, possibleLoginHandlers);
304 }
305
306 if (loginContext.isPassiveAuthRequired()) {
307 filterByPassiveAuthentication(idpSession, loginContext, possibleLoginHandlers);
308 }
309
310
311
312 LOG.debug("Possible authentication handlers after filtering: {}", possibleLoginHandlers);
313 LoginHandler loginHandler;
314 if (idpSession != null && possibleLoginHandlers.containsKey(AuthnContext.PREVIOUS_SESSION_AUTHN_CTX)) {
315 loginContext.setAttemptedAuthnMethod(AuthnContext.PREVIOUS_SESSION_AUTHN_CTX);
316 loginHandler = possibleLoginHandlers.get(AuthnContext.PREVIOUS_SESSION_AUTHN_CTX);
317 } else {
318 possibleLoginHandlers.remove(AuthnContext.PREVIOUS_SESSION_AUTHN_CTX);
319 Entry<String, LoginHandler> chosenLoginHandler = possibleLoginHandlers.entrySet().iterator().next();
320 loginContext.setAttemptedAuthnMethod(chosenLoginHandler.getKey());
321 loginHandler = chosenLoginHandler.getValue();
322 }
323
324
325 LOG.debug("Authenticating user with login handler of type {}", loginHandler.getClass().getName());
326 loginContext.setAuthenticationAttempted();
327 loginContext.setAuthenticationEngineURL(HttpHelper.getRequestUriWithoutContext(httpRequest));
328 storeLoginContext(loginContext, httpRequest, httpResponse);
329 loginHandler.login(httpRequest, httpResponse);
330 } catch (AuthenticationException e) {
331 loginContext.setAuthenticationFailure(e);
332 returnToProfileHandler(loginContext, httpRequest, httpResponse);
333 }
334 }
335
336
337
338
339
340
341
342
343
344
345 protected Map<String, LoginHandler> determinePossibleLoginHandlers(LoginContext loginContext)
346 throws AuthenticationException {
347 Map<String, LoginHandler> supportedLoginHandlers = new HashMap<String, LoginHandler>(handlerManager
348 .getLoginHandlers());
349 LOG.trace("Supported login handlers: {}", supportedLoginHandlers);
350 LOG.trace("Requested authentication methods: {}", loginContext.getRequestedAuthenticationMethods());
351
352
353 if (loginContext.getRequestedAuthenticationMethods().isEmpty()) {
354 LOG.trace("No preference given for authentication methods");
355 return supportedLoginHandlers;
356 }
357
358
359
360 Iterator<Entry<String, LoginHandler>> supportedLoginHandlerItr = supportedLoginHandlers.entrySet().iterator();
361 Entry<String, LoginHandler> supportedLoginHandler;
362 while (supportedLoginHandlerItr.hasNext()) {
363 supportedLoginHandler = supportedLoginHandlerItr.next();
364 if (!supportedLoginHandler.getKey().equals(AuthnContext.PREVIOUS_SESSION_AUTHN_CTX)
365 && !loginContext.getRequestedAuthenticationMethods().contains(supportedLoginHandler.getKey())) {
366 supportedLoginHandlerItr.remove();
367 continue;
368 }
369 }
370
371 if (supportedLoginHandlers.isEmpty()) {
372 LOG.error("No authentication method, requested by the service provider, is supported");
373 throw new AuthenticationException(
374 "No authentication method, requested by the service provider, is supported");
375 }
376
377 return supportedLoginHandlers;
378 }
379
380
381
382
383
384
385
386
387
388
389
390
391
392 protected void filterByForceAuthentication(Session idpSession, LoginContext loginContext,
393 Map<String, LoginHandler> loginHandlers) throws ForceAuthenticationException {
394 LOG.debug("Forced authentication is required, filtering possible login handlers accordingly");
395
396 ArrayList<AuthenticationMethodInformation> activeMethods = new ArrayList<AuthenticationMethodInformation>();
397 if (idpSession != null) {
398 activeMethods.addAll(idpSession.getAuthenticationMethods().values());
399 }
400
401 loginHandlers.remove(AuthnContext.PREVIOUS_SESSION_AUTHN_CTX);
402
403 LoginHandler loginHandler;
404 for (AuthenticationMethodInformation activeMethod : activeMethods) {
405 loginHandler = loginHandlers.get(activeMethod.getAuthenticationMethod());
406 if (loginHandler != null && !loginHandler.supportsForceAuthentication()) {
407 for (String handlerSupportedMethods : loginHandler.getSupportedAuthenticationMethods()) {
408 loginHandlers.remove(handlerSupportedMethods);
409 }
410 }
411 }
412
413 LOG.debug("Authentication handlers remaining after forced authentication requirement filtering: {}",
414 loginHandlers);
415
416 if (loginHandlers.isEmpty()) {
417 LOG.info("Force authentication requested but no login handlers available to support it");
418 throw new ForceAuthenticationException();
419 }
420 }
421
422
423
424
425
426
427
428
429
430
431
432 protected void filterByPassiveAuthentication(Session idpSession, LoginContext loginContext,
433 Map<String, LoginHandler> loginHandlers) throws PassiveAuthenticationException {
434 LOG.debug("Passive authentication is required, filtering poassible login handlers accordingly.");
435
436 if (idpSession == null) {
437 loginHandlers.remove(AuthnContext.PREVIOUS_SESSION_AUTHN_CTX);
438 }
439
440 LoginHandler loginHandler;
441 Iterator<Entry<String, LoginHandler>> authnMethodItr = loginHandlers.entrySet().iterator();
442 while (authnMethodItr.hasNext()) {
443 loginHandler = authnMethodItr.next().getValue();
444 if (!loginHandler.supportsPassive()) {
445 authnMethodItr.remove();
446 }
447 }
448
449 LOG.debug("Authentication handlers remaining after passive authentication requirement filtering: {}",
450 loginHandlers);
451
452 if (loginHandlers.isEmpty()) {
453 LOG.error("Passive authentication required but no login handlers available to support it");
454 throw new PassiveAuthenticationException();
455 }
456 }
457
458
459
460
461
462
463
464
465
466 protected void storeLoginContext(LoginContext loginContext, HttpServletRequest httpRequest,
467 HttpServletResponse httpResponse) {
468 String contextId = idGen.generateIdentifier();
469
470 storageService.put(loginContextPartitionName, contextId, new LoginContextEntry(loginContext,
471 loginContextEntryLifetime));
472
473 httpRequest.setAttribute(LOGIN_CONTEXT_KEY_NAME, contextId);
474
475 Cookie cookie = new Cookie(LOGIN_CONTEXT_KEY_NAME, contextId);
476 String contextPath = httpRequest.getContextPath();
477 if (DatatypeHelper.isEmpty(contextPath)) {
478 cookie.setPath("/");
479 } else {
480 cookie.setPath(contextPath);
481 }
482 cookie.setSecure(httpRequest.isSecure());
483 cookie.setMaxAge(-1);
484 httpResponse.addCookie(cookie);
485 }
486
487
488
489
490
491
492
493
494
495
496
497
498 protected void completeAuthentication(LoginContext loginContext, HttpServletRequest httpRequest,
499 HttpServletResponse httpResponse) {
500 LOG.debug("Completing user authentication process");
501
502 Session idpSession = (Session) httpRequest.getAttribute(Session.HTTP_SESSION_BINDING_ATTRIBUTE);
503
504 try {
505
506 validateSuccessfulAuthentication(loginContext, httpRequest);
507
508
509
510 String actualAuthnMethod = DatatypeHelper.safeTrimOrNullString((String) httpRequest
511 .getAttribute(LoginHandler.AUTHENTICATION_METHOD_KEY));
512 if (actualAuthnMethod == null) {
513 actualAuthnMethod = loginContext.getAttemptedAuthnMethod();
514 }
515
516
517
518 Subject subject = getLoginHandlerSubject(httpRequest);
519 if (loginContext.isForceAuthRequired()) {
520 validateForcedReauthentication(idpSession, actualAuthnMethod, subject);
521 }
522
523 loginContext.setPrincipalAuthenticated(true);
524 updateUserSession(loginContext, subject, actualAuthnMethod, httpRequest, httpResponse);
525 LOG.debug("User {} authenticated with method {}", loginContext.getPrincipalName(), actualAuthnMethod);
526 } catch (AuthenticationException e) {
527 LOG.error("Authentication failed with the error:", e);
528 loginContext.setPrincipalAuthenticated(false);
529 loginContext.setAuthenticationFailure(e);
530 }
531
532 returnToProfileHandler(loginContext, httpRequest, httpResponse);
533 }
534
535
536
537
538
539
540
541
542
543
544
545
546 protected void validateSuccessfulAuthentication(LoginContext loginContext, HttpServletRequest httpRequest)
547 throws AuthenticationException {
548 LOG.debug("Validating authentication was performed successfully");
549
550 String errorMessage = DatatypeHelper.safeTrimOrNullString((String) httpRequest
551 .getAttribute(LoginHandler.AUTHENTICATION_ERROR_KEY));
552 if (errorMessage != null) {
553 LOG.error("Error returned from login handler for authentication method {}:\n{}", loginContext
554 .getAttemptedAuthnMethod(), errorMessage);
555 throw new AuthenticationException(errorMessage);
556 }
557
558 Subject subject = (Subject) httpRequest.getAttribute(LoginHandler.SUBJECT_KEY);
559 Principal principal = (Principal) httpRequest.getAttribute(LoginHandler.PRINCIPAL_KEY);
560 String principalName = DatatypeHelper.safeTrimOrNullString((String) httpRequest
561 .getAttribute(LoginHandler.PRINCIPAL_NAME_KEY));
562
563 if (subject == null && principal == null && principalName == null) {
564 LOG.error("No user identified by login handler.");
565 throw new AuthenticationException("No user identified by login handler.");
566 }
567 }
568
569
570
571
572
573
574
575
576
577
578 protected Subject getLoginHandlerSubject(HttpServletRequest httpRequest) throws AuthenticationException {
579 Subject subject = (Subject) httpRequest.getAttribute(LoginHandler.SUBJECT_KEY);
580 Principal principal = (Principal) httpRequest.getAttribute(LoginHandler.PRINCIPAL_KEY);
581 String principalName = DatatypeHelper.safeTrimOrNullString((String) httpRequest
582 .getAttribute(LoginHandler.PRINCIPAL_NAME_KEY));
583
584 if (subject == null && (principal != null || principalName != null)) {
585 subject = new Subject();
586 if (principal == null) {
587 principal = new UsernamePrincipal(principalName);
588 }
589 subject.getPrincipals().add(principal);
590 }
591
592 return subject;
593 }
594
595
596
597
598
599
600
601
602
603
604
605
606 protected void validateForcedReauthentication(Session idpSession, String authnMethod, Subject subject)
607 throws AuthenticationException {
608 if (idpSession != null) {
609 AuthenticationMethodInformation authnMethodInfo = idpSession.getAuthenticationMethods().get(authnMethod);
610 if (authnMethodInfo != null) {
611 boolean princpalMatch = false;
612 for (Principal princpal : subject.getPrincipals()) {
613 if (authnMethodInfo.getAuthenticationPrincipal().equals(princpal)) {
614 princpalMatch = true;
615 break;
616 }
617 }
618
619 if (!princpalMatch) {
620 throw new ForceAuthenticationException(
621 "Authenticated principal does not match previously authenticated principal");
622 }
623 }
624 }
625 }
626
627
628
629
630
631
632
633
634
635
636
637 protected void updateUserSession(LoginContext loginContext, Subject authenticationSubject,
638 String authenticationMethod, HttpServletRequest httpRequest, HttpServletResponse httpResponse) {
639 Principal authenticationPrincipal = authenticationSubject.getPrincipals().iterator().next();
640 LOG.debug("Updating session information for principal {}", authenticationPrincipal.getName());
641
642 Session idpSession = (Session) httpRequest.getAttribute(Session.HTTP_SESSION_BINDING_ATTRIBUTE);
643 if (idpSession == null) {
644 LOG.debug("Creating shibboleth session for principal {}", authenticationPrincipal.getName());
645 idpSession = (Session) sessionManager.createSession();
646 loginContext.setSessionID(idpSession.getSessionID());
647 addSessionCookie(httpRequest, httpResponse, idpSession);
648 }
649
650
651
652 idpSession.setSubject(mergeSubjects(idpSession.getSubject(), authenticationSubject));
653
654 LOG.debug("Recording authentication and service information in Shibboleth session for principal: {}",
655 authenticationPrincipal.getName());
656 LoginHandler loginHandler = handlerManager.getLoginHandlers().get(authenticationMethod);
657 AuthenticationMethodInformation authnMethodInfo = new AuthenticationMethodInformationImpl(idpSession
658 .getSubject(), authenticationPrincipal, authenticationMethod, new DateTime(), loginHandler
659 .getAuthenticationDuration());
660
661 loginContext.setAuthenticationMethodInformation(authnMethodInfo);
662 idpSession.getAuthenticationMethods().put(authnMethodInfo.getAuthenticationMethod(), authnMethodInfo);
663 sessionManager.indexSession(idpSession, authnMethodInfo.getAuthenticationPrincipal().getName());
664
665 ServiceInformation serviceInfo = new ServiceInformationImpl(loginContext.getRelyingPartyId(), new DateTime(),
666 authnMethodInfo);
667 idpSession.getServicesInformation().put(serviceInfo.getEntityID(), serviceInfo);
668 }
669
670
671
672
673
674
675
676
677
678 protected Subject mergeSubjects(Subject subject1, Subject subject2) {
679 if (subject1 == null) {
680 return subject2;
681 }
682
683 if (subject2 == null) {
684 return subject1;
685 }
686
687 if (subject1 == null && subject2 == null) {
688 return new Subject();
689 }
690
691 Set<Principal> principals = new HashSet<Principal>();
692 principals.addAll(subject1.getPrincipals());
693 principals.addAll(subject2.getPrincipals());
694
695 Set<Object> publicCredentials = new HashSet<Object>();
696 publicCredentials.addAll(subject1.getPublicCredentials());
697 publicCredentials.addAll(subject2.getPublicCredentials());
698
699 Set<Object> privateCredentials = new HashSet<Object>();
700 privateCredentials.addAll(subject1.getPrivateCredentials());
701 privateCredentials.addAll(subject2.getPrivateCredentials());
702
703 return new Subject(false, principals, publicCredentials, privateCredentials);
704 }
705
706
707
708
709
710
711
712
713 protected void addSessionCookie(HttpServletRequest httpRequest, HttpServletResponse httpResponse,
714 Session userSession) {
715 httpRequest.setAttribute(Session.HTTP_SESSION_BINDING_ATTRIBUTE, userSession);
716
717 byte[] remoteAddress = httpRequest.getRemoteAddr().getBytes();
718 byte[] sessionId = userSession.getSessionID().getBytes();
719
720 String signature = null;
721 try {
722 MessageDigest digester = MessageDigest.getInstance("SHA");
723 digester.update(userSession.getSessionSecret());
724 digester.update(remoteAddress);
725 digester.update(sessionId);
726 signature = Base64.encodeBytes(digester.digest());
727 } catch (GeneralSecurityException e) {
728 LOG.error("Unable to compute signature over session cookie material", e);
729 }
730
731 LOG.debug("Adding IdP session cookie to HTTP response");
732 StringBuilder cookieValue = new StringBuilder();
733 cookieValue.append(Base64.encodeBytes(remoteAddress, Base64.DONT_BREAK_LINES)).append("|");
734 cookieValue.append(Base64.encodeBytes(sessionId, Base64.DONT_BREAK_LINES)).append("|");
735 cookieValue.append(signature);
736 Cookie sessionCookie = new Cookie(IDP_SESSION_COOKIE_NAME, HTTPTransportUtils.urlEncode(cookieValue.toString()));
737
738 String contextPath = httpRequest.getContextPath();
739 if (DatatypeHelper.isEmpty(contextPath)) {
740 sessionCookie.setPath("/");
741 } else {
742 sessionCookie.setPath(contextPath);
743 }
744
745 sessionCookie.setSecure(httpRequest.isSecure());
746 sessionCookie.setMaxAge(-1);
747
748 httpResponse.addCookie(sessionCookie);
749 }
750 }