public String authenticate(Credentials credentials, String token)
            throws GuacamoleException {

        String authToken;
        try {

            // Allow extensions to make updated to credentials prior to
            // actual authentication (NOTE: We do this here instead of in a
            // separate function to ensure that failure events accurately
            // represent the credentials that failed when a chain of credential
            // updates is involved)
            for (AuthenticationProvider authProvider : authProviders) {
                try {
                    credentials = authProvider.updateCredentials(credentials);
                }
                catch (GuacamoleException | RuntimeException | Error e) {
                    throw new GuacamoleAuthenticationProcessException("User "
                            + "authentication aborted during credential "
                            + "update/revision.", authProvider, e);
                }
            }

            // Fire pre-authentication event before ANY authn/authz occurs at all
            final Credentials updatedCredentials = credentials;
            listenerService.handleEvent((AuthenticationRequestReceivedEvent) () -> updatedCredentials);

            // Pull existing session if token provided
            GuacamoleSession existingSession;
            if (token != null)
                existingSession = tokenSessionMap.get(token);
            else
                existingSession = null;

            // Get up-to-date AuthenticatedUser and associated UserContexts
            AuthenticatedUser authenticatedUser = getAuthenticatedUser(existingSession, updatedCredentials);
            List<DecoratedUserContext> userContexts = getUserContexts(existingSession, authenticatedUser, updatedCredentials);

            // Update existing session, if it exists
            if (existingSession != null) {
                authToken = token;
                existingSession.setAuthenticatedUser(authenticatedUser);
                existingSession.setUserContexts(userContexts);
            }

            // If no existing session, generate a new token/session pair
            else {
                authToken = authTokenGenerator.getToken();
                tokenSessionMap.put(authToken, new GuacamoleSession(listenerService, authenticatedUser, userContexts));
            }

            // Report authentication success
            try {
                listenerService.handleEvent(new AuthenticationSuccessEvent(authenticatedUser,
                        existingSession != null));
            }
            catch (GuacamoleException e) {
                throw new GuacamoleAuthenticationProcessException("User "
                        + "authentication aborted by event listener.", null, e);
            }

        }

        // Log and rethrow any authentication errors
        catch (GuacamoleAuthenticationProcessException e) {

            // NOTE: The credentials referenced here are intentionally NOT the
            // final updatedCredentials reference (though they may often be
            // equivalent) to ensure that failure events accurately represent
            // the credentials that failed if that failure occurs in the middle
            // of a chain of credential updates via updateCredentials()
            listenerService.handleEvent(new AuthenticationFailureEvent(credentials,
                    e.getAuthenticationProvider(), e.getCause()));

            // Rethrow exception
            e.rethrowCause();

            // This line SHOULD be unreachable unless a bug causes
            // rethrowCause() to not actually rethrow the underlying failure
            Throwable cause = e.getCause();
            if (cause != null) {
                logger.warn("An underlying internal error was not correctly rethrown by rethrowCause(): {}", cause.getMessage());
                logger.debug("Internal error not rethrown by rethrowCause().", cause);
            }
            else
                logger.warn("An underlying internal error was not correctly rethrown by rethrowCause().");

            throw e.getCauseAsGuacamoleException();

        }

        return authToken;

    }
Authenticates a user using the given credentials and optional
authentication token, returning the authentication token associated with
the user's Guacamole session, which may be newly generated. If an
existing token is provided, the authentication procedure will attempt to
update or reuse the provided token, but it is possible that a new token
will be returned. Note that this function CANNOT return null.

@param credentials
    The credentials to use when authenticating the user.

@param token
    The authentication token to use if attempting to re-authenticate an
    existing session, or null to request a new token.

@return
    The authentication token associated with the newly created or
    existing session.

@throws GuacamoleException
    If the authentication or re-authentication attempt fails.
guacamole/src/main/java/org/apache/guacamole/rest/auth/AuthenticationService.java