You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by mc...@apache.org on 2015/10/21 23:25:02 UTC

nifi git commit: NIFI-655: - Moving to token api to web-api. - Creating an LoginProvider API for user/pass based authentication. - Creating a module for funneling access to the authorized useres.

Repository: nifi
Updated Branches:
  refs/heads/NIFI-655 d21d8f316 -> 852dbea95


NIFI-655:
- Moving to token api to web-api.
- Creating an LoginProvider API for user/pass based authentication.
- Creating a module for funneling access to the authorized useres.

Project: http://git-wip-us.apache.org/repos/asf/nifi/repo
Commit: http://git-wip-us.apache.org/repos/asf/nifi/commit/852dbea9
Tree: http://git-wip-us.apache.org/repos/asf/nifi/tree/852dbea9
Diff: http://git-wip-us.apache.org/repos/asf/nifi/diff/852dbea9

Branch: refs/heads/NIFI-655
Commit: 852dbea9501c307f50fb464c6dcfb9be6de9fb59
Parents: d21d8f3
Author: Matt Gilman <ma...@gmail.com>
Authored: Wed Oct 21 14:24:40 2015 -0700
Committer: Matt Gilman <ma...@gmail.com>
Committed: Wed Oct 21 14:24:40 2015 -0700

----------------------------------------------------------------------
 nifi-login-api/pom.xml                          |  17 +
 .../authentication/LoginIdentityProvider.java   |  42 ++
 .../nifi-authorized-users/pom.xml               |  53 +++
 .../nifi/authorized/users/AuthorizedUsers.java  | 230 ++++++++++
 .../src/main/xsd/users.xsd                      |  64 +++
 .../nifi-file-authorization-provider/pom.xml    |   4 +
 .../FileAuthorizationProvider.java              | 454 ++++++++-----------
 .../nifi-web/nifi-web-api/pom.xml               |   4 +
 .../web/NiFiWebApiSecurityConfiguration.java    |  73 ++-
 .../nifi-web/nifi-web-security/pom.xml          |   4 +
 .../security/form/FormAuthenticationFilter.java |  20 +-
 .../LoginIdentityProviderFactoryBean.java       |  73 +++
 .../NewAccountAuthenticationRequestToken.java   |   2 +-
 .../resources/nifi-web-security-context.xml     |   5 +
 .../nifi-framework/pom.xml                      |   1 +
 nifi-nar-bundles/nifi-framework-bundle/pom.xml  |   5 +
 nifi-nar-bundles/nifi-jetty-bundle/pom.xml      |   6 +
 nifi-nar-bundles/pom.xml                        |   6 +
 pom.xml                                         |   6 +
 19 files changed, 796 insertions(+), 273 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi/blob/852dbea9/nifi-login-api/pom.xml
----------------------------------------------------------------------
diff --git a/nifi-login-api/pom.xml b/nifi-login-api/pom.xml
new file mode 100644
index 0000000..a1849ce
--- /dev/null
+++ b/nifi-login-api/pom.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.nifi</groupId>
+        <artifactId>nifi</artifactId>
+        <version>0.3.1-SNAPSHOT</version>
+    </parent>
+    <artifactId>nifi-login-api</artifactId>
+    <packaging>jar</packaging>
+    <dependencies>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>javax.servlet-api</artifactId>
+        </dependency>
+    </dependencies>
+</project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/852dbea9/nifi-login-api/src/main/java/org/apache/nifi/authentication/LoginIdentityProvider.java
----------------------------------------------------------------------
diff --git a/nifi-login-api/src/main/java/org/apache/nifi/authentication/LoginIdentityProvider.java b/nifi-login-api/src/main/java/org/apache/nifi/authentication/LoginIdentityProvider.java
new file mode 100644
index 0000000..c2bb18f
--- /dev/null
+++ b/nifi-login-api/src/main/java/org/apache/nifi/authentication/LoginIdentityProvider.java
@@ -0,0 +1,42 @@
+/*
+ * 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.authentication;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * Identity provider that is able to authentication a user with 
+ * username/password credentials.
+ */
+public interface LoginIdentityProvider {
+    
+    /**
+     * Returns whether this provider supports user registration.
+     * 
+     * @return whether user registration is supported
+     */
+    boolean supportsRegistration();
+    
+    /**
+     * Authenticates the user making the specified request. Returns
+     * the user principal or null if the user is not authenticated.
+     * 
+     * @param request the request
+     * @return the user principal
+     */
+    String authenticate(HttpServletRequest request);
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/852dbea9/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorized-users/pom.xml
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorized-users/pom.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorized-users/pom.xml
new file mode 100644
index 0000000..458819e
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorized-users/pom.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.nifi</groupId>
+        <artifactId>nifi-framework</artifactId>
+        <version>0.3.1-SNAPSHOT</version>
+    </parent>
+    <artifactId>nifi-authorized-users</artifactId>
+    <build>
+        <resources>
+            <resource>
+                <directory>src/main/resources</directory>
+            </resource>
+            <resource>
+                <directory>src/main/xsd</directory>
+            </resource>
+        </resources>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>jaxb2-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>xjc</id>
+                        <goals>
+                            <goal>xjc</goal>
+                        </goals>
+                        <configuration>
+                            <packageName>org.apache.nifi.user.generated</packageName>
+                        </configuration>
+                    </execution>
+                </executions>
+                <configuration>
+                    <generateDirectory>${project.build.directory}/generated-sources/jaxb</generateDirectory>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-checkstyle-plugin</artifactId>
+                <configuration>
+                    <excludes>**/user/generated/*.java</excludes>
+                </configuration>
+            </plugin>            
+        </plugins>
+    </build>
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-api</artifactId>
+        </dependency>
+    </dependencies>
+</project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/852dbea9/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorized-users/src/main/java/org/apache/nifi/authorized/users/AuthorizedUsers.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorized-users/src/main/java/org/apache/nifi/authorized/users/AuthorizedUsers.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorized-users/src/main/java/org/apache/nifi/authorized/users/AuthorizedUsers.java
new file mode 100644
index 0000000..58c7856
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorized-users/src/main/java/org/apache/nifi/authorized/users/AuthorizedUsers.java
@@ -0,0 +1,230 @@
+/*
+ * 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.authorized.users;
+
+import java.io.File;
+import java.util.List;
+import javax.xml.XMLConstants;
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBElement;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Marshaller;
+import javax.xml.bind.Unmarshaller;
+import javax.xml.transform.stream.StreamSource;
+import javax.xml.validation.Schema;
+import javax.xml.validation.SchemaFactory;
+import org.apache.nifi.authorization.exception.AuthorityAccessException;
+import org.apache.nifi.authorization.exception.UnknownIdentityException;
+import org.apache.nifi.user.generated.User;
+import org.apache.nifi.user.generated.Users;
+import org.xml.sax.SAXException;
+
+/**
+ * Access to the configured Authorized Users.
+ */
+public final class AuthorizedUsers {
+
+    private static final String USERS_XSD = "/users.xsd";
+    private static final String JAXB_GENERATED_PATH = "org.apache.nifi.user.generated";
+    private static final JAXBContext JAXB_CONTEXT = initializeJaxbContext();
+
+    /**
+     * Load the JAXBContext.
+     */
+    private static JAXBContext initializeJaxbContext() {
+        try {
+            return JAXBContext.newInstance(JAXB_GENERATED_PATH, AuthorizedUsers.class.getClassLoader());
+        } catch (JAXBException e) {
+            throw new RuntimeException("Unable to create JAXBContext.");
+        }
+    }
+
+    public static synchronized Users getUsers(final File usersFile) {
+        try {
+            // ensure the directory exists and it can be created
+            if (!usersFile.exists() && !usersFile.mkdirs()) {
+                throw new IllegalStateException("The users file does not exist and could not be created.");
+            }
+
+            // find the schema
+            final SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
+            final Schema schema = schemaFactory.newSchema(AuthorizedUsers.class.getResource(USERS_XSD));
+
+            // attempt to unmarshal
+            final Unmarshaller unmarshaller = JAXB_CONTEXT.createUnmarshaller();
+            unmarshaller.setSchema(schema);
+            final JAXBElement<Users> element = unmarshaller.unmarshal(new StreamSource(usersFile), Users.class);
+            return element.getValue();
+        } catch (SAXException | JAXBException e) {
+            throw new AuthorityAccessException(e.getMessage(), e);
+        }
+    }
+
+    public static synchronized User getUser(final File usersFile, final FindUser finder) {
+        // load the users
+        final Users users = getUsers(usersFile);
+
+        // find the desired user
+        return finder.findUser(users.getUser());
+    }
+
+    public static synchronized List<User> getUsers(final File usersFile, final FindUsers finder) {
+        // load the users
+        final Users users = getUsers(usersFile);
+
+        // find the desired user
+        return finder.findUsers(users.getUser());
+    }
+
+    public static synchronized void createUser(final File usersFile, final File restoreFile, final CreateUser creator) {
+        // add the user
+        final Users users = getUsers(usersFile);
+        final List<User> userList = users.getUser();
+
+        // create the user
+        final User newUsers = creator.createUser();
+        userList.add(newUsers);
+
+        // save the users
+        saveUsers(usersFile, restoreFile, users);
+    }
+
+    public static synchronized void updateUser(final File usersFile, final File restoreFile, final FindUser finder, final UpdateUser updater) {
+        // update the user
+        final Users users = getUsers(usersFile);
+        final User user = finder.findUser(users.getUser());
+
+        // update the user
+        updater.updateUser(user);
+
+        // save the users
+        saveUsers(usersFile, restoreFile, users);
+    }
+
+    public static synchronized void updateUsers(final File usersFile, final File restoreFile, final FindUsers finder, final UpdateUsers updater) {
+        // update the user
+        final Users users = getUsers(usersFile);
+        final List<User> userToUpdate = finder.findUsers(users.getUser());
+
+        // update the user
+        updater.updateUsers(userToUpdate);
+
+        // save the users
+        saveUsers(usersFile, restoreFile, users);
+    }
+
+    public static synchronized Users removeUser(final File usersFile, final File restoreFile, final FindUser finder) {
+        // load the users
+        final Users users = getUsers(usersFile);
+        final List<User> userList = users.getUser();
+
+        // find the desired user
+        final User user = finder.findUser(userList);
+        userList.remove(user);
+
+        // save the users
+        saveUsers(usersFile, restoreFile, users);
+
+        return users;
+    }
+
+    public static synchronized Users removeUsers(final File usersFile, final File restoreFile, final FindUsers finder) {
+        // load the users
+        final Users users = getUsers(usersFile);
+        final List<User> userList = users.getUser();
+
+        // find the desired user
+        final List<User> usersToRemove = finder.findUsers(userList);
+        userList.removeAll(usersToRemove);
+
+        // save the users
+        saveUsers(usersFile, restoreFile, users);
+
+        return users;
+    }
+
+    private static synchronized void saveUsers(final File usersFile, final File restoreFile, final Users users) {
+        try {
+            final Marshaller marshaller = JAXB_CONTEXT.createMarshaller();
+            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
+
+            // save users to restore directory before primary directory
+            if (restoreFile != null) {
+                marshaller.marshal(users, restoreFile);
+            }
+
+            // save users to primary directory
+            marshaller.marshal(users, usersFile);
+        } catch (JAXBException e) {
+            throw new AuthorityAccessException(e.getMessage(), e);
+        }
+    }
+
+    public static interface FindUser {
+
+        /**
+         * Finds the desired user. If the user cannot be found throws an UnknownIdentityException. Never returns null.
+         *
+         * @param users the users
+         * @return the desired user
+         * @throws UnknownIdentityException if the user cannot be found
+         */
+        User findUser(List<User> users) throws UnknownIdentityException;
+    }
+
+    public static interface FindUsers {
+
+        /**
+         * Finds the specified users.
+         *
+         * @param users
+         * @return
+         * @throws UnknownIdentityException
+         */
+        List<User> findUsers(List<User> users) throws UnknownIdentityException;
+    }
+
+    public static interface CreateUser {
+
+        /**
+         * Creates the user to add.
+         *
+         * @return the users to add
+         */
+        User createUser();
+    }
+
+    public static interface UpdateUser {
+
+        /**
+         * Updates the specified user.
+         *
+         * @param user the user
+         */
+        void updateUser(User user);
+    }
+
+    public static interface UpdateUsers {
+
+        /**
+         * Updates the specified users.
+         *
+         * @param users the users to update
+         */
+        void updateUsers(List<User> users);
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/852dbea9/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorized-users/src/main/xsd/users.xsd
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorized-users/src/main/xsd/users.xsd b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorized-users/src/main/xsd/users.xsd
new file mode 100644
index 0000000..4ee1e17
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorized-users/src/main/xsd/users.xsd
@@ -0,0 +1,64 @@
+<?xml version="1.0"?>
+<!--
+  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.
+-->
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
+    <!-- role -->
+    <xs:complexType name="Role">
+        <xs:attribute name="name">
+            <xs:simpleType>
+                <xs:restriction base="xs:string">
+                    <xs:enumeration value="ROLE_MONITOR"/>
+                    <xs:enumeration value="ROLE_PROVENANCE"/>
+                    <xs:enumeration value="ROLE_DFM"/>
+                    <xs:enumeration value="ROLE_ADMIN"/>
+                    <xs:enumeration value="ROLE_PROXY"/>
+                    <xs:enumeration value="ROLE_NIFI"/>
+                </xs:restriction>
+            </xs:simpleType>
+        </xs:attribute>
+    </xs:complexType>
+
+    <!-- user -->
+    <xs:complexType name="User">
+        <xs:sequence>
+            <xs:element name="role" type="Role" minOccurs="0" maxOccurs="unbounded"/>
+        </xs:sequence>
+        <xs:attribute name="dn">
+            <xs:simpleType>
+                <xs:restriction base="xs:string">
+                    <xs:minLength value="1"/>
+                    <xs:pattern value=".*[^\s].*"/>
+                </xs:restriction>
+            </xs:simpleType>
+        </xs:attribute>
+        <xs:attribute name="group">
+            <xs:simpleType>
+                <xs:restriction base="xs:string">
+                    <xs:minLength value="1"/>
+                    <xs:pattern value=".*[^\s].*"/>
+                </xs:restriction>
+            </xs:simpleType>
+        </xs:attribute>
+    </xs:complexType>
+
+    <!-- users -->
+    <xs:element name="users">
+        <xs:complexType>
+            <xs:sequence>
+                <xs:element name="user" type="User" minOccurs="0" maxOccurs="unbounded"/>
+            </xs:sequence>
+        </xs:complexType>
+    </xs:element>
+</xs:schema>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/852dbea9/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorization-provider/pom.xml
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorization-provider/pom.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorization-provider/pom.xml
index d014262..c37378a 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorization-provider/pom.xml
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorization-provider/pom.xml
@@ -66,6 +66,10 @@
         </dependency>
         <dependency>
             <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-authorized-users</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
             <artifactId>nifi-utils</artifactId>
         </dependency>
         <dependency>

http://git-wip-us.apache.org/repos/asf/nifi/blob/852dbea9/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorization-provider/src/main/java/org/apache/nifi/authorization/FileAuthorizationProvider.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorization-provider/src/main/java/org/apache/nifi/authorization/FileAuthorizationProvider.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorization-provider/src/main/java/org/apache/nifi/authorization/FileAuthorizationProvider.java
index 9c2cad5..d3e5061 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorization-provider/src/main/java/org/apache/nifi/authorization/FileAuthorizationProvider.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorization-provider/src/main/java/org/apache/nifi/authorization/FileAuthorizationProvider.java
@@ -18,21 +18,12 @@ package org.apache.nifi.authorization;
 
 import java.io.File;
 import java.io.IOException;
-import java.util.Collection;
+import java.util.ArrayList;
 import java.util.EnumSet;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import javax.xml.XMLConstants;
-import javax.xml.bind.JAXBContext;
-import javax.xml.bind.JAXBElement;
-import javax.xml.bind.JAXBException;
-import javax.xml.bind.Marshaller;
-import javax.xml.bind.Unmarshaller;
-import javax.xml.transform.stream.StreamSource;
-import javax.xml.validation.Schema;
-import javax.xml.validation.SchemaFactory;
 import org.apache.nifi.authorization.annotation.AuthorityProviderContext;
 import org.apache.nifi.authorization.exception.AuthorityAccessException;
 import org.apache.nifi.authorization.exception.IdentityAlreadyExistsException;
@@ -42,12 +33,17 @@ import org.apache.nifi.util.file.FileUtils;
 import org.apache.nifi.user.generated.ObjectFactory;
 import org.apache.nifi.user.generated.Role;
 import org.apache.nifi.user.generated.User;
-import org.apache.nifi.user.generated.Users;
 import org.apache.nifi.util.NiFiProperties;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.authorized.users.AuthorizedUsers;
+import org.apache.nifi.authorized.users.AuthorizedUsers.CreateUser;
+import org.apache.nifi.authorized.users.AuthorizedUsers.FindUser;
+import org.apache.nifi.authorized.users.AuthorizedUsers.FindUsers;
+import org.apache.nifi.authorized.users.AuthorizedUsers.UpdateUser;
+import org.apache.nifi.authorized.users.AuthorizedUsers.UpdateUsers;
+import org.apache.nifi.user.generated.Users;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.xml.sax.SAXException;
 
 /**
  * Provides identity checks and grants authorities.
@@ -55,25 +51,10 @@ import org.xml.sax.SAXException;
 public class FileAuthorizationProvider implements AuthorityProvider {
 
     private static final Logger logger = LoggerFactory.getLogger(FileAuthorizationProvider.class);
-    private static final String USERS_XSD = "/users.xsd";
-    private static final String JAXB_GENERATED_PATH = "org.apache.nifi.user.generated";
-    private static final JAXBContext JAXB_CONTEXT = initializeJaxbContext();
-
-    /**
-     * Load the JAXBContext.
-     */
-    private static JAXBContext initializeJaxbContext() {
-        try {
-            return JAXBContext.newInstance(JAXB_GENERATED_PATH, FileAuthorizationProvider.class.getClassLoader());
-        } catch (JAXBException e) {
-            throw new RuntimeException("Unable to create JAXBContext.");
-        }
-    }
 
     private NiFiProperties properties;
     private File usersFile;
     private File restoreUsersFile;
-    private Users users;
     private final Set<String> defaultAuthorities = new HashSet<>();
 
     @Override
@@ -118,22 +99,6 @@ public class FileAuthorizationProvider implements AuthorityProvider {
 
             }
 
-            // load the users from the specified file
-            if (usersFile.exists()) {
-                // find the schema
-                final SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
-                final Schema schema = schemaFactory.newSchema(FileAuthorizationProvider.class.getResource(USERS_XSD));
-
-                // attempt to unmarshal
-                final Unmarshaller unmarshaller = JAXB_CONTEXT.createUnmarshaller();
-                unmarshaller.setSchema(schema);
-                final JAXBElement<Users> element = unmarshaller.unmarshal(new StreamSource(usersFile), Users.class);
-                users = element.getValue();
-            } else {
-                final ObjectFactory objFactory = new ObjectFactory();
-                users = objFactory.createUsers();
-            }
-
             // attempt to load a default roles
             final String rawDefaultAuthorities = configurationContext.getProperty("Default User Roles");
             if (StringUtils.isNotBlank(rawDefaultAuthorities)) {
@@ -157,7 +122,7 @@ public class FileAuthorizationProvider implements AuthorityProvider {
                             StringUtils.join(invalidDefaultAuthorities, ", "), StringUtils.join(Authority.getRawAuthorities(), ", ")));
                 }
             }
-        } catch (IOException | ProviderCreationException | SAXException | JAXBException e) {
+        } catch (IOException | ProviderCreationException e) {
             throw new ProviderCreationException(e);
         }
 
@@ -172,64 +137,61 @@ public class FileAuthorizationProvider implements AuthorityProvider {
     }
 
     @Override
-    public boolean doesDnExist(String dn) throws AuthorityAccessException {
+    public boolean doesDnExist(final String dn) throws AuthorityAccessException {
         if (hasDefaultRoles()) {
             return true;
         }
 
-        final User user = getUser(dn);
-        return user != null;
+        return AuthorizedUsers.getUser(usersFile, new FindUserByDn(dn)) != null;
     }
 
     @Override
-    public synchronized Set<Authority> getAuthorities(String dn) throws UnknownIdentityException, AuthorityAccessException {
+    public Set<Authority> getAuthorities(final String dn) throws UnknownIdentityException, AuthorityAccessException {
         final Set<Authority> authorities = EnumSet.noneOf(Authority.class);
 
         // get the user
-        final User user = getUser(dn);
-
-        // ensure the user was located
-        if (user == null) {
-            if (hasDefaultRoles()) {
-                logger.debug(String.format("User DN not found: %s. Creating new user with default roles.", dn));
-
-                // create the user (which will automatically add any default authorities)
-                addUser(dn, null);
+        final User user = AuthorizedUsers.getUser(usersFile, new FindUser() {
+            @Override
+            public User findUser(List<User> users) {
+                final FindUser byDn = new FindUserByDn(dn);
+                User user = byDn.findUser(users);
+
+                // if the user is not found, add them and locate them
+                if (user == null) {
+                    if (hasDefaultRoles()) {
+                        logger.debug(String.format("User DN not found: %s. Creating new user with default roles.", dn));
+
+                        // create the user (which will automatically add any default authorities)
+                        addUser(dn, null);
+
+                        // find the user that was just added
+                        user = byDn.findUser(users);
+                    } else {
+                        throw new UnknownIdentityException(String.format("User DN not found: %s.", dn));
+                    }
+                }
 
-                // get the authorities for the newly created user
-                authorities.addAll(getAuthorities(dn));
-            } else {
-                throw new UnknownIdentityException(String.format("User DN not found: %s.", dn));
-            }
-        } else {
-            // create the authorities that this user has
-            for (final Role role : user.getRole()) {
-                authorities.add(Authority.valueOfAuthority(role.getName()));
+                return user;
             }
+        });
+
+        // create the authorities that this user has
+        for (final Role role : user.getRole()) {
+            authorities.add(Authority.valueOfAuthority(role.getName()));
         }
 
         return authorities;
     }
 
     @Override
-    public synchronized void setAuthorities(String dn, Set<Authority> authorities) throws UnknownIdentityException, AuthorityAccessException {
-        // get the user
-        final User user = getUser(dn);
-
-        // ensure the user was located
-        if (user == null) {
-            throw new UnknownIdentityException(String.format("User DN not found: %s.", dn));
-        }
-
-        // add the user authorities
-        setUserAuthorities(user, authorities);
-
-        try {
-            // save the file
-            save();
-        } catch (Exception e) {
-            throw new AuthorityAccessException(e.getMessage(), e);
-        }
+    public void setAuthorities(final String dn, final Set<Authority> authorities) throws UnknownIdentityException, AuthorityAccessException {
+        AuthorizedUsers.updateUser(usersFile, restoreUsersFile, new FindUserByDn(dn), new UpdateUser() {
+            @Override
+            public void updateUser(User user) {
+                // add the user authorities
+                setUserAuthorities(user, authorities);
+            }
+        });
     }
 
     private void setUserAuthorities(final User user, final Set<Authority> authorities) {
@@ -248,46 +210,45 @@ public class FileAuthorizationProvider implements AuthorityProvider {
     }
 
     @Override
-    public synchronized void addUser(String dn, String group) throws IdentityAlreadyExistsException, AuthorityAccessException {
-        final User user = getUser(dn);
+    public void addUser(final String dn, final String group) throws IdentityAlreadyExistsException, AuthorityAccessException {
+        AuthorizedUsers.createUser(usersFile, restoreUsersFile, new CreateUser() {
+            @Override
+            public User createUser() {
+                final User user = AuthorizedUsers.getUser(usersFile, new FindUserByDn(dn));
+
+                // ensure the user doesn't already exist
+                if (user != null) {
+                    throw new IdentityAlreadyExistsException(String.format("User DN already exists: %s", dn));
+                }
 
-        // ensure the user doesn't already exist
-        if (user != null) {
-            throw new IdentityAlreadyExistsException(String.format("User DN already exists: %s", dn));
-        }
+                // create the new user
+                final ObjectFactory objFactory = new ObjectFactory();
+                final User newUser = objFactory.createUser();
 
-        // create the new user
-        final ObjectFactory objFactory = new ObjectFactory();
-        final User newUser = objFactory.createUser();
+                // set the user properties
+                newUser.setDn(dn);
+                newUser.setGroup(group);
 
-        // set the user properties
-        newUser.setDn(dn);
-        newUser.setGroup(group);
+                // add default roles if appropriate
+                if (hasDefaultRoles()) {
+                    for (final String authority : defaultAuthorities) {
+                        Role role = objFactory.createRole();
+                        role.setName(authority);
 
-        // add default roles if appropriate
-        if (hasDefaultRoles()) {
-            for (final String authority : defaultAuthorities) {
-                Role role = objFactory.createRole();
-                role.setName(authority);
+                        // add the role
+                        newUser.getRole().add(role);
+                    }
+                }
 
-                // add the role
-                newUser.getRole().add(role);
+                return newUser;
             }
-        }
-
-        // add the user
-        users.getUser().add(newUser);
-
-        try {
-            // save the file
-            save();
-        } catch (Exception e) {
-            throw new AuthorityAccessException(e.getMessage(), e);
-        }
+        });
     }
 
     @Override
-    public synchronized Set<String> getUsers(Authority authority) throws AuthorityAccessException {
+    public Set<String> getUsers(final Authority authority) throws AuthorityAccessException {
+        final Users users = AuthorizedUsers.getUsers(usersFile);
+
         final Set<String> userSet = new HashSet<>();
         for (final User user : users.getUser()) {
             for (final Role role : user.getRole()) {
@@ -296,138 +257,61 @@ public class FileAuthorizationProvider implements AuthorityProvider {
                 }
             }
         }
+
         return userSet;
     }
 
     @Override
-    public synchronized void revokeUser(String dn) throws UnknownIdentityException, AuthorityAccessException {
-        // get the user
-        final User user = getUser(dn);
-
-        // ensure the user was located
-        if (user == null) {
-            throw new UnknownIdentityException(String.format("User DN not found: %s.", dn));
-        }
-
-        // remove the specified user
-        users.getUser().remove(user);
-
-        try {
-            // save the file
-            save();
-        } catch (Exception e) {
-            throw new AuthorityAccessException(e.getMessage(), e);
-        }
+    public void revokeUser(final String dn) throws UnknownIdentityException, AuthorityAccessException {
+        AuthorizedUsers.removeUser(usersFile, restoreUsersFile, new FindUserByDn(dn));
     }
 
     @Override
-    public void setUsersGroup(Set<String> dns, String group) throws UnknownIdentityException, AuthorityAccessException {
-        final Collection<User> groupedUsers = new HashSet<>();
-
-        // get the specified users
-        for (final String dn : dns) {
-            // get the user
-            final User user = getUser(dn);
-
-            // ensure the user was located
-            if (user == null) {
-                throw new UnknownIdentityException(String.format("User DN not found: %s.", dn));
+    public void setUsersGroup(final Set<String> dns, final String group) throws UnknownIdentityException, AuthorityAccessException {
+        AuthorizedUsers.updateUsers(usersFile, restoreUsersFile, new FindUsersByDn(dns), new UpdateUsers() {
+            @Override
+            public void updateUsers(List<User> users) {
+                // update each user group
+                for (final User user : users) {
+                    user.setGroup(group);
+                }
             }
-
-            groupedUsers.add(user);
-        }
-
-        // update each user group
-        for (final User user : groupedUsers) {
-            user.setGroup(group);
-        }
-
-        try {
-            // save the file
-            save();
-        } catch (Exception e) {
-            throw new AuthorityAccessException(e.getMessage(), e);
-        }
+        });
     }
 
     @Override
     public void ungroupUser(String dn) throws UnknownIdentityException, AuthorityAccessException {
-        // get the user
-        final User user = getUser(dn);
-
-        // ensure the user was located
-        if (user == null) {
-            throw new UnknownIdentityException(String.format("User DN not found: %s.", dn));
-        }
-
-        // remove the users group
-        user.setGroup(null);
-
-        try {
-            // save the file
-            save();
-        } catch (Exception e) {
-            throw new AuthorityAccessException(e.getMessage(), e);
-        }
+        AuthorizedUsers.updateUser(usersFile, restoreUsersFile, new FindUserByDn(dn), new UpdateUser() {
+            @Override
+            public void updateUser(User user) {
+                // remove the users group
+                user.setGroup(null);
+            }
+        });
     }
 
     @Override
-    public void ungroup(String group) throws AuthorityAccessException {
-        // get the user group
-        final Collection<User> userGroup = getUserGroup(group);
-
-        // ensure the user group was located
-        if (userGroup == null) {
-            return;
-        }
-
-        // update each user group
-        for (final User user : userGroup) {
-            user.setGroup(null);
-        }
-
-        try {
-            // save the file
-            save();
-        } catch (Exception e) {
-            throw new AuthorityAccessException(e.getMessage(), e);
-        }
+    public void ungroup(final String group) throws AuthorityAccessException {
+        AuthorizedUsers.updateUsers(usersFile, restoreUsersFile, new FindUsersByGroup(group), new UpdateUsers() {
+            @Override
+            public void updateUsers(List<User> users) {
+                // update each user group
+                for (final User user : users) {
+                    user.setGroup(null);
+                }
+            }
+        });
     }
 
     @Override
-    public String getGroupForUser(String dn) throws UnknownIdentityException, AuthorityAccessException {
-        // get the user
-        final User user = getUser(dn);
-
-        // ensure the user was located
-        if (user == null) {
-            throw new UnknownIdentityException(String.format("User DN not found: %s.", dn));
-        }
-
+    public String getGroupForUser(final String dn) throws UnknownIdentityException, AuthorityAccessException {
+        final User user = AuthorizedUsers.getUser(usersFile, new FindUserByDn(dn));
         return user.getGroup();
     }
 
     @Override
     public void revokeGroup(String group) throws UnknownIdentityException, AuthorityAccessException {
-        // get the user group
-        final Collection<User> userGroup = getUserGroup(group);
-
-        // ensure the user group was located
-        if (userGroup == null) {
-            throw new UnknownIdentityException(String.format("User group not found: %s.", group));
-        }
-
-        // remove each user in the group
-        for (final User user : userGroup) {
-            users.getUser().remove(user);
-        }
-
-        try {
-            // save the file
-            save();
-        } catch (Exception e) {
-            throw new AuthorityAccessException(e.getMessage(), e);
-        }
+        AuthorizedUsers.removeUsers(usersFile, restoreUsersFile, new FindUsersByGroup(group));
     }
 
     /**
@@ -438,59 +322,107 @@ public class FileAuthorizationProvider implements AuthorityProvider {
         return DownloadAuthorization.approved();
     }
 
-    private User getUser(String dn) throws UnknownIdentityException {
-        // ensure the DN was specified
-        if (dn == null) {
-            throw new UnknownIdentityException("User DN not specified.");
-        }
+    @AuthorityProviderContext
+    public void setNiFiProperties(NiFiProperties properties) {
+        this.properties = properties;
+    }
 
-        // attempt to get the user and ensure it was located
-        User desiredUser = null;
-        for (final User user : users.getUser()) {
-            if (dn.equalsIgnoreCase(user.getDn())) {
-                desiredUser = user;
-                break;
+    public static class FindUserByDn implements FindUser {
+
+        private final String dn;
+
+        public FindUserByDn(String dn) {
+            // ensure the DN was specified
+            if (dn == null) {
+                throw new UnknownIdentityException("User DN not specified.");
             }
+
+            this.dn = dn;
         }
 
-        return desiredUser;
+        @Override
+        public User findUser(List<User> users) {
+            // attempt to get the user and ensure it was located
+            User desiredUser = null;
+            for (final User user : users) {
+                if (dn.equalsIgnoreCase(user.getDn())) {
+                    desiredUser = user;
+                    break;
+                }
+            }
+
+            if (desiredUser == null) {
+                throw new UnknownIdentityException(String.format("User DN not found: %s.", dn));
+            }
+
+            return desiredUser;
+        }
     }
 
-    private Collection<User> getUserGroup(String group) throws UnknownIdentityException {
-        // ensure the DN was specified
-        if (group == null) {
-            throw new UnknownIdentityException("User group not specified.");
+    public static class FindUsersByGroup implements FindUsers {
+
+        private final String group;
+
+        public FindUsersByGroup(String group) {
+            // ensure the group was specified
+            if (group == null) {
+                throw new UnknownIdentityException("User group not specified.");
+            }
+
+            this.group = group;
         }
 
-        // get all users with this group
-        Collection<User> userGroup = null;
-        for (final User user : users.getUser()) {
-            if (group.equals(user.getGroup())) {
-                if (userGroup == null) {
-                    userGroup = new HashSet<>();
+        @Override
+        public List<User> findUsers(List<User> users) throws UnknownIdentityException {
+            // get all users with this group
+            List<User> userGroup = new ArrayList<>();
+            for (final User user : users) {
+                if (group.equals(user.getGroup())) {
+                    userGroup.add(user);
                 }
-                userGroup.add(user);
             }
-        }
 
-        return userGroup;
+            // ensure the user group was located
+            if (userGroup.isEmpty()) {
+                throw new UnknownIdentityException(String.format("User group not found: %s.", group));
+            }
+
+            return userGroup;
+        }
     }
 
-    private void save() throws Exception {
-        final Marshaller marshaller = JAXB_CONTEXT.createMarshaller();
-        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
+    public static class FindUsersByDn implements FindUsers {
+
+        private final Set<String> dns;
+
+        public FindUsersByDn(Set<String> dns) {
+            // ensure the group was specified
+            if (dns == null) {
+                throw new UnknownIdentityException("User group not specified.");
+            }
 
-        // save users to restore directory before primary directory
-        if (restoreUsersFile != null) {
-            marshaller.marshal(users, restoreUsersFile);
+            this.dns = dns;
         }
 
-        // save users to primary directory
-        marshaller.marshal(users, usersFile);
-    }
+        @Override
+        public List<User> findUsers(List<User> users) throws UnknownIdentityException {
+            final Set<String> copy = new HashSet<>(dns);
 
-    @AuthorityProviderContext
-    public void setNiFiProperties(NiFiProperties properties) {
-        this.properties = properties;
+            // get all users with this group
+            List<User> userList = new ArrayList<>();
+            for (final User user : users) {
+                if (copy.contains(user.getDn())) {
+                    copy.remove(user.getDn());
+                    userList.add(user);
+                }
+            }
+
+            if (!copy.isEmpty()) {
+                throw new UnknownIdentityException("Unable to find users with DNs: " + StringUtils.join(copy, ", "));
+            }
+
+            return userList;
+        }
     }
+
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/852dbea9/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/pom.xml
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/pom.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/pom.xml
index a74dfb0..1ba5dab 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/pom.xml
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/pom.xml
@@ -146,6 +146,10 @@
         </dependency>
         <dependency>
             <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-login-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
             <artifactId>nifi-nar-utils</artifactId>
         </dependency>
         <dependency>

http://git-wip-us.apache.org/repos/asf/nifi/blob/852dbea9/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiWebApiSecurityConfiguration.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiWebApiSecurityConfiguration.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiWebApiSecurityConfiguration.java
index 0317f19..7341545 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiWebApiSecurityConfiguration.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiWebApiSecurityConfiguration.java
@@ -16,11 +16,17 @@
  */
 package org.apache.nifi.web;
 
+import javax.servlet.Filter;
 import org.apache.nifi.admin.service.UserService;
+import org.apache.nifi.authentication.LoginIdentityProvider;
 import org.apache.nifi.util.NiFiProperties;
 import org.apache.nifi.web.security.NiFiAuthenticationProvider;
 import org.apache.nifi.web.security.anonymous.NiFiAnonymousUserFilter;
 import org.apache.nifi.web.security.NiFiAuthenticationEntryPoint;
+import org.apache.nifi.web.security.form.FormAuthenticationFilter;
+import org.apache.nifi.web.security.jwt.JwtAuthenticationFilter;
+import org.apache.nifi.web.security.jwt.JwtAuthenticationProvider;
+import org.apache.nifi.web.security.jwt.JwtService;
 import org.apache.nifi.web.security.node.NodeAuthorizedUserFilter;
 import org.apache.nifi.web.security.x509.SubjectDnX509PrincipalExtractor;
 import org.apache.nifi.web.security.x509.X509AuthenticationFilter;
@@ -40,6 +46,7 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur
 import org.springframework.security.config.http.SessionCreationPolicy;
 import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
 import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
 
 /**
  * NiFi Web Api Spring security
@@ -52,6 +59,8 @@ public class NiFiWebApiSecurityConfiguration extends WebSecurityConfigurerAdapte
     private NiFiProperties properties;
     private UserService userService;
     private AuthenticationUserDetailsService userDetailsService;
+    private JwtService jwtService;
+    private LoginIdentityProvider loginIdentityProvider;
 
     public NiFiWebApiSecurityConfiguration() {
         super(true); // disable defaults
@@ -61,7 +70,7 @@ public class NiFiWebApiSecurityConfiguration extends WebSecurityConfigurerAdapte
     protected void configure(HttpSecurity http) throws Exception {
         http
                 .rememberMe().disable()
-                    .exceptionHandling()
+                .exceptionHandling()
                     .authenticationEntryPoint(new NiFiAuthenticationEntryPoint())
                     .and()
                 .authorizeRequests()
@@ -70,13 +79,26 @@ public class NiFiWebApiSecurityConfiguration extends WebSecurityConfigurerAdapte
                 .sessionManagement()
                     .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
 
-        // cluster - authorized user
-        final NodeAuthorizedUserFilter authorizedUserFilter = new NodeAuthorizedUserFilter(properties);
-        http.addFilterBefore(authorizedUserFilter, AnonymousAuthenticationFilter.class);
+        // verify that login authentication is enabled
+        if (loginIdentityProvider != null) {
+            // login authentication for /token - exchanges for JWT for subsequent API usage
+            http.addFilterBefore(buildFormLoginFilter("/token"), UsernamePasswordAuthenticationFilter.class);
+
+            // login registration
+            if (loginIdentityProvider.supportsRegistration()) {
+                http.addFilterBefore(buildRegistrationFilter("/registration"), UsernamePasswordAuthenticationFilter.class);
+            }
+        }
+        
+        // cluster authorized user
+        http.addFilterBefore(buildNodeAuthorizedUserFilter(), AnonymousAuthenticationFilter.class);
 
         // x509
         http.addFilterBefore(buildX509Filter(), AnonymousAuthenticationFilter.class);
 
+        // jwt
+        http.addFilterBefore(buildJwtFilter(), AnonymousAuthenticationFilter.class);
+        
         // anonymous
         http.anonymous().authenticationFilter(buildAnonymousFilter());
     }
@@ -90,11 +112,37 @@ public class NiFiWebApiSecurityConfiguration extends WebSecurityConfigurerAdapte
 
     @Override
     protected void configure(AuthenticationManagerBuilder auth) throws Exception {
-        // x509
+        // TODO - dont think we need different authenticationproviders...
         final AuthenticationProvider x509AuthenticationProvider = new NiFiAuthenticationProvider(new X509AuthenticationProvider(), userDetailsService);
-
+        final AuthenticationProvider jwtAuthenticationProvider = new NiFiAuthenticationProvider(new JwtAuthenticationProvider(), userDetailsService);
+        
         auth
-                .authenticationProvider(x509AuthenticationProvider);
+                .authenticationProvider(x509AuthenticationProvider)
+                .authenticationProvider(jwtAuthenticationProvider)
+                .inMemoryAuthentication()
+                    .withUser("user").password("password").roles("USER");
+    }
+
+    private FormAuthenticationFilter buildFormLoginFilter(final String url) {
+        final FormAuthenticationFilter loginFilter = new FormAuthenticationFilter(url);
+        loginFilter.setJwtService(jwtService);
+        loginFilter.setLoginIdentityProvider(loginIdentityProvider);
+        return loginFilter;
+    }
+
+    private Filter buildRegistrationFilter(final String url) {
+        return null;
+    }
+
+    private NodeAuthorizedUserFilter buildNodeAuthorizedUserFilter() {
+        return new NodeAuthorizedUserFilter(properties);
+    }
+    
+    private JwtAuthenticationFilter buildJwtFilter() throws Exception {
+        final JwtAuthenticationFilter jwtFilter = new JwtAuthenticationFilter();
+        jwtFilter.setJwtService(jwtService);
+        jwtFilter.setAuthenticationManager(authenticationManager());
+        return jwtFilter;
     }
 
     private X509AuthenticationFilter buildX509Filter() throws Exception {
@@ -111,7 +159,7 @@ public class NiFiWebApiSecurityConfiguration extends WebSecurityConfigurerAdapte
         anonymousFilter.setUserService(userService);
         return anonymousFilter;
     }
-
+    
     @Autowired
     public void setUserDetailsService(AuthenticationUserDetailsService userDetailsService) {
         this.userDetailsService = userDetailsService;
@@ -127,4 +175,13 @@ public class NiFiWebApiSecurityConfiguration extends WebSecurityConfigurerAdapte
         this.properties = properties;
     }
 
+    @Autowired
+    public void setJwtService(JwtService jwtService) {
+        this.jwtService = jwtService;
+    }
+
+    @Autowired
+    public void setLoginIdentityProvider(LoginIdentityProvider loginIdentityProvider) {
+        this.loginIdentityProvider = loginIdentityProvider;
+    }
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/852dbea9/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/pom.xml
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/pom.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/pom.xml
index 59d81e1..4956095 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/pom.xml
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/pom.xml
@@ -33,6 +33,10 @@
         </dependency>
         <dependency>
             <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-login-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
             <artifactId>nifi-web-utils</artifactId>
         </dependency>
         <dependency>

http://git-wip-us.apache.org/repos/asf/nifi/blob/852dbea9/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/form/FormAuthenticationFilter.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/form/FormAuthenticationFilter.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/form/FormAuthenticationFilter.java
index 46e74f3..e60c7d1 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/form/FormAuthenticationFilter.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/form/FormAuthenticationFilter.java
@@ -22,6 +22,7 @@ import javax.servlet.FilterChain;
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
+import org.apache.nifi.authentication.LoginIdentityProvider;
 import org.apache.nifi.web.security.jwt.JwtService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -31,22 +32,31 @@ import org.springframework.security.core.AuthenticationException;
 import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
 
 /**
+ * Exchanges a successful login with the configured provider for a ID token for accessing the API.
  */
 public class FormAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
 
     private static final Logger logger = LoggerFactory.getLogger(FormAuthenticationFilter.class);
 
     private JwtService jwtService;
+    private LoginIdentityProvider loginIdentityProvider;
 
     public FormAuthenticationFilter(final String defaultFilterProcessesUrl) {
         super(defaultFilterProcessesUrl);
+        
+        // do not continue filter chain... simply exchaning authentication for token
+        setContinueChainBeforeSuccessfulAuthentication(false);
     }
 
     @Override
     public Authentication attemptAuthentication(final HttpServletRequest request, final HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
-        final String username = request.getParameter("username");
-        final String password = request.getParameter("password");
-        return getAuthenticationManager().authenticate(new UsernamePasswordAuthenticationToken(username, password));
+        final String principal = loginIdentityProvider.authenticate(request);
+        
+        if (principal == null) {
+            return null;
+        }
+        
+        return new UsernamePasswordAuthenticationToken(principal, null);
     }
 
     @Override
@@ -71,4 +81,8 @@ public class FormAuthenticationFilter extends AbstractAuthenticationProcessingFi
         this.jwtService = jwtService;
     }
 
+    public void setLoginIdentityProvider(LoginIdentityProvider loginIdentityProvider) {
+        this.loginIdentityProvider = loginIdentityProvider;
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/852dbea9/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..7a09f93
--- /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,73 @@
+/*
+ * 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 javax.servlet.http.HttpServletRequest;
+import org.apache.nifi.authentication.LoginIdentityProvider;
+import org.apache.nifi.util.NiFiProperties;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.FactoryBean;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+
+/**
+ *
+ */
+public class LoginIdentityProviderFactoryBean implements FactoryBean, ApplicationContextAware {
+
+    private ApplicationContext context;
+    private NiFiProperties properties;
+    private LoginIdentityProvider provider;
+
+    @Override
+    public Object getObject() throws Exception {
+//        if (provider == null) {
+//            provider = new LoginIdentityProvider() {
+//                @Override
+//                public boolean supportsRegistration() {
+//                    return false;
+//                }
+//
+//                @Override
+//                public String authenticate(HttpServletRequest request) {
+//                    return "Yo!";
+//                }
+//            };
+//        }
+
+        return provider;
+    }
+
+    @Override
+    public Class getObjectType() {
+        return LoginIdentityProvider.class;
+    }
+
+    @Override
+    public boolean isSingleton() {
+        return true;
+    }
+
+    public void setProperties(NiFiProperties properties) {
+        this.properties = properties;
+    }
+
+    @Override
+    public void setApplicationContext(ApplicationContext context) throws BeansException {
+        this.context = context;
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/852dbea9/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
index 41cc0c0..6fee4ec 100644
--- 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
@@ -19,7 +19,7 @@ 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 request authentication in order to submit a new account request.
+ * 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 {
 

http://git-wip-us.apache.org/repos/asf/nifi/blob/852dbea9/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/resources/nifi-web-security-context.xml
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/resources/nifi-web-security-context.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/resources/nifi-web-security-context.xml
index b201364..5f4e1b2 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/resources/nifi-web-security-context.xml
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/resources/nifi-web-security-context.xml
@@ -63,6 +63,11 @@
     <!-- jwt service -->
     <bean id="jwtService" class="org.apache.nifi.web.security.jwt.JwtService"></bean>
     
+    <!-- login identity provider -->
+    <bean id="loginIdentityProvider" class="org.apache.nifi.web.security.spring.LoginIdentityProviderFactoryBean">
+        <property name="properties" ref="nifiProperties"/>
+    </bean>
+    
     <!-- performs ocsp certificate validation -->
 <!--    <bean id="ocspCertificateValidator" class="org.apache.nifi.web.security.x509.ocsp.OcspCertificateValidator">
         <constructor-arg ref="nifiProperties"/>

http://git-wip-us.apache.org/repos/asf/nifi/blob/852dbea9/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/pom.xml
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/pom.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/pom.xml
index b6f3f9c..61d1b17 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/pom.xml
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/pom.xml
@@ -40,6 +40,7 @@
         <module>nifi-web</module>
         <module>nifi-resources</module>
         <module>nifi-documentation</module>
+        <module>nifi-authorized-users</module>
     </modules>
     <dependencies>
         <dependency>

http://git-wip-us.apache.org/repos/asf/nifi/blob/852dbea9/nifi-nar-bundles/nifi-framework-bundle/pom.xml
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/pom.xml b/nifi-nar-bundles/nifi-framework-bundle/pom.xml
index f78e497..3f90b49 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/pom.xml
+++ b/nifi-nar-bundles/nifi-framework-bundle/pom.xml
@@ -60,6 +60,11 @@
             </dependency>
             <dependency>
                 <groupId>org.apache.nifi</groupId>
+                <artifactId>nifi-authorized-users</artifactId>
+                <version>0.3.1-SNAPSHOT</version>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.nifi</groupId>
                 <artifactId>nifi-client-dto</artifactId>
                 <version>0.3.1-SNAPSHOT</version>
             </dependency>

http://git-wip-us.apache.org/repos/asf/nifi/blob/852dbea9/nifi-nar-bundles/nifi-jetty-bundle/pom.xml
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-jetty-bundle/pom.xml b/nifi-nar-bundles/nifi-jetty-bundle/pom.xml
index 3cdb3b8..287cd4d 100644
--- a/nifi-nar-bundles/nifi-jetty-bundle/pom.xml
+++ b/nifi-nar-bundles/nifi-jetty-bundle/pom.xml
@@ -73,5 +73,11 @@
             <artifactId>jetty-jsp-jdt</artifactId>
             <scope>compile</scope>
         </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-login-api</artifactId>
+            <version>0.3.1-SNAPSHOT</version>
+            <scope>compile</scope>
+        </dependency>
     </dependencies>
 </project>

http://git-wip-us.apache.org/repos/asf/nifi/blob/852dbea9/nifi-nar-bundles/pom.xml
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/pom.xml b/nifi-nar-bundles/pom.xml
index 841818a..fb24aa8 100644
--- a/nifi-nar-bundles/pom.xml
+++ b/nifi-nar-bundles/pom.xml
@@ -113,6 +113,12 @@
             </dependency>
             <dependency>
                 <groupId>org.apache.nifi</groupId>
+                <artifactId>nifi-login-api</artifactId>
+                <version>0.3.1-SNAPSHOT</version>
+                <scope>provided</scope>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.nifi</groupId>
                 <artifactId>nifi-runtime</artifactId>
                 <version>0.3.1-SNAPSHOT</version>
                 <scope>provided</scope>

http://git-wip-us.apache.org/repos/asf/nifi/blob/852dbea9/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 94a3e96..b32583e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -32,6 +32,7 @@
         <module>nifi-docs</module>
         <module>nifi-maven-archetypes</module>
         <module>nifi-external</module>
+        <module>nifi-login-api</module>
     </modules>
     <url>http://nifi.apache.org</url>
     <organization>
@@ -687,6 +688,11 @@
             </dependency>
             <dependency>
                 <groupId>org.apache.nifi</groupId>
+                <artifactId>nifi-login-api</artifactId>
+                <version>0.3.1-SNAPSHOT</version>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.nifi</groupId>
                 <artifactId>nifi-utils</artifactId>
                 <version>0.3.1-SNAPSHOT</version>
             </dependency>