class X509AuthenticationProvider extends NiFiAuthenticationProvider {

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        final X509AuthenticationRequestToken request = (X509AuthenticationRequestToken) authentication;

        // attempt to authenticate if certificates were found
        final AuthenticationResponse authenticationResponse;
        try {
            authenticationResponse = certificateIdentityProvider.authenticate(request.getCertificates());
        } catch (final IllegalArgumentException iae) {
            throw new InvalidAuthenticationException(iae.getMessage(), iae);
        }

        if (StringUtils.isBlank(request.getProxiedEntitiesChain())) {
            final String mappedIdentity = mapIdentity(authenticationResponse.getIdentity());
            return new NiFiAuthenticationToken(new NiFiUserDetails(new StandardNiFiUser(mappedIdentity, request.getClientAddress())));
        } else {
            // build the entire proxy chain if applicable - <end-user><proxy1><proxy2>
            final List<String> proxyChain = new ArrayList<>(ProxiedEntitiesUtils.tokenizeProxiedEntitiesChain(request.getProxiedEntitiesChain()));
            proxyChain.add(authenticationResponse.getIdentity());

            // add the chain as appropriate to each proxy
            NiFiUser proxy = null;
            for (final ListIterator<String> chainIter = proxyChain.listIterator(proxyChain.size()); chainIter.hasPrevious(); ) {
                String identity = chainIter.previous();

                // determine if the user is anonymous
                final boolean isAnonymous = StringUtils.isBlank(identity);
                if (isAnonymous) {
                    identity = StandardNiFiUser.ANONYMOUS_IDENTITY;
                } else {
                    identity = mapIdentity(identity);
                }

                if (chainIter.hasPrevious()) {
                    // authorize this proxy in order to authenticate this user
                    final AuthorizationRequest proxyAuthorizationRequest = new AuthorizationRequest.Builder()
                            .identity(identity)
                            .anonymous(isAnonymous)
                            .accessAttempt(true)
                            .action(RequestAction.WRITE)
                            .resource(ResourceFactory.getProxyResource())
                            .userContext(proxy == null ? getUserContext(request) : null) // only set the context for the real user
                            .build();

                    final AuthorizationResult proxyAuthorizationResult = authorizer.authorize(proxyAuthorizationRequest);
                    if (!Result.Approved.equals(proxyAuthorizationResult.getResult())) {
                        throw new UntrustedProxyException(String.format("Untrusted proxy %s", identity));
                    }
                }

                // Only set the client address for user making the request because we don't know the client address of the proxies
                String clientAddress = (proxy == null) ? request.getClientAddress() : null;
                proxy = createUser(identity, proxy, clientAddress, isAnonymous);
            }

            return new NiFiAuthenticationToken(new NiFiUserDetails(proxy));
        }
    }

    public  X509AuthenticationProvider(final X509IdentityProvider certificateIdentityProvider, final Authorizer authorizer, final NiFiProperties nifiProperties);

}