public static Status handle(LocalSession session, Element doc)
    {
        try
        {
            if ( !doc.getNamespaceURI().equals( SASL_NAMESPACE ) )
            {
                throw new IllegalStateException( "Unexpected data received while negotiating SASL authentication. Name of the offending root element: " + doc.getName() + " Namespace: " + doc.getNamespaceURI() );
            }

            switch ( ElementType.valueOfCaseInsensitive( doc.getName() ) )
            {
                case ABORT:
                    throw new SaslFailureException( Failure.ABORTED );

                case AUTH:
                    if ( doc.attributeValue( "mechanism" ) == null )
                    {
                        throw new SaslFailureException( Failure.INVALID_MECHANISM, "Peer did not specify a mechanism." );
                    }

                    final String mechanismName = doc.attributeValue( "mechanism" ).toUpperCase();

                    // See if the mechanism is supported by configuration as well as by implementation.
                    if ( !mechanisms.contains( mechanismName ) )
                    {
                        throw new SaslFailureException( Failure.INVALID_MECHANISM, "The configuration of Openfire does not contain or allow the mechanism." );
                    }

                    // OF-477: The SASL implementation requires the fully qualified host name (not the domain name!) of this server,
                    // yet, most of the XMPP implemenations of DIGEST-MD5 will actually use the domain name. To account for that,
                    // here, we'll use the host name, unless DIGEST-MD5 is being negotiated!
                    final XMPPServerInfo serverInfo = XMPPServer.getInstance().getServerInfo();
                    final String serverName = ( mechanismName.equals( "DIGEST-MD5" ) ? serverInfo.getXMPPDomain() : serverInfo.getHostname() );

                    // Construct the configuration properties
                    final Map<String, Object> props = new HashMap<>();
                    props.put( LocalSession.class.getCanonicalName(), session );
                    props.put(Sasl.POLICY_NOANONYMOUS, Boolean.toString(!AnonymousSaslServer.ENABLED.getValue()));
                    props.put( "com.sun.security.sasl.digest.realm", serverInfo.getXMPPDomain() );

                    SaslServer saslServer = Sasl.createSaslServer( mechanismName, "xmpp", serverName, props, new XMPPCallbackHandler() );
                    if ( saslServer == null )
                    {
                        throw new SaslFailureException( Failure.INVALID_MECHANISM, "There is no provider that can provide a SASL server for the desired mechanism and properties." );
                    }

                    session.setSessionData( "SaslServer", saslServer );

                    if ( mechanismName.equals( "DIGEST-MD5" ) )
                    {
                        // RFC2831 (DIGEST-MD5) says the client MAY provide data in the initial response. Java SASL does
                        // not (currently) support this and throws an exception. For XMPP, such data violates
                        // the RFC, so we just strip any initial token.
                        doc.setText( "" );
                    }

                    // intended fall-through
                case RESPONSE:

                    saslServer = (SaslServer) session.getSessionData( "SaslServer" );

                    if ( saslServer == null )
                    {
                        // Client sends response without a preceding auth?
                        throw new IllegalStateException( "A SaslServer instance was not initialized and/or stored on the session." );
                    }

                    // Decode any data that is provided in the client response.
                    final String encoded = doc.getTextTrim();
                    final byte[] decoded;

                    // OF-2514: Java SaslServer cannot handle a null, but some SASL mechanisms need to differentiate
                    //          between having received no initial response, and an empty response.
                    if ( encoded == null || encoded.isEmpty() )
                    {
                        session.removeSessionData(SASL_LAST_RESPONSE_WAS_PROVIDED_BUT_EMPTY);
                        decoded = new byte[ 0 ];
                    }
                    else if (encoded.equals("="))
                    {
                        session.setSessionData(SASL_LAST_RESPONSE_WAS_PROVIDED_BUT_EMPTY, true);
                        decoded = new byte[ 0 ];
                    }
                    else
                    {
                        session.removeSessionData(SASL_LAST_RESPONSE_WAS_PROVIDED_BUT_EMPTY);
                        // TODO: OF-2515 We shouldn't depend on regex-based validation. Instead, use a proper decoder implementation and handle any exceptions that it throws.
                        if ( !BASE64_ENCODED.matcher( encoded ).matches() )
                        {
                            throw new SaslFailureException( Failure.INCORRECT_ENCODING );
                        }

                        decoded = Base64.getDecoder().decode(encoded);
                    }

                    // Process client response.
                    final byte[] challenge = saslServer.evaluateResponse( decoded ); // Either a challenge or success data.

                    if ( !saslServer.isComplete() )
                    {
                        // Not complete: client is challenged for additional steps.
                        sendChallenge( session, challenge );
                        return Status.needResponse;
                    }

                    // Success!
                    if ( session instanceof LocalIncomingServerSession )
                    {
                        final LocalIncomingServerSession incomingServerSession = (LocalIncomingServerSession) session;

                        // Flag that indicates if certificates of the remote server should be validated.
                        final boolean verify = JiveGlobals.getBooleanProperty( ConnectionSettings.Server.TLS_CERTIFICATE_VERIFY, true );
                        if ( verify )
                        {
                            if ( verifyCertificates( incomingServerSession.getConnection().getPeerCertificates(), saslServer.getAuthorizationID(), true ) )
                            {
                                ( (LocalIncomingServerSession) session ).setAuthenticationMethod(ServerSession.AuthenticationMethod.SASL_EXTERNAL);
                            }
                            else
                            {
                                throw new SaslFailureException( Failure.NOT_AUTHORIZED, "Server-to-Server certificate verification failed." );
                            }
                        }
                    }

                    authenticationSuccessful( session, saslServer.getAuthorizationID(), challenge );
                    session.removeSessionData( "SaslServer" );
                    session.setSessionData("SaslMechanism", saslServer.getMechanismName());
                    return Status.authenticated;

                default:
                    throw new IllegalStateException( "Unexpected data received while negotiating SASL authentication. Name of the offending root element: " + doc.getName() + " Namespace: " + doc.getNamespaceURI() );
            }
        }
        catch ( SaslException ex )
        {
            Log.debug( "SASL negotiation failed for session: {}", session, ex );
            final Failure failure;
            if ( ex instanceof SaslFailureException && ((SaslFailureException) ex).getFailure() != null )
            {
                failure = ((SaslFailureException) ex).getFailure();
            }
            else
            {
                failure = Failure.NOT_AUTHORIZED;
            }
            authenticationFailed( session, failure );
            session.removeSessionData( "SaslServer" );
            return Status.failed;
        }
        catch( Exception ex )
        {
            Log.warn( "An unexpected exception occurred during SASL negotiation. Affected session: {}", session, ex );
            authenticationFailed( session, Failure.NOT_AUTHORIZED );
            session.removeSessionData( "SaslServer" );
            return Status.failed;
        }
    }
Handles the SASL authentication packet. The entity may be sending an initial
authentication request or a response to a challenge made by the server. The returned
value indicates whether the authentication has finished either successfully or not or
if the entity is expected to send a response to a challenge.

@param session the session that is authenticating with the server.
@param doc the stanza sent by the authenticating entity.
@return value that indicates whether the authentication has finished either successfully
        or not or if the entity is expected to send a response to a challenge.
xmppserver/src/main/java/org/jivesoftware/openfire/net/SASLAuthentication.java