We have moved our forum to GitHub Discussions. For questions about Phalcon v3/v4/v5 you can visit here and for Phalcon v6 here.

Vanilla auth system, feedback and suggestions required

Hi to all :)
I discovered phalcon a couple of weeks ago, while I was looking around for a framework to switch from codeigniter,
I was impressed by its power, sense and completeness to mention a few !!
Since then I try to rebuild, based on Vokuro and my codeigniter experience, an authentication system which in turn will be used as a vanilla template to start several projects with different functionalities.

some of the specifications :

  • There are only two user roles : admin and user, guests are out of the application and have access only to static content such as "about us", "terms of use" and login / register / forgot password / contact forms.
  • In some applications guests won't have the ability to register at all, users will be created by the admin (or users that have the authority to create new users)
  • Only admin(s) has access to any function in the application.
  • Users will have access rights on different functions and modules based on their real live role in the company and not based on their user role. The way i used to handle this was with a database table, a row for every user with on/off fields for every function (e.g. user/create, user/ban, ....., invoice/view, invoice/issue, ...) that the application offers.
  • Based on the user's access rights record, I examine either in a controller as a whole or in every action of the controller if access is granted to a specific user
  • In some cases, user has access to an action but he has limited access to the result of the action
    examples :
    a user is allowed to see an employee's data (hire date, position, qualifications) except contact (e.g. personal phone, home address) and financial (salary).
    another user is allowed to see a project's financial data (contract, payments, invoices) but is not allowed to edit them.
  • Besides the above, the application is split in servers (environments) internally, every record is marked with the server_id and a user can enter only one server at a time. (There are no servers actually, it is "marketing" name of data separation).

All the above are presented in hope to attract attention and hopefully get suggestions and ideas.

Now I'm at the position where the auth system is almost ready, and I would like to use the community experience in order to evaluate the solutions I picked from the options that phalcon provides.
In the next post(s) i will start presenting some modules with the specific questions I might have.

Thank you for your time :)



7.9k
edited Mar '14

ControllerBase I use it to decide what resources will be available for the request throughout the application

if visitor is not logged, I preload 3 forms because i use bootstrap 3 with login / register forms as modals and forms should be available to avery public page (splash, about, terms etc) of the application

if visitor is logged, i read from session and database some data to keep them throughout the request
in case that i fail to get a user record, i want to clear the session to consider the visitor as guest and send him to the splash page
i'm not sure that i use dispatcher appropriate as i send the visitor to the splash page but the URL in the browser is not changing (not a redirect), i actually want to achieve a redirect.

Please advise if you think that my approach is not appropriate or if there are better solutions

<?php
use Phalcon\Mvc\Controller;
use Phalcon\Mvc\Dispatcher;

class ControllerBase extends Controller
{
    public function beforeExecuteRoute(Dispatcher $dispatcher)
    {
        if (! $this->session->has('logged')){$this->session->set('logged', FALSE);}
        if($this->session->get('logged') == FALSE)
        {
            // pre-load forms for top nav menu
            $this->view->loginform = new LoginForm();
            $this->view->forgotform = new ForgotPasswordForm();
            $this->view->registerform = new RegisterForm();
            // common csrf for all forms, only one will be submitted in any case
            $this->view->tokenKey = $this->security->getTokenKey();
            $this->view->token = $this->security->getToken();
        }
        else
        {
            // variables are set as $this->var to be available in all controllers
            $this->sessionUser = $this->session->get('user');
            $id = $this->filter->sanitize($this->sessionUser['userID'], 'int');
            // get the user model of the guest, it is useful throughout the application
            $this->user = HumugusAuth\Models\Users::findFirstById($id);
            if(! $this->user)
            {
                // in case the model was not set... something is wrong !!!
                $this->flashSession->warning('Requested account not found.');
                $this->session->remove('logged');
                $this->session->remove('user');
                $this->session->remove('server');
                $dispatcher->forward(array('controller' => 'index', 'action' => 'index'));
                return false;
            }
            if($this->session->has('server') == TRUE){$this->sessionServer = $this->session->get('server');}
            // variable are are set as $this->view->var to be available to all views 
            $this->view->user = $this->user;
            $this->view->sessionUser = $this->sessionUser;
            if(isset($this->sessionServer)){$this->view->sessionServer = $this->sessionServer;}
        }
    }
} //end of class


7.9k
edited Mar '14

Index Controller, the typical landing page

what is important here is to define the status of the visitor,
--if he is guest he sees the splash zone
--if he has login, the possibilities are two :
----if already a server (environment, see first post) is set, user will be redirected to a more appropriate page as the account overview (vanilla version)
----or user will be prompted to set a server

since there is, and that won't change, only one action, all the checks happen in the action itself

<?php
class IndexController extends ControllerBase
{
    public function indexAction()
    {
        if(isset($this->user))
        {
            //visitor is logged, he is a user
            if(! isset($this->sessionServer))
            {
                //select server to work
                return $this->response->redirect('servers/select');
            }
            // show his/her account page, later will be redirected to a "my tasks" page
            return $this->response->redirect('account');
        }
        //visitor is not logged, he is a guest, show splash page (default : views/index/index)
    }
}//end of class


7.9k
edited Mar '14

Account Controller, with several actions, a couple are presented

the onConstruct is not recommended in phalcon documentation, but was the only way i could achieve to block access to anonymous guests in the whole controller, othewise i would had to check in every action
that is a part where i need input from more experienced people :)

indexAction shows user's account by default
profileActions shows the profile of any valid user based on $id parameter

<?php
use HumugusAuth\Models\Users;
use HumugusAuth\Models\Logins;
use HumugusAuth\Models\Profiles;

class AccountController extends ControllerBase
{
    public function onConstruct()
    {
        // the whole controller should NOT be accessible for anonymous guests
        if($this->session->get('logged') == FALSE)
        {
            $this->flashSession->warning('You have to sign in to access this page.');
            $this->dispatcher->forward(array('controller' => 'index', 'action' => 'index'));
            return false;
        }
    }

    public function indexAction()
    {
        //just to show some content at vanilla template
        $logins = Logins::find(array('conditions' => 'user_id = '.$this->user->id, 'order' => 'logtime DESC', 'limit' => 12));
        $this->view->logins = $logins;

        $this->view->title = $this->user->name;
        $this->view->subtitle = 'Account overview';
    }

    public function profileAction($id = 0) 
    {
        //here user might examine somebody else's profile !
        $id = $this->filter->sanitize($id, 'int');
        if($id != $this->user->id and $id > 0)
        {
            // if user is a different account's profile 
            $profileUser = Users::findFirstById($id);
        }
        else
        {
            // user sees himself
            $profileUser = $this->user;
        }
        if(! $profileUser)
        {
            $this->flashSession->warning('Requested profile not found.');
            return $this->response->redirect('account');
        }
        $this->view->profileUser = $profileUser;
        $this->view->profile = $profileUser->profile;
        $this->view->title = $profileUser->name;
        $this->view->subtitle = 'Profile overview';
    }

// more actions

Do you have a Login controller? Where does the LoginForm post?

edited Mar '14

A few suggestions.

You are storing a lot of information in your session/controllers that you don't need - and it is duplicate.

Unless there is a specific requirement that you store the user data from the db and the data that exists already in the session, you are duplicating data.

Here is how I would go about doing it:

<?php
use Phalcon\Mvc\Controller;
use Phalcon\Mvc\Dispatcher;

class ControllerBase extends Controller
{
    const AUTH_USER   = 'auth-user';
    const AUTH_SERVER = 'auth-server';

    private $viewVars = [];

    // Set a view var in the internal array
    protected function setViewVar($key, $value)
    {
        $this->viewVars[$key] = $value;
    }

    // Set all the view vars in the view
    public function afterExecuteRoute(Dispatcher $dispatcher)
    {
        $this->view->setVars($this->viewVars);
    }

    // Logged in or not
    public function isLoggedIn()
    {
        return (bool) ($this->getUserId() > 0);
    }

    // Get the user from the session
    public function getUser()
    {
        if ($this->session->has(self::AUTH_USER)) {
            return $this->session->get(self::AUTH_USER);
        }

        return false;
    }

    // Get the user id from the session
    public function getUserId()
    {
        if ($this->session->has(self::AUTH_USER)) {
            $user = $this->session->get(self::AUTH_USER);
            if (isset($user['id'])) {
                return intval($user['id']);
            }
        }

        return false;
    }

    // Get the server from the session
    public function getServer()
    {
        if ($this->session->has(self::AUTH_SERVER)) {
            return $this->session->get(self::AUTH_SERVER);
        }

        return false;
    }

    // Set the user array in the session. All the data we need for this user
    public function setUser($user)
    {
        $data['id']   = intval($user['userID']);
        $data['name'] = $user['name'];
        // .....

        $this->session->set(self::AUTH_USER, $data);
    }

    public function beforeExecuteRoute(Dispatcher $dispatcher)
    {
        if (!$this->isLoggedIn()) {

            $this->setViewVar('loginform', new LoginForm());
            $this->setViewVar('forgotform', new ForgotPasswordForm());
            $this->setViewVar('registerform', new RegisterForm());
            // common csrf for all forms, only one will be submitted in any case
            $this->setViewVar('tokenKey', $this->security->getTokenKey());
            $this->setViewVar('token', $this->security->getToken());

        } else {

            /**
             * There is no reason to make this query here. You get the user 
             * from the login form. In here we assume that the session is already
             * set. This logic will need to go to the Login controller
             */
            $this->setViewVar('user', $this->getUser());
            $this->setViewVar('server', $this->getServer());

            /**
             * All the below needs to go to the login controller
            // variables are set as $this->var to be available in all controllers
            $this->sessionUser = $this->session->get('user');
            $id = $this->filter->sanitize($this->sessionUser['userID'], 'int');
            // get the user model of the guest, it is useful throughout the application
            $this->user = HumugusAuth\Models\Users::findFirstById($id);
            if(! $this->user)
            {
                // in case the model was not set... something is wrong !!!
                $this->flashSession->warning('Requested account not found.');
                $this->session->remove('logged');
                $this->session->remove('user');
                $this->session->remove('server');
                $dispatcher->forward(array('controller' => 'index', 'action' => 'index'));
                return false;
            }
            if($this->session->has('server') == TRUE){$this->sessionServer = $this->session->get('server');}
            // variable are are set as $this->view->var to be available to all views 
            $this->view->user = $this->user;
            $this->view->sessionUser = $this->sessionUser;
            if(isset($this->sessionServer)){$this->view->sessionServer = $this->sessionServer;}
            */
        }
    }
} //end of class

Your index controller can change slightly to:

<?php
class IndexController extends ControllerBase
{
    public function indexAction()
    {
        if ($this->isLoggedIn()) {

            //visitor is logged, he is a user
            if (!$this->getServer()) {

                //select server to work
                return $this->response->redirect('servers/select');

            }

            // show his/her account page, later will be redirected to a "my tasks" page
            return $this->response->redirect('account');
        }
        //visitor is not logged, he is a guest, show splash page (default : views/index/index)
    }
}//end of class

You can achieve the same thing with beforeExecuteRoute vs. onConstruct

<?php
use HumugusAuth\Models\Users;
use HumugusAuth\Models\Logins;
use HumugusAuth\Models\Profiles;

class AccountController extends ControllerBase
{
    public function beforeExecuteRoute(Dispatcher $dispatcher)
    {
        // the whole controller should NOT be accessible for anonymous guests
        if (!$this->isLoggedIn()) {

            $this->flashSession->warning('You have to sign in to access this page.');
            $this->dispatcher->forward(array('controller' => 'index', 'action' => 'index'));
            return false;
        }

        parent::beforeExecuteRoute();
    }

    public function indexAction()
    {
        //just to show some content at vanilla template
        $logins = Logins::find(
           array(
                'conditions' => 'user_id = '. $this->getUserId(), 
                'order'      => 'logtime DESC', 
                'limit'      => 12
            )
        );

        $user = $this->getUser();
        $name = ($user) ? $user['name'] : '';

        $this->setViewVar('logins', $logins);
        $this->setViewVar('title', $name;
        $this->setViewVar('subtitle', 'Account overview');
    }

// more actions
}

In your Login controller or wherever your LoginForm posts you can have something like this:

<?php
use HumugusAuth\Models\Users;
use HumugusAuth\Models\Logins;
use HumugusAuth\Models\Profiles;

class LoginController extends ControllerBase
{
    public function loginAction()
    {
        $this->view->disable();

        if ($this->request->isPost()) {

            $suppliedUsername = $this->request->getPost('username', 'email');
            $suppliedPassword = $this->request->getPost('password');

            // Check for the credentials here querying the user 
            // $user = Users::findFirst(.......);

            if ($user) {

                $this->setUser($user);

                $this->flash->success('Welcome');
                return $this->response->redirect('');

            } else {
                $this->flash->error('User not found');
            }
        }
    }
}


7.9k
edited Mar '14

thank you Nikos you for your reply :)
Yes , i have an auth controller where i take care about login, logout, register, forgot password etc.
My plan was to open a discussion for best practices with phalconphp and post more bits where i need more suggestions.
Hopefully my needs will cover issues for other newcomers as well.

I will adapt the suggestions on your second post and i will repost the parts above, although they are more complicated now as i have integrated more features such as translation.

I'm not familiar with git or any repository, but in case it is helpfull for the community i could pass you the whole thing to make it available.

ευχαριστώ, Δημήτρης



7.9k
edited Mar '14

Auth Controller, as it is now , changes pending in the implementation , not the logic
more commenting is required and some issues are not resolved

the flow :
if registration is allowed (config), a guest can fill a registration form,
if successfull an email will be sent with a link to confirm registration

when a user is logged in he can reset his password

when a user has forgotten his/her password a form to request reset has to be filled with the account email
upon valid request, an email will be sent with a link to confirm reset
if reset request is confirmed via the sent link, a random password will be sent with a new email
or if user login with old password the reset request will be cancelled

emails with PHPMailer

<?php

use HumugusAuth\Models\HumAuth\Users;
use HumugusAuth\Models\HumAuth\Logins;
use HumugusAuth\Models\HumAuth\Profiles;

class AuthController extends ControllerBase
{
    public function indexAction()
    {
        if($this->session->get('logged') == TRUE)
        {
            return $this->response->redirect('account');
        }
        else
        {
            return $this->response->redirect('');
        }
    }

    public function loginAction()
    {
        if($this->session->get('logged') == TRUE)
        {
            $this->flashSession->notice('You are already logged in.');
            return $this->response->redirect('account');
        }
        $form = new LoginForm();
        try
        {
            if ($this->request->isPost()) 
            {
                if($this->security->checkToken() == FALSE)
                {
                    if(! isset($error)){$error = new stdClass();}
                    $error->CSRF = TRUE;
                }
                if ($form->isValid($this->request->getPost()) == false)
                {
                    foreach ($form->getMessages() as $message){$this->flashSession->error($message);}
                }
                else
                {
                    $user = Users::findFirstByEmail($this->request->getPost('email'));
                    if($user)
                    {
                        if ($this->security->checkHash($this->request->getPost('password'), $user->password)) 
                        {
                            if($user->active == 0)
                            {
                                //not confirmed
                                $this->flashSession->warning('Your account is not confirmed yet.<br />Please check your email box for instructions how to activate your account.');
                                return $this->response->redirect('');
                            }
                            if($user->active == -1)
                            {
                                //inactive
                                $this->flashSession->error('Your account is inactive.<br />Please ask your administrator to restore your account.');
                                return $this->response->redirect('');
                            }                           

                            /* NOT working, have to resolve it..
                            if(isset($error))
                            {
                                if(isset($error->CSRF)){$this->flashSession->error('Login form session expired, please re-submit the form.');} //'CSRF validation failed'
                            }
                            else
                            */
                            {
                                if($user->new_pass_key != NULL or $user->new_pass_time != NULL)
                                {
                                    $user->new_pass_key = NULL;
                                    $user->new_pass_time = NULL;
                                    $user->save();
                                }
                                $this->session->set('logged', TRUE);
                                $profile = Profiles::findFirstByUser_id($user->id);
                                $this->session->set('user', array('userID' => $user->id, 'bg_body' => $profile->bg_body,'bg_well' => $profile->bg_well));
                                $this->flashSession->success('You have successfully log in.');
                                $login = new Logins();
                                $login->user_id = $user->id;
                                $login->save();
                                return $this->response->redirect('');
                            }
                        }
                        else
                        {
                            $this->flashSession->warning('Invalid email / password.');
                        }
                    }
                    else
                    {
                        $this->flashSession->warning('Email not found in our database.');
                    }
                }
            }
        }catch (AuthException $e){$this->flashSession->error($e->getMessage());}
    }

    public function logoutAction()
    {
        if($this->session->get('logged') == TRUE)
        {
            $this->session->remove('logged');
            $this->session->remove('user');
            $this->session->remove('server');
            $this->flashSession->notice('You have logout from the application.');
        }
        else
        {
            $this->flashSession->warning('You have already logout from the application.');
        }
        return $this->response->redirect('');
    }

    public function forgotPasswordAction()
    {
        $form = new ForgotPasswordForm();
        try 
        {
            if ($this->request->isPost()) 
            {
                if($this->security->checkToken() == FALSE)
                {
                    if(! isset($error)){$error = new stdClass();}
                    $error->CSRF = TRUE;
                }
                if ($form->isValid($this->request->getPost()) == false)
                {
                    foreach ($form->getMessages() as $message){$this->flashSession->error($message);}
                }
                else
                {
                    $user = Users::findFirstByEmail($this->request->getPost('email'));
                    if($user)
                    {
                        if($user->new_pass_time != NULL)
                        {
                            if(time() - $user->new_pass_time < (5 * 60)) //5 minutes
                            {
                                $this->flashSession->warning('You requested a password reset less than 5 minutes ago. <br />Please check your email box.');
                                return $this->response->redirect('');
                            }
                        }
                        $tempKey = preg_replace('/[^a-zA-Z0-9]/', '', base64_encode(openssl_random_pseudo_bytes(16)));
                        $user->new_pass_key = $tempKey;
                        $user->new_pass_time = time();
                        $user->save();
                        $result = EmailHelper::sendForgotPasswordEmail($user);
                        if($result['output'] == 'error')
                        {
                            $this->flashSession->error($result['message']);
                        }
                        else
                        {
                            $this->flashSession->success($result['message']);
                        }
                    }
                    else
                    {
                        $this->flashSession->warning('Email not found in our database.');
                    }
                }
            }
        }catch (AuthException $e){$this->flashSession->error($e->getMessage());}
        return $this->response->redirect('');
    }

    public function confirmPasswordResetAction($id = 0, $key = '')
    {
        if($this->session->get('logged') == TRUE)
        {
            $this->flashSession->notice('You cannot reset your password while you are logged in.<br /> Change your password in your account settings page.');
            return $this->response->redirect('account/settings');
        }
        $id = $this->filter->sanitize($id, 'int');
        $key = $this->filter->sanitize($key, 'string');
        $user = Users::findFirst($id);
        if(! $user)
        {
            $this->flashSession->error('Could not reset password. <br />The requested account not found in our records.');
            return $this->response->redirect('');
        }
        if($user->new_pass_key != $key)
        {
            $this->flashSession->error('Could not reset password. <br />Reset key is not valid.');
            return $this->response->redirect('');
        }
        $tempPassword = preg_replace('/[^a-zA-Z0-9]/', '', base64_encode(openssl_random_pseudo_bytes(12)));
        $user->password = $this->security->hash($tempPassword);
        $user->new_pass_key = NULL;
        $user->new_pass_time = NULL;
        $result = EmailHelper::sendNewPasswordEmail($user, $tempPassword);
        if($result['output'] == 'error')
        {
            $this->flashSession->error($result['message']);
        }
        else
        {
            $this->flashSession->success($result['message']);
            $user->save();
        }
        return $this->response->redirect('');
    }

    public function registerAction()
    {
        $form = new RegisterForm();
        try 
        {
            if ($this->request->isPost()) 
            {
                if($this->security->checkToken() == FALSE)
                {
                    if(! isset($error)){$error = new stdClass();}
                    $error->CSRF = TRUE;
                }
                if ($form->isValid($this->request->getPost()) == false) 
                {
                    foreach ($form->getMessages() as $message){$this->flashSession->error($message);}
                } 
                else 
                {
                    /* NOT working, have to resolve it..
                    if(isset($error))
                    {
                        if(isset($error->CSRF)){$this->flashSession->error('Login form session expired, please re-submit the form.');} //'CSRF validation failed'
                    }
                    else
                    */
                    {
                        $user = new Users();
                        $user->email = $this->request->getPost('email');
                        $user->name = $this->request->getPost('name');
                        $password = $this->request->getPost('password');
                        $user->password = $this->security->hash($password);
                        $success = $user->save();
                        if ($success) 
                        {
                            $result = EmailHelper::sendRegistrationEmail($user);
                            if($result['output'] == 'error')
                            {
                                $this->flashSession->error($result['message']);
                            }
                            else
                            {
                                $this->flashSession->success($result['message']);
                            }               
                        }
                        else
                        {
                            $this->flashSession->error('Sorry, the following problems were generated: ');
                            foreach ($user->getMessages() as $message){$this->flashSession->error($message);}
                        }
                    }
                }
            }
        }catch (AuthException $e){$this->flashSession->error($e->getMessage());}
    }

    public function confirmRegistrationAction($name = '', $key = '')
    {
        $name = $this->filter->sanitize($name, 'string');
        $key = $this->filter->sanitize($key, 'string');

        $user = Users::findFirst(array('conditions' => 'name = "'.$name.'" AND activation_key = "'.$key.'" AND active = 0'));
        if(! $user)
        {
            $this->flashSession->error('Sorry, we could not find any account with your credentials.');
            return $this->response->redirect('');
        }
        $user->active = 1;
        $user->activation_key = NULL;
        $user->confirmed_at = time();
        if($user->save() == FALSE)
        {
            $this->flashSession->error('We could not activate your account.<br />Please try again later.');
        }
        else
        {
            $this->flashSession->success('Your account has been activated successfully.<br />You can now sign in your account.');
        }
        return $this->response->redirect('');
    }

}//end of class

Good to hear that your project is progressing.

Re: translations, have a look on how we do them for the main website. We use Transifex (www.transifex.com) for our translations which in effect end up as an array of keys/values for the site to use.

Register the component for Volt https://github.com/niden/website/blob/master/app/var/bootstrap.php#L251

Usage https://github.com/niden/website/blob/master/app/views/download/index.volt#L24

Usage with parameters https://github.com/niden/website/blob/master/app/views/download/index.volt#L24

The actual array of keys/values that Transifex handles. https://github.com/niden/website/blob/master/app/var/languages/en.php#L104

HTH

PS: Parakalw :)



7.9k
edited Mar '14

Re: Unless there is a specific requirement that you store the user data from the db and the data that exists already in the session, you are duplicating data.

There are scenarios where is necessary to have live user data instead of those in stored in session :

Consider a case where a user (badUser) in a forum is spamming, if an admin ban badUser, it will take effect only after badUser tries to login again.
Likewise, consider that Boss requests admin (an employee of Boss) for more access rights on application resources, it is not always possible to force him / her logout and login again to check if new access rights setting suits his / her needs
I know that i describe rare cases, but we are programmers right ? :)

Re: Translation
thank you for your time to suggest me more options
so far, \Phalcon\Translate\Adapter\NativeArray() is sufficient for my needs,
as we develop business applications i cannot rely on community for translations, everything has to happen in house.

edited Mar '14

Understood.

For the permissions, you can do this also (another suggestion)

  1. In your user table store a column called last_update_date (datetime).
  2. When you first get the user logged in (Login controller) you store the whole user object in the session
  3. You create an action that will return a JSON object (AJAX) with a true/false value
  4. The action effectively picks up the session-user object and checks the last_update_date column. It then queries the db for that user. If the last_update_date has been changed i.e. admin change, then it will force the user to logout immediately.
  5. The AJAX action can be queried using javascript every X minutes OR every beforeExecuteRoute call i.e. every request

This way you will have an active user that clicks around the application. When the admin changes their permissions, the ajax related action will return TRUE (i.e. profile has been changed) and that will force a permission refresh or a logout. If the user stays idle, the javascript polling mechanism will call the ajax action and if again it returns TRUE the user will be logged out etc.

Re: Translations I have found that using Transifex helps a lot in my projects since I don't have to remember what strings to translate and what not. It all depends on the complexity of your project.



7.9k
edited Mar '14

Thank you for your reply :)
What i usually do to refresh session data is to store the lastUpdated in the session itself, if the session is older than some minutes i query the db once again

Actually what i try to build (as described in this thread) is already up and running (used to use codeigniter) and the logic and analysis is considered sufficient
since i decided to rewrite the whole project, and have chosen phalcon for that what i seek in this thread is mostly suggestions like : you can do that this way in phalcon

For example, since access settings are per user, ACL is not appropriate for my needs, therefore the old access rights on a database table (a row per user) will be used , please see below a combination of my approach with your suggestions :
in ControllerBase :

    const AUTH_ACCESS   = 'authAccess';     // user access settings
    public function setAccess($access)
    {
        foreach($access as $key => $val){$data[$key] = $val;}
        $this->session->set(self::AUTH_ACCESS, $data);
    }
    public function getAccess(){if ($this->session->has(self::AUTH_ACCESS)){return $this->session->get(self::AUTH_ACCESS);} return false;}

    public function beforeExecuteRoute(Dispatcher $dispatcher)
    {
        if ($this->isLogged() == TRUE)
        {
            $this->userAccess = self::getAccess();
            $this->setViewVar('sessionAccess', $this->userAccess);
        }
    }

i still duplicate data, but i do that in order to have the accessRights available everywhere in the application, although duplicated it is more DRY than examining the session in any place i have to decide if something is available to the specific user or not

In Auth / login, if login success :

    $access = UserAccess::findFirst(array('conditions' => 'user_id = '.$user->id, 'hydration' => \Phalcon\Mvc\Model\Resultset::HYDRATE_ARRAYS));  // prefer it as array
    $this->setAccess($access); 

in a random Controller :

    public function indexAction($id = 0)
    {
        if($this->userAccess['emplView'] == 0)
        {
            $this->flashSession->error('You are not allowed to commit this action.');
            return $this->response->redirect('account/index');
        }
        $id = $this->filter->sanitize($id, 'int');
        $employee = Employees::findFirstById($id);
        ...  
        ...  

in a random View


        <ul class="dropdown-menu">
            <?php if($sessionAccess['emplView'] > 0){ ?>
            <li><?php echo $this->tag->linkTo('employees/seek', '<span class="glyphicon glyphicon-user"></span> Employees') ?></li>
            <?php } ?>
            ..
            ..

Of course good programming technics and habits, and more sophisticated approaches and goodies like that are always welcome
the discussion is fruitfull and hopefully other users will find them usefull and pick what is best for them,
as you can see some of your earlier suggestions are already implemented although they are not phalcon specific.

again, athank you for your time !

I don't see anything wrong with your implementation. It works and that is what is most important right?

My personal implementation on this would have been a Auth class on its own (to handle authentication) as well as an ACL class. You register those in your DI container and they are available throughout the application.

Your Auth class would give you functions like isLoggedIn, getUserId etc., more like a separation of logic to what I posted earlier in the base controller.

The ACL will offer the same functionality as you have there, but you will move that logic in a separate component which will make it a lot easier for you to test and ensure that there isn't some odd case you missed :)