001/* 002 * Copyright 2022 Anyware Services 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.ametys.plugins.extrausermgt.authentication.oidc; 017 018import java.io.IOException; 019import java.net.URI; 020import java.net.URISyntaxException; 021import java.net.URL; 022import java.util.Date; 023import java.util.List; 024import java.util.Map; 025import java.util.stream.Collectors; 026import java.util.stream.Stream; 027 028import org.apache.avalon.framework.context.Context; 029import org.apache.avalon.framework.context.ContextException; 030import org.apache.avalon.framework.context.Contextualizable; 031import org.apache.avalon.framework.service.ServiceException; 032import org.apache.avalon.framework.service.ServiceManager; 033import org.apache.avalon.framework.service.Serviceable; 034import org.apache.cocoon.ProcessingException; 035import org.apache.cocoon.components.ContextHelper; 036import org.apache.cocoon.environment.Redirector; 037import org.apache.cocoon.environment.Request; 038import org.apache.cocoon.environment.Session; 039import org.apache.commons.lang3.StringUtils; 040 041import org.ametys.core.authentication.AbstractCredentialProvider; 042import org.ametys.core.authentication.AuthenticateAction; 043import org.ametys.core.authentication.BlockingCredentialProvider; 044import org.ametys.core.authentication.NonBlockingCredentialProvider; 045import org.ametys.core.user.UserIdentity; 046import org.ametys.core.user.directory.NotUniqueUserException; 047import org.ametys.core.user.directory.StoredUser; 048import org.ametys.core.user.directory.UserDirectory; 049import org.ametys.core.user.population.UserPopulation; 050import org.ametys.plugins.extrausermgt.authentication.oidc.endofauthenticationprocess.EndOfAuthenticationProcess; 051import org.ametys.runtime.authentication.AccessDeniedException; 052import org.ametys.workspaces.extrausermgt.authentication.oidc.OIDCCallbackAction; 053 054import com.nimbusds.jose.JOSEException; 055import com.nimbusds.jose.JWSAlgorithm; 056import com.nimbusds.jose.proc.BadJOSEException; 057import com.nimbusds.jwt.JWT; 058import com.nimbusds.oauth2.sdk.AuthorizationCode; 059import com.nimbusds.oauth2.sdk.AuthorizationCodeGrant; 060import com.nimbusds.oauth2.sdk.AuthorizationGrant; 061import com.nimbusds.oauth2.sdk.ParseException; 062import com.nimbusds.oauth2.sdk.RefreshTokenGrant; 063import com.nimbusds.oauth2.sdk.ResponseType; 064import com.nimbusds.oauth2.sdk.Scope; 065import com.nimbusds.oauth2.sdk.SerializeException; 066import com.nimbusds.oauth2.sdk.TokenErrorResponse; 067import com.nimbusds.oauth2.sdk.TokenRequest; 068import com.nimbusds.oauth2.sdk.TokenResponse; 069import com.nimbusds.oauth2.sdk.auth.ClientAuthentication; 070import com.nimbusds.oauth2.sdk.auth.ClientSecretBasic; 071import com.nimbusds.oauth2.sdk.auth.Secret; 072import com.nimbusds.oauth2.sdk.http.HTTPResponse; 073import com.nimbusds.oauth2.sdk.id.ClientID; 074import com.nimbusds.oauth2.sdk.id.Issuer; 075import com.nimbusds.oauth2.sdk.id.State; 076import com.nimbusds.oauth2.sdk.token.AccessToken; 077import com.nimbusds.oauth2.sdk.token.RefreshToken; 078import com.nimbusds.openid.connect.sdk.AuthenticationRequest; 079import com.nimbusds.openid.connect.sdk.OIDCTokenResponse; 080import com.nimbusds.openid.connect.sdk.OIDCTokenResponseParser; 081import com.nimbusds.openid.connect.sdk.Prompt; 082import com.nimbusds.openid.connect.sdk.UserInfoRequest; 083import com.nimbusds.openid.connect.sdk.UserInfoResponse; 084import com.nimbusds.openid.connect.sdk.claims.IDTokenClaimsSet; 085import com.nimbusds.openid.connect.sdk.claims.UserInfo; 086import com.nimbusds.openid.connect.sdk.token.OIDCTokens; 087import com.nimbusds.openid.connect.sdk.validators.IDTokenValidator; 088 089/** 090 * Sign in (through Google, facebook...) using the OpenId Connect (OIDC) protocol. 091 */ 092public abstract class AbstractOIDCCredentialProvider extends AbstractCredentialProvider implements BlockingCredentialProvider, NonBlockingCredentialProvider, Contextualizable, Serviceable 093{ 094 /** Session attribute for OIDC */ 095 public static final String REDIRECT_URI_SESSION_ATTRIBUTE = "oidc_actualRedirectUri"; 096 /** Session attribute for OIDC*/ 097 public static final String TOKEN_SESSION_ATTRIBUTE = "oidc_token"; 098 /** Session date attribute for OIDC*/ 099 public static final String EXPDATE_SESSION_ATTRIBUTE = "oidc_expirationDate"; 100 /** Session attribute for OIDC*/ 101 public static final String REFRESH_TOKEN_SESSION_ATTRIBUTE = "oidc_refreshToken"; 102 /** Session attribute for OIDC*/ 103 public static final String STATE_SESSION_ATTRIBUTE = "oidc_state"; 104 105 private static final String __ATTRIBUTE_SILENT = "oidc_silent"; 106 107 /** Scope for the authentication request */ 108 protected Scope _scope; 109 110 /** URI for the authentication request */ 111 protected URI _authUri; 112 113 /** URI for the token request */ 114 protected URI _tokenEndpointUri; 115 116 /** URI for the user info request */ 117 protected URI _userInfoEndpoint; 118 119 /** jwk URL for the validation of the token */ 120 protected URL _jwkSetURL; 121 122 /** Issuer for the validation of the token */ 123 protected Issuer _iss; 124 125 /** Ametys context */ 126 protected Context _context; 127 /** Client ID */ 128 protected ClientID _clientID; 129 /** Client secret */ 130 protected Secret _clientSecret; 131 132 /** If we should try to authenticate silently */ 133 protected boolean _silent; 134 135 private EndOfAuthenticationProcess _endOfAuthenticationProcess; 136 137 public void contextualize(Context context) throws ContextException 138 { 139 _context = context; 140 } 141 142 public void service(ServiceManager manager) throws ServiceException 143 { 144 _endOfAuthenticationProcess = (EndOfAuthenticationProcess) manager.lookup(EndOfAuthenticationProcess.ROLE); 145 } 146 147 @Override 148 public void init(String id, String cpModelId, Map<String, Object> paramValues, String label) throws Exception 149 { 150 super.init(id, cpModelId, paramValues, label); 151 _clientID = new ClientID(paramValues.get("authentication.oidc.idclient").toString()); 152 _clientSecret = new Secret(paramValues.get("authentication.oidc.clientsecret").toString()); 153 _silent = (boolean) paramValues.get("authentication.oidc.silent"); 154 155 initUrisScope(); 156 } 157 158 /** 159 * get the client authentication info for the token end point 160 * @return the client authentication 161 */ 162 protected ClientAuthentication getClientAuthentication() 163 { 164 return new ClientSecretBasic(_clientID, _clientSecret); 165 } 166 167 public boolean blockingGrantAnonymousRequest() 168 { 169 return false; 170 } 171 172 @Override 173 public boolean nonBlockingGrantAnonymousRequest() 174 { 175 return false; 176 } 177 178 public boolean blockingIsStillConnected(UserIdentity userIdentity, Redirector redirector) throws Exception 179 { 180 Request request = ContextHelper.getRequest(_context); 181 Session session = request.getSession(true); 182 183 Date expDat = (Date) session.getAttribute(EXPDATE_SESSION_ATTRIBUTE); 184 if (new Date().before(expDat)) 185 { 186 return true; 187 } 188 189 RefreshToken refreshToken = (RefreshToken) session.getAttribute(REFRESH_TOKEN_SESSION_ATTRIBUTE); 190 AuthorizationGrant refreshTokenGrant = new RefreshTokenGrant(refreshToken); 191 192 // The credentials to authenticate the client at the token endpoint 193 ClientAuthentication clientAuth = getClientAuthentication(); 194 195 // Make the token request 196 OIDCTokens tokens = requestToken(clientAuth, refreshTokenGrant); 197 198 // idToken to validate the token 199 JWT idToken = tokens.getIDToken(); 200 // accessToken to be able to access the user info 201 AccessToken accessToken = tokens.getAccessToken(); 202 IDTokenClaimsSet claims = validateIdToken(idToken); 203 session.setAttribute(EXPDATE_SESSION_ATTRIBUTE, claims.getExpirationTime()); 204 session.setAttribute(TOKEN_SESSION_ATTRIBUTE, accessToken); 205 206 return true; 207 } 208 209 @Override 210 public boolean nonBlockingIsStillConnected(UserIdentity userIdentity, Redirector redirector) throws Exception 211 { 212 return blockingIsStillConnected(userIdentity, redirector); 213 } 214 215 private UserIdentity _login(boolean silent, Redirector redirector) throws Exception 216 { 217 Request request = ContextHelper.getRequest(_context); 218 Session session = request.getSession(true); 219 220 URI redirectUri = _buildRedirectUri(); 221 222 getLogger().debug("OIDCCredentialProvider callback URI: {}", redirectUri); 223 224 boolean wasSilent = false; 225 if (silent) 226 { 227 wasSilent = "true".equals(session.getAttribute(__ATTRIBUTE_SILENT)); 228 } 229 230 String code = request.getParameter("code"); 231 // if the code is null, then this is the first time the user sign-in 232 // if no state are stored in session, then the code belongs to a previous session. Restart 233 if (code == null || session.getAttribute(STATE_SESSION_ATTRIBUTE) == null) 234 { 235 signIn(redirector, redirectUri, silent, wasSilent, session); 236 return null; 237 } 238 239 // we got an authorization code 240 // but first, check the state to prevent CSRF attacks 241 checkState(); 242 AuthorizationCode authCode = new AuthorizationCode(code); 243 // get the tokens (id token and access token) 244 OIDCTokens tokens = requestToken(authCode, redirectUri); 245 246 // idToken to validate the token 247 JWT idToken = tokens.getIDToken(); 248 // accessToken to be able to access the user info 249 AccessToken accessToken = tokens.getAccessToken(); 250 RefreshToken refreshToken = tokens.getRefreshToken(); 251 252 session.setAttribute(REFRESH_TOKEN_SESSION_ATTRIBUTE, refreshToken); 253 254 // validate id token 255 IDTokenClaimsSet claims = validateIdToken(idToken); 256 257 // set expirationTime 258 claims.getExpirationTime(); 259 session.setAttribute(EXPDATE_SESSION_ATTRIBUTE, claims.getExpirationTime()); 260 261 UserInfo userInfo = getUserInfo(accessToken); 262 263 // then the user is finally logged in 264 return getUserIdentity(userInfo, request, redirector); 265 } 266 267 public UserIdentity blockingGetUserIdentity(Redirector redirector) throws Exception 268 { 269 return _login(false, redirector); 270 } 271 272 public UserIdentity nonBlockingGetUserIdentity(Redirector redirector) throws Exception 273 { 274 if (!_silent) 275 { 276 return null; 277 } 278 279 return _login(true, redirector); 280 } 281 282 public void blockingUserNotAllowed(Redirector redirector) throws Exception 283 { 284 // Nothing to do. 285 } 286 287 @Override 288 public void nonBlockingUserNotAllowed(Redirector redirector) throws Exception 289 { 290 // Nothing to do. 291 } 292 293 public void blockingUserAllowed(UserIdentity userIdentity, Redirector redirector) throws Exception 294 { 295 Request request = ContextHelper.getRequest(_context); 296 Session session = request.getSession(true); 297 String redirectUri = (String) session.getAttribute(AbstractOIDCCredentialProvider.REDIRECT_URI_SESSION_ATTRIBUTE); 298 redirector.redirect(true, redirectUri); 299 } 300 301 @Override 302 public void nonBlockingUserAllowed(UserIdentity userIdentity, Redirector redirector) throws Exception 303 { 304 blockingUserAllowed(userIdentity, redirector); 305 } 306 307 public boolean requiresNewWindow() 308 { 309 return true; 310 } 311 312 private UserPopulation _getPopulation(Request request) 313 { 314 @SuppressWarnings("unchecked") 315 List<UserPopulation> userPopulations = (List<UserPopulation>) request.getAttribute(AuthenticateAction.REQUEST_ATTRIBUTE_AVAILABLE_USER_POPULATIONS_LIST); 316 317 // If the list has only one element 318 if (userPopulations.size() == 1) 319 { 320 return userPopulations.get(0); 321 } 322 323 // In this list a population was maybe chosen? 324 final String chosenUserPopulationId = (String) request.getAttribute(AuthenticateAction.REQUEST_ATTRIBUTE_USER_POPULATION_ID); 325 if (StringUtils.isNotBlank(chosenUserPopulationId)) 326 { 327 return userPopulations.stream() 328 .filter(userPopulation -> StringUtils.equals(userPopulation.getId(), chosenUserPopulationId)) 329 .findFirst() 330 .get(); 331 } 332 333 // Cannot work here... 334 throw new IllegalStateException("The " + this.getClass().getName() + " does not work when population is not known"); 335 } 336 337 /** 338 * Initialize the URIs 339 * @throws AccessDeniedException If an error occurs 340 */ 341 protected abstract void initUrisScope() throws AccessDeniedException; 342 343 /** 344 * Builds the redirect URI and the actual redirect URI 345 * @return The redirect <code>URI</code> and saves the actual redirect <code>URI</code> 346 * @throws URISyntaxException If an error occurs 347 */ 348 private URI _buildRedirectUri() throws URISyntaxException 349 { 350 Request request = ContextHelper.getRequest(_context); 351 352 // creation of the actual redirect URI (The one we actually want to go back to) 353 StringBuilder actualRedirectUri = new StringBuilder(request.getRequestURI()); 354 String queryString = request.getQueryString(); 355 if (queryString != null) 356 { 357 // remove any existing code or state from the request param if any exist, they are outdated 358 queryString = Stream.of(StringUtils.split(queryString, "&")) 359 .filter(str -> !StringUtils.startsWithAny(str, "code=", "state=")) 360 .collect(Collectors.joining("&")); 361 if (StringUtils.isNotEmpty(queryString)) 362 { 363 actualRedirectUri.append("?"); 364 actualRedirectUri.append(queryString); 365 } 366 } 367 368 // saving the actualRedirectUri to enable its use in "OIDCCallbackAction" 369 Session session = request.getSession(true); 370 session.setAttribute(REDIRECT_URI_SESSION_ATTRIBUTE, actualRedirectUri.toString()); 371 372 // creation of redirect URI (the issuer (google, facebook, etc.) is going to redirect to) 373 return buildAbsoluteURI(request, OIDCCallbackAction.CALLBACK_URL); 374 } 375 376 /** 377 * Computes the callback uri 378 * @param request the current request 379 * @param path the callback path 380 * @return the callback uri 381 */ 382 protected URI buildAbsoluteURI(Request request, String path) 383 { 384 StringBuilder uriBuilder = new StringBuilder() 385 .append(request.getScheme()) 386 .append("://") 387 .append(request.getServerName()); 388 389 if (request.isSecure()) 390 { 391 if (request.getServerPort() != 443) 392 { 393 uriBuilder.append(":"); 394 uriBuilder.append(request.getServerPort()); 395 } 396 } 397 else 398 { 399 if (request.getServerPort() != 80) 400 { 401 uriBuilder.append(":"); 402 uriBuilder.append(request.getServerPort()); 403 } 404 } 405 406 uriBuilder.append(request.getContextPath()); 407 uriBuilder.append(path); 408 409 return URI.create(uriBuilder.toString()); 410 } 411 412 /** 413 * Sign the user in by sending an authentication request to the issuer 414 * @param redirector The redirector 415 * @param redirectUri The redirect URI 416 * @param silent if the user should be silently signed in 417 * @param wasSilent indicates that we already passed through this, to prevent infinite loops 418 * @param session The current session 419 * @throws ProcessingException If an error occurs 420 * @throws IOException If an error occurs 421 */ 422 protected void signIn(Redirector redirector, URI redirectUri, boolean silent, boolean wasSilent, Session session) throws ProcessingException, IOException 423 { 424 // sign-in request: redirect the client through the actual authentication process 425 426 if (wasSilent) 427 { 428 // already passed through this, there should have been some error somewhere 429 return; 430 } 431 432 if (silent) 433 { 434 session.setAttribute(__ATTRIBUTE_SILENT, "true"); 435 } 436 437 // creation of the state used to secure the process 438 State state = new State(); 439 session.setAttribute(STATE_SESSION_ATTRIBUTE, state); 440 441 // compose the request 442 AuthenticationRequest authenticationRequest = new AuthenticationRequest.Builder(new ResponseType(ResponseType.Value.CODE), _scope, _clientID, redirectUri) 443 .endpointURI(_authUri) 444 .state(state) 445 .prompt(silent ? Prompt.Type.NONE : null) 446 .build(); 447 448 String authReqURI = authenticationRequest.toURI().toString() + "&access_type=offline"; 449 450 redirector.redirect(false, authReqURI); 451 } 452 453 /** 454 * Checks the State parameter of the request to prevent CSRF attacks 455 * @throws AccessDeniedException If an error occurs 456 */ 457 protected void checkState() throws AccessDeniedException 458 { 459 Request request = ContextHelper.getRequest(_context); 460 Session session = request.getSession(true); 461 String storedState = session.getAttribute(STATE_SESSION_ATTRIBUTE).toString(); 462 String stateRequest = request.getParameter("state"); 463 464 if (!storedState.equals(stateRequest)) 465 { 466 getLogger().error("OIDC state mismatch. Method checkState of AbstractOIDCCredentialProvider"); 467 throw new AccessDeniedException("OIDC state mismatch"); 468 } 469 470 session.setAttribute(STATE_SESSION_ATTRIBUTE, null); 471 } 472 473 /** 474 * Request the tokens (ID token and Access token) 475 * @param authCode The authorization code from the authentication request 476 * @param redirectUri The redirect URI 477 * @return The <code>OIDCTokens</code> that contains the access token and the id token 478 * @throws AccessDeniedException If an error occurs 479 */ 480 protected OIDCTokens requestToken(AuthorizationCode authCode, URI redirectUri) throws AccessDeniedException 481 { 482 // token request: checking if the user is known 483 TokenRequest tokenReq = new TokenRequest(_tokenEndpointUri, getClientAuthentication(), new AuthorizationCodeGrant(authCode, redirectUri), null); 484 // sending request 485 HTTPResponse tokenHTTPResp = null; 486 try 487 { 488 tokenHTTPResp = tokenReq.toHTTPRequest().send(); 489 } 490 catch (SerializeException | IOException e) 491 { 492 getLogger().error("OIDC token request failed ", e); 493 throw new AccessDeniedException("OIDC token request failed"); 494 } 495 496 // cast the HTTPResponse to TokenResponse 497 TokenResponse tokenResponse = null; 498 try 499 { 500 tokenResponse = OIDCTokenResponseParser.parse(tokenHTTPResp); 501 } 502 catch (ParseException e) 503 { 504 getLogger().error("OIDC token request result invalid ", e); 505 throw new AccessDeniedException("OIDC token request result invalid"); 506 } 507 508 if (tokenResponse instanceof TokenErrorResponse) 509 { 510 getLogger().error("OIDC token request invalid token response instance of TokenErrorResponse in method requestToken from AbstractOIDCCredentialProvider"); 511 throw new AccessDeniedException("OIDC token request result invalid"); 512 } 513 514 // get the tokens 515 OIDCTokenResponse accessTokenResponse = (OIDCTokenResponse) tokenResponse; 516 517 return accessTokenResponse.getOIDCTokens(); 518 } 519 520 /** 521 * Request the tokens using a refresh token 522 * @param clientAuth The client authentication 523 * @param refreshTokenGrant The refreshtokenGrant 524 * @return The <code>OIDCTokens</code> that contains the access token and the id token 525 * @throws AccessDeniedException If an error occurs 526 * @throws URISyntaxException If an error occurs 527 */ 528 protected OIDCTokens requestToken(ClientAuthentication clientAuth, AuthorizationGrant refreshTokenGrant) throws AccessDeniedException, URISyntaxException 529 { 530 // token request: checking if the user is known 531 TokenRequest tokenReq = new TokenRequest(_tokenEndpointUri, clientAuth, refreshTokenGrant, null); 532 // sending request 533 534 HTTPResponse tokenHTTPResp = null; 535 try 536 { 537 tokenHTTPResp = tokenReq.toHTTPRequest().send(); 538 } 539 catch (SerializeException | IOException e) 540 { 541 getLogger().error("OIDC token request failed ", e); 542 throw new AccessDeniedException("OIDC token request failed"); 543 } 544 545 // cast the HTTPResponse to TokenResponse 546 TokenResponse tokenResponse = null; 547 try 548 { 549 tokenResponse = OIDCTokenResponseParser.parse(tokenHTTPResp); 550 } 551 catch (ParseException e) 552 { 553 getLogger().error("OIDC token request result invalid ", e); 554 throw new AccessDeniedException("OIDC token request result invalid"); 555 } 556 557 if (tokenResponse instanceof TokenErrorResponse) 558 { 559 getLogger().error("OIDC token request result invalid: tokenResponse instance of TokenErrorResponse in method requestToken from AbstractOIDCCredentialProvider"); 560 throw new AccessDeniedException("OIDC token request result invalid"); 561 } 562 563 // get the tokens 564 OIDCTokenResponse accessTokenResponse = (OIDCTokenResponse) tokenResponse; 565 566 return accessTokenResponse.getOIDCTokens(); 567 } 568 569 /** 570 * Validate the id token from the token request 571 * @param idToken The id token from the token request 572 * @return The <code>IDTokenClaimsSet</code> that contains information on the connection such as the expiration time 573 * @throws AccessDeniedException If an error occurs 574 */ 575 protected IDTokenClaimsSet validateIdToken(JWT idToken) throws AccessDeniedException 576 { 577 JWSAlgorithm jwsAlg = JWSAlgorithm.RS256; 578 // create validator for signed ID tokens 579 IDTokenValidator validator = new IDTokenValidator(_iss, _clientID, jwsAlg, _jwkSetURL); 580 IDTokenClaimsSet claims; 581 582 try 583 { 584 claims = validator.validate(idToken, null); 585 } 586 catch (BadJOSEException e) 587 { 588 getLogger().error("OIDC invalid : issuer, clientId, jwsAlg or jwkSetURL", e); 589 throw new AccessDeniedException("OIDC invalid signature issuer, clientId, jwsAlg or jwkSetURL"); 590 } 591 catch (JOSEException e) 592 { 593 getLogger().error("OIDC error while validating token", e); 594 throw new AccessDeniedException("OIDC error while validating token"); 595 } 596 597 return claims; 598 } 599 600 /** 601 * Request the userInfo using the user info end point and an access token 602 * @param accessToken the access token to retrieve the user info 603 * @return a representation of the user info from the scope requested with the token 604 * @throws IOException if an error occurred while contacting the end point 605 * @throws ParseException if an error occurred while parsing the end point answer 606 */ 607 protected UserInfo getUserInfo(AccessToken accessToken) throws IOException, ParseException 608 { 609 HTTPResponse httpResponse = new UserInfoRequest(_userInfoEndpoint, accessToken).toHTTPRequest().send(); 610 UserInfoResponse userInfoResponse = UserInfoResponse.parse(httpResponse); 611 612 if (userInfoResponse.indicatesSuccess()) 613 { 614 return userInfoResponse.toSuccessResponse().getUserInfo(); 615 } 616 else 617 { 618 String error = userInfoResponse.toErrorResponse().getErrorObject().toJSONObject().toJSONString(); 619 getLogger().error("Failed to retrieve the user info. The server indicate the following error :\n" + error); 620 throw new AccessDeniedException("Failed to retrieve the user info. The server indicate the following error :\n" + error); 621 } 622 } 623 624 /** 625 * Compute a user identity based on the user info 626 * @param userInfo the user info 627 * @param request the original request 628 * @param redirector the redirector to use if need be 629 * @return the identified user info or null if no matching user were found 630 * @throws NotUniqueUserException if multiple user matched 631 */ 632 protected UserIdentity getUserIdentity(UserInfo userInfo, Request request, Redirector redirector) throws NotUniqueUserException 633 { 634 // get the user email 635 String login = userInfo.getEmailAddress(); 636 if (login == null) 637 { 638 getLogger().error("Email not found, connection canceled "); 639 throw new AccessDeniedException("Email not found, connection canceled"); 640 } 641 642 // create a UserIdentity from the email 643 UserPopulation userPopulation = _getPopulation(request); 644 UserIdentity user = _getUserIdentity(login, userPopulation); 645 646 // If we found a UserIdentity, we return it 647 if (user != null) 648 { 649 return user; 650 } 651 652 // If not, we are going to pre-sign-up the user with its email, firstname and lastname 653 String firstName = userInfo.getGivenName(); 654 String lastName = userInfo.getFamilyName(); 655 if (firstName == null || lastName == null) 656 { 657 getLogger().info("The fields could not be pre-filled"); 658 } 659 660 // We call the temporarySignup method from the endOfSignupProcess, which will do nothing if it is in the CMS and temporary sign the user up if it is the site 661 _endOfAuthenticationProcess.unexistingUser(login, firstName, lastName, userPopulation, redirector, request); 662 663 return null; 664 } 665 666 private UserIdentity _getUserIdentity(String login, UserPopulation userPopulation) throws NotUniqueUserException 667 { 668 StoredUser storedUser = null; 669 670 for (UserDirectory userDirectory : userPopulation.getUserDirectories()) 671 { 672 storedUser = userDirectory.getStoredUser(login); 673 674 if (storedUser == null) 675 { 676 // Try to get user by email 677 storedUser = userDirectory.getStoredUserByEmail(login); 678 } 679 680 if (storedUser != null) 681 { 682 return userDirectory.getUserIdentity(storedUser); 683 } 684 } 685 686 return null; 687 } 688}