class ExternalClientSaslServer implements SaslServer
{
    public static final SystemProperty<Boolean> PROPERTY_SASL_EXTERNAL_CLIENT_SUPPRESS_MATCHING_REALMNAME = SystemProperty.Builder
        .ofType( Boolean.class )
        .setKey( "xmpp.auth.sasl.external.client.suppress-matching-realmname" )
        .setDefaultValue( true )
        .setDynamic( true )
        .build();

    public static final Logger Log = LoggerFactory.getLogger( ExternalClientSaslServer.class );

    public static final String NAME = "EXTERNAL";

    private boolean complete = false;

    private String authorizationID = null;

    private LocalClientSession session;

    public ExternalClientSaslServer( LocalClientSession session ) throws SaslException
    {
        this.session = session;
    }

    @Override
    public String getMechanismName()
    {
        return NAME;
    }

    @Override
    public byte[] evaluateResponse( @Nonnull final byte[] response ) throws SaslException
    {<FILL_FUNCTION_BODY>}

    @Override
    public boolean isComplete()
    {
        return complete;
    }

    @Override
    public String getAuthorizationID()
    {
        if ( !isComplete() )
        {
            throw new IllegalStateException( "Authentication exchange not completed." );
        }

        return authorizationID;
    }

    @Override
    public byte[] unwrap( byte[] incoming, int offset, int len ) throws SaslException
    {
        if ( !isComplete() )
        {
            throw new IllegalStateException( "Authentication exchange not completed." );
        }

        throw new IllegalStateException( "SASL Mechanism '" + getMechanismName() + " does not support integrity nor privacy." );
    }

    @Override
    public byte[] wrap( byte[] outgoing, int offset, int len ) throws SaslException
    {
        if ( !isComplete() )
        {
            throw new IllegalStateException( "Authentication exchange not completed." );
        }

        throw new IllegalStateException( "SASL Mechanism '" + getMechanismName() + " does not support integrity nor privacy." );
    }

    @Override
    public Object getNegotiatedProperty( String propName )
    {
        if ( !isComplete() )
        {
            throw new IllegalStateException( "Authentication exchange not completed." );
        }

        if ( propName.equals( Sasl.QOP ) )
        {
            return "auth";
        }
        else
        {
            return null;
        }
    }

    @Override
    public void dispose() throws SaslException
    {
        complete = false;
        authorizationID = null;
        session = null;
    }
}

        if ( isComplete() )
        {
            throw new IllegalStateException( "Authentication exchange already completed." );
        }

        if (response.length == 0 && session.getSessionData(SASLAuthentication.SASL_LAST_RESPONSE_WAS_PROVIDED_BUT_EMPTY) != null) {
            // No initial response. Send a challenge to get one, per RFC 4422 appendix-A.
            return new byte[ 0 ];
        }

        // There will be no further steps. Either authentication succeeds or fails, but in any case, we're done.
        complete = true;

        final Connection connection = session.getConnection();
        assert connection != null; // While the peer is performing a SASL negotiation, the connection can't be null.
        Certificate[] peerCertificates = connection.getPeerCertificates();
        if ( peerCertificates == null || peerCertificates.length < 1 )
        {
            throw new SaslException( "No peer certificates." );
        }

        final X509Certificate trusted;
        if ( SASLAuthentication.SKIP_PEER_CERT_REVALIDATION_CLIENT.getValue() ) {
            // Trust that the peer certificate has been validated when TLS got established.
            trusted = (X509Certificate) peerCertificates[0];
        } else {
            // Re-evaluate the validity of the peer certificate.
            final TrustStore trustStore = connection.getConfiguration().getTrustStore();
            trusted = trustStore.getEndEntityCertificate( peerCertificates );
        }

        if ( trusted == null )
        {
            throw new SaslException( "Certificate chain of peer is not trusted." );
        }

        // Process client authentication identities / principals.
        final ArrayList<String> authenticationIdentities = new ArrayList<>();
        authenticationIdentities.addAll( CertificateManager.getClientIdentities( trusted ) );
        String authenticationIdentity;
        switch ( authenticationIdentities.size() )
        {
            case 0:
                authenticationIdentity = "";
                break;

            default:
                Log.debug( "More than one authentication identity found, using the first one." );
                // intended fall-through;
            case 1:
                authenticationIdentity = authenticationIdentities.get( 0 );
                break;
        }

        // Process requested username to act as.
        String authorizationIdentity;
        if ( response.length > 0 )
        {
            authorizationIdentity = new String( response, StandardCharsets.UTF_8 );
            if( PROPERTY_SASL_EXTERNAL_CLIENT_SUPPRESS_MATCHING_REALMNAME.getValue() && authorizationIdentity.contains("@") ) {
                String authzUser = authorizationIdentity.substring(0,authorizationIdentity.lastIndexOf("@"));
                String authzRealm = authorizationIdentity.substring((authorizationIdentity.lastIndexOf("@")+1));
                if ( XMPPServer.getInstance().getServerInfo().getXMPPDomain().equals( authzRealm ) ) {
                    authorizationIdentity = authzUser;
                }
            }
        }
        else
        {
            authorizationIdentity = null;
        }

        if ( authorizationIdentity == null || authorizationIdentity.length() == 0 )
        {
            // No authorization identity was provided, according to XEP-0178 we need to:
            //    * attempt to get it from the cert first
            //    * have the server assign one

            // There shouldn't be more than a few authentication identities in here. One ideally. We set authcid to the
            // first one in the list to have a sane default. If this list is empty, then the cert had no identity at all, which will
            // cause an authorization failure.
            for ( String authcid : authenticationIdentities )
            {
                final String mappedUsername = AuthorizationManager.map( authcid );
                if ( !mappedUsername.equals( authcid ) )
                {
                    authorizationIdentity = mappedUsername;
                    authenticationIdentity = authcid;
                    break;
                }
            }

            if ( authorizationIdentity == null || authorizationIdentity.length() == 0 )
            {
                // Still no username to act as.  Punt.
                authorizationIdentity = authenticationIdentity;
            }
            Log.debug( "No username requested, using: {}", authorizationIdentity );
        }

        // It's possible that either/both authzid and authcid are null here. The providers should not allow a null authorization.
        if ( AuthorizationManager.authorize( authorizationIdentity, authenticationIdentity ) )
        {
            Log.debug( "Authcid {} authorized to authzid (username) {}", authenticationIdentity, authorizationIdentity );
            authorizationID = authorizationIdentity;
            return null; // Success!
        }

        throw new SaslException();
    
<no_super_class>