You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@netbeans.apache.org by ma...@apache.org on 2019/11/22 20:39:18 UTC

[netbeans-tools] branch master updated: Implement OAuth2/OpenID based/inspired login flows

This is an automated email from the ASF dual-hosted git repository.

matthiasblaesing pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/netbeans-tools.git


The following commit(s) were added to refs/heads/master by this push:
     new 7509031  Implement OAuth2/OpenID based/inspired login flows
     new 2a1ed5a  Merge pull request #15 from matthiasblaesing/pp3-oauth-login
7509031 is described below

commit 7509031320a082243b96c38cf40cd27535c7a7a8
Author: Matthias Bläsing <mb...@doppel-helix.eu>
AuthorDate: Sat Oct 26 20:40:26 2019 +0200

    Implement OAuth2/OpenID based/inspired login flows
    
    - Implemented frontend and backend implementation for OAuth2 Code flow
      (or similar implementations)
    - Added sample configuration for the currently supported identity
      providers: GitHub, Google and Amazon.
    - Don't use email as user id, but introduce user table, that maps
      authentication to email/name
    - Add bootstrap configuration, that does not swallow exceptions, but
      logs them to the Apache error log
    - Updated composer.json to project data and not generic ZendFrame work
      information
    - Removed google sign-in library
    - Removed image files copied from ZendFramework (unsed)
---
 pp3/composer.json                                  |  17 +-
 pp3/config/pp3.sql                                 | 108 ++------
 pp3/module/Application/Module.php                  |  19 +-
 .../Application/config/module.config.php.dist      |  33 ++-
 .../src/Application/Controller/AdminController.php |  83 ++++--
 .../Controller/AuthenticatedController.php         |  22 ++
 .../src/Application/Controller/BaseController.php  |  42 ++-
 .../src/Application/Controller/IndexController.php |   6 +-
 .../src/Application/Controller/LoginController.php | 302 ++++++++++++++++++---
 .../Application/Controller/PluginController.php    |  44 ++-
 .../Controller/PluginVersionController.php         |  11 +-
 .../Controller/VerificationController.php          |  33 ++-
 .../src/Application/Entity/Base/Plugin.php         |  10 +-
 .../src/Application/Entity/Base/User.php           | 134 +++++++++
 .../src/Application/Entity/Base/Verification.php   |   3 +
 .../Entity/Base/VerificationRequest.php            |   2 +-
 .../src/Application/Entity/Base/Verifier.php       |  50 ----
 .../Application/src/Application/Entity/Plugin.php  |   7 +-
 .../Application/Entity/{Verifier.php => User.php}  |   4 +-
 .../src/Application/Entity/Verification.php        |   7 +-
 .../src/Application/Entity/VerificationRequest.php |   5 +-
 .../Application/Factory/AdminControllerFactory.php |  16 +-
 .../Application/Factory/LoginControllerFactory.php |   8 +-
 .../Factory/PluginControllerFactory.php            |   8 +-
 .../Factory/VerificationControllerFactory.php      |   8 +-
 .../Application/src/Application/Pp/Catalog.php     |   2 +-
 .../Application/Repository/NbVersionRepository.php |   5 +-
 .../Application/Repository/PluginRepository.php    |  26 +-
 .../src/Application/Repository/UserRepository.php  |  64 +++++
 .../Repository/VerificationRequestRepository.php   |   2 +-
 .../Application/Repository/VerifierRepository.php  |  23 --
 .../view/application/admin/_plugin-listrow.phtml   |   9 +-
 .../view/application/admin/_pluginRowItem.phtml    |   2 +-
 .../view/application/admin/verifiers.phtml         |  83 +++++-
 .../view/application/index/catalogue.phtml         |   2 +-
 .../Application/view/application/index/index.phtml |   6 +-
 .../Application/view/application/login/index.phtml |  33 +--
 .../view/application/plugin/_plugin-form.phtml     |   4 -
 pp3/module/Application/view/layout/layout.phtml    |  48 ++--
 .../view/partials/_categories-select.phtml         |   2 +
 pp3/public/img/favicon.ico                         | Bin 1406 -> 0 bytes
 pp3/public/img/login/amazon.svg                    |  30 ++
 pp3/public/img/login/github.svg                    |  30 ++
 pp3/public/img/login/google.svg                    |  30 ++
 pp3/public/img/zf2-logo.png                        | Bin 738 -> 0 bytes
 pp3/public/js/script.js                            |  54 ----
 pp3/public/scss/style.css                          |  69 ++---
 pp3/public/scss/style.css.map                      |  14 +-
 pp3/public/scss/style.scss                         |  24 ++
 49 files changed, 1026 insertions(+), 518 deletions(-)

diff --git a/pp3/composer.json b/pp3/composer.json
index 6eb73f0..4cfd5f8 100755
--- a/pp3/composer.json
+++ b/pp3/composer.json
@@ -1,23 +1,22 @@
 {
-    "name": "zendframework/skeleton-application",
-    "description": "Skeleton Application for ZF2",
+    "name": "apache/netbeans-pp3",
+    "description": "NetBeans Plugin Portal 3",
     "license": "BSD-3-Clause",
     "keywords": [
-        "framework",
-        "zf2"
+        "apache",
+        "netbeans",
+        "pluginportal"
     ],
     "config": {
         "discard-changes": true
-    },  
-    "homepage": "http://framework.zend.com/",
+    },
+    "homepage": "https://github.com/apache/netbeans-tools/pp3",
     "require": {
         "php": ">=5.5",
         "zendframework/zendframework": "~2.5",
         "zendframework/zend-developer-tools": "dev-master",
         "doctrine/doctrine-orm-module": "^0.9.1",
         "ezyang/htmlpurifier": "^4.10",
-        "zendframework/zendoauth": "2.0.*",
-        "knplabs/knp-paginator-bundle": "^3.0",
-        "google/apiclient": "^2.4"
+        "knplabs/knp-paginator-bundle": "^3.0"
     }
 }
diff --git a/pp3/config/pp3.sql b/pp3/config/pp3.sql
index 03fdfd9..77b3b1c 100644
--- a/pp3/config/pp3.sql
+++ b/pp3/config/pp3.sql
@@ -18,15 +18,17 @@ SET time_zone = "+00:00";
 /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
 /*!40101 SET NAMES utf8mb4 */;
 
---
--- Databáze: `pp3`
---
-
--- --------------------------------------------------------
-
---
--- Struktura tabulky `category`
---
+CREATE TABLE IF NOT EXISTS `user` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `idp_provider_id` varchar(20) COLLATE utf8_czech_ci NOT NULL,
+  `idp_user_id` varchar(100) COLLATE utf8_czech_ci NOT NULL,
+  `email` varchar(255) COLLATE utf8_czech_ci NOT NULL,
+  `name` varchar(255) COLLATE utf8_czech_ci NOT NULL,
+  `admin` boolean DEFAULT false NOT NULL,
+  `verifier` boolean DEFAULT false NOT NULL,
+  PRIMARY KEY (`id`),
+  UNIQUE KEY(idp_provider_id, idp_user_id)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_czech_ci;
 
 CREATE TABLE IF NOT EXISTS `category` (
   `id` int(11) NOT NULL AUTO_INCREMENT,
@@ -34,12 +36,6 @@ CREATE TABLE IF NOT EXISTS `category` (
   PRIMARY KEY (`id`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_czech_ci;
 
--- --------------------------------------------------------
-
---
--- Struktura tabulky `nb_version`
---
-
 CREATE TABLE IF NOT EXISTS `nb_version` (
   `id` int(11) NOT NULL AUTO_INCREMENT,
   `version` varchar(255) COLLATE utf8_czech_ci NOT NULL,
@@ -47,32 +43,12 @@ CREATE TABLE IF NOT EXISTS `nb_version` (
   PRIMARY KEY (`id`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_czech_ci;
 
--- --------------------------------------------------------
-
---
--- Struktura tabulky `nb_version_plugin_version`
---
-
-CREATE TABLE IF NOT EXISTS `nb_version_plugin_version` (
-  `id` int(11) NOT NULL AUTO_INCREMENT,
-  `nb_version_id` int(11) NOT NULL,
-  `plugin_version_id` int(11) NOT NULL,
-  `verification_id` int(11) DEFAULT NULL,
-  PRIMARY KEY (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_czech_ci;
-
--- --------------------------------------------------------
-
---
--- Struktura tabulky `plugin`
---
-
 CREATE TABLE IF NOT EXISTS `plugin` (
   `id` int(11) NOT NULL AUTO_INCREMENT,
   `name` varchar(255) COLLATE utf8_czech_ci NOT NULL,
   `artifactid` varchar(255) COLLATE utf8_czech_ci DEFAULT NULL,
   `license` varchar(255) COLLATE utf8_czech_ci DEFAULT NULL,
-  `author` varchar(255) COLLATE utf8_czech_ci DEFAULT NULL,
+  `author_id` int(11) NOT NULL REFERENCES user(id),
   `added_at` datetime DEFAULT CURRENT_TIMESTAMP,
   `last_updated_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
   `approved_at` datetime DEFAULT NULL,
@@ -89,52 +65,39 @@ CREATE TABLE IF NOT EXISTS `plugin` (
   PRIMARY KEY (`id`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_czech_ci;
 
--- --------------------------------------------------------
-
---
--- Struktura tabulky `plugin_category`
---
-
-CREATE TABLE IF NOT EXISTS `plugin_category` (
-  `plugin_id` int(11) NOT NULL,
-  `category_id` int(11) NOT NULL,
-  PRIMARY KEY (`plugin_id`,`category_id`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_czech_ci;
-
--- --------------------------------------------------------
-
---
--- Struktura tabulky `plugin_version`
---
-
 CREATE TABLE IF NOT EXISTS `plugin_version` (
   `id` int(11) NOT NULL AUTO_INCREMENT,
   `version` varchar(255) COLLATE utf8_czech_ci NOT NULL,
   `url` varchar(255) COLLATE utf8_czech_ci DEFAULT NULL,
   `relnotes` text COLLATE utf8_czech_ci,
-  `plugin_id` int(11) DEFAULT NULL,
+  `plugin_id` int(11) NOT NULL REFERENCES plugin(id),
   PRIMARY KEY (`id`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_czech_ci;
 
--- --------------------------------------------------------
-
---
--- Struktura tabulky `verification`
---
-
 CREATE TABLE IF NOT EXISTS `verification` (
   `id` int(11) NOT NULL AUTO_INCREMENT,
   `status` int(11) DEFAULT NULL,
   `created_at` datetime DEFAULT CURRENT_TIMESTAMP,
-  `plugin_version_id` int(11) DEFAULT NULL,
+  `plugin_version_id` int(11) NOT NULL REFERENCES plugin_version(id),
   PRIMARY KEY (`id`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_czech_ci;
 
--- --------------------------------------------------------
 
---
--- Struktura tabulky `verification_request`
---
+CREATE TABLE IF NOT EXISTS `nb_version_plugin_version` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `nb_version_id` int(11) NOT NULL REFERENCES nb_version(id),
+  `plugin_version_id` int(11) NOT NULL REFERENCES plugin_version(id),
+  `verification_id` int(11) DEFAULT NULL,
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_czech_ci;
+
+
+CREATE TABLE IF NOT EXISTS `plugin_category` (
+  `plugin_id` int(11) NOT NULL REFERENCES plugin(id),
+  `category_id` int(11) NOT NULL REFERENCES category(id),
+  PRIMARY KEY (`plugin_id`,`category_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_czech_ci;
+
 
 CREATE TABLE IF NOT EXISTS `verification_request` (
   `id` int(11) NOT NULL AUTO_INCREMENT,
@@ -143,21 +106,10 @@ CREATE TABLE IF NOT EXISTS `verification_request` (
   `voted_at` datetime DEFAULT NULL,
   `comment` text COLLATE utf8_czech_ci,
   `verification_id` int(11) DEFAULT NULL,
-  `verifier_id` int(11) DEFAULT NULL,
+  `verifier_id` int(11) NOT NULL REFERENCES user(id),
   PRIMARY KEY (`id`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_czech_ci;
 
--- --------------------------------------------------------
-
---
--- Struktura tabulky `verifier`
---
-
-CREATE TABLE IF NOT EXISTS `verifier` (
-  `id` int(11) NOT NULL AUTO_INCREMENT,
-  `user_id` varchar(255) COLLATE utf8_czech_ci DEFAULT NULL,
-  PRIMARY KEY (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_czech_ci;
 COMMIT;
 
 /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
diff --git a/pp3/module/Application/Module.php b/pp3/module/Application/Module.php
index 7021063..7e197da 100755
--- a/pp3/module/Application/Module.php
+++ b/pp3/module/Application/Module.php
@@ -27,7 +27,24 @@ class Module {
             'remember_me_seconds' => 180,
             'use_cookies' => true,
             //'cookie_httponly' => true,
-        ]);        
+        ]);
+
+        // Why this code is necessary will be the secret of the Zend Developers
+        // instead of logging, they hide exception and just show meaningless
+        // error messages
+        //
+        //Attach render errors
+        $eventManager->attach(MvcEvent::EVENT_RENDER_ERROR, function($e) {
+            if ($e->getParam('exception')) {
+                error_log($e->getParam('exception')); //Custom error render function.
+            }
+        });
+        //Attach dispatch errors
+        $eventManager->attach(MvcEvent::EVENT_DISPATCH_ERROR, function($e) {
+            if ($e->getParam('exception')) {
+                error_log($e->getParam('exception')); //Custom error render function.
+            }
+        });
     }
 
     public function getConfig() {
diff --git a/pp3/module/Application/config/module.config.php.dist b/pp3/module/Application/config/module.config.php.dist
index 13fd914..c0a3c2d 100755
--- a/pp3/module/Application/config/module.config.php.dist
+++ b/pp3/module/Application/config/module.config.php.dist
@@ -15,13 +15,34 @@ return array(
         'mavenRepoUrl' => 'https://repo1.maven.org/maven2/',
         'catalogSavepath' => '/home/honza/checkout/pp3/public/data',
         'catalogUrlPath' => 'http://localhost/checkout/pp3/public/data',    
-        'dtdPath' => 'http://localhost/checkout/pp3/public/data/autoupdate-catalog-2_6.dtd',    
-        'admin' => array(
-            'jan.pirek@gmail.com',
-            'jiri.kovalsky@gmail.com'
+        'dtdPath' => 'http://localhost/checkout/pp3/public/data/autoupdate-catalog-2_6.dtd'
+    ),
+    'loginConfig' => array (
+        array(
+            'id' => 'github',
+            'name' => 'GitHub',
+            'icon' => '/img/login/github.svg',
+            'clientId' => 'DO_NOT_COMMIT',
+            'clientSecret' => 'DO_NOT_COMMIT',
+            'type' => 'github'
+        ),
+        array(
+            'id' => 'google',
+            'name' => 'Google',
+            'icon' => '/img/login/google.svg',
+            'clientId' => 'DO_NOT_COMMIT',
+            'clientSecret' => 'DO_NOT_COMMIT',
+            'type' => 'google'
         ),
-        'googleClientId' => '432862904114-ierlf9j6qmmuhtd44ecmlfqivlirq7uc.apps.googleusercontent.com',
-    ), 
+        array(
+            'id' => 'amazon',
+            'name' => 'Amazon',
+            'icon' => '/img/login/amazon.svg',
+            'clientId' => 'DO_NOT_COMMIT',
+            'clientSecret' => 'DO_NOT_COMMIT',
+            'type' => 'amazon'
+        )
+    ),
     'router' => array(
         'routes' => array(  
             'home' => array(
diff --git a/pp3/module/Application/src/Application/Controller/AdminController.php b/pp3/module/Application/src/Application/Controller/AdminController.php
index 215fbb5..70075cf 100644
--- a/pp3/module/Application/src/Application/Controller/AdminController.php
+++ b/pp3/module/Application/src/Application/Controller/AdminController.php
@@ -2,43 +2,44 @@
 
 namespace Application\Controller;
 
-use Application\Controller\BaseController;
 use Zend\View\Model\ViewModel;
 use Application\Entity\Plugin;
-use Zend\Session\Container;
 use Application\Pp\MavenDataLoader;
 use Application\Pp\Catalog;
-use Application\Entity\Verifier;
+use Application\Entity\User;
 use Application\Entity\NbVersion;
 use Application\Entity\Category;
-use Zend\Mvc\MvcEvent;
 use HTMLPurifier;
 use HTMLPurifier_Config;
-use Zend\Http\Response;
+use Zend\Http\PhpEnvironment\Response;
 
-class AdminController extends BaseController {
+class AdminController extends AuthenticatedController {
 
     private $_pluginRepository;
     private $_pluginVersionRepository;
     private $_nbVersionPluginVersionRepository;
     private $_verificationRepository;
-    private $_verifierRepository;
     private $_verificationRequestRepository;
+    /**
+     * @var \Application\Repository\UserRepository
+     */
+    private $_userRepository;
     private $_nbVersionRepository;
     private $_categoryRepository;
 
     public function __construct($pluginRepository, $nbVersionPluginVersionRepo, $verificationRepo, 
-                                $verifierRepository, $verificationRequestRepository, $nbVersionRepository,
-                                $pluginVersionRepository, $config, $categoryRepository) {
+                                $verificationRequestRepository, $nbVersionRepository,
+                                $pluginVersionRepository, $config, $categoryRepository,
+                                $userRepository) {
         parent::__construct($config);
         $this->_pluginRepository = $pluginRepository;
         $this->_pluginVersionRepository = $pluginVersionRepository;
         $this->_nbVersionPluginVersionRepository = $nbVersionPluginVersionRepo;
         $this->_verificationRepository = $verificationRepo;
-        $this->_verifierRepository = $verifierRepository;
         $this->_verificationRequestRepository = $verificationRequestRepository;
         $this->_nbVersionRepository = $nbVersionRepository;
         $this->_categoryRepository = $categoryRepository;
+        $this->_userRepository = $userRepository;
     } 
 
     public function deletePendingAction() {
@@ -166,30 +167,58 @@ class AdminController extends BaseController {
         ]);
     }
 
+    public function searchUserByEmailAction() {
+        /* @var $response Response */
+        $response = $this->getResponse();
+        $response->getHeaders()->addHeaderLine("Content-Type", "application/json");
+        $email = $this->params()->fromQuery('email');
+        $result = array();
+        if(! empty($email)) {
+            foreach($this->_userRepository->findByEmail($email) as $user) {
+                $result[] = array(
+                    'id' => $user->getId(),
+                    'name' => $user->getName(),
+                    'email' => $user->getEmail(),
+                    'idpProviderId' => $user->getIdpProviderId(),
+                    'verifier' => !!$user->isVerifier(),
+                    'admin' => !!$user->isVerifier()
+                );
+            }
+        }
+        $response->setContent(json_encode($result));
+        return $response;
+    }
+
     public function verifiersAction() {
         $this->_checkAdminUser();
-        $req = $this->request;   
-        $id = $this->params()->fromQuery('id');
-        if (!empty($id)) {
-            $verifier = $this->_verifierRepository->find($id);
+        $removeVerifierStatusId = $this->params()->fromPost('removeVerifierStatusId');
+        if (!empty($removeVerifierStatusId)) {
+            /* @var $verifier User */
+            $verifier = $this->_userRepository->find($removeVerifierStatusId);
             if ($verifier) {
-                $this->_verifierRepository->remove($verifier);$this->flashMessenger()->setNamespace('success')->addMessage('Verifier '.$verifier->getUserId().' deleted.');
+                $verifier->setVerifier(false);
+                $this->_userRepository->persist($verifier);
+                $this->flashMessenger()->setNamespace('success')->addMessage('Verifier '.$verifier->getName().' removed.');
                 return $this->redirect()->toRoute('admin', array(
                     'action' => 'verifiers'
                 ));
             }
         }
-        if ($req->isPost() && $this->params()->fromPost('userId')) {
-            $verifier = new Verifier();
-            $verifier->setUserId($this->params()->fromPost('userId'));
-            $this->_verifierRepository->persist($verifier);
-            $this->flashMessenger()->setNamespace('success')->addMessage('Verifier '.$verifier->getUserId().' added.');
+        $addVerifierStatusId = $this->params()->fromPost('addVerifierStatusId');
+        if (!empty($addVerifierStatusId)) {
+            /* @var $verifier User */
+            $verifier = $this->_userRepository->find($addVerifierStatusId);
+            if ($verifier) {
+                $verifier->setVerifier(true);
+                $this->_userRepository->persist($verifier);
+                $this->flashMessenger()->setNamespace('success')->addMessage('Verifier '.$verifier->getName().' added.');
                 return $this->redirect()->toRoute('admin', array(
                     'action' => 'verifiers'
                 ));
+            }
         }
         return new ViewModel([
-            'verifiers' => $this->_verifierRepository->findAll()
+            'verifiers' => $this->_userRepository->findVerifier()
         ]);
     }
 
@@ -301,7 +330,7 @@ class AdminController extends BaseController {
     }
 
     private function _checkAdminUser() {
-        if (!$this->_isAdmin) {
+        if (!$this->isAdmin()) {
             return $this->redirect()->toRoute('plugin', array(
                 'action' => 'index'
             ));
@@ -318,7 +347,6 @@ class AdminController extends BaseController {
         $req = $this->request;
         if ($req->isPost()) {
             $validatedData = $this->_validateAndCleanPluginData(
-                $this->params()->fromPost('author'),
                 $this->params()->fromPost('name'),
                 $this->params()->fromPost('license'),
                 $this->params()->fromPost('description'),
@@ -326,7 +354,6 @@ class AdminController extends BaseController {
                 $this->params()->fromPost('category')
             );
             if ($validatedData) {
-                $plugin->setAuthor($validatedData['author']);
                 $plugin->setName($validatedData['name']);
                 $plugin->setLicense($validatedData['license']);
                 $plugin->setDescription($validatedData['description']);
@@ -359,7 +386,7 @@ class AdminController extends BaseController {
                 $this->_pluginRepository->persist($plugin);
                 $this->flashMessenger()->setNamespace('success')->addMessage('Plugin updated.');               
             } else {
-                $this->flashMessenger()->setNamespace('error')->addMessage('Missing required data or Author email not in correct format.');
+                $this->flashMessenger()->setNamespace('error')->addMessage('Missing required data.');
             }
             return $this->redirect()->toUrl('./edit?id='.$plugin->getId());      
         }
@@ -370,15 +397,13 @@ class AdminController extends BaseController {
         ));
     }
 
-    private function _validateAndCleanPluginData($author, $name, $license, $description, $shortDescription, $category) {
-        if (empty($author) || empty($name) || empty($license) || empty($category) || empty($shortDescription)
-            || !filter_var($author, FILTER_VALIDATE_EMAIL)) {
+    private function _validateAndCleanPluginData($name, $license, $description, $shortDescription, $category) {
+        if (empty($name) || empty($license) || empty($category) || empty($shortDescription)) {
             return false;
         }
         $config = HTMLPurifier_Config::createDefault();
         $purifier = new HTMLPurifier($config);
         return  array(
-            'author' => $purifier->purify($author),
             'name' => $purifier->purify($name),
             'license' => $purifier->purify($license),
             'description' => $purifier->purify($description),
diff --git a/pp3/module/Application/src/Application/Controller/AuthenticatedController.php b/pp3/module/Application/src/Application/Controller/AuthenticatedController.php
new file mode 100644
index 0000000..04937e2
--- /dev/null
+++ b/pp3/module/Application/src/Application/Controller/AuthenticatedController.php
@@ -0,0 +1,22 @@
+<?php
+
+namespace Application\Controller;
+
+use Zend\Mvc\MvcEvent;
+
+class AuthenticatedController extends BaseController {
+
+    function __construct($config) {
+        parent::__construct($config);
+    }
+
+
+    public function onDispatch(MvcEvent $e) {
+        if (!$this->isAuthenticated()) {
+            return $this->redirect()->toRoute('home', array(
+                        'action' => 'index'
+            ));
+        }
+        return parent::onDispatch($e);
+    }
+}
diff --git a/pp3/module/Application/src/Application/Controller/BaseController.php b/pp3/module/Application/src/Application/Controller/BaseController.php
index 3ea84e1..b2a116e 100644
--- a/pp3/module/Application/src/Application/Controller/BaseController.php
+++ b/pp3/module/Application/src/Application/Controller/BaseController.php
@@ -14,7 +14,6 @@ class BaseController extends AbstractActionController {
     protected $_session;
     protected $_config;
     protected $_isAdmin;
-    protected $_sessionUserId;
     
     public function __construct($config) {
         $this->_config = $config;
@@ -22,23 +21,40 @@ class BaseController extends AbstractActionController {
     }
     
     public function onDispatch(MvcEvent $e) {
-        if (!$this->isAuthenticated()) {
-            return $this->redirect()->toRoute('home', array(
-                'action' => 'index'
-            ));
+        $idp = null;
+        $idpProviderId = array_key_exists('sessionIdpProviderId', $_SESSION) ? $_SESSION['sessionIdpProviderId'] : false;
+        if($idpProviderId) {
+            foreach($this->_config['loginConfig'] as $lc) {
+                if($lc['id'] === $idpProviderId) {
+                    $idp = $lc['name'];
+                    break;
+                }
+            }
         }
-        $this->_sessionUserId = $_SESSION['sessionUserId'];
-        $this->layout()->setVariable('sessionUserId', $this->_sessionUserId);
-        $this->isAdmin();
+
+        $this->layout()->setVariable('sessionUserId', array_key_exists('sessionUserId', $_SESSION) ? $_SESSION['sessionUserId'] : null);
+        $this->layout()->setVariable('sessionUserName', array_key_exists('sessionUserName', $_SESSION) ? $_SESSION['sessionUserName'] : null);
+        $this->layout()->setVariable('sessionIdp', $idp);
+        $this->layout()->setVariable('sessionUserEmail', array_key_exists('sessionUserEmail', $_SESSION) ? $_SESSION['sessionUserEmail'] : false);
+        $this->layout()->setVariable('isAdmin', $this->isAdmin());
+        $this->layout()->setVariable('isAuthenticated', $this->isAuthenticated());
+        $this->layout()->setVariable('isVerifier', $this->isVerifier());
         return parent::onDispatch($e);
     }
 
-    private function isAuthenticated() {
+    protected function isAdmin() {
+        return array_key_exists('isAdmin', $_SESSION) && $_SESSION['isAdmin'];
+    }
+
+    protected function isAuthenticated() {
         return !empty($_SESSION['sessionUserId']);
     }
-    
-    private function isAdmin() {
-        $this->_isAdmin = $_SESSION['isAdmin'];        
-        $this->layout()->setVariable('isAdmin', $this->_isAdmin);
+
+    protected function isVerifier() {
+        return array_key_exists('isVerifier', $_SESSION) && $_SESSION['isVerifier'];
+    }
+
+    protected function getAuthenticatedUserId() {
+        return empty($_SESSION['sessionUserId']) ? false : $_SESSION['sessionUserId'];
     }
 }
\ No newline at end of file
diff --git a/pp3/module/Application/src/Application/Controller/IndexController.php b/pp3/module/Application/src/Application/Controller/IndexController.php
index 9397657..182f59b 100755
--- a/pp3/module/Application/src/Application/Controller/IndexController.php
+++ b/pp3/module/Application/src/Application/Controller/IndexController.php
@@ -2,23 +2,21 @@
 
 namespace Application\Controller;
 
-use Zend\Mvc\Controller\AbstractActionController;
 use Knp\Component\Pager\PaginatorInterface;
 use Zend\View\Model\ViewModel;
 use DoctrineORMModule\Paginator\Adapter\DoctrinePaginator as DoctrineAdapter;
 use Doctrine\ORM\Tools\Pagination\Paginator as ORMPaginator;
 use Zend\Paginator\Paginator;
 
-class IndexController extends AbstractActionController {
+class IndexController extends BaseController {
 
-    private $_config;
     private $_pluginRepository;
     private $_paginator;
     private $_categoryRepository;
     private $_nbVersionRepository;
 
     public function __construct($pluginRepo, $config, PaginatorInterface $paginator, $nbVersionRepository, $categoryRepository, $pvRepo) {
-        $this->_config = $config;
+        parent::__construct($config);
         $this->_pluginRepository = $pluginRepo;
         $this->_paginator = $paginator;
         $this->_nbVersionRepository = $nbVersionRepository;
diff --git a/pp3/module/Application/src/Application/Controller/LoginController.php b/pp3/module/Application/src/Application/Controller/LoginController.php
index 52ce609..c53cd2a 100644
--- a/pp3/module/Application/src/Application/Controller/LoginController.php
+++ b/pp3/module/Application/src/Application/Controller/LoginController.php
@@ -1,71 +1,285 @@
 <?php
 
-
 namespace Application\Controller;
 
-use Zend\Mvc\Controller\AbstractActionController;
-use Zend\View\Model\ViewModel;
 use Zend\Session\Container;
-// use \Google\Apiclient\Client;
+use Application\Entity\User;
+use Zend\View\Model\ViewModel;
 
-class LoginController extends AbstractActionController {
+class LoginController extends BaseController {
 
-    private $_config;
-    private $_session;
-    private $_verifierRepository;
+    /**
+     * @var \Application\Repository\UserRepository
+     */
+    private $_userRepository;
 
-    public function __construct($config, $verifierRepository) {
-        $this->_config = $config;
+    public function __construct($config, $userRepository) {
+        parent::__construct($config);
         $this->_session = new Container();
-        $this->_verifierRepository = $verifierRepository;
+        $this->_userRepository = $userRepository;
+    }
+
+    public function indexAction() {
+        $providers = array();
+        foreach($this->_config['loginConfig'] as $loginConfig) {
+            $iconUrl = $loginConfig['icon'];
+            if(strpos($iconUrl, '/') === 0) {
+                $iconUrl = $this->url()->fromRoute("home", array(), array('force_canonical'=>true)) . substr($iconUrl, 1);
+            }
+            $loginUrl = $this->url()->fromRoute('login', array('action' => 'login-start'), array('force_canonical'=>true));
+            $loginUrl .= '?';
+            $loginUrl .= http_build_query(array('id' => $loginConfig['id']));
+            $providers[] = array(
+                'id' => $loginConfig['id'],
+                'name' => $loginConfig['name'],
+                'iconUrl' => $iconUrl,
+                'loginUrl' => $loginUrl
+            );
+        }
+        return new ViewModel([
+            'providers' => $providers
+        ]);
+    }
+
+    private function findLoginConfig(string $id) {
+        if(! $id) {
+            return null;
+        }
+        foreach($this->_config['loginConfig'] as $loginConfig) {
+            if($loginConfig['id'] === $id) {
+                return $loginConfig;
+            }
+        }
+        return null;
+    }
+
+    public function loginStartAction() {
+        $loginConfig = $this->findLoginConfig($this->params()->fromQuery('id'));
+        if (!$loginConfig) {
+            $response = $this->getResponse();
+            $response->setStatusCode(404);
+            $response->setContent("Unknown authentication provider");
+            return $response;
+        }
+
+        $stateBytes = random_bytes(64);
+        $state = bin2hex($stateBytes);
+        $_SESSION['oauthState'] = $state;
+        $_SESSION['oauthConfig'] = $loginConfig['id'];
+        $scopeData = self::scopesFromType($loginConfig['type']);
+        $queryData = array(
+            'client_id' => $loginConfig['clientId'],
+            'state' => $state,
+            'response_type' => 'code',
+            'redirect_uri' => $this->redirectUrl()
+        );
+        if($scopeData) {
+            $queryData['scope'] = $scopeData;
+        }
+        $url = self::authenticationUrlFromType($loginConfig['type']);
+        $url .= '?';
+        $url .= http_build_query($queryData);
+        return $this->redirect()->toUrl($url);
     }
-    
-    public function onGoogleSignInAjaxAction() {
+
+    public function callbackAction() {
         $response = $this->getResponse();
-        // user info passed from Google auth
-        $req = $this->request;        
-        if($_SESSION['sessionUserId']) {
-            $response->setContent('');
-        } else {
-            if ($req->isPost()) {
-                $client = new \Google_Client(['client_id' => $this->_config['pp3']['googleClientId']]);  // Specify the CLIENT_ID of the app that accesses the backend
-                $idToken = $this->params()->fromPost('idtoken');
-                $payload = $client->verifyIdToken($idToken);
-                if ($payload) {
-                    $email = $payload['email'];
-                    $_SESSION['sessionUserId'] = $email;
-                    $_SESSION['sessionUserEmail'] = $email;        
-                    $this->checkVerifier($email);
-                    $this->checkAdmin($email);
-                    $response->setContent('reload');
-                } else {
-                    // Invalid ID token
-                    $response->setContent('Failure');
+        $response->getHeaders()->addHeaderLine('Content-Type', 'application/json');
+
+        $parameters = $this->params()->fromQuery();
+        $state = $this->params()->fromQuery('state');
+        $code = $this->params()->fromQuery('code');
+
+        if((!array_key_exists('oauthState', $_SESSION)) || $_SESSION['oauthState'] != $state) {
+            error_log('Invalid / no state was transfered - received: ' . json_encode($parameters));
+            $response->setStatusCode(400);
+            $response->setContent(json_encode(array('success' => false, 'reason' => 'INVALID_STATE')));
+            return $response;
+        }
+
+        $loginConfig = $this->findLoginConfig($_SESSION['oauthConfig']);
+
+        if (!$loginConfig) {
+            error_log("Login Config was not found for: " . $_SESSION['oauthConfig'] . " received: " . json_encode($parameters));
+            $response->setStatusCode(400);
+            $response->setContent(json_encode(array('success' => false, 'reason' => 'INVALID_LOGIN_CONFIG')));
+            return $response;
+        }
+
+        $tokenRequest = self::tokenRequest($code, $loginConfig);
+        $queryTokenResult = file_get_contents(self::tokenUrlFromType($loginConfig['type']), false, stream_context_create([
+            'http' => [
+                'method' => 'POST',
+                'header' => ["Content-type: application/json", "Accept: application/json"],
+                'content' => json_encode($tokenRequest)
+            ]
+        ]));
+
+        if(!$queryTokenResult) {
+            error_log("Empty response");
+            $response->setStatusCode(500);
+            $response->setContent(json_encode(array('success' => false, 'reason' => 'INVALID_TOKEN')));
+            return $response;
+        }
+
+        $tokenData = json_decode($queryTokenResult, true);
+
+        if((! $tokenData) || (! $tokenData['access_token']) || (strtolower($tokenData['token_type']) != 'bearer')) {
+            error_log("Failed to decode token data: " . $queryTokenResult);
+            $response->setStatusCode(500);
+            $response->setContent(json_encode(array('success' => false, 'reason' => 'INVALID_TOKEN')));
+            return $response;
+        }
+
+        $queryProfileResult = file_get_contents(self::profileUrlFromType($loginConfig['type']), false, stream_context_create([
+            'http' => [
+                'header' => ['Accept: application/json', 'Authorization: Bearer ' . $tokenData['access_token'], 'User-Agent: Netbeans Plugin Portal'],
+                "ignore_errors" => true,
+            ]
+        ]));
+
+        $userinfo = $this->extractUserInfo($loginConfig['type'], $loginConfig['id'], $queryProfileResult);
+
+        if($userinfo == null) {
+            error_log("Failed to parse: " . $queryProfileResult);
+            $response->setStatusCode(500);
+            $response->setContent(json_encode(array('success' => false, 'reason' => 'INVALID_USERINFO')));
+            return $response;
+        }
+
+        if(!$userinfo['email']) {
+            $emailQueryUrl = self::emailQueryUrl($loginConfig['type']);
+            if ($emailQueryUrl) {
+                $queryEmailResult = file_get_contents($emailQueryUrl, false, stream_context_create([
+                    'http' => [
+                        'header' => ['Accept: application/json', 'Authorization: Bearer ' . $tokenData['access_token'], 'User-Agent: Netbeans Plugin Portal'],
+                        "ignore_errors" => true,
+                    ]
+                ]));
+                $queryEmail = json_decode($queryEmailResult, true);
+                foreach($queryEmail as $emailInfo) {
+                    if(array_key_exists('email', $emailInfo) && $emailInfo['email']) {
+                        $userinfo['email'] = $emailInfo['email'];
+                        break;
+                    }
                 }
             }
+
+            if (!$userinfo['email']) {
+                error_log("Userinfo did not contain email");
+                $response->setStatusCode(500);
+                $response->setContent(json_encode(array('success' => false, 'reason' => 'NO_EMAIL')));
+                return $response;
+            }
+        }
+
+        $user = $this->_userRepository->findByIdpData($userinfo['providerId'], $userinfo['id']);
+        if($user == null) {
+            $user = new User();
         }
-       return $response;
+
+        $user->setEmail($userinfo['email']);
+        $user->setIdpProviderId($userinfo['providerId']);
+        $user->setIdpUserId($userinfo['id']);
+        $user->setName($userinfo['name']);
+        $this->_userRepository->persist($user);
+
+        $_SESSION['sessionUserId'] = $user->getId();
+        $_SESSION['sessionUserEmail'] = $user->getEmail();
+        $_SESSION['sessionIdpProviderId'] = $user->getIdpProviderId();
+        $_SESSION['sessionUserName'] = $user->getName();
+        $_SESSION['isVerifier'] = $user->isVerifier();
+        $_SESSION['isAdmin'] = $user->isAdmin();
+
+        return $this->redirect()->toRoute("home");
+    }
+
+    private function redirectUrl() {
+        return $this->url()->fromRoute("login", array("action" => "callback"), array('force_canonical'=>true));
     }
 
-    public function onGoogleSignOutAjaxAction() {
+    public function logoutAction() {
         $_SESSION['sessionUserId'] = null;
         $_SESSION['sessionUserEmail'] = null;
+        $_SESSION['sessionIdpProviderId'] = null;
+        $_SESSION['sessionUserName'] = null;
         $_SESSION['isVerifier'] = null;
         $_SESSION['isAdmin'] = null;
-        die();
+        $response = $this->getResponse();
+        return $this->redirect()->toRoute("home");
     }
 
-    private function checkVerifier($userId) {
-        if (!empty($userId)) {
-            $verifier = $this->_verifierRepository->findOneBy('user_id', $userId);
-            if ($verifier) {
-                $_SESSION['isVerifier'] = true;
-            }
+    private static function authenticationUrlFromType($type) {
+        switch ($type) {
+            case 'github': return 'https://github.com/login/oauth/authorize';
+            case 'google': return 'https://accounts.google.com/o/oauth2/v2/auth';
+            case 'amazon': return 'https://www.amazon.com/ap/oa';
+        }
+    }
+
+    private static function tokenUrlFromType($type) {
+        switch ($type) {
+            case 'github': return 'https://github.com/login/oauth/access_token';
+            case 'google': return 'https://oauth2.googleapis.com/token';
+            case 'amazon': return 'https://api.amazon.com/auth/o2/token';
+        }
+    }
+
+    private static function profileUrlFromType($type) {
+        switch ($type) {
+            case 'github': return 'https://api.github.com/user';
+            case 'google': return 'https://openidconnect.googleapis.com/v1/userinfo';
+            case 'amazon': return 'https://api.amazon.com/user/profile';
+        }
+    }
+
+    private static function emailQueryUrl($type) {
+        switch($type) {
+            case 'github': return 'https://api.github.com/user/emails';
+            default: return false;
+        }
+    }
+
+    private static function scopesFromType($type) {
+        switch ($type) {
+            case 'github': return 'user:email';
+            case 'google': return 'openid email profile';
+            case 'amazon': return 'profile';
+        }
+    }
+
+    private function tokenRequest( $code, $loginConfig) {
+        $data = array('code' => $code, 'client_id' => $loginConfig['clientId'], 'client_secret' => $loginConfig['clientSecret']);
+        if($loginConfig['type'] != 'github') {
+            $data['redirect_uri'] = $this->redirectUrl();
+            $data['grant_type'] = 'authorization_code';
         }
+        return $data;
     }
 
-    private function checkAdmin($userId) {
-        $admins = $this->_config['pp3']['admin'];
-        $_SESSION['isAdmin'] = in_array($userId, $admins);
+    private function extractUserInfo($type, $providerId, $data) {
+        if(! $data) {
+            return null;
+        }
+        $json = json_decode($data, true);
+        if(! $json) {
+            return null;
+        }
+        $userinfo = array();
+        $userinfo['providerId'] = $providerId;
+        if($type == 'github') {
+            $userinfo['id'] = "" . $json['id'];
+            $userinfo['email'] = "" . $json['email'];
+            $userinfo['name'] = "" . $json['name'];
+        }  else if ($type == 'google') {
+            $userinfo['id'] = "" . $json['sub'];
+            $userinfo['email'] = "" . $json['email'];
+            $userinfo['name'] = "" . $json['name'];
+        } else if ($type == 'amazon') {
+            $userinfo['id'] = "" . $json['user_id'];
+            $userinfo['email'] = "" . $json['email'];
+            $userinfo['name'] = "" . $json['name'];
+        }
+        return $userinfo;
     }
 }
diff --git a/pp3/module/Application/src/Application/Controller/PluginController.php b/pp3/module/Application/src/Application/Controller/PluginController.php
index 8e03082..735cb14 100644
--- a/pp3/module/Application/src/Application/Controller/PluginController.php
+++ b/pp3/module/Application/src/Application/Controller/PluginController.php
@@ -2,7 +2,6 @@
 
 namespace Application\Controller;
 
-use Application\Controller\BaseController;
 use Zend\View\Model\ViewModel;
 use Zend\Session\Container;
 use Application\Pp\MavenDataLoader;
@@ -15,28 +14,31 @@ use HTMLPurifier_Config;
 
 define('PLUGIN_SESSION_NAMESPACE', 'pp3_plugin_session');
 
-class PluginController extends BaseController {
+class PluginController extends AuthenticatedController {
 
     private $_pluginRepository;
     private $_pluginVersionRepository;
     private $_categoryRepository;
-    private $_verifierRepository;
     private $_nbVersionRepository;
+    /**
+     * @var \Application\Repository\UserRepository
+     */
+    private $_userRepository;
 
-    public function __construct($pluginRepo, $pvRepo, $categRepository, $config, $verifierRepository, $nbVersionRepository) {
+    public function __construct($pluginRepo, $pvRepo, $categRepository, $config, $nbVersionRepository, $userRepository) {
         parent::__construct($config);
         $this->_pluginRepository = $pluginRepo;
         $this->_pluginVersionRepository = $pvRepo;
         $this->_categoryRepository = $categRepository;       
-        $this->_verifierRepository = $verifierRepository;
         $this->_nbVersionRepository = $nbVersionRepository;
+        $this->_userRepository = $userRepository;
     }
 
     public function syncAction() {
         $pId = $this->params()->fromQuery('id');
         $plugin = $this->_pluginRepository->find($pId);        
         $plugin->setDataLoader(new MavenDataLoader());
-        if (!$plugin->isOwnedBy($this->_sessionUserId)) {
+        if (!$plugin->isOwnedBy($this->getAuthenticatedUserId())) {
             return $this->redirect()->toRoute('plugin', array(
                 'action' => 'list'
             ));
@@ -78,11 +80,12 @@ class PluginController extends BaseController {
             
             if (!empty($groupId) && !empty($artifactId)) {
                 $url = $this->_config['pp3']['mavenRepoUrl'].str_replace('.','/', $groupId).'/'.$artifactId.'/maven-metadata.xml';
+                $user = $this->_userRepository->find($this->getAuthenticatedUserId());
                 $plugin->setUrl($url);
                 $plugin->setArtifactId($artifactId);
                 $plugin->setGroupId($groupId);
                 $plugin->setStatus(Plugin::STATUS_PRIVATE);
-                $plugin->setAuthor($this->_sessionUserId);
+                $plugin->setAuthor($user);
                 $plugin->setDataLoader(new MavenDataLoader());
                 try {
                     if ($plugin->loadData()) {
@@ -125,7 +128,6 @@ class PluginController extends BaseController {
         $req = $this->request;
         if ($req->isPost()) {
             $validatedData = $this->_validateAndCleanPluginData(
-                $this->params()->fromPost('author'),
                 $this->params()->fromPost('name'),
                 $this->params()->fromPost('license'),
                 $this->params()->fromPost('description'),
@@ -134,7 +136,8 @@ class PluginController extends BaseController {
                 $this->params()->fromPost('homepage')
             );
             if ($validatedData) {
-                $plugin->setAuthor($validatedData['author']);
+                $user = $this->_userRepository->find($this->getAuthenticatedUserId());
+                $plugin->setAuthor($user);
                 $plugin->setName($validatedData['name']);
                 $plugin->setLicense($validatedData['license']);
                 $plugin->setDescription($validatedData['description']);
@@ -199,7 +202,7 @@ class PluginController extends BaseController {
     }
 
     public function listAction() {
-        $plugins = $this->_pluginRepository->getPluginsByAuthor($this->_sessionUserId);
+        $plugins = $this->_pluginRepository->getPluginsByAuthorId($this->getAuthenticatedUserId());
         return new ViewModel(array(
             'plugins' => $plugins,
         ));
@@ -208,7 +211,7 @@ class PluginController extends BaseController {
     public function editAction() {
         $pId = $this->params()->fromQuery('id');
         $plugin = $this->_pluginRepository->find($pId);        
-        if (!$plugin || empty($pId) || !$plugin->isOwnedBy($this->_sessionUserId)) {
+        if (!$plugin || empty($pId) || !$plugin->isOwnedBy($this->getAuthenticatedUserId())) {
             return $this->redirect()->toRoute('plugin', array(
                 'action' => 'list'
             ));
@@ -216,7 +219,6 @@ class PluginController extends BaseController {
         $req = $this->request;
         if ($req->isPost()) {
             $validatedData = $this->_validateAndCleanPluginData(
-                $this->params()->fromPost('author'),
                 $this->params()->fromPost('name'),
                 $this->params()->fromPost('license'),
                 $this->params()->fromPost('description'),
@@ -225,7 +227,6 @@ class PluginController extends BaseController {
                 $this->params()->fromPost('homepage')
             );
             if ($validatedData) {
-                $plugin->setAuthor($validatedData['author']);
                 $plugin->setName($validatedData['name']);
                 $plugin->setLicense($validatedData['license']);
                 $plugin->setDescription($validatedData['description']);
@@ -273,7 +274,7 @@ class PluginController extends BaseController {
     public function deleteAction() {
         $pId = $this->params()->fromQuery('id');
         $plugin = $this->_pluginRepository->find($pId);    
-        if (!$plugin || empty($pId) || !$plugin->isOwnedBy($this->_sessionUserId)) {
+        if (!$plugin || empty($pId) || !$plugin->isOwnedBy($this->getAuthenticatedUserId())) {
             return $this->redirect()->toRoute('plugin', array(
                 'action' => 'list'
             ));
@@ -311,14 +312,13 @@ class PluginController extends BaseController {
         return $_SERVER["REQUEST_SCHEME"].'://'.$_SERVER["HTTP_HOST"].$this->url()->fromRoute('catalogue', array('action' => 'download')).'?id=';
     }
 
-    private function _validateAndCleanPluginData($author, $name, $license, $description, $shortDescription, $category, $homepage) {
-        if (empty($author) || empty($name) || empty($license) || empty($category) || empty($shortDescription)) {
+    private function _validateAndCleanPluginData($name, $license, $description, $shortDescription, $category, $homepage) {
+        if (empty($name) || empty($license) || empty($category) || empty($shortDescription)) {
             return false;
         }
         $config = HTMLPurifier_Config::createDefault();
         $purifier = new HTMLPurifier($config);
         return  array(
-            'author' => $purifier->purify($author),
             'name' => $purifier->purify($name),
             'license' => $purifier->purify($license),
             'description' => $purifier->purify($description),
@@ -348,14 +348,8 @@ P.S.: This is an automatic email. DO NOT REPLY to this email. ');
         $mail->setFrom('webmaster@netbeans.apache.org', 'NetBeans webmaster');
         $mail->setSubject('NetBeans plugin waiting for approval: '.$plugin->getName());
         $transport = new Mail\Transport\Sendmail();
-        $i=0;
-        foreach($this->_verifierRepository->findAll() as $verifier) {
-            if ($i == 0) {
-                $mail->addTo($verifier->getUserId());
-                $i++;
-            } else {
-                $mail->addBcc($verifier->getUserId());
-            }
+        foreach($this->_userRepository->findAdmins() as $verifier) {
+            $mail->addBcc($verifier->getEmail());
         }
         $transport->send($mail);
     }
diff --git a/pp3/module/Application/src/Application/Controller/PluginVersionController.php b/pp3/module/Application/src/Application/Controller/PluginVersionController.php
index 236b6d7..4468887 100644
--- a/pp3/module/Application/src/Application/Controller/PluginVersionController.php
+++ b/pp3/module/Application/src/Application/Controller/PluginVersionController.php
@@ -2,12 +2,7 @@
 
 namespace Application\Controller;
 
-use Application\Controller\BaseController;
 use Zend\View\Model\ViewModel;
-use Zend\Session\Container;
-use Application\Pp\MavenDataLoader;
-use Application\Entity\Plugin;
-use Application\Entity\PluginVersion;
 use Application\Entity\NbVersionPluginVersion;
 use Application\Pp\Catalog;
 use HTMLPurifier;
@@ -15,7 +10,7 @@ use HTMLPurifier_Config;
 
 define('PLUGIN_SESSION_NAMESPACE', 'pp3_plugin_session');
 
-class PluginVersionController extends BaseController {
+class PluginVersionController extends AuthenticatedController {
 
     public function __construct($pluginRepo, $pvRepo, $nbvRepo, $nbVersionPluginVersionRepo, $config, $verificationRepo) {
         parent::__construct($config);
@@ -29,7 +24,7 @@ class PluginVersionController extends BaseController {
     public function editAction() {
         $pvId = $this->params()->fromQuery('id');
         $pluginVersion = $this->_pluginVersionRepository->find($pvId);        
-        if (!$pluginVersion || empty($pvId) || !$pluginVersion->getPlugin()->isOwnedBy($this->_sessionUserId)) {
+        if (!$pluginVersion || empty($pvId) || !$pluginVersion->getPlugin()->isOwnedBy($this->getAuthenticatedUserId())) {
             return $this->redirect()->toRoute('plugin', array(
                 'action' => 'list'
             ));
@@ -115,7 +110,7 @@ class PluginVersionController extends BaseController {
     public function deleteAction() {
         $pId = $this->params()->fromQuery('id');
         $pluginVersion = $this->_pluginVersionRepository->find($pId);        
-        if (!$pluginVersion || empty($pId) || !$pluginVersion->getPlugin()->isOwnedBy($this->_sessionUserId)) {
+        if (!$pluginVersion || empty($pId) || !$pluginVersion->getPlugin()->isOwnedBy($this->getAuthenticatedUserId())) {
             return $this->redirect()->toRoute('plugin', array(
                 'action' => 'list'
             ));
diff --git a/pp3/module/Application/src/Application/Controller/VerificationController.php b/pp3/module/Application/src/Application/Controller/VerificationController.php
index 0b37463..bb396b2 100644
--- a/pp3/module/Application/src/Application/Controller/VerificationController.php
+++ b/pp3/module/Application/src/Application/Controller/VerificationController.php
@@ -2,7 +2,6 @@
 
 namespace Application\Controller;
 
-use Application\Controller\BaseController;
 use Zend\View\Model\ViewModel;
 use Application\Entity\Verification;
 use Application\Pp\Catalog;
@@ -11,31 +10,31 @@ use Zend\Mail;
 use HTMLPurifier;
 use HTMLPurifier_Config;
 
-class VerificationController extends BaseController {
+class VerificationController extends AuthenticatedController {
 
     private $_nbVersionPluginVersionRepository;
     private $_verificationRepository;
-    private $_verifierRepository;
+    /**
+     * @var \Application\Repository\UserRepository
+     */
+    private $_userRepository;
     private $_verificationRequestRepository;
     private $_pluginVersionRepository;
 
     public function __construct($nbVersionPluginVersionRepo, $verificationRepo, 
-                                $verifierRepository, $verificationRequestRepository, $config,
+                                $userRepository, $verificationRequestRepository, $config,
                                 $pluginVersionRepository) {
         parent::__construct($config);
         $this->_nbVersionPluginVersionRepository = $nbVersionPluginVersionRepo;
         $this->_verificationRepository = $verificationRepo;
-        $this->_verifierRepository = $verifierRepository;
+        $this->_userRepository = $userRepository;
         $this->_verificationRequestRepository = $verificationRequestRepository;
         $this->_pluginVersionRepository = $pluginVersionRepository;
-
-        $session = new Container();
-        $this->_sessionUserId = $session->userId ? $session->userId : ANONUSER;
     } 
 
     public function listAction() {
         return new ViewModel([
-            'verificationRequests' => $this->_verificationRequestRepository->getVerificationRequestsForVerifier($this->_sessionUserId),
+            'verificationRequests' => $this->_verificationRequestRepository->getVerificationRequestsForVerifier($this->getAuthenticatedUserId()),
             'isAdmin' => $this->_isAdmin,
         ]);
     }
@@ -59,7 +58,7 @@ class VerificationController extends BaseController {
             $bailOut = true;
         }
         $req = $this->_verificationRequestRepository->find($reqId);
-        if (!$req || $req->getVerifier()->getUserId() !== $this->_sessionUserId) {
+        if (!$req || $req->getVerifier()->getId() !== $this->getAuthenticatedUserId()) {
             $bailOut = true;
         }
         if ($bailOut) {
@@ -148,12 +147,13 @@ class VerificationController extends BaseController {
         if (empty($nbvPvId)) {
             $bailOut = true;
         }
+        /** @var \Application\Entity\NbVersionPluginVersion $nbVersionPluginVersion   */
         $nbVersionPluginVersion = $this->_nbVersionPluginVersionRepository->find($nbvPvId);
         if (!$nbVersionPluginVersion) {
             $bailOut = true;
         }
         $plugin = $nbVersionPluginVersion->getPluginVersion()->getPlugin();
-        if (!$plugin->isOwnedBy($this->_sessionUserId)) {
+        if (!$plugin->isOwnedBy($this->getAuthenticatedUserId())) {
             $bailOut = true;
         }
         if ($bailOut) {
@@ -165,14 +165,19 @@ class VerificationController extends BaseController {
         $verification = new Verification();
         $verification->setStatus(Verification::STATUS_REQUESTED);
         $verification->setCreatedAt(new \DateTime('now'));
+        $verification->setPluginVersionId($nbvPvId);
         $this->_verificationRepository->persist($verification);
         // join it to nbVersionPluginVersion
         $nbVersionPluginVersion->setVerification($verification);
         $this->_nbVersionPluginVersionRepository->persist($nbVersionPluginVersion);
+        $verification->setNbVersionPluginVersion($nbVersionPluginVersion);
         // generate requests for all verifiers
-        $verifiers = $this->_verifierRepository->findAll();
+        $verifiers = $this->_userRepository->findVerifier();
         $verification->createRequests($verifiers, $plugin);
         $this->_verificationRepository->persist($verification);
+        foreach($verification->getVerificationRequests() as $req) {
+            $req->sendVerificationMail($plugin);
+        }
         $this->flashMessenger()->setNamespace('success')->addMessage('Verification Requested.');
         return $this->redirect()->toUrl('../plugin-version/edit?id='.$nbVersionPluginVersion->getPluginVersion()->getId());         
     }
@@ -182,7 +187,7 @@ class VerificationController extends BaseController {
         $nbVersion = $verification->getNbVersionPluginVersion()->getNbVersion()->getVersion();
         $pluginVersion = $verification->getNbVersionPluginVersion()->getPluginVersion()->getVersion();
         $mail = new Mail\Message();
-        $mail->addTo($plugin->getAuthor());
+        $mail->addTo($plugin->getAuthor()->getEmail());
         $mail->setFrom('webmaster@netbeans.apache.org', 'NetBeans webmaster');
         $mail->setSubject('Verification of your '.$plugin->getName().' is complete');
         $mail->setBody('Hello plugin owner,
@@ -212,7 +217,7 @@ P.S.: This is an automatic email. DO NOT REPLY to this email.');
         $nbVersion = $verification->getNbVersionPluginVersion()->getNbVersion()->getVersion();
         $pluginVersion = $verification->getNbVersionPluginVersion()->getPluginVersion()->getVersion();
         $mail = new Mail\Message();
-        $mail->addTo($plugin->getAuthor());
+        $mail->addTo($plugin->getAuthor()->getEmail());
         $mail->setFrom('webmaster@netbeans.apache.org', 'NetBeans webmaster');
         $mail->setSubject('Verification of your '.$plugin->getName().' is complete');
         $mail->setBody('Hello plugin owner,
diff --git a/pp3/module/Application/src/Application/Entity/Base/Plugin.php b/pp3/module/Application/src/Application/Entity/Base/Plugin.php
index 30b7f3b..1379187 100644
--- a/pp3/module/Application/src/Application/Entity/Base/Plugin.php
+++ b/pp3/module/Application/src/Application/Entity/Base/Plugin.php
@@ -32,7 +32,9 @@ class Plugin {
     /** @ORM\Column(type="string", length=255) */
     protected $license;
 
-    /** @ORM\Column(type="string", length=255) */
+    /** 
+     * @ORM\ManyToOne(targetEntity="User") 
+     */
     protected $author;
 
     /** @ORM\Column(type="datetime") */
@@ -138,10 +140,16 @@ class Plugin {
         $this->license = $license;
     }
 
+    /**
+     * @return User
+     */
     public function getAuthor() {
         return $this->author;
     }
 
+    /**
+     * @param User $author
+     */
     public function setAuthor($author) {
         $this->author = $author;
     }
diff --git a/pp3/module/Application/src/Application/Entity/Base/User.php b/pp3/module/Application/src/Application/Entity/Base/User.php
new file mode 100644
index 0000000..1fb3b18
--- /dev/null
+++ b/pp3/module/Application/src/Application/Entity/Base/User.php
@@ -0,0 +1,134 @@
+<?php
+
+namespace Application\Entity\Base;
+
+use Doctrine\ORM\Mapping as ORM;
+
+class User {
+
+    /**
+     * @ORM\Id
+     * @ORM\GeneratedValue(strategy="AUTO")
+     * @ORM\Column(type="integer")
+     */
+    protected $id;
+
+    /** @ORM\Column(name="idp_provider_id", type="string", length=20) */
+    protected $idpProviderId;
+
+    /** @ORM\Column(name="idp_user_id", type="string", length=100) */
+    protected $idpUserId;
+
+    /** @ORM\Column(name="name", type="string", length=255) */
+    protected $name;
+
+    /** @ORM\Column(name="email", type="string", length=100) */
+    protected $email;
+
+    /** @ORM\Column(name="admin", type="boolean") */
+    protected $admin = false;
+
+    /** @ORM\Column(name="verifier", type="boolean") */
+    protected $verifier = false;
+
+    public function __construct() {
+        return $this;
+    }
+
+    /**
+     * @return integer
+     */
+    function getId() {
+        return $this->id;
+    }
+
+    /**
+     * @return string
+     */
+    function getIdpProviderId() {
+        return $this->idpProviderId;
+    }
+
+    /**
+     * @return string
+     */
+    function getIdpUserId() {
+        return $this->idpUserId;
+    }
+
+    /**
+     * @return string
+     */
+    function getName() {
+        return $this->name;
+    }
+
+    /**
+     * @return string
+     */
+    function getEmail() {
+        return $this->email;
+    }
+
+    /**
+     * @param string $idpProviderId
+     * @return void
+     */
+    function setIdpProviderId($idpProviderId): void {
+        $this->idpProviderId = $idpProviderId;
+    }
+
+    /**
+     * @param string $idpUserId
+     * @return void
+     */
+    function setIdpUserId($idpUserId): void {
+        $this->idpUserId = $idpUserId;
+    }
+
+    /**
+     * @param string $name
+     * @return void
+     */
+    function setName($name): void {
+        $this->name = $name;
+    }
+
+    /**
+     * @param string $email
+     * @return void
+     */
+    function setEmail($email): void {
+        $this->email = $email;
+    }
+
+    /**
+     * @return boolean
+     */
+    function isAdmin() {
+        return $this->admin;
+    }
+
+    /**
+     * @return boolean
+     */
+    function isVerifier() {
+        return $this->verifier;
+    }
+
+    /**
+     * @param boolean $admin
+     * @return void
+     */
+    function setAdmin($admin): void {
+        $this->admin = $admin;
+    }
+
+    /**
+     * @param boolean $verifier
+     * @return void
+     */
+    function setVerifier($verifier): void {
+        $this->verifier = $verifier;
+    }
+}
diff --git a/pp3/module/Application/src/Application/Entity/Base/Verification.php b/pp3/module/Application/src/Application/Entity/Base/Verification.php
index df8d157..33cd939 100644
--- a/pp3/module/Application/src/Application/Entity/Base/Verification.php
+++ b/pp3/module/Application/src/Application/Entity/Base/Verification.php
@@ -82,4 +82,7 @@ class Verification {
         return $this->nbVersionPluginVersion;
     }
 
+    function setNbVersionPluginVersion($nbVersionPluginVersion): void {
+        $this->nbVersionPluginVersion = $nbVersionPluginVersion;
+    }
 }
diff --git a/pp3/module/Application/src/Application/Entity/Base/VerificationRequest.php b/pp3/module/Application/src/Application/Entity/Base/VerificationRequest.php
index 9badafa..9adeddf 100644
--- a/pp3/module/Application/src/Application/Entity/Base/VerificationRequest.php
+++ b/pp3/module/Application/src/Application/Entity/Base/VerificationRequest.php
@@ -38,7 +38,7 @@ class VerificationRequest {
     protected $verification;
     
     /**
-     * @ORM\ManyToOne(targetEntity="Verifier", inversedBy="verification_requests")
+     * @ORM\ManyToOne(targetEntity="User", inversedBy="verification_requests")
      * @ORM\JoinColumn(name="verifier_id", referencedColumnName="id")
      */
     protected $verifier;
diff --git a/pp3/module/Application/src/Application/Entity/Base/Verifier.php b/pp3/module/Application/src/Application/Entity/Base/Verifier.php
deleted file mode 100644
index 176e551..0000000
--- a/pp3/module/Application/src/Application/Entity/Base/Verifier.php
+++ /dev/null
@@ -1,50 +0,0 @@
-<?php
-
-namespace Application\Entity\Base;
-
-use Doctrine\ORM\Mapping as ORM;
-use Doctrine\Common\Collections\ArrayCollection;
-
-class Verifier {
-
-    /**
-     * @ORM\Id
-     * @ORM\GeneratedValue(strategy="AUTO")
-     * @ORM\Column(type="integer")
-     */
-    protected $id;
-
-    /** @ORM\Column(type="string", length=255) */
-    protected $user_id;
-
-    /**
-     * @ORM\OneToMany(targetEntity="VerificationRequest", mappedBy="verifier")
-     */
-    protected $verification_requests;
-
-    public function __construct() {
-        $this->verification_requests = new ArrayCollection();
-        return $this;
-    }
-
-    public function getId() {
-        return $this->id;
-    }
-
-    public function setId($id) {
-        $this->id = $id;
-    }
-    
-    public function getUserId() {
-        return $this->user_id;
-    }
-    
-    public function setUserId($uid) {
-        $this->user_id = $uid;
-    }
-
-    public function getVerificationRequests() {
-        return $this->verification_requests;
-    }
-
-}
diff --git a/pp3/module/Application/src/Application/Entity/Plugin.php b/pp3/module/Application/src/Application/Entity/Plugin.php
index 9e24daf..1ebffd9 100644
--- a/pp3/module/Application/src/Application/Entity/Plugin.php
+++ b/pp3/module/Application/src/Application/Entity/Plugin.php
@@ -133,11 +133,6 @@ class Plugin extends Base\Plugin {
         }
     }
 
-    public function getAuthorName() {
-        $split = explode('@', $this->getAuthor());
-        return $split[0];
-    }
-
     public function incrementDownloadCounter() {
         $this->downloads++;
     }
@@ -147,7 +142,7 @@ class Plugin extends Base\Plugin {
     }
 
     public function isOwnedBy($userId) {
-        return $this->author == $userId;
+        return $this->getAuthor()->getId() == $userId;
     }
 
     public function setUrl($url) {
diff --git a/pp3/module/Application/src/Application/Entity/Verifier.php b/pp3/module/Application/src/Application/Entity/User.php
similarity index 58%
rename from pp3/module/Application/src/Application/Entity/Verifier.php
rename to pp3/module/Application/src/Application/Entity/User.php
index 6e183fd..755ef79 100644
--- a/pp3/module/Application/src/Application/Entity/Verifier.php
+++ b/pp3/module/Application/src/Application/Entity/User.php
@@ -6,8 +6,8 @@ use Doctrine\ORM\Mapping as ORM;
 
 /**
  * @ORM\Entity
- * @ORM\Table(name="verifier")
+ * @ORM\Table(name="user")
  */
-class Verifier extends Base\Verifier {
+class User extends Base\User {
 
 }
diff --git a/pp3/module/Application/src/Application/Entity/Verification.php b/pp3/module/Application/src/Application/Entity/Verification.php
index c003f89..71da1a3 100644
--- a/pp3/module/Application/src/Application/Entity/Verification.php
+++ b/pp3/module/Application/src/Application/Entity/Verification.php
@@ -21,7 +21,11 @@ class Verification extends Base\Verification {
     public function addVerificationRequest($req) {
         $this->verification_requests[] = $req;
     }
-    
+
+    /**
+     * @param User[] $verifiers
+     * @param Plugin $plugin
+     */
     public function createRequests($verifiers, $plugin) {
         foreach($verifiers as $verifier) {
             $req = new VerificationRequest();
@@ -30,7 +34,6 @@ class Verification extends Base\Verification {
             $req->setVerifier($verifier);
             $req->setVote(VerificationRequest::VOTE_UNDECIDED);
             $this->addVerificationRequest($req);
-            $req->sendVerificationMail($plugin);
         }
     }
 
diff --git a/pp3/module/Application/src/Application/Entity/VerificationRequest.php b/pp3/module/Application/src/Application/Entity/VerificationRequest.php
index e2525d2..86a8a8d 100644
--- a/pp3/module/Application/src/Application/Entity/VerificationRequest.php
+++ b/pp3/module/Application/src/Application/Entity/VerificationRequest.php
@@ -16,9 +16,6 @@ class VerificationRequest extends Base\VerificationRequest {
     const VOTE_UNDECIDED = 0;
 
     public function sendVerificationMail($plugin) {
-        // TODO - remove when we have auth ready
-        return;
-
         $body = 'Hello verifier,
 
 this is to inform you about new verification request requiring your immediate attention.
@@ -38,7 +35,7 @@ P.S.: This is an automatic email. DO NOT REPLY to this email.';
         $mail = new Mail\Message();
         $mail->setBody($body);
         $mail->setFrom('webmaster@netbeans.apache.org', 'NetBeans webmaster');
-        $mail->addTo($this->getVerifier()->getUserId());
+        $mail->addTo($this->getVerifier()->getEmail());
         $mail->setSubject('Verification request for NetBeans plugin: '.$plugin->getName());
         $transport = new Mail\Transport\Sendmail();
         
diff --git a/pp3/module/Application/src/Application/Factory/AdminControllerFactory.php b/pp3/module/Application/src/Application/Factory/AdminControllerFactory.php
index bcb8f72..cff79e7 100755
--- a/pp3/module/Application/src/Application/Factory/AdminControllerFactory.php
+++ b/pp3/module/Application/src/Application/Factory/AdminControllerFactory.php
@@ -9,7 +9,7 @@ use Application\Repository\PluginRepository;
 use Application\Repository\PluginVersionRepository;
 use Application\Repository\NbVersionPluginVersionRepository;
 use Application\Repository\VerificationRepository;
-use Application\Repository\VerifierRepository;
+use Application\Repository\UserRepository;
 use Application\Repository\VerificationRequestRepository;
 use Application\Repository\NbVersionRepository;
 use Application\Repository\CategoryRepository;
@@ -24,9 +24,9 @@ class AdminControllerFactory implements FactoryInterface
         
         $vrepository = new VerificationRepository();
         $vrepository->setEntityManager($em);
-       
-        $verifierRepository = new VerifierRepository();
-        $verifierRepository->setEntityManager($em);
+
+        $userRepository = new UserRepository();
+        $userRepository->setEntityManager($em);
 
         $verificationRequestRepository = new VerificationRequestRepository();
         $verificationRequestRepository->setEntityManager($em);
@@ -42,11 +42,13 @@ class AdminControllerFactory implements FactoryInterface
 
         $categoryRepository = new CategoryRepository();
         $categoryRepository->setEntityManager($em);
-        
+
         $config = $serviceLocator->getServiceLocator()->get('config');
 
         return new AdminController($pluginRepository, $repository, $vrepository, 
-                                    $verifierRepository, $verificationRequestRepository, 
-                                    $nbVersionRepository, $pluginVersionRepository, $config, $categoryRepository);
+                                    $verificationRequestRepository,
+                                    $nbVersionRepository,
+                                    $pluginVersionRepository, $config,
+                                    $categoryRepository, $userRepository);
     }
 }
diff --git a/pp3/module/Application/src/Application/Factory/LoginControllerFactory.php b/pp3/module/Application/src/Application/Factory/LoginControllerFactory.php
index f0e2431..3b49893 100755
--- a/pp3/module/Application/src/Application/Factory/LoginControllerFactory.php
+++ b/pp3/module/Application/src/Application/Factory/LoginControllerFactory.php
@@ -5,7 +5,7 @@ namespace Application\Factory;
 use Zend\ServiceManager\ServiceLocatorInterface;
 use Zend\ServiceManager\FactoryInterface;
 use Application\Controller\LoginController;
-use Application\Repository\VerifierRepository;
+use Application\Repository\UserRepository;
 
 class LoginControllerFactory implements FactoryInterface
 {
@@ -14,9 +14,9 @@ class LoginControllerFactory implements FactoryInterface
 
         $config = $serviceLocator->getServiceLocator()->get('config');
         $em = $serviceLocator->getServiceLocator()->get('Doctrine\ORM\EntityManager');
-        $verifierRepository = new VerifierRepository();
-        $verifierRepository->setEntityManager($em);
+        $userRepository = new UserRepository();
+        $userRepository->setEntityManager($em);
 
-        return new LoginController($config, $verifierRepository);
+        return new LoginController($config, $userRepository);
     }
 }
diff --git a/pp3/module/Application/src/Application/Factory/PluginControllerFactory.php b/pp3/module/Application/src/Application/Factory/PluginControllerFactory.php
index bd35002..66f0455 100755
--- a/pp3/module/Application/src/Application/Factory/PluginControllerFactory.php
+++ b/pp3/module/Application/src/Application/Factory/PluginControllerFactory.php
@@ -8,7 +8,7 @@ use Application\Repository\PluginRepository;
 use Application\Repository\PluginVersionRepository;
 use Application\Repository\CategoryRepository;
 use Application\Controller\PluginController;
-use Application\Repository\VerifierRepository;
+use Application\Repository\UserRepository;
 use Application\Repository\NbVersionRepository;
 
 class PluginControllerFactory implements FactoryInterface
@@ -27,12 +27,12 @@ class PluginControllerFactory implements FactoryInterface
 
         $config = $serviceLocator->getServiceLocator()->get('config');
 
-        $verifierRepository = new VerifierRepository();
-        $verifierRepository->setEntityManager($em);
+        $userRepository = new UserRepository();
+        $userRepository->setEntityManager($em);
 
         $nbVersionRepository = new NbVersionRepository();
         $nbVersionRepository->setEntityManager($em);
 
-        return new PluginController($repository, $pvRepository, $categRepository, $config, $verifierRepository, $nbVersionRepository);
+        return new PluginController($repository, $pvRepository, $categRepository, $config, $nbVersionRepository, $userRepository);
     }
 }
diff --git a/pp3/module/Application/src/Application/Factory/VerificationControllerFactory.php b/pp3/module/Application/src/Application/Factory/VerificationControllerFactory.php
index 64dc946..853fef2 100755
--- a/pp3/module/Application/src/Application/Factory/VerificationControllerFactory.php
+++ b/pp3/module/Application/src/Application/Factory/VerificationControllerFactory.php
@@ -7,7 +7,7 @@ use Zend\ServiceManager\FactoryInterface;
 use Application\Controller\VerificationController;
 use Application\Repository\NbVersionPluginVersionRepository;
 use Application\Repository\VerificationRepository;
-use Application\Repository\VerifierRepository;
+use Application\Repository\UserRepository;
 use Application\Repository\VerificationRequestRepository;
 use Application\Repository\PluginVersionRepository;
 
@@ -22,8 +22,8 @@ class VerificationControllerFactory implements FactoryInterface
         $vrepository = new VerificationRepository();
         $vrepository->setEntityManager($em);
        
-        $verifierRepository = new VerifierRepository();
-        $verifierRepository->setEntityManager($em);
+        $userRepository = new UserRepository();
+        $userRepository->setEntityManager($em);
 
         $verificationRequestRepository = new VerificationRequestRepository();
         $verificationRequestRepository->setEntityManager($em);
@@ -32,6 +32,6 @@ class VerificationControllerFactory implements FactoryInterface
         $pluginVersionRepository = new PluginVersionRepository();
         $pluginVersionRepository->setEntityManager($em);
 
-        return new VerificationController($repository, $vrepository, $verifierRepository, $verificationRequestRepository, $config, $pluginVersionRepository);
+        return new VerificationController($repository, $vrepository, $userRepository, $verificationRequestRepository, $config, $pluginVersionRepository);
     }
 }
diff --git a/pp3/module/Application/src/Application/Pp/Catalog.php b/pp3/module/Application/src/Application/Pp/Catalog.php
index 2a805d9..649c1ae 100644
--- a/pp3/module/Application/src/Application/Pp/Catalog.php
+++ b/pp3/module/Application/src/Application/Pp/Catalog.php
@@ -46,7 +46,7 @@ class Catalog {
             $moduleElement->setAttribute(self::REQ_ATTRS_MODULE_distribution, $this->_downloadPath.$item->getId());
             $moduleElement->setAttribute(self::REQ_ATTRS_MODULE_downloadsize, '1024');
             $moduleElement->setAttribute('targetcluster', 'nbms');            
-            $moduleElement->setAttribute('moduleauthor', $item->getPlugin()->getAuthor());            
+            $moduleElement->setAttribute('moduleauthor', $item->getPlugin()->getAuthor()->getEmail());
             
             $manifestElement =$xml->createElement('manifest');  
             $manifestElement->setAttribute(self::REQ_ATTRS_MANIFEST_OpenIDE_Module, $item->getPlugin()->getArtifactId());            
diff --git a/pp3/module/Application/src/Application/Repository/NbVersionRepository.php b/pp3/module/Application/src/Application/Repository/NbVersionRepository.php
index 73c2a2c..c5fee31 100644
--- a/pp3/module/Application/src/Application/Repository/NbVersionRepository.php
+++ b/pp3/module/Application/src/Application/Repository/NbVersionRepository.php
@@ -27,12 +27,11 @@ class NbVersionRepository extends DoctrineEntityRepository {
         ->from('Application\Entity\NbVersion', 'nbv')
         ->join('nbv.nbVersionsPluginVersions', 'nbvpv')
         ->join('nbvpv.pluginVersion', 'pv')
-        ->join('pv.plugin p WITH p.id = :pid')
-        ->join('nbvpv.verification v WITH v.status >= 0')
+        ->join('pv.plugin', 'p', 'WITH', 'p.id = :pid')
+        ->join('nbvpv.verification', 'v', 'WITH', 'v.status >= 0')
         ->setParameter('pid', $pluginId)
         ->groupBy('nbv.id');
 
         return $queryBuilder->getQuery()->getResult();
-        // return $queryBuilder->getQuery()->getSQL();
     }
 }
diff --git a/pp3/module/Application/src/Application/Repository/PluginRepository.php b/pp3/module/Application/src/Application/Repository/PluginRepository.php
index c743b47..d25e2a5 100644
--- a/pp3/module/Application/src/Application/Repository/PluginRepository.php
+++ b/pp3/module/Application/src/Application/Repository/PluginRepository.php
@@ -48,16 +48,18 @@ class PluginRepository extends DoctrineEntityRepository {
         return $queryBuilder->getQuery()->getResult();
     }
 
-    public function getPluginsByAuthor($author) {
+    public function getPluginsByAuthorId($author) {
         $queryBuilder = $this->getEntityManager()->createQueryBuilder();
         $queryBuilder->select('p, v, nbvPv, nbv, verif')
-        ->from('Application\Entity\Plugin', 'p')
-        ->leftJoin('p.versions', 'v')
-        ->leftJoin('v.nbVersionsPluginVersions', 'nbvPv')
-        ->leftJoin('nbvPv.nbVersion', 'nbv')
-        ->leftJoin('nbvPv.verification', 'verif')
-        ->where('p.author = :author')->orderBy('p.id', 'DESC')
-        ->setParameter('author', $author);
+                ->from('Application\Entity\Plugin', 'p')
+                ->leftJoin('p.versions', 'v')
+                ->leftJoin('v.nbVersionsPluginVersions', 'nbvPv')
+                ->leftJoin('nbvPv.nbVersion', 'nbv')
+                ->leftJoin('nbvPv.verification', 'verif')
+                ->leftJoin('p.author', 'a')
+                ->where('a.id = :author')
+                ->orderBy('p.id', 'DESC')
+                ->setParameter('author', $author);
         return $queryBuilder->getQuery()->getResult();        
     }
 
@@ -79,8 +81,9 @@ class PluginRepository extends DoctrineEntityRepository {
         ->from('Application\Entity\Plugin', 'p')
         ->leftJoin('p.versions', 'v')
         ->leftJoin('v.nbVersionsPluginVersions', 'nbvPv')
-        ->leftJoin('nbvPv.nbVersion', 'nbv')     
-        ->where('(p.name LIKE :name) OR (p.author LIKE :name)')->orderBy('p.id', 'ASC')
+        ->leftJoin('nbvPv.nbVersion', 'nbv')
+        ->leftJoin('p.author', 'a')
+        ->where('(p.name LIKE :name) OR (a.email LIKE :name)')->orderBy('p.id', 'ASC')
         ->setParameter('name', '%'.$name.'%');
         return $queryBuilder->getQuery()->getResult();
     }
@@ -94,10 +97,11 @@ class PluginRepository extends DoctrineEntityRepository {
         ->leftJoin('nbvPv.verification', 'verif')
         ->leftJoin('nbvPv.nbVersion', 'nbv')
         ->leftJoin('p.categories', 'cat')
+        ->leftJoin('p.author', 'a')
         ->where('p.status = :status')
         ->setParameter('status', \Application\Entity\Plugin::STATUS_PUBLIC);
         if ($name) {
-            $queryBuilder->andWhere('(p.name LIKE :name OR p.artifactid LIKE :name OR p.author LIKE :name OR p.short_description LIKE :name OR p.description LIKE :name
+            $queryBuilder->andWhere('(p.name LIKE :name OR p.artifactid LIKE :name OR a.email LIKE :name OR p.short_description LIKE :name OR p.description LIKE :name
             OR p.license LIKE :name OR nbv.version LIKE :name OR cat.name LIKE :name)')->setParameter('name', '%'.$name.'%');
         }     
         //$queryBuilder->orderBy('p.name', 'ASC');
diff --git a/pp3/module/Application/src/Application/Repository/UserRepository.php b/pp3/module/Application/src/Application/Repository/UserRepository.php
new file mode 100644
index 0000000..fc15126
--- /dev/null
+++ b/pp3/module/Application/src/Application/Repository/UserRepository.php
@@ -0,0 +1,64 @@
+<?php
+
+namespace Application\Repository;
+
+use Application\Entity\User;
+
+class UserRepository extends DoctrineEntityRepository {
+
+    public function getEntityRepository() {
+        if (null === $this->entityRepository) {
+            $this->setEntityRepository($this->getEntityManager()->getRepository('Application\Entity\User'));
+        }
+        return $this->entityRepository;
+    }
+
+    /**
+     * @param string $idpProviderId id of the identity provider
+     * @param string $idpUserId id of the user in the scope of the provider
+     * @return User
+     */
+    public function findByIdpData($idpProviderId, $idpUserId) {
+        $queryBuilder = $this->getEntityManager()->createQueryBuilder();
+        $queryBuilder->select('user')
+                ->from('Application\Entity\User', 'user')
+                ->where('user.idpProviderId = :idpProviderId AND user.idpUserId = :idpUserId')
+                ->setParameter('idpProviderId', $idpProviderId)
+                ->setParameter('idpUserId', $idpUserId);
+        return $queryBuilder->getQuery()->getOneOrNullResult();
+    }
+
+    /**
+     * @return User[] list of users, that have verifier status
+     */
+    public function findVerifier() {
+        $queryBuilder = $this->getEntityManager()->createQueryBuilder();
+        $queryBuilder->select('user')
+                ->from('Application\Entity\User', 'user')
+                ->where('user.verifier = true');
+        return $queryBuilder->getQuery()->getResult();
+    }
+
+    /**
+     * @return User[] list of users, that have admin status
+     */
+    public function findAdmins() {
+        $queryBuilder = $this->getEntityManager()->createQueryBuilder();
+        $queryBuilder->select('user')
+                ->from('Application\Entity\User', 'user')
+                ->where('user.admin = true');
+        return $queryBuilder->getQuery()->getResult();
+    }
+
+    /**
+     * @return User[] list of users, that are registered with the supplied email
+     */
+    public function findByEmail($email) {
+        $queryBuilder = $this->getEntityManager()->createQueryBuilder();
+        $queryBuilder->select('user')
+                ->from('Application\Entity\User', 'user')
+                ->where('lower(user.email) = :email')
+                ->setParameter('email', mb_strtolower($email, 'UTF-8'));
+        return $queryBuilder->getQuery()->getResult();
+    }
+}
diff --git a/pp3/module/Application/src/Application/Repository/VerificationRequestRepository.php b/pp3/module/Application/src/Application/Repository/VerificationRequestRepository.php
index 186e10a..1332016 100644
--- a/pp3/module/Application/src/Application/Repository/VerificationRequestRepository.php
+++ b/pp3/module/Application/src/Application/Repository/VerificationRequestRepository.php
@@ -29,7 +29,7 @@ class VerificationRequestRepository extends DoctrineEntityRepository {
         ->join('nbVersionPluginVersion.pluginVersion', 'pluginVersion')
         ->join('nbVersionPluginVersion.nbVersion', 'nbVersion')
         ->join('pluginVersion.plugin', 'plugin')
-        ->where('verifier.user_id = :userId')
+        ->where('verifier.id = :userId')
         ->andWhere('verification.status IN (:status)')
         ->orderBy('vrq.created_at', 'DESC')
         ->setParameter('status', array(\Application\Entity\Verification::STATUS_REQUESTED, \Application\Entity\Verification::STATUS_PENDING))
diff --git a/pp3/module/Application/src/Application/Repository/VerifierRepository.php b/pp3/module/Application/src/Application/Repository/VerifierRepository.php
deleted file mode 100644
index fb7e468..0000000
--- a/pp3/module/Application/src/Application/Repository/VerifierRepository.php
+++ /dev/null
@@ -1,23 +0,0 @@
-<?php
-
-namespace Application\Repository;
-
-class VerifierRepository extends DoctrineEntityRepository {
-
-    public function getEntityRepository() {
-        if (null === $this->entityRepository) {
-            $this->setEntityRepository($this->getEntityManager()->getRepository('Application\Entity\Verifier'));
-        }
-        return $this->entityRepository;
-    }
-
-    public function findOneBy($column, $value) {
-        $queryBuilder = $this->getEntityManager()->createQueryBuilder();
-        $queryBuilder->select('verifier')
-        ->from('Application\Entity\Verifier', 'verifier')       
-        ->where('verifier.'.$column.' = :value')
-        ->setParameter('value', $value);    
-
-        return $queryBuilder->getQuery()->getOneOrNullResult();    
-    }
-}
diff --git a/pp3/module/Application/view/application/admin/_plugin-listrow.phtml b/pp3/module/Application/view/application/admin/_plugin-listrow.phtml
index 646b64b..f6cf3a7 100644
--- a/pp3/module/Application/view/application/admin/_plugin-listrow.phtml
+++ b/pp3/module/Application/view/application/admin/_plugin-listrow.phtml
@@ -24,7 +24,7 @@ echo '<tr>
     <p>Status: <b>'.$plugin->getStatusTitle().'</b></p>
     <div>
         <p>ArtifactId: <b>'.$plugin->getArtifactId().'</b><p>
-        <p>Author: <b>'.$plugin->getAuthor().'</b><p>
+        <p>Author: <b>'.$plugin->getAuthor()->getName().'</b><p>
         <p>
         <i class="fas fa-asterisk"></i> '.$plugin->getAddedAt()->format('Y-m-d').' &nbsp; &nbsp;
         <i class="fas fa-edit"></i> '.$plugin->getLastUpdatedAt()->format('Y-m-d').' &nbsp; &nbsp; 
@@ -78,7 +78,12 @@ foreach ($plugin->getVersions() as $version) {
                                 <table class="table table-striped">'; 
                             foreach($nbvPv->getVerification()->getVerificationRequests() as $vrq) {
                                 echo '<tr>
-                                        <td>'.$vrq->getVerifier()->getuserId().'</td>
+                                        <td>'
+                                            . htmlentities($vrq->getVerifier()->getName(), ENT_HTML5, 'UTF-8')
+                                            . ' &lt;'
+                                            . htmlentities($vrq->getVerifier()->getEmail(), ENT_HTML5, 'UTF-8')
+                                            . '&gt;'
+                                            .'</td>
                                         <td><span class="badge '.$vrq->getVoteBadgeClass().'" title="'.$vrq->getVoteBadgeTitle().'">'.$vrq->getVoteBadgeTitle().'</span></td>
                                         <td>'.($vrq->getVotedAt() ? $vrq->getVotedAt()->format('Y-m-d') : '').'</td>
                                     </tr>';
diff --git a/pp3/module/Application/view/application/admin/_pluginRowItem.phtml b/pp3/module/Application/view/application/admin/_pluginRowItem.phtml
index b4d0d0e..f7c0fca 100644
--- a/pp3/module/Application/view/application/admin/_pluginRowItem.phtml
+++ b/pp3/module/Application/view/application/admin/_pluginRowItem.phtml
@@ -12,7 +12,7 @@ echo '
 <p>ArtifactId: <b>'.$plugin->getArtifactId().'</b><p>
 <p>
 <p>Status: <b>'.$plugin->getStatusTitle().'</b></p>
-<i class="fas fa-user"></i> '.$plugin->getAuthorName().' &nbsp; &nbsp;&nbsp; 
+<i class="fas fa-user"></i> '.$plugin->getAuthor()->getName().' &nbsp; &nbsp;&nbsp;
 <i class="fas fa-asterisk"></i> '.$plugin->getAddedAt()->format('Y-m-d').' &nbsp; &nbsp;&nbsp; 
 <i class="fas fa-edit"></i> '.$plugin->getLastUpdatedAt()->format('Y-m-d').'   &nbsp; &nbsp;  &nbsp;            
 <i class="fas fa-file-contract"></i> '.$plugin->getLicense().'
diff --git a/pp3/module/Application/view/application/admin/verifiers.phtml b/pp3/module/Application/view/application/admin/verifiers.phtml
index 00bd346..222a652 100644
--- a/pp3/module/Application/view/application/admin/verifiers.phtml
+++ b/pp3/module/Application/view/application/admin/verifiers.phtml
@@ -1,29 +1,82 @@
 <?= $this->partial('_nav.phtml'); ?>
-<h4>Verifiers</h4>
+<h4>New verifier</h4>
 <p>
 <?= $this->partial('layout/flash.phtml'); ?>
 </p>
 <p>
 <form class="form-inline" method="post" action="">
   <div class="form-group">
-    <label for="exampleInputName2">New verifier: </label>
-    <input type="text" class="form-control" name="userId" id="search" placeholder="User ID" style="width: 300px;" value="">
+    <label for="search">Search: </label>
+    <input type="text" class="form-control" name="email" id="search" placeholder="Email" style="width: 300px;" value="">
   </div>
-  <button type="submit" class="btn btn-primary">Add Verifier</button>
+  <button type="button" id="searchAccount" class="btn btn-primary">Search Account</button>
+  <table class="table table-striped table-hover" id="addVerifierStatusList" style="margin-top: 1ex; margin-bottom: 1ex">
+
+  </table>
 </form>
 </p>
-<br/>
-<table class="table table-striped table-hover" style="width: 50%">
+<h4>Existing verifiers</h4>
+<table class="table table-striped table-hover">
 <?php
     foreach ($this->verifiers as $v) {
-        echo '<tr>
-            <td><h5>'.$v->getUserId().'</h5></td>            
-            <td>
-                <a class="btn btn-danger" href="'.$this->url('admin',array('action'=>'verifiers'),array('query' => array('id'=>$v->getId()))).'" role="button" title="Delete">
-                    Delete
-                </a>
-            </td>
-        </tr>';
+        printf('
+                <tr>
+                    <td>%s &lt;%s&gt; (ID: %d, IdP: %s)</td>
+                    <td>
+                        <form action="%s" method="POST">
+                            <button type="submit" class="btn btn-danger" role="button" name="removeVerifierStatusId" value="%d">
+                            Remove verifier status
+                            </button>
+                        </form>
+                    </td>
+                </tr>',
+                htmlentities($v->getName(), ENT_HTML5, 'UTF-8'),
+                htmlentities($v->getEmail(), ENT_HTML5, 'UTF-8'),
+                $v->getId(),
+                htmlentities($v->getIdpProviderId(), ENT_HTML5, 'UTF-8'),
+                $this->url('admin',array('action'=>'verifiers')),
+                $v->getId()
+        );
     }
 ?>
-</table>
\ No newline at end of file
+</table>
+
+<script>
+    $(document).ready(function() {
+        var inputField = $('#search');
+        var searchButton = $('#searchAccount');
+        var resultList = $('#addVerifierStatusList');
+
+        function updateAddVerifierStatusList() {
+            var url = BASE_URL;
+            url += 'admin/search-user-by-email?email=';
+            url += encodeURIComponent(inputField.val());
+            $.ajax(url, {dataType: 'json'}).done(function(data) {
+                resultList.empty();
+                for(var idx in data) {
+                    var row = data[idx];
+                    if(row['verifier']) {
+                        continue;
+                    }
+                    resultList
+                            .append(
+                                $("<tr></tr>").append(
+                                    $('<td></td>')
+                                        .text(row['name'] + "<" + row['email'] + "> (ID: " + row['id'] + ", IdP: " + row['idpProviderId'] + ")"),
+                                    $('<td></td>')
+                                        .append($('<form action="%s" method="POST"></form>')
+                                            .attr('action', BASE_URL + "admin/verifiers")
+                                            .append($('<button type="submit" class="btn btn-success" role="button" name="addVerifierStatusId">')
+                                                .attr('value', row['id'])
+                                                .text('Add verifier status')
+                                            )
+                                            )
+                            )
+                    );
+                }
+            });
+        }
+
+        searchButton.click(updateAddVerifierStatusList);
+    });
+</script>
\ No newline at end of file
diff --git a/pp3/module/Application/view/application/index/catalogue.phtml b/pp3/module/Application/view/application/index/catalogue.phtml
index 545eb9d..d61c271 100644
--- a/pp3/module/Application/view/application/index/catalogue.phtml
+++ b/pp3/module/Application/view/application/index/catalogue.phtml
@@ -18,7 +18,7 @@ if ($plugin) {
         }    
         
     echo '</p>
-    <p>Author: <b>'.$plugin->getAuthorName().'</b></p>
+    <p>Author: <b>'.$plugin->getAuthor()->getName().'</b></p>
     <p>License: <b>'.$plugin->getLicense().'</b></p>
     <p>Homepage: <a href="'.$plugin->getHomepage().'">'.$plugin->getHomepage().'</a></p>
     <p>
diff --git a/pp3/module/Application/view/application/index/index.phtml b/pp3/module/Application/view/application/index/index.phtml
index f622f65..5f1f61d 100755
--- a/pp3/module/Application/view/application/index/index.phtml
+++ b/pp3/module/Application/view/application/index/index.phtml
@@ -52,7 +52,7 @@
           <option value="">Any</option>
           <?php
           foreach($this->versions as $version) {
-              $sel = ($_GET['nbv'] == $version->getVersion())? 'selected' : ''; 
+              $sel = (array_key_exists('nbv', $_GET) && $_GET['nbv'] == $version->getVersion()) ? 'selected' : '';
               echo '<option value="'.$version->getVersion().'" '.$sel.'>'.$version->getVersion().'</option>';
           }
           ?>
@@ -64,7 +64,7 @@
           <option value="">Any</option>
           <?php
           foreach($this->categories as $cat) {
-              $sel = ($_GET['cat'] == $cat->getName())? 'selected' : ''; 
+              $sel = (array_key_exists('cat', $_GET) && $_GET['cat'] == $cat->getName()) ? 'selected' : '';
               echo '<option value="'.$cat->getName().'" '.$sel.'>'.$cat->getName().'</option>';
           }
           ?>
@@ -96,7 +96,7 @@
         <td>
             <h4 class="text-primary"><a href="'.$this->url('catalogue', array(), array('query' => array('id'=>$plugin->getId()))).'">'.$plugin->getName().'</a></h4>
             <p>ArtifactId: <b>'.$plugin->getArtifactId().'</b> &nbsp; &nbsp; <i class="fas fa-download"></i> '.number_format($plugin->getDownloads()).' <p>
-            <p>Author: <b>'.$plugin->getAuthorName().'</b></p> 
+            <p>Author: <b>'.$plugin->getAuthor()->getName().'</b></p>
             <p>License: <b>'.$plugin->getLicense().'</b></p> 
             <p>'.implode('&nbsp;', $versionBadges).'</p>   
         </td>
diff --git a/pp3/module/Application/view/application/login/index.phtml b/pp3/module/Application/view/application/login/index.phtml
index 319bed5..0c622ba 100644
--- a/pp3/module/Application/view/application/login/index.phtml
+++ b/pp3/module/Application/view/application/login/index.phtml
@@ -1,16 +1,17 @@
-
-  <div class="row">
-    <div class="col-sm-3">
-      <form action="" method="post">
-        <div class="form-group">
-          <label for="exampleInputEmail1">Email</label>
-          <input type="email" class="form-control" id="email" aria-describedby="emailHelp" placeholder="Email">
-        </div>
-        <div class="form-group">
-          <label for="exampleInputPassword1">Heslo</label>
-          <input type="password" class="form-control" id="password" placeholder="Heslo">
-        </div>
-        <button type="submit" class="btn btn-primary">Přihlásit</button>
-      </form>
-    </div>   
-  </div>
+<div class="panel panel-default">
+    <div class="panel-heading">
+        <h3 class="panel-title">Login Provider</h3>
+    </div>
+    <div class="panel-body">
+        <ul class="list-group" id="loginProviderList">
+            <?php foreach ($this->providers as $p) { ?>
+                <li class="list-group-item">
+                    <a href='<?= htmlentities($p['loginUrl'], ENT_HTML5, 'UTF-8') ?>' style='display: block'>
+                        <div style='background-image: url(<?= htmlentities($p['iconUrl'], ENT_HTML5, 'UTF-8') ?>);' class="providerIcon"></div>
+                        <?= htmlentities($p['name'], ENT_HTML5, 'UTF-8') ?>
+                    </a>
+                </li>
+            <?php } ?>
+        </ul>
+    </div>
+</div>
\ No newline at end of file
diff --git a/pp3/module/Application/view/application/plugin/_plugin-form.phtml b/pp3/module/Application/view/application/plugin/_plugin-form.phtml
index ea07e10..794e29d 100644
--- a/pp3/module/Application/view/application/plugin/_plugin-form.phtml
+++ b/pp3/module/Application/view/application/plugin/_plugin-form.phtml
@@ -1,9 +1,5 @@
 
     <div class="form-group">
-        <label for="url">Author <span class="text-primary">*</span></label>
-        <input type="email" class="form-control" id="author" name="author" required placeholder="Author" value="<?= $this->plugin->getAuthor() ?>" xreadonly="true">
-    </div>
-    <div class="form-group">
         <label for="urgroupIdl">GroupId</label>
         <input type="text" class="form-control" id="groupId" name="groupid" placeholder="Enter groupId" value="<?= $this->plugin->getGroupId() ?>" readonly="true">
     </div>
diff --git a/pp3/module/Application/view/layout/layout.phtml b/pp3/module/Application/view/layout/layout.phtml
index 3bef292..fa29238 100755
--- a/pp3/module/Application/view/layout/layout.phtml
+++ b/pp3/module/Application/view/layout/layout.phtml
@@ -3,7 +3,6 @@
 <html lang="en">
     <head>
         <meta charset="utf-8">
-        <meta name="google-signin-client_id" content="432862904114-ierlf9j6qmmuhtd44ecmlfqivlirq7uc.apps.googleusercontent.com">
         <?php echo $this->headTitle('Apache NetBeans Plugin Portal ') ?>
 
         <?php
@@ -41,7 +40,6 @@
                         array('conditional' => 'lt IE 9',))
         ;
         ?>
-        <script src="https://apis.google.com/js/platform.js?onload=onLoad" async defer></script>
         <script type="text/javascript">
             const BASE_URL = '<?= $this->url('home')?>';
         </script>   
@@ -62,36 +60,34 @@
                 <div class="collapse navbar-collapse">
                     <ul class="nav navbar-nav" id="mynav">
                         <li><a href="<?= $this->url('home') ?>">Plugin Catalog</a></li>
-                        <?php if ($_SESSION['sessionUserId']) { ?>
+                        <?php if ($this->isAuthenticated) { ?>
                         <li><a href="<?= $this->url('plugin') ?>">Add plugin</a></li>
-                        <li ><a href="<?= $this->url('plugin', array('action' => 'list'))?>">My plugins</a></li>
-                        
-                        <?php
-                            if ($_SESSION['isVerifier']) {
-                                echo '<li><a href="'.$this->url('verification', array('action' => 'list')).'">Verification requests</a></li>';
-                            }
-                            ?>
-                            <?php } ?>
-                            <?php
-                        if ($_SESSION['isAdmin']) {
-                            echo '<li><a href="'.$this->url('admin').'">Admin</a></li>';
-                        }
-                        ?>
+                        <li><a href="<?= $this->url('plugin', array('action' => 'list'))?>">My plugins</a></li>
+
+                        <?php } ?>
+                        <?php if ($this->isVerifier) { ?>
+                            <li><a href="<?= $this->url('verification', array('action' => 'list')) ?>">Verification requests</a></li>
+                        <?php } ?>
+                        <?php if ($this->isAdmin) { ?>
+                            <li><a href="<?= $this->url('admin') ?>">Admin</a></li>';
+                        <?php } ?>
                         <li><a href="https://cwiki.apache.org/confluence/display/NETBEANS/How+to+get+plugin+on+Plugin+Portal+Update+Center">Help</a></li>
                     </ul>
-                    <div class="pull-right">
-                        <div style="margin: 7px 20px;">
-                        <?php                        
-                            if (!$_SESSION['sessionUserId']) {                            
-                                echo '<div class="g-signin2" data-onsuccess="onSignIn" approvalprompt="force" ></div>';
+                    <ul class="nav navbar-nav pull-right">
+                        <li>
+                        <?php
+                            if (!(array_key_exists('sessionUserId', $_SESSION) && $_SESSION['sessionUserId'])) {
+                                echo '<a href="'.$this->url('login').'">Login</a>';
                             } else {
-                                echo '<div style = "display:none" align="middle" class="g-signin2" data-cookiepolicy=\'single_host_origin\' data-onsuccess="onSignIn"></div>';
-                                echo '<a href="#" onclick="signOut();" class="btn btn-default" role="button">Sign out '.$_SESSION['sessionUserId'].'</a>';
-                                // echo '<span class="badge" style="display:inline-block;margin-top: 10px;">'.$_SESSION['sessionUserId'].'</a>';
+                                printf('<a href="%s" style="text-align: right; padding: 0.5ex">Logout %s (%s)<br />%s</a>',
+                                       $this->url('login', array('action' => 'logout')),
+                                       htmlentities($this->sessionUserName, ENT_HTML5, 'UTF-8'),
+                                       htmlentities($this->sessionIdp, ENT_HTML5, 'UTF-8'),
+                                       htmlentities($this->sessionUserEmail, ENT_HTML5, 'UTF-8'));
                             }
                         ?>
-                        </div>
-                    </div>
+                        </li>
+                    </ul>
                 </div><!--/.nav-collapse -->
                
             </div>
diff --git a/pp3/module/Application/view/partials/_categories-select.phtml b/pp3/module/Application/view/partials/_categories-select.phtml
index 758495e..896e5d2 100644
--- a/pp3/module/Application/view/partials/_categories-select.phtml
+++ b/pp3/module/Application/view/partials/_categories-select.phtml
@@ -4,6 +4,8 @@
 foreach ($this->categories as $cat) {
     if ($this->selected) {
         $sel = $cat->getId() == $this->selected->getId() ? 'selected' : '';
+    } else {
+        $sel = '';
     }
     echo '<option value="'.$cat->getId().'" '.$sel.'>'.$cat->getName().'</option>';
 }
diff --git a/pp3/public/img/favicon.ico b/pp3/public/img/favicon.ico
deleted file mode 100755
index 17f1199..0000000
Binary files a/pp3/public/img/favicon.ico and /dev/null differ
diff --git a/pp3/public/img/login/amazon.svg b/pp3/public/img/login/amazon.svg
new file mode 100644
index 0000000..80a1358
--- /dev/null
+++ b/pp3/public/img/login/amazon.svg
@@ -0,0 +1,30 @@
+<?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.
+
+-->
+<!--
+- Icon was downloaded from https://github.com/simple-icons/simple-icons/blob/4b882220efc78dc824c7647a81b47dd1d4fdd3d8/icons/amazon.svg
+- License is CC0 1.0 Universal: https://github.com/simple-icons/simple-icons/blob/7d037d95f91847634536a592f1126c4ff040fc4d/LICENSE.md
+- File was relicensed to ALv2
+-->
+<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
+    <title>Amazon icon</title>
+    <path d="M.045 18.02c.072-.116.187-.124.348-.022 3.636 2.11 7.594 3.166 11.87 3.166 2.852 0 5.668-.533 8.447-1.595l.315-.14c.138-.06.234-.1.293-.13.226-.088.39-.046.525.13.12.174.09.336-.12.48-.256.19-.6.41-1.006.654-1.244.743-2.64 1.316-4.185 1.726-1.53.406-3.045.61-4.516.61-2.265 0-4.41-.396-6.435-1.187-2.02-.794-3.82-1.91-5.43-3.35-.1-.074-.15-.15-.15-.22 0-.047.02-.09.05-.13zm6.565-6.218c0-1.005.247-1.863.743-2.577.495-.71 1.17-1.25 2.04-1.615.796-.335 1.756-.575 2.912-.72.39-.04 [...]
+</svg>
\ No newline at end of file
diff --git a/pp3/public/img/login/github.svg b/pp3/public/img/login/github.svg
new file mode 100644
index 0000000..c2f1fe4
--- /dev/null
+++ b/pp3/public/img/login/github.svg
@@ -0,0 +1,30 @@
+<?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.
+
+-->
+<!--
+- Icon was downloaded from https://github.com/simple-icons/simple-icons/blob/4b882220efc78dc824c7647a81b47dd1d4fdd3d8/icons/github.svg
+- License is CC0 1.0 Universal: https://github.com/simple-icons/simple-icons/blob/7d037d95f91847634536a592f1126c4ff040fc4d/LICENSE.md
+- File was relicensed to ALv2
+-->
+<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
+    <title>GitHub icon</title>
+    <path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.2 [...]
+</svg>
\ No newline at end of file
diff --git a/pp3/public/img/login/google.svg b/pp3/public/img/login/google.svg
new file mode 100644
index 0000000..5a30d9b
--- /dev/null
+++ b/pp3/public/img/login/google.svg
@@ -0,0 +1,30 @@
+<?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.
+
+-->
+<!--
+- Icon was downloaded from https://github.com/simple-icons/simple-icons/blob/4b882220efc78dc824c7647a81b47dd1d4fdd3d8/icons/google.svg
+- License is CC0 1.0 Universal: https://github.com/simple-icons/simple-icons/blob/7d037d95f91847634536a592f1126c4ff040fc4d/LICENSE.md
+- File was relicensed to ALv2
+-->
+<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
+    <title>Google icon</title>
+    <path d="M12.24 10.285V14.4h6.806c-.275 1.765-2.056 5.174-6.806 5.174-4.095 0-7.439-3.389-7.439-7.574s3.345-7.574 7.439-7.574c2.33 0 3.891.989 4.785 1.849l3.254-3.138C18.189 1.186 15.479 0 12.24 0c-6.635 0-12 5.365-12 12s5.365 12 12 12c6.926 0 11.52-4.869 11.52-11.726 0-.788-.085-1.39-.189-1.989H12.24z"/>
+</svg>
\ No newline at end of file
diff --git a/pp3/public/img/zf2-logo.png b/pp3/public/img/zf2-logo.png
deleted file mode 100755
index 03c8dae..0000000
Binary files a/pp3/public/img/zf2-logo.png and /dev/null differ
diff --git a/pp3/public/js/script.js b/pp3/public/js/script.js
index 97e5797..40d4faa 100755
--- a/pp3/public/js/script.js
+++ b/pp3/public/js/script.js
@@ -17,57 +17,3 @@ $(function () {
     }, false);
 
 });
-
-
-function onLoad() {
-    // gapi.load('auth2', function() {
-    //   //gapi.auth2.init();
-    // });
-  }
-
-function onSignIn(googleUser) {
-    var profile = googleUser.getBasicProfile();
-    var auth2 = gapi.auth2.getAuthInstance();
-    id_token = googleUser.getAuthResponse().id_token;
-    googleUser.disconnect()
-    auth2.disconnect();
-    $.ajax({
-        type: 'post',
-        url: `${BASE_URL}login/on-google-sign-in-ajax`,
-        data: {
-            name: profile.getName(),
-            email: profile.getEmail(),
-            idtoken: id_token,
-        },
-        success: (response) => {
-            if(response === 'reload') {            
-                location.reload();
-            }            
-        },
-        failure: () => {
-            console.log('Error processing login data')
-        },
-    });
-    // console.log('ID: ' + profile.getId()); // Do not send to your backend! Use an ID token instead.
-    // console.log('Name: ' + profile.getName());
-    // console.log('Image URL: ' + profile.getImageUrl());
-    // console.log('Email: ' + profile.getEmail()); // This is null if the 'email' scope is not present.
-}
-
-function signOut() {
-    var auth2 = gapi.auth2.getAuthInstance();
-    auth2.signOut().then(function () {
-        $.ajax({
-            type: 'post',
-            url: `${BASE_URL}login/on-google-sign-out-ajax`,
-            data: {},
-            success: (response) => {
-                location.reload();                    
-                console.log('User signed out.');
-            },
-            failure: () => {
-                console.log('Error processing login data')
-            },
-        }); 
-    });
-}
diff --git a/pp3/public/scss/style.css b/pp3/public/scss/style.css
index 736832f..088b86d 100644
--- a/pp3/public/scss/style.css
+++ b/pp3/public/scss/style.css
@@ -1,69 +1,72 @@
 body {
-  padding-bottom: 20px;
-}
+  padding-bottom: 20px; }
 
 .navbar {
-  margin-bottom: 10px;
-}
+  margin-bottom: 10px; }
 
 .navbar-brand .fa {
   margin-right: 5px;
-  color: white;
-}
+  color: white; }
 
 .navbar-nav {
-  margin: 5px;
-}
+  margin: 5px; }
 
 .navbar-fixed-top {
-  z-index: 1;
-}
+  z-index: 1; }
 
 .badge-red {
-  background-color: #c12e2a;
-}
+  background-color: #c12e2a; }
 
 .badge-green {
-  background-color: #099c09;
-}
+  background-color: #099c09; }
 
 .badge-blue {
-  background-color: #269eda;
-}
+  background-color: #269eda; }
 
 .badge-brown {
-  background-color: #b59c70;
-}
+  background-color: #b59c70; }
 
 .color-red {
-  color: #c12e2a;
-}
+  color: #c12e2a; }
 
 .color-green {
-  color: #099c09;
-}
+  color: #099c09; }
 
 .admin-nav a {
   display: inline-block;
-  margin-right: 20px;
-}
+  margin-right: 20px; }
 
 .r-white {
-  color: white;
-}
+  color: white; }
 
 .navbar-inverse .navbar-nav > li > a {
-  color: #c7c7c7;
-}
+  color: #c7c7c7; }
 
 .pp-footer {
   border-top: 1px solid #ddd;
   padding-top: 20px;
   text-align: center;
-  margin-top: 20px;
-}
+  margin-top: 20px; }
 
 #pp-navbar {
-  position: relative;
-}
-/*# sourceMappingURL=style.css.map */
\ No newline at end of file
+  position: relative; }
+
+#loginProviderList .providerIcon {
+  display: inline-block;
+  vertical-align: middle;
+  width: 32px;
+  height: 32px;
+  background-size: contain;
+  margin-right: 1ex;
+  background-repeat: no-repeat; }
+
+#loginProviderList .list-group-item {
+  padding: 0; }
+  #loginProviderList .list-group-item a {
+    padding: 10px 15px;
+    text-decoration: none;
+    color: black;
+    background-color: white; }
+    #loginProviderList .list-group-item a:hover, #loginProviderList .list-group-item a:focus {
+      color: black;
+      background-color: #e8e8e8; }
diff --git a/pp3/public/scss/style.css.map b/pp3/public/scss/style.css.map
index d68979f..1fce345 100644
--- a/pp3/public/scss/style.css.map
+++ b/pp3/public/scss/style.css.map
@@ -1,9 +1,7 @@
 {
-    "version": 3,
-    "mappings": "AAAA,AAAA,IAAI,CAAC;EAED,cAAc,EAAE,IAAI;CACvB;;AAED,AAAA,OAAO,CAAC;EACJ,aAAa,EAAE,IAAI;CACtB;;AACD,AAAA,aAAa,CAAC,GAAG,CAAC;EACd,YAAY,EAAE,GAAG;EACjB,KAAK,EAAE,KAAK;CACf;;AACD,AAAA,WAAW,CAAC;EACR,MAAM,EAAE,GAAG;CACd;;AACD,AAAA,iBAAiB,CAAC;EACd,OAAO,EAAE,CAAC;CACb;;AACD,AAAA,UAAU,CAAC;EACP,gBAAgB,EAAE,OAAO;CAC5B;;AACD,AAAA,YAAY,CAAC;EACT,gBAAgB,EAAE,OAAO;CAC5B;;AACD,AAAA,WAAW,CAAC;EACR,gBAAgB,EAAC,OAAO;CAC3B;;AACD,AAAA,YAAY,CAAC;EAET,gBAAgB,EAAE,OAAO;CAC5B;;AAED,AAAA,UAAU [...]
-    "sources": [
-        "style.scss"
-    ],
-    "names": [],
-    "file": "style.css"
-}
\ No newline at end of file
+"version": 3,
+"mappings": "AAAA,IAAK;EAED,cAAc,EAAE,IAAI;;AAGxB,OAAQ;EACJ,aAAa,EAAE,IAAI;;AAEvB,iBAAkB;EACd,YAAY,EAAE,GAAG;EACjB,KAAK,EAAE,KAAK;;AAEhB,WAAY;EACR,MAAM,EAAE,GAAG;;AAEf,iBAAkB;EACd,OAAO,EAAE,CAAC;;AAEd,UAAW;EACP,gBAAgB,EAAE,OAAO;;AAE7B,YAAa;EACT,gBAAgB,EAAE,OAAO;;AAE7B,WAAY;EACR,gBAAgB,EAAC,OAAO;;AAE5B,YAAa;EAET,gBAAgB,EAAE,OAAO;;AAG7B,UAAW;EACP,KAAK,EAAE,OAAO;;AAElB,YAAa;EACT,KAAK,EAAE,OAAO;;AAElB,YAAa;EACT,OAAO,EAAE,YAAY;EACrB,YAAY,EAAE,IAAI;;AAEtB,QAAS;EACL,KAAK,EAAE,KAAK;;AAEhB,oCAAiC [...]
+"sources": ["style.scss"],
+"names": [],
+"file": "style.css"
+}
diff --git a/pp3/public/scss/style.scss b/pp3/public/scss/style.scss
index fb7f87f..2f9fed0 100755
--- a/pp3/public/scss/style.scss
+++ b/pp3/public/scss/style.scss
@@ -54,4 +54,28 @@ body {
 }
 #pp-navbar {
     position: relative;
+}
+
+#loginProviderList .providerIcon {
+    display: inline-block;
+    vertical-align: middle;
+    width: 32px;
+    height: 32px;
+    background-size: contain;
+    margin-right: 1ex;
+    background-repeat: no-repeat;
+}
+
+#loginProviderList .list-group-item {
+    padding: 0;
+    a {
+        padding: 10px 15px;
+        text-decoration: none;
+        color: black;
+        background-color: white;
+        &:hover, &:focus {
+            color: black;
+            background-color: #e8e8e8;
+        }
+    }
 }
\ No newline at end of file


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@netbeans.apache.org
For additional commands, e-mail: commits-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists