docs: public void authenticate(
              HttpServletRequest request,
              Map<String, String> principals,
              Map<String, String> credentials,
              IPerson person)
              throws PortalSecurityException {
  
          // Retrieve the security context for the user
          final ISecurityContext securityContext = person.getSecurityContext();
  
          // Set the principals and credentials for the security context chain
          this.configureSecurityContextChain(
                  principals, credentials, securityContext, BASE_CONTEXT_NAME);
  
          // NOTE: PortalPreAuthenticatedProcessingFilter looks in the security.properties file to
          // determine what tokens to look for that represent the principals and
          // credentials for each context. It then retrieves the values from the request
          // and stores the values in the principals and credentials HashMaps that are
          // passed to the Authentication service.
  
          // Attempt to authenticate the user
          final long start = System.currentTimeMillis();
          securityContext.authenticate();
          final long elapsed = System.currentTimeMillis() - start;
          // Check to see if the user was authenticated
          if (securityContext.isAuthenticated()) {
              lastAuthentication = authenticationTimes.add(elapsed); // metric
              // Add the authenticated username to the person object
              // the login name may have been provided or reset by the security provider
              // so this needs to be done after authentication.
              final String userName = securityContext.getPrincipal().getUID();
              person.setAttribute(IPerson.USERNAME, userName);
              if (log.isDebugEnabled()) {
                  log.debug(
                          "FINISHED SecurityContext authentication for user '"
                                  + userName
                                  + "' in "
                                  + elapsed
                                  + "ms #milestone");
              }
  
              threadNamingRequestFilter.updateCurrentUsername(userName);
  
              /*
               * Clear cached group info for this user.
               *
               * There seem to be 2 systems in place for this information:
               *   - The old system based on EntityCachingService
               *   - The new system based on ehcache
               *
               * For uPortal 5, we should work to remove the old system.
               */
              GroupService.finishedSession(person); // Old system
              for (IAuthenticationListener authListener : authenticationListeners) { // New system
                  authListener.userAuthenticated(person);
              }
  
              // Clear all existing cached data about the person
              this.usernameTaggedCacheEntryPurger.purgeTaggedCacheEntries(userName);
  
              // Retrieve the additional descriptor from the security context
              final IAdditionalDescriptor addInfo =
                      person.getSecurityContext().getAdditionalDescriptor();
              // Process the additional descriptor if one was created
              if (addInfo != null) {
                  // Replace the passed in IPerson with the additional descriptor if the
                  // additional descriptor is an IPerson object created by the security context
                  // NOTE: This is not the preferred method, creation of IPerson objects should be
                  //       handled by the PersonManager.
                  if (addInfo instanceof IPerson) {
                      final IPerson newPerson = (IPerson) addInfo;
                      person.setFullName(newPerson.getFullName());
  
                      for (final String attributeName : newPerson.getAttributeMap().keySet()) {
                          person.setAttribute(attributeName, newPerson.getAttribute(attributeName));
                      }
                  }
                  // If the additional descriptor is a map then we can
                  // simply copy all of these additional attributes into the IPerson
                  else if (addInfo instanceof Map) {
                      // Cast the additional descriptor as a Map
                      final Map<?, ?> additionalAttributes = (Map<?, ?>) addInfo;
                      // Copy each additional attribute into the person object
                      for (final Iterator<?> keys = additionalAttributes.keySet().iterator();
                              keys.hasNext(); ) {
                          // Get a key
                          final String key = (String) keys.next();
                          // Set the attribute
                          person.setAttribute(key, additionalAttributes.get(key));
                      }
                  } else if (addInfo
                          instanceof ChainingSecurityContext.ChainingAdditionalDescriptor) {
                      // do nothing
                  } else {
                      if (log.isWarnEnabled()) {
                          log.warn(
                                  "Authentication Service received unknown additional descriptor ["
                                          + addInfo
                                          + "]");
                      }
                  }
              }
  
              // Populate the person object using the PersonDirectory if applicable
              if (PropertiesManager.getPropertyAsBoolean(
                      "org.apereo.portal.services.Authentication.usePersonDirectory")) {
                  // Retrieve all of the attributes associated with the person logging in
                  final String username = person.getUserName();
  
                  final long timestamp = System.currentTimeMillis();
                  if (log.isDebugEnabled()) {
                      log.debug(
                              "STARTING user attribute gathering for user '"
                                      + userName
                                      + "' #milestone");
                  }
  
                  final IPersonAttributes personAttributes =
                          this.personAttributeDao.getPerson(username);
  
                  if (log.isDebugEnabled()) {
                      log.debug(
                              "FINISHED user attribute gathering for user '"
                                      + userName
                                      + "' in "
                                      + Long.toString(System.currentTimeMillis() - timestamp)
                                      + "ms #milestone");
                  }
  
                  if (personAttributes != null) {
                      // attribs may be null.  IPersonAttributeDao returns null when it does not
                      // recognize a user at all, as
                      // distinguished from returning an empty Map of attributes when it recognizes a
                      // user has having no
                      // attributes.
  
                      person.setAttributes(personAttributes.getAttributes());
                  }
              }
              // Make sure the the user's fullname is set
              if (person.getFullName() == null) {
                  // Use portal display name if one exists
                  if (person.getAttribute("portalDisplayName") != null) {
                      person.setFullName((String) person.getAttribute("portalDisplayName"));
                  }
                  // If not try the eduPerson displayName
                  else if (person.getAttribute("displayName") != null) {
                      person.setFullName((String) person.getAttribute("displayName"));
                  }
                  // If still no FullName use an unrecognized string
                  if (person.getFullName() == null) {
                      person.setFullName(
                              "Unrecognized person: " + person.getAttribute(IPerson.USERNAME));
                  }
              }
              // Find the uPortal userid for this user or flunk authentication if not found.
              final boolean autocreate =
                      PropertiesManager.getPropertyAsBoolean(
                              "org.apereo.portal.services.Authentication.autoCreateUsers");
              try {
                  // Attempt to retrieve the UID
                  final int newUID = this.userIdentityStore.getPortalUID(person, autocreate);
                  person.setID(newUID);
              } catch (final AuthorizationException ae) {
                  log.error("Exception retrieving ID", ae);
                  throw new PortalSecurityException(
                          "Authentication Service: Exception retrieving UID");
              }
          }
  
          // Publish a login event for the person
          this.portalEventFactory.publishLoginEvent(request, this, person);
      }