http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtService.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtService.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtService.java
new file mode 100644
index 0000000..9635354
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtService.java
@@ -0,0 +1,162 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.web.security.jwt;
+
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.ExpiredJwtException;
+import io.jsonwebtoken.Jws;
+import io.jsonwebtoken.JwsHeader;
+import io.jsonwebtoken.JwtException;
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.MalformedJwtException;
+import io.jsonwebtoken.SignatureAlgorithm;
+import io.jsonwebtoken.SignatureException;
+import io.jsonwebtoken.SigningKeyResolverAdapter;
+import io.jsonwebtoken.UnsupportedJwtException;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.admin.service.AdministrationException;
+import org.apache.nifi.admin.service.KeyService;
+import org.apache.nifi.web.security.token.LoginAuthenticationToken;
+import org.slf4j.LoggerFactory;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Calendar;
+import org.apache.nifi.key.Key;
+
+/**
+ *
+ */
+public class JwtService {
+    private static final org.slf4j.Logger logger = 
LoggerFactory.getLogger(JwtService.class);
+
+    private static final SignatureAlgorithm SIGNATURE_ALGORITHM = 
SignatureAlgorithm.HS256;
+    private static final String KEY_ID_CLAIM = "kid";
+    private static final String USERNAME_CLAIM = "preferred_username";
+
+    private final KeyService keyService;
+
+    public JwtService(final KeyService keyService) {
+        this.keyService = keyService;
+    }
+
+    public String getAuthenticationFromToken(final String base64EncodedToken) 
throws JwtException {
+        // The library representations of the JWT should be kept internal to 
this service.
+        try {
+            final Jws<Claims> jws = 
parseTokenFromBase64EncodedString(base64EncodedToken);
+
+            if (jws == null) {
+                throw new JwtException("Unable to parse token");
+            }
+
+            // Additional validation that subject is present
+            if (StringUtils.isEmpty(jws.getBody().getSubject())) {
+                throw new JwtException("No subject available in token");
+            }
+
+            // TODO: Validate issuer against active registry?
+            if (StringUtils.isEmpty(jws.getBody().getIssuer())) {
+                // TODO: Remove after testing
+//                logger.info("Decoded JWT payload: " + jws.toString());
+                throw new JwtException("No issuer available in token");
+            }
+            return jws.getBody().getSubject();
+        } catch (JwtException e) {
+            logger.debug("The Base64 encoded JWT: " + base64EncodedToken);
+            final String errorMessage = "There was an error validating the 
JWT";
+            logger.error(errorMessage, e);
+            throw e;
+        }
+    }
+
+    private Jws<Claims> parseTokenFromBase64EncodedString(final String 
base64EncodedToken) throws JwtException {
+        try {
+            return Jwts.parser().setSigningKeyResolver(new 
SigningKeyResolverAdapter() {
+                @Override
+                public byte[] resolveSigningKeyBytes(JwsHeader header, Claims 
claims) {
+                    final String identity = claims.getSubject();
+
+                    // Get the key based on the key id in the claims
+                    final Integer keyId = claims.get(KEY_ID_CLAIM, 
Integer.class);
+                    final Key key = keyService.getKey(keyId);
+
+                    // Ensure we were able to find a key that was previously 
issued by this key service for this user
+                    if (key == null || key.getKey() == null) {
+                        throw new UnsupportedJwtException("Unable to determine 
signing key for " + identity + " [kid: " + keyId + "]");
+                    }
+
+                    return key.getKey().getBytes(StandardCharsets.UTF_8);
+                }
+            }).parseClaimsJws(base64EncodedToken);
+        } catch (final MalformedJwtException | UnsupportedJwtException | 
SignatureException | ExpiredJwtException | IllegalArgumentException | 
AdministrationException e) {
+            // TODO: Exercise all exceptions to ensure none leak key material 
to logs
+            final String errorMessage = "There was an error validating the 
JWT";
+            throw new JwtException(errorMessage, e);
+        }
+    }
+
+    /**
+     * Generates a signed JWT token from the provided (Spring Security) login 
authentication token.
+     *
+     * @param authenticationToken an instance of the Spring Security token 
after login credentials have been verified against the respective information 
source
+     * @return a signed JWT containing the user identity and the identity 
provider, Base64-encoded
+     * @throws JwtException if there is a problem generating the signed token
+     */
+    public String generateSignedToken(final LoginAuthenticationToken 
authenticationToken) throws JwtException {
+        if (authenticationToken == null) {
+            throw new IllegalArgumentException("Cannot generate a JWT for a 
null authentication token");
+        }
+
+        // Set expiration from the token
+        final Calendar expiration = Calendar.getInstance();
+        expiration.setTimeInMillis(authenticationToken.getExpiration());
+
+        final Object principal = authenticationToken.getPrincipal();
+        if (principal == null || StringUtils.isEmpty(principal.toString())) {
+            final String errorMessage = "Cannot generate a JWT for a token 
with an empty identity issued by " + authenticationToken.getIssuer();
+            logger.error(errorMessage);
+            throw new JwtException(errorMessage);
+        }
+
+        // Create a JWT with the specified authentication
+        final String identity = principal.toString();
+        final String username = authenticationToken.getName();
+
+        try {
+            // Get/create the key for this user
+            final Key key = keyService.getOrCreateKey(identity);
+            final byte[] keyBytes = 
key.getKey().getBytes(StandardCharsets.UTF_8);
+
+            logger.trace("Generating JWT for " + authenticationToken);
+
+            // TODO: Implement "jti" claim with nonce to prevent replay 
attacks and allow blacklisting of revoked tokens
+
+            // Build the token
+            return Jwts.builder().setSubject(identity)
+                    .setIssuer(authenticationToken.getIssuer())
+                    .setAudience(authenticationToken.getIssuer())
+                    .claim(USERNAME_CLAIM, username)
+                    .claim(KEY_ID_CLAIM, key.getId())
+                    .setExpiration(expiration.getTime())
+                    .setIssuedAt(Calendar.getInstance().getTime())
+                    .signWith(SIGNATURE_ALGORITHM, keyBytes).compact();
+        } catch (NullPointerException | AdministrationException e) {
+            final String errorMessage = "Could not retrieve the signing key 
for JWT for " + identity;
+            logger.error(errorMessage, e);
+            throw new JwtException(errorMessage, e);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/node/NodeAuthorizedUserFilter.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/node/NodeAuthorizedUserFilter.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/node/NodeAuthorizedUserFilter.java
new file mode 100644
index 0000000..a3e6c3c
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/node/NodeAuthorizedUserFilter.java
@@ -0,0 +1,127 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.web.security.node;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.security.cert.X509Certificate;
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import org.apache.nifi.controller.FlowController;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.authentication.AuthenticationResponse;
+import org.apache.nifi.web.security.user.NiFiUserDetails;
+import org.apache.nifi.user.NiFiUser;
+import org.apache.nifi.util.NiFiProperties;
+import org.apache.nifi.web.security.token.NiFiAuthorizationToken;
+import org.apache.nifi.web.security.x509.X509CertificateExtractor;
+import org.apache.nifi.web.security.x509.X509IdentityProvider;
+import org.apache.nifi.web.util.WebUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.ApplicationContext;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.web.context.support.WebApplicationContextUtils;
+import org.springframework.web.filter.GenericFilterBean;
+
+/**
+ * Custom filter to extract a user's authorities from the request where the 
user was authenticated by the cluster manager and populate the threadlocal with 
the authorized user. If the request contains
+ * the appropriate header with authorities and the application instance is a 
node connected to the cluster, then the authentication/authorization steps 
remaining in the filter chain are skipped.
+ *
+ * Checking if the application instance is a connected node is important 
because it prevents external clients from faking the request headers and 
bypassing the authentication processing chain.
+ */
+public class NodeAuthorizedUserFilter extends GenericFilterBean {
+
+    private static final Logger LOGGER = 
LoggerFactory.getLogger(NodeAuthorizedUserFilter.class);
+
+    public static final String PROXY_USER_DETAILS = 
"X-ProxiedEntityUserDetails";
+
+    private NiFiProperties properties;
+    private X509CertificateExtractor certificateExtractor;
+    private X509IdentityProvider certificateIdentityProvider;
+
+    @Override
+    public void doFilter(ServletRequest request, ServletResponse response, 
FilterChain chain) throws IOException, ServletException {
+        final HttpServletRequest httpServletRequest = (HttpServletRequest) 
request;
+
+        // get the proxied user's authorities
+        final String hexEncodedUserDetails = 
httpServletRequest.getHeader(PROXY_USER_DETAILS);
+
+        // check if the request has the necessary header information and this 
instance is configured as a node
+        if (StringUtils.isNotBlank(hexEncodedUserDetails) && 
properties.isNode()) {
+
+            // get the flow controller from the Spring context
+            final ApplicationContext ctx = 
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
+            final FlowController flowController = 
ctx.getBean("flowController", FlowController.class);
+
+            // check that we are connected to the cluster
+            if (flowController.getNodeId() != null) {
+                try {
+                    // attempt to extract the client certificate
+                    final X509Certificate[] certificate = 
certificateExtractor.extractClientCertificate(httpServletRequest);
+                    if (certificate != null) {
+                        // authenticate the certificate
+                        final AuthenticationResponse authenticationResponse = 
certificateIdentityProvider.authenticate(certificate);
+
+                        // only consider the pre-authorized user when the 
request came directly from the NCM according to the DN in the certificate
+                        final String clusterManagerIdentity = 
flowController.getClusterManagerDN();
+                        if (clusterManagerIdentity != null && 
clusterManagerIdentity.equals(authenticationResponse.getIdentity())) {
+                            // deserialize hex encoded object
+                            final Serializable userDetailsObj = 
WebUtils.deserializeHexToObject(hexEncodedUserDetails);
+
+                            // if we have a valid object, set the 
authentication token and bypass the remaining authentication processing chain
+                            if (userDetailsObj instanceof NiFiUserDetails) {
+                                final NiFiUserDetails userDetails = 
(NiFiUserDetails) userDetailsObj;
+                                final NiFiUser user = 
userDetails.getNiFiUser();
+
+                                // log the request attempt - response details 
will be logged later
+                                logger.info(String.format("Attempting request 
for (%s) %s %s (source ip: %s)", user.getIdentity(), 
httpServletRequest.getMethod(),
+                                        
httpServletRequest.getRequestURL().toString(), request.getRemoteAddr()));
+
+                                // create the authorized nifi token
+                                final NiFiAuthorizationToken token = new 
NiFiAuthorizationToken(userDetails);
+                                
SecurityContextHolder.getContext().setAuthentication(token);
+                            }
+                        }
+                    }
+                } catch (final ClassNotFoundException cnfe) {
+                    LOGGER.warn("Classpath issue detected because failed to 
deserialize authorized user in request header due to: " + cnfe, cnfe);
+                } catch (final IllegalArgumentException iae) {
+                    // unable to authenticate a serialized user from the 
incoming request
+                }
+            }
+        }
+
+        chain.doFilter(request, response);
+    }
+
+    public void setProperties(NiFiProperties properties) {
+        this.properties = properties;
+    }
+
+    public void setCertificateIdentityProvider(X509IdentityProvider 
certificateIdentityProvider) {
+        this.certificateIdentityProvider = certificateIdentityProvider;
+    }
+
+    public void setCertificateExtractor(X509CertificateExtractor 
certificateExtractor) {
+        this.certificateExtractor = certificateExtractor;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/spring/LoginIdentityProviderFactoryBean.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/spring/LoginIdentityProviderFactoryBean.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/spring/LoginIdentityProviderFactoryBean.java
new file mode 100644
index 0000000..92a27ae
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/spring/LoginIdentityProviderFactoryBean.java
@@ -0,0 +1,312 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.web.security.spring;
+
+import java.io.File;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+import javax.xml.XMLConstants;
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBElement;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Unmarshaller;
+import javax.xml.transform.stream.StreamSource;
+import javax.xml.validation.Schema;
+import javax.xml.validation.SchemaFactory;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.authentication.AuthenticationResponse;
+import org.apache.nifi.authentication.LoginCredentials;
+import org.apache.nifi.authentication.LoginIdentityProvider;
+import 
org.apache.nifi.authentication.LoginIdentityProviderConfigurationContext;
+import 
org.apache.nifi.authentication.LoginIdentityProviderInitializationContext;
+import org.apache.nifi.authentication.LoginIdentityProviderLookup;
+import org.apache.nifi.authentication.annotation.LoginIdentityProviderContext;
+import org.apache.nifi.authentication.generated.LoginIdentityProviders;
+import org.apache.nifi.authentication.generated.Property;
+import org.apache.nifi.authentication.generated.Provider;
+import org.apache.nifi.authorization.exception.ProviderCreationException;
+import org.apache.nifi.authorization.exception.ProviderDestructionException;
+import org.apache.nifi.nar.ExtensionManager;
+import org.apache.nifi.nar.NarCloseable;
+import org.apache.nifi.util.NiFiProperties;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.DisposableBean;
+import org.springframework.beans.factory.FactoryBean;
+import org.xml.sax.SAXException;
+
+/**
+ *
+ */
+public class LoginIdentityProviderFactoryBean implements FactoryBean, 
DisposableBean, LoginIdentityProviderLookup {
+
+    private static final Logger logger = 
LoggerFactory.getLogger(LoginIdentityProviderFactoryBean.class);
+    private static final String LOGIN_IDENTITY_PROVIDERS_XSD = 
"/login-identity-providers.xsd";
+    private static final String JAXB_GENERATED_PATH = 
"org.apache.nifi.authentication.generated";
+    private static final JAXBContext JAXB_CONTEXT = initializeJaxbContext();
+
+    /**
+     * Load the JAXBContext.
+     */
+    private static JAXBContext initializeJaxbContext() {
+        try {
+            return JAXBContext.newInstance(JAXB_GENERATED_PATH, 
LoginIdentityProviderFactoryBean.class.getClassLoader());
+        } catch (JAXBException e) {
+            throw new RuntimeException("Unable to create JAXBContext.");
+        }
+    }
+
+    private NiFiProperties properties;
+    private LoginIdentityProvider loginIdentityProvider;
+    private final Map<String, LoginIdentityProvider> loginIdentityProviders = 
new HashMap<>();
+
+    @Override
+    public LoginIdentityProvider getLoginIdentityProvider(String identifier) {
+        return loginIdentityProviders.get(identifier);
+    }
+
+    @Override
+    public Object getObject() throws Exception {
+        if (loginIdentityProvider == null) {
+            // look up the login identity provider to use
+            final String loginIdentityProviderIdentifier = 
properties.getProperty(NiFiProperties.SECURITY_USER_LOGIN_IDENTITY_PROVIDER);
+
+            // ensure the login identity provider class name was specified
+            if (StringUtils.isNotBlank(loginIdentityProviderIdentifier)) {
+                final LoginIdentityProviders 
loginIdentityProviderConfiguration = loadLoginIdentityProvidersConfiguration();
+
+                // create each login identity provider
+                for (final Provider provider : 
loginIdentityProviderConfiguration.getProvider()) {
+                    loginIdentityProviders.put(provider.getIdentifier(), 
createLoginIdentityProvider(provider.getIdentifier(), provider.getClazz()));
+                }
+
+                // configure each login identity provider
+                for (final Provider provider : 
loginIdentityProviderConfiguration.getProvider()) {
+                    final LoginIdentityProvider instance = 
loginIdentityProviders.get(provider.getIdentifier());
+                    
instance.onConfigured(loadLoginIdentityProviderConfiguration(provider));
+                }
+
+                // get the login identity provider instance
+                loginIdentityProvider = 
getLoginIdentityProvider(loginIdentityProviderIdentifier);
+
+                // ensure it was found
+                if (loginIdentityProvider == null) {
+                    throw new Exception(String.format("The specified login 
identity provider '%s' could not be found.", loginIdentityProviderIdentifier));
+                }
+            }
+        }
+
+        return loginIdentityProvider;
+    }
+
+    private LoginIdentityProviders loadLoginIdentityProvidersConfiguration() 
throws Exception {
+        final File loginIdentityProvidersConfigurationFile = 
properties.getLoginIdentityProviderConfiguraitonFile();
+
+        // load the users from the specified file
+        if (loginIdentityProvidersConfigurationFile.exists()) {
+            try {
+                // find the schema
+                final SchemaFactory schemaFactory = 
SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
+                final Schema schema = 
schemaFactory.newSchema(LoginIdentityProviders.class.getResource(LOGIN_IDENTITY_PROVIDERS_XSD));
+
+                // attempt to unmarshal
+                final Unmarshaller unmarshaller = 
JAXB_CONTEXT.createUnmarshaller();
+                unmarshaller.setSchema(schema);
+                final JAXBElement<LoginIdentityProviders> element = 
unmarshaller.unmarshal(new 
StreamSource(loginIdentityProvidersConfigurationFile), 
LoginIdentityProviders.class);
+                return element.getValue();
+            } catch (SAXException | JAXBException e) {
+                throw new Exception("Unable to load the login identity 
provider configuration file at: " + 
loginIdentityProvidersConfigurationFile.getAbsolutePath());
+            }
+        } else {
+            throw new Exception("Unable to find the login identity provider 
configuration file at " + 
loginIdentityProvidersConfigurationFile.getAbsolutePath());
+        }
+    }
+
+    private LoginIdentityProvider createLoginIdentityProvider(final String 
identifier, final String loginIdentityProviderClassName) throws Exception {
+        // get the classloader for the specified login identity provider
+        final ClassLoader loginIdentityProviderClassLoader = 
ExtensionManager.getClassLoader(loginIdentityProviderClassName);
+        if (loginIdentityProviderClassLoader == null) {
+            throw new Exception(String.format("The specified login identity 
provider class '%s' is not known to this nifi.", 
loginIdentityProviderClassName));
+        }
+
+        // get the current context classloader
+        final ClassLoader currentClassLoader = 
Thread.currentThread().getContextClassLoader();
+
+        final LoginIdentityProvider instance;
+        try {
+            // set the appropriate class loader
+            
Thread.currentThread().setContextClassLoader(loginIdentityProviderClassLoader);
+
+            // attempt to load the class
+            Class<?> rawLoginIdentityProviderClass = 
Class.forName(loginIdentityProviderClassName, true, 
loginIdentityProviderClassLoader);
+            Class<? extends LoginIdentityProvider> loginIdentityProviderClass 
= rawLoginIdentityProviderClass.asSubclass(LoginIdentityProvider.class);
+
+            // otherwise create a new instance
+            Constructor constructor = 
loginIdentityProviderClass.getConstructor();
+            instance = (LoginIdentityProvider) constructor.newInstance();
+
+            // method injection
+            performMethodInjection(instance, loginIdentityProviderClass);
+
+            // field injection
+            performFieldInjection(instance, loginIdentityProviderClass);
+
+            // call post construction lifecycle event
+            instance.initialize(new 
StandardLoginIdentityProviderInitializationContext(identifier, this));
+        } finally {
+            if (currentClassLoader != null) {
+                
Thread.currentThread().setContextClassLoader(currentClassLoader);
+            }
+        }
+
+        return withNarLoader(instance);
+    }
+
+    private LoginIdentityProviderConfigurationContext 
loadLoginIdentityProviderConfiguration(final Provider provider) {
+        final Map<String, String> providerProperties = new HashMap<>();
+
+        for (final Property property : provider.getProperty()) {
+            providerProperties.put(property.getName(), property.getValue());
+        }
+
+        return new 
StandardLoginIdentityProviderConfigurationContext(provider.getIdentifier(), 
providerProperties);
+    }
+
+    private void performMethodInjection(final LoginIdentityProvider instance, 
final Class loginIdentityProviderClass)
+            throws IllegalAccessException, IllegalArgumentException, 
InvocationTargetException {
+
+        for (final Method method : loginIdentityProviderClass.getMethods()) {
+            if 
(method.isAnnotationPresent(LoginIdentityProviderContext.class)) {
+                // make the method accessible
+                final boolean isAccessible = method.isAccessible();
+                method.setAccessible(true);
+
+                try {
+                    final Class<?>[] argumentTypes = 
method.getParameterTypes();
+
+                    // look for setters (single argument)
+                    if (argumentTypes.length == 1) {
+                        final Class<?> argumentType = argumentTypes[0];
+
+                        // look for well known types
+                        if 
(NiFiProperties.class.isAssignableFrom(argumentType)) {
+                            // nifi properties injection
+                            method.invoke(instance, properties);
+                        }
+                    }
+                } finally {
+                    method.setAccessible(isAccessible);
+                }
+            }
+        }
+
+        final Class parentClass = loginIdentityProviderClass.getSuperclass();
+        if (parentClass != null && 
LoginIdentityProvider.class.isAssignableFrom(parentClass)) {
+            performMethodInjection(instance, parentClass);
+        }
+    }
+
+    private void performFieldInjection(final LoginIdentityProvider instance, 
final Class loginIdentityProviderClass) throws IllegalArgumentException, 
IllegalAccessException {
+        for (final Field field : 
loginIdentityProviderClass.getDeclaredFields()) {
+            if (field.isAnnotationPresent(LoginIdentityProviderContext.class)) 
{
+                // make the method accessible
+                final boolean isAccessible = field.isAccessible();
+                field.setAccessible(true);
+
+                try {
+                    // get the type
+                    final Class<?> fieldType = field.getType();
+
+                    // only consider this field if it isn't set yet
+                    if (field.get(instance) == null) {
+                        // look for well known types
+                        if (NiFiProperties.class.isAssignableFrom(fieldType)) {
+                            // nifi properties injection
+                            field.set(instance, properties);
+                        }
+                    }
+
+                } finally {
+                    field.setAccessible(isAccessible);
+                }
+            }
+        }
+
+        final Class parentClass = loginIdentityProviderClass.getSuperclass();
+        if (parentClass != null && 
LoginIdentityProvider.class.isAssignableFrom(parentClass)) {
+            performFieldInjection(instance, parentClass);
+        }
+    }
+
+    private LoginIdentityProvider withNarLoader(final LoginIdentityProvider 
baseProvider) {
+        return new LoginIdentityProvider() {
+
+            @Override
+            public AuthenticationResponse authenticate(LoginCredentials 
credentials) {
+                try (final NarCloseable narCloseable = 
NarCloseable.withNarLoader()) {
+                    return baseProvider.authenticate(credentials);
+                }
+            }
+
+            @Override
+            public void initialize(LoginIdentityProviderInitializationContext 
initializationContext) throws ProviderCreationException {
+                try (final NarCloseable narCloseable = 
NarCloseable.withNarLoader()) {
+                    baseProvider.initialize(initializationContext);
+                }
+            }
+
+            @Override
+            public void onConfigured(LoginIdentityProviderConfigurationContext 
configurationContext) throws ProviderCreationException {
+                try (final NarCloseable narCloseable = 
NarCloseable.withNarLoader()) {
+                    baseProvider.onConfigured(configurationContext);
+                }
+            }
+
+            @Override
+            public void preDestruction() throws ProviderDestructionException {
+                try (final NarCloseable narCloseable = 
NarCloseable.withNarLoader()) {
+                    baseProvider.preDestruction();
+                }
+            }
+        };
+    }
+
+    @Override
+    public Class getObjectType() {
+        return LoginIdentityProvider.class;
+    }
+
+    @Override
+    public boolean isSingleton() {
+        return true;
+    }
+
+    @Override
+    public void destroy() throws Exception {
+        if (loginIdentityProvider != null) {
+            loginIdentityProvider.preDestruction();
+        }
+    }
+
+    public void setProperties(NiFiProperties properties) {
+        this.properties = properties;
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/spring/StandardLoginIdentityProviderConfigurationContext.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/spring/StandardLoginIdentityProviderConfigurationContext.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/spring/StandardLoginIdentityProviderConfigurationContext.java
new file mode 100644
index 0000000..5c662c7
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/spring/StandardLoginIdentityProviderConfigurationContext.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.web.security.spring;
+
+import java.util.Collections;
+import java.util.Map;
+import 
org.apache.nifi.authentication.LoginIdentityProviderConfigurationContext;
+
+/**
+ *
+ */
+public class StandardLoginIdentityProviderConfigurationContext implements 
LoginIdentityProviderConfigurationContext {
+
+    private final String identifier;
+    private final Map<String, String> properties;
+
+    public StandardLoginIdentityProviderConfigurationContext(String 
identifier, Map<String, String> properties) {
+        this.identifier = identifier;
+        this.properties = properties;
+    }
+
+    @Override
+    public String getIdentifier() {
+        return identifier;
+    }
+
+    @Override
+    public Map<String, String> getProperties() {
+        return Collections.unmodifiableMap(properties);
+    }
+
+    @Override
+    public String getProperty(String property) {
+        return properties.get(property);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/spring/StandardLoginIdentityProviderInitializationContext.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/spring/StandardLoginIdentityProviderInitializationContext.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/spring/StandardLoginIdentityProviderInitializationContext.java
new file mode 100644
index 0000000..af54df9
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/spring/StandardLoginIdentityProviderInitializationContext.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.web.security.spring;
+
+import 
org.apache.nifi.authentication.LoginIdentityProviderInitializationContext;
+import org.apache.nifi.authentication.LoginIdentityProviderLookup;
+
+/**
+ *
+ */
+public class StandardLoginIdentityProviderInitializationContext implements 
LoginIdentityProviderInitializationContext {
+
+    private final String identifier;
+    private final LoginIdentityProviderLookup lookup;
+
+    public StandardLoginIdentityProviderInitializationContext(String 
identifier, final LoginIdentityProviderLookup lookup) {
+        this.identifier = identifier;
+        this.lookup = lookup;
+    }
+
+    @Override
+    public String getIdentifier() {
+        return identifier;
+    }
+
+    @Override
+    public LoginIdentityProviderLookup getAuthorityProviderLookup() {
+        return lookup;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/LoginAuthenticationToken.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/LoginAuthenticationToken.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/LoginAuthenticationToken.java
new file mode 100644
index 0000000..3591239
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/LoginAuthenticationToken.java
@@ -0,0 +1,123 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.web.security.token;
+
+import org.apache.nifi.security.util.CertificateUtils;
+import org.springframework.security.authentication.AbstractAuthenticationToken;
+
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+
+/**
+ * This is an Authentication Token for logging in. Once a user is 
authenticated, they can be issued an ID token.
+ */
+public class LoginAuthenticationToken extends AbstractAuthenticationToken {
+
+    private final String identity;
+    private final String username;
+    private final long expiration;
+    private final String issuer;
+
+    /**
+     * Creates a representation of the authentication token for a user.
+     *
+     * @param identity   The unique identifier for this user
+     * @param expiration The relative time to expiration in milliseconds
+     * @param issuer     The IdentityProvider implementation that generated 
this token
+     */
+    public LoginAuthenticationToken(final String identity, final long 
expiration, final String issuer) {
+        this(identity, null, expiration, issuer);
+    }
+
+    /**
+     * Creates a representation of the authentication token for a user.
+     *
+     * @param identity   The unique identifier for this user (cannot be null 
or empty)
+     * @param username   The preferred username for this user
+     * @param expiration The relative time to expiration in milliseconds
+     * @param issuer     The IdentityProvider implementation that generated 
this token
+     */
+    public LoginAuthenticationToken(final String identity, final String 
username, final long expiration, final String issuer) {
+        super(null);
+        setAuthenticated(true);
+        this.identity = identity;
+        this.username = username;
+        this.issuer = issuer;
+        Calendar now = Calendar.getInstance();
+        this.expiration = now.getTimeInMillis() + expiration;
+    }
+
+    @Override
+    public Object getCredentials() {
+        return null;
+    }
+
+    @Override
+    public Object getPrincipal() {
+        return identity;
+    }
+
+    /**
+     * Returns the expiration instant in milliseconds. This value is an 
absolute point in time (i.e. Nov
+     * 16, 2015 11:30:00.000 GMT), not a relative time (i.e. 60 minutes). It 
is calculated by adding the
+     * relative expiration from the constructor to the timestamp at object 
creation.
+     *
+     * @return the expiration in millis
+     */
+    public long getExpiration() {
+        return expiration;
+    }
+
+    public String getIssuer() {
+        return issuer;
+    }
+
+    @Override
+    public String getName() {
+        if (username == null) {
+            // if the username is a DN this will extract the username or CN... 
if not will return what was passed
+            return CertificateUtils.extractUsername(identity);
+        } else {
+            return username;
+        }
+    }
+
+    @Override
+    public String toString() {
+        Calendar expirationTime = Calendar.getInstance();
+        expirationTime.setTimeInMillis(getExpiration());
+        long remainingTime = expirationTime.getTimeInMillis() - 
Calendar.getInstance().getTimeInMillis();
+
+        SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy 
HH:mm:ss.SSS");
+        dateFormat.setTimeZone(expirationTime.getTimeZone());
+        String expirationTimeString = 
dateFormat.format(expirationTime.getTime());
+
+        return new StringBuilder("LoginAuthenticationToken for ")
+                .append(getName())
+                .append(" issued by ")
+                .append(getIssuer())
+                .append(" expiring at ")
+                .append(expirationTimeString)
+                .append(" [")
+                .append(getExpiration())
+                .append(" ms, ")
+                .append(remainingTime)
+                .append(" ms remaining]")
+                .toString();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/NewAccountAuthenticationRequestToken.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/NewAccountAuthenticationRequestToken.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/NewAccountAuthenticationRequestToken.java
new file mode 100644
index 0000000..6fee4ec
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/NewAccountAuthenticationRequestToken.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.web.security.token;
+
+import org.apache.nifi.web.security.user.NewAccountRequest;
+
+/**
+ * This is an Authentication Token for a user that is requesting 
authentication in order to submit a new account request.
+ */
+public class NewAccountAuthenticationRequestToken extends 
NiFiAuthenticationRequestToken {
+
+    final NewAccountRequest newAccountRequest;
+
+    public NewAccountAuthenticationRequestToken(final NewAccountRequest 
newAccountRequest) {
+        super(newAccountRequest.getChain());
+        this.newAccountRequest = newAccountRequest;
+    }
+
+    public String getJustification() {
+        return newAccountRequest.getJustification();
+    }
+
+    public NewAccountRequest getNewAccountRequest() {
+        return newAccountRequest;
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/NewAccountAuthenticationToken.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/NewAccountAuthenticationToken.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/NewAccountAuthenticationToken.java
new file mode 100644
index 0000000..5fe3a1d
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/NewAccountAuthenticationToken.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.web.security.token;
+
+import org.apache.nifi.web.security.user.NewAccountRequest;
+import org.springframework.security.authentication.AbstractAuthenticationToken;
+
+/**
+ * This is an Authentication Token for a user that has been authenticated but 
is not authorized to access the NiFi APIs. Typically, this authentication token 
is used successfully when requesting a
+ * NiFi account. Requesting any other endpoint would be rejected due to lack 
of roles.
+ */
+public class NewAccountAuthenticationToken extends AbstractAuthenticationToken 
{
+
+    final NewAccountRequest newAccountRequest;
+
+    public NewAccountAuthenticationToken(final NewAccountRequest 
newAccountRequest) {
+        super(null);
+        super.setAuthenticated(true);
+        this.newAccountRequest = newAccountRequest;
+    }
+
+    @Override
+    public Object getCredentials() {
+        return null;
+    }
+
+    @Override
+    public Object getPrincipal() {
+        return newAccountRequest;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/NiFiAuthenticationRequestToken.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/NiFiAuthenticationRequestToken.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/NiFiAuthenticationRequestToken.java
new file mode 100644
index 0000000..3ae6491
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/NiFiAuthenticationRequestToken.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.web.security.token;
+
+import java.util.Collections;
+import java.util.List;
+import org.springframework.security.authentication.AbstractAuthenticationToken;
+
+/**
+ * An authentication token that is used as an authentication request. The 
request chain is specified during creation and is used authenticate the 
user(s). If the user is authenticated, the token is
+ * used to authorized the user(s).
+ */
+public class NiFiAuthenticationRequestToken extends 
AbstractAuthenticationToken {
+
+    private final List<String> chain;
+
+    public NiFiAuthenticationRequestToken(final List<String> chain) {
+        super(null);
+        this.chain = chain;
+    }
+
+    @Override
+    public Object getCredentials() {
+        return null;
+    }
+
+    @Override
+    public Object getPrincipal() {
+        return chain;
+    }
+
+    public List<String> getChain() {
+        return Collections.unmodifiableList(chain);
+    }
+
+    @Override
+    public final void setAuthenticated(boolean authenticated) {
+        throw new IllegalArgumentException("Cannot change the authenticated 
state.");
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/NiFiAuthorizationToken.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/NiFiAuthorizationToken.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/NiFiAuthorizationToken.java
new file mode 100644
index 0000000..0cb0353
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/NiFiAuthorizationToken.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.web.security.token;
+
+import org.springframework.security.authentication.AbstractAuthenticationToken;
+import org.springframework.security.core.userdetails.UserDetails;
+
+/**
+ * An authentication token that represents an Authenticated and Authorized 
user of the NiFi Apis. The authorities are based off the specified UserDetails.
+ */
+public class NiFiAuthorizationToken extends AbstractAuthenticationToken {
+
+    final UserDetails nifiUserDetails;
+
+    public NiFiAuthorizationToken(final UserDetails nifiUserDetails) {
+        super(nifiUserDetails.getAuthorities());
+        super.setAuthenticated(true);
+        setDetails(nifiUserDetails);
+        this.nifiUserDetails = nifiUserDetails;
+    }
+
+    @Override
+    public Object getCredentials() {
+        return nifiUserDetails.getPassword();
+    }
+
+    @Override
+    public Object getPrincipal() {
+        return nifiUserDetails;
+    }
+
+    @Override
+    public final void setAuthenticated(boolean authenticated) {
+        throw new IllegalArgumentException("Cannot change the authenticated 
state.");
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/user/NewAccountRequest.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/user/NewAccountRequest.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/user/NewAccountRequest.java
new file mode 100644
index 0000000..3ec147a
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/user/NewAccountRequest.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.web.security.user;
+
+import java.util.List;
+
+/**
+ *
+ */
+public class NewAccountRequest {
+
+    private final List<String> chain;
+    private final String justification;
+
+    public NewAccountRequest(final List<String> chain, final String 
justification) {
+        this.chain = chain;
+        this.justification = justification;
+    }
+
+    public List<String> getChain() {
+        return chain;
+    }
+
+    public String getJustification() {
+        return justification;
+    }
+
+    public String getUsername() {
+        // the end user is the first item in the chain
+        return chain.get(0);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/user/NiFiUserDetails.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/user/NiFiUserDetails.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/user/NiFiUserDetails.java
index c69b1e6..b559269 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/user/NiFiUserDetails.java
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/user/NiFiUserDetails.java
@@ -73,10 +73,9 @@ public class NiFiUserDetails implements UserDetails {
 
     @Override
     public String getUsername() {
-        return user.getDn();
+        return user.getIdentity();
     }
 
-    // TODO: not sure how to handle these yet
     @Override
     public boolean isAccountNonExpired() {
         return true;

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/user/NiFiUserUtils.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/user/NiFiUserUtils.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/user/NiFiUserUtils.java
index bf1fe43..341663e 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/user/NiFiUserUtils.java
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/user/NiFiUserUtils.java
@@ -25,8 +25,7 @@ import 
org.springframework.security.core.context.SecurityContext;
 import org.springframework.security.core.context.SecurityContextHolder;
 
 /**
- * Utility methods for retrieving information about the current application
- * user.
+ * Utility methods for retrieving information about the current application 
user.
  *
  */
 public final class NiFiUserUtils {
@@ -58,8 +57,7 @@ public final class NiFiUserUtils {
     }
 
     /**
-     * Returns the current NiFiUser or null if the current user is not a
-     * NiFiUser.
+     * Returns the current NiFiUser or null if the current user is not a 
NiFiUser.
      *
      * @return user
      */
@@ -79,6 +77,27 @@ public final class NiFiUserUtils {
         return user;
     }
 
+    /**
+     * Returns the NewAccountRequest or null if this is not a new account 
request.
+     *
+     * @return new account request
+     */
+    public static NewAccountRequest getNewAccountRequest() {
+        NewAccountRequest newAccountRequest = null;
+
+        // obtain the principal in the current authentication
+        final SecurityContext context = SecurityContextHolder.getContext();
+        final Authentication authentication = context.getAuthentication();
+        if (authentication != null) {
+            Object principal = authentication.getPrincipal();
+            if (principal instanceof NewAccountRequest) {
+                newAccountRequest = (NewAccountRequest) principal;
+            }
+        }
+
+        return newAccountRequest;
+    }
+
     public static String getNiFiUserName() {
         // get the nifi user to extract the username
         NiFiUser user = NiFiUserUtils.getNiFiUser();

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509AuthenticationFilter.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509AuthenticationFilter.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509AuthenticationFilter.java
index 72baecb..dd7d47e 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509AuthenticationFilter.java
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509AuthenticationFilter.java
@@ -16,302 +16,66 @@
  */
 package org.apache.nifi.web.security.x509;
 
-import org.apache.nifi.web.security.x509.ocsp.OcspCertificateValidator;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.security.cert.CertificateExpiredException;
-import java.security.cert.CertificateNotYetValidException;
 import java.security.cert.X509Certificate;
-import javax.servlet.FilterChain;
-import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
+import java.util.List;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
-import org.apache.nifi.admin.service.AdministrationException;
-import org.apache.nifi.admin.service.UserService;
-import org.apache.nifi.web.security.DnUtils;
-import org.apache.nifi.web.security.UntrustedProxyException;
-import org.apache.nifi.util.NiFiProperties;
-import org.apache.commons.lang3.StringUtils;
-import org.springframework.security.authentication.AccountStatusException;
-import 
org.springframework.security.authentication.AuthenticationServiceException;
-import org.springframework.security.core.Authentication;
-import org.springframework.security.core.AuthenticationException;
-import org.springframework.security.core.userdetails.UsernameNotFoundException;
-import 
org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;
-import 
org.springframework.security.web.authentication.preauth.x509.X509PrincipalExtractor;
+import org.apache.nifi.authentication.AuthenticationResponse;
+import org.apache.nifi.web.security.InvalidAuthenticationException;
+import org.apache.nifi.web.security.NiFiAuthenticationFilter;
+import org.apache.nifi.web.security.ProxiedEntitiesUtils;
+import org.apache.nifi.web.security.token.NewAccountAuthenticationRequestToken;
+import org.apache.nifi.web.security.token.NiFiAuthenticationRequestToken;
+import org.apache.nifi.web.security.user.NewAccountRequest;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
- * Custom X509 filter that will inspect the HTTP headers for a proxied user
- * before extracting the user details from the client certificate.
+ * Custom X509 filter that will inspect the HTTP headers for a proxied user 
before extracting the user details from the client certificate.
  */
-public class X509AuthenticationFilter extends 
AbstractPreAuthenticatedProcessingFilter {
+public class X509AuthenticationFilter extends NiFiAuthenticationFilter {
 
-    public static final String PROXY_ENTITIES_CHAIN = "X-ProxiedEntitiesChain";
-    public static final String PROXY_ENTITIES_ACCEPTED = 
"X-ProxiedEntitiesAccepted";
-    public static final String PROXY_ENTITIES_DETAILS = 
"X-ProxiedEntitiesDetails";
+    private static final Logger logger = 
LoggerFactory.getLogger(X509AuthenticationFilter.class);
 
-    private final X509CertificateExtractor certificateExtractor = new 
X509CertificateExtractor();
-    private final X509PrincipalExtractor principalExtractor = new 
SubjectDnX509PrincipalExtractor();
-    private OcspCertificateValidator certificateValidator;
-    private NiFiProperties properties;
-    private UserService userService;
+    private X509CertificateExtractor certificateExtractor;
+    private X509IdentityProvider certificateIdentityProvider;
 
     @Override
-    public void doFilter(ServletRequest request, ServletResponse response, 
FilterChain chain) throws IOException, ServletException {
-        final HttpServletResponse httpResponse = (HttpServletResponse) 
response;
-
-        // determine if this request is attempting to create a new account
-        if (isNewAccountRequest((HttpServletRequest) request)) {
-            // determine if this nifi supports new account requests
-            if (properties.getSupportNewAccountRequests()) {
-                // ensure there is a certificate in the request
-                X509Certificate certificate = 
certificateExtractor.extractClientCertificate((HttpServletRequest) request);
-                if (certificate != null) {
-                    // extract the principal from the certificate
-                    Object certificatePrincipal = 
principalExtractor.extractPrincipal(certificate);
-                    String principal = certificatePrincipal.toString();
-
-                    // log the new user account request
-                    logger.info("Requesting new user account for " + 
principal);
-
-                    try {
-                        // get the justification
-                        String justification = 
request.getParameter("justification");
-                        if (justification == null) {
-                            justification = StringUtils.EMPTY;
-                        }
-
-                        // create the pending user account
-                        userService.createPendingUserAccount(principal, 
justification);
-
-                        // generate a response
-                        httpResponse.setStatus(HttpServletResponse.SC_CREATED);
-                        httpResponse.setContentType("text/plain");
-
-                        // write the response message
-                        PrintWriter out = response.getWriter();
-                        out.println("Not authorized. User account created. 
Authorization pending.");
-                    } catch (IllegalArgumentException iae) {
-                        handleUserServiceError((HttpServletRequest) request, 
httpResponse, HttpServletResponse.SC_BAD_REQUEST, iae.getMessage());
-                    } catch (AdministrationException ae) {
-                        handleUserServiceError((HttpServletRequest) request, 
httpResponse, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ae.getMessage());
-                    }
-                } else {
-                    // can this really happen?
-                    handleMissingCertificate((HttpServletRequest) request, 
httpResponse);
-                }
-            } else {
-                handleUserServiceError((HttpServletRequest) request, 
httpResponse, HttpServletResponse.SC_NOT_FOUND, "This NiFi does not support new 
account requests.");
-            }
-        } else {
-            try {
-                // this not a request to create a user account - try to 
authorize
-                super.doFilter(request, response, chain);
-            } catch (AuthenticationException ae) {
-                // continue the filter chain since anonymous access should be 
supported
-                if (!properties.getNeedClientAuth()) {
-                    chain.doFilter(request, response);
-                } else {
-                    // create an appropriate response for the given exception
-                    handleUnsuccessfulAuthentication((HttpServletRequest) 
request, httpResponse, ae);
-                }
-            }
-        }
-    }
-
-    @Override
-    protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {
-        String principal;
-
-        // extract the cert
-        X509Certificate certificate = 
certificateExtractor.extractClientCertificate(request);
-
-        // ensure the cert was found
-        if (certificate == null) {
+    public NiFiAuthenticationRequestToken 
attemptAuthentication(HttpServletRequest request, HttpServletResponse response) 
{
+        // only suppport x509 login when running securely
+        if (!request.isSecure()) {
             return null;
         }
 
-        // extract the principal
-        Object certificatePrincipal = 
principalExtractor.extractPrincipal(certificate);
-        principal = DnUtils.formatProxyDn(certificatePrincipal.toString());
-
-        try {
-            // ensure the cert is valid
-            certificate.checkValidity();
-        } catch (CertificateExpiredException cee) {
-            final String message = String.format("Client certificate for (%s) 
is expired.", principal);
-            logger.info(message, cee);
-            if (logger.isDebugEnabled()) {
-                logger.debug("", cee);
-            }
-            return null;
-        } catch (CertificateNotYetValidException cnyve) {
-            final String message = String.format("Client certificate for (%s) 
is not yet valid.", principal);
-            logger.info(message, cnyve);
-            if (logger.isDebugEnabled()) {
-                logger.debug("", cnyve);
-            }
+        // look for a client certificate
+        final X509Certificate[] certificates = 
certificateExtractor.extractClientCertificate(request);
+        if (certificates == null) {
             return null;
         }
 
-        // validate the certificate in question
+        // attempt to authenticate if certificates were found
+        final AuthenticationResponse authenticationResponse;
         try {
-            certificateValidator.validate(request);
-        } catch (final Exception e) {
-            logger.info(e.getMessage());
-            if (logger.isDebugEnabled()) {
-                logger.debug("", e);
-            }
-            return null;
+            authenticationResponse = 
certificateIdentityProvider.authenticate(certificates);
+        } catch (final IllegalArgumentException iae) {
+            throw new InvalidAuthenticationException(iae.getMessage(), iae);
         }
 
-        // look for a proxied user
-        if (StringUtils.isNotBlank(request.getHeader(PROXY_ENTITIES_CHAIN))) {
-            principal = request.getHeader(PROXY_ENTITIES_CHAIN) + principal;
-        }
-
-        // log the request attempt - response details will be logged later
-        logger.info(String.format("Attempting request for (%s) %s %s (source 
ip: %s)", principal, request.getMethod(),
-                request.getRequestURL().toString(), request.getRemoteAddr()));
-
-        return principal;
-    }
-
-    @Override
-    protected Object getPreAuthenticatedCredentials(HttpServletRequest 
request) {
-        return certificateExtractor.extractClientCertificate(request);
-    }
-
-    @Override
-    protected void successfulAuthentication(HttpServletRequest request, 
HttpServletResponse response, Authentication authResult) {
-        if (StringUtils.isNotBlank(request.getHeader(PROXY_ENTITIES_CHAIN))) {
-            response.setHeader(PROXY_ENTITIES_ACCEPTED, 
Boolean.TRUE.toString());
-        }
-        super.successfulAuthentication(request, response, authResult);
-    }
-
-    @Override
-    protected void unsuccessfulAuthentication(HttpServletRequest request, 
HttpServletResponse response, AuthenticationException failed) {
-        if (StringUtils.isNotBlank(request.getHeader(PROXY_ENTITIES_CHAIN))) {
-            response.setHeader(PROXY_ENTITIES_DETAILS, failed.getMessage());
-        }
-        super.unsuccessfulAuthentication(request, response, failed);
-    }
-
-    /**
-     * Determines if the specified request is attempting to register a new user
-     * account.
-     *
-     * @param request http request
-     * @return true if new user
-     */
-    private boolean isNewAccountRequest(HttpServletRequest request) {
-        if ("POST".equalsIgnoreCase(request.getMethod())) {
-            String path = request.getPathInfo();
-            if (StringUtils.isNotBlank(path)) {
-                if ("/controller/users".equals(path)) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Handles requests that were unable to be authorized.
-     *
-     * @param request request
-     * @param response response
-     * @param ae ex
-     * @throws IOException ex
-     */
-    private void handleUnsuccessfulAuthentication(HttpServletRequest request, 
HttpServletResponse response, AuthenticationException ae) throws IOException {
-        // set the response status
-        response.setContentType("text/plain");
-
-        // write the response message
-        PrintWriter out = response.getWriter();
-
-        // use the type of authentication exception to determine the response 
code
-        if (ae instanceof UsernameNotFoundException) {
-            if (properties.getSupportNewAccountRequests()) {
-                response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
-                out.println("Not authorized.");
-            } else {
-                response.setStatus(HttpServletResponse.SC_FORBIDDEN);
-                out.println("Access is denied.");
-            }
-        } else if (ae instanceof AccountStatusException) {
-            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
-            out.println(ae.getMessage());
-        } else if (ae instanceof UntrustedProxyException) {
-            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
-            out.println(ae.getMessage());
-        } else if (ae instanceof AuthenticationServiceException) {
-            logger.error(String.format("Unable to authorize: %s", 
ae.getMessage()), ae);
-            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
-            out.println(String.format("Unable to authorize: %s", 
ae.getMessage()));
+        final List<String> proxyChain = 
ProxiedEntitiesUtils.buildProxiedEntitiesChain(request, 
authenticationResponse.getIdentity());
+        if (isNewAccountRequest(request)) {
+            return new NewAccountAuthenticationRequestToken(new 
NewAccountRequest(proxyChain, getJustification(request)));
         } else {
-            logger.error(String.format("Unable to authorize: %s", 
ae.getMessage()), ae);
-            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
-            out.println("Access is denied.");
+            return new NiFiAuthenticationRequestToken(proxyChain);
         }
-
-        // log the failure
-        logger.info(String.format("Rejecting access to web api: %s", 
ae.getMessage()));
-
-        // optionally log the stack trace
-        if (logger.isDebugEnabled()) {
-            logger.debug(StringUtils.EMPTY, ae);
-        }
-    }
-
-    private void handleUserServiceError(HttpServletRequest request, 
HttpServletResponse response, int responseCode, String message) throws 
IOException {
-        // set the response status
-        response.setContentType("text/plain");
-        response.setStatus(responseCode);
-
-        // write the response message
-        PrintWriter out = response.getWriter();
-        out.println(message);
-
-        // log the failure
-        logger.info(String.format("Unable to process request because %s", 
message));
-    }
-
-    /**
-     * Handles requests that failed because they were bad input.
-     *
-     * @param request request
-     * @param response response
-     * @throws IOException ioe
-     */
-    private void handleMissingCertificate(HttpServletRequest request, 
HttpServletResponse response) throws IOException {
-        // set the response status
-        response.setContentType("text/plain");
-        response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
-
-        // write the response message
-        PrintWriter out = response.getWriter();
-        out.println("Unable to process request because the user certificate 
was not specified.");
-
-        // log the failure
-        logger.info("Unable to process request because the user certificate 
was not specified.");
     }
 
     /* setters */
-    public void setProperties(NiFiProperties properties) {
-        this.properties = properties;
-    }
-
-    public void setUserService(UserService userService) {
-        this.userService = userService;
+    public void setCertificateExtractor(X509CertificateExtractor 
certificateExtractor) {
+        this.certificateExtractor = certificateExtractor;
     }
 
-    public void setCertificateValidator(OcspCertificateValidator 
certificateValidator) {
-        this.certificateValidator = certificateValidator;
+    public void setCertificateIdentityProvider(X509IdentityProvider 
certificateIdentityProvider) {
+        this.certificateIdentityProvider = certificateIdentityProvider;
     }
 
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509CertificateExtractor.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509CertificateExtractor.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509CertificateExtractor.java
index b40d5a5..98d0154 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509CertificateExtractor.java
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509CertificateExtractor.java
@@ -35,11 +35,11 @@ public class X509CertificateExtractor {
      * @param request http request
      * @return cert
      */
-    public X509Certificate extractClientCertificate(HttpServletRequest 
request) {
+    public X509Certificate[] extractClientCertificate(HttpServletRequest 
request) {
         X509Certificate[] certs = (X509Certificate[]) 
request.getAttribute("javax.servlet.request.X509Certificate");
 
         if (certs != null && certs.length > 0) {
-            return certs[0];
+            return certs;
         }
 
         if (logger.isDebugEnabled()) {

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509CertificateValidator.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509CertificateValidator.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509CertificateValidator.java
new file mode 100644
index 0000000..cb56a11
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509CertificateValidator.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.web.security.x509;
+
+import java.security.cert.CertificateExpiredException;
+import java.security.cert.CertificateNotYetValidException;
+import java.security.cert.X509Certificate;
+import org.apache.nifi.web.security.x509.ocsp.CertificateStatusException;
+import org.apache.nifi.web.security.x509.ocsp.OcspCertificateValidator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Extracts client certificates from Http requests.
+ */
+public class X509CertificateValidator {
+
+    private final Logger logger = LoggerFactory.getLogger(getClass());
+
+    private OcspCertificateValidator ocspValidator;
+
+    /**
+     * Extract the client certificate from the specified HttpServletRequest or 
null if none is specified.
+     *
+     * @param certificates the client certificates
+     * @throws java.security.cert.CertificateExpiredException cert is expired
+     * @throws java.security.cert.CertificateNotYetValidException cert is not 
yet valid
+     * @throws 
org.apache.nifi.web.security.x509.ocsp.CertificateStatusException ocsp 
validation issue
+     */
+    public void validateClientCertificate(final X509Certificate[] certificates)
+            throws CertificateExpiredException, 
CertificateNotYetValidException, CertificateStatusException {
+
+        // ensure the cert is valid
+        certificates[0].checkValidity();
+
+        // perform ocsp validator if necessary
+        ocspValidator.validate(certificates);
+    }
+
+    public void setOcspValidator(OcspCertificateValidator ocspValidator) {
+        this.ocspValidator = ocspValidator;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509IdentityProvider.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509IdentityProvider.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509IdentityProvider.java
new file mode 100644
index 0000000..db0b529
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509IdentityProvider.java
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.web.security.x509;
+
+import java.security.cert.CertificateExpiredException;
+import java.security.cert.CertificateNotYetValidException;
+import java.security.cert.X509Certificate;
+import java.util.concurrent.TimeUnit;
+import org.apache.nifi.authentication.AuthenticationResponse;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import 
org.springframework.security.web.authentication.preauth.x509.X509PrincipalExtractor;
+
+/**
+ * Identity provider for extract the authenticating a ServletRequest with a 
X509Certificate.
+ */
+public class X509IdentityProvider {
+
+    private static final Logger logger = 
LoggerFactory.getLogger(X509IdentityProvider.class);
+
+    private final String issuer = getClass().getSimpleName();
+
+    private X509CertificateValidator certificateValidator;
+    private X509PrincipalExtractor principalExtractor;
+
+    /**
+     * Authenticates the specified request by checking certificate validity.
+     *
+     * @param certificates the client certificates
+     * @return an authentication response
+     * @throws IllegalArgumentException the request did not contain a valid 
certificate (or no certificate)
+     */
+    public AuthenticationResponse authenticate(final X509Certificate[] 
certificates) throws IllegalArgumentException {
+        // ensure the cert was found
+        if (certificates == null || certificates.length == 0) {
+            throw new IllegalArgumentException("The specified request does not 
contain a client certificate.");
+        }
+
+        // extract the principal
+        final Object certificatePrincipal = 
principalExtractor.extractPrincipal(certificates[0]);
+        final String principal = certificatePrincipal.toString();
+
+        try {
+            certificateValidator.validateClientCertificate(certificates);
+        } catch (CertificateExpiredException cee) {
+            final String message = String.format("Client certificate for (%s) 
is expired.", principal);
+            logger.info(message, cee);
+            if (logger.isDebugEnabled()) {
+                logger.debug("", cee);
+            }
+            throw new IllegalArgumentException(message, cee);
+        } catch (CertificateNotYetValidException cnyve) {
+            final String message = String.format("Client certificate for (%s) 
is not yet valid.", principal);
+            logger.info(message, cnyve);
+            if (logger.isDebugEnabled()) {
+                logger.debug("", cnyve);
+            }
+            throw new IllegalArgumentException(message, cnyve);
+        } catch (final Exception e) {
+            logger.info(e.getMessage());
+            if (logger.isDebugEnabled()) {
+                logger.debug("", e);
+            }
+            throw new IllegalArgumentException(e.getMessage(), e);
+        }
+
+        // build the authentication response
+        return new AuthenticationResponse(principal, principal, 
TimeUnit.MILLISECONDS.convert(1, TimeUnit.DAYS), issuer);
+    }
+
+    /* setters */
+    public void setCertificateValidator(X509CertificateValidator 
certificateValidator) {
+        this.certificateValidator = certificateValidator;
+    }
+
+    public void setPrincipalExtractor(X509PrincipalExtractor 
principalExtractor) {
+        this.principalExtractor = principalExtractor;
+    }
+
+}

Reply via email to