Auth - authentication and authorization

The authentication and authorization system in Jet is a unified abstract interface for performing basic operations for user login, authentication, and permission checking for arbitrary operations.

What does this mean in practice? In almost every application we need some users to be able to log in, we also need to check if they are logged in, if their account is valid and if so, if they are allowed to do what they are about to do. This is actually always the same, it's always the same operations with authentication and authorization. What's different is the actual low-level implementation. For example, where are user accounts stored and how? Is it in the database? What is the table structure? Or is the user account information not stored in the application database at all and access is controlled by the corporate network? What about logging in using third-party services? And, of course, the range of information we record about users can vary substantially, and so on.

What a user entity looks like, where and how it is stored, and how its login is authenticated is incredibly variable and varied. And at the same time, it must be possible to implement anything. Of course, the rest of the application doesn't much care whether the user is in the database or somewhere else. No, the rest of the application simply needs to know: the user is/is not logged in, his account is/is not valid, and he is/is not allowed to perform this operation. So whatever complexity is behind the scenes is actually a simple interface on the surface, and again a facade and a container holding the controller that already takes care of everything. So in terms of application usage, it is a very simple system that is concise and clear. But which is designed to cover anything and does not put any restrictions.

Let's first talk about what we'll encounter in Jet Auth:

  • Controller
    The controller controls the authentication and authorization logic.
    That is, it controls the logic of logging in, logging out, user validation, and verifying that the user has the necessary permissions (see below).
    The controller represents the Jet\Auth_Controller_Interface interface.
  • User
    A user is an entity that must have a few basic general properties and methods (for example, every user has a user name) and that can of course be freely extended in the application. The entity takes care of the actual retrieval and storage of user information wherever the data is stored in whatever form - this is an internal matter of the entity.
    Represented by the Jet\Auth_User_Interface interface.
  • Role
    Permissions (for operations) in Jet are handled by a system of roles. There can be any number of roles and each user can be in any number of roles. (permissions then escalate - they add up)
    Naturally, there must be a role entity which has defined basic methods, but which can be extended freely in the application.
    Represented by the interface Jet\Auth_Role_Interface.
  • Permissions
    A permission is a subentity of a role and represents a specific permission (for an operation). A role can, of course, have any number of permissions.
    Represented by the Jet\Auth_Role_Privilege_Interface.
  • Main facade
    It is through this class and its static methods that the application works with the authentication and authorization system.
    Represented by class Jet\Auth

Initialization

Since the Auth system is just an empty container, it must first be "filled", i.e. told which contorter is to be used to control the logic. The implementation of the controller is purely a matter of the application. Thus, you must have a class (in practice, more like a class) in the application that implements the Jet\Auth_Controller_Interface interface.

There is no pre-made controller in Jet (in the library), but sample controllers are included in the sample application. These are classes:

  • JetApplication\Auth_Controller_Admin
  • JetApplication\Auth_RESTClient_User
  • JetApplication\Auth_Controller_Web

If you're already familiar with Jet MVC, you'll already guess that there are different controllers for different bases. So logging into the administration is completely different than logging into the REST API client. And furthermore, you'll guess that it's going to be some sort of base initializer and that's exactly right - we'll get to that in a moment.

No Auth controller is initialized at the start of the application. No, Jet Auth needs to be initialized only when you know what the situation is and what controller you need. In case you are using Jet MVC, the initialization is done in the base initializer. So for example:

namespace JetApplication;

use 
Jet\MVC_Router;
use 
Jet\Auth;
// ... ... ... ...
class Application_Admin
{
    
// ... ... ... ...
    
public static function initMVC_Router $router ): void
    
{
        
// ... ... ... ...
        
Auth::setController( new Auth_Controller_Admin() );
        
// ... ... ... ...
    
}

    
// ... ... ... ...
}

If you are not going to make a Jet MVC based application, then it is up to you where you initialize. But you always need to do something like this:

Auth::setController( new MyMegaCoolAuthController() );

Usage

That being said, the authentication and authorization system is used through the Jet\Auth facade. Let's now show this in a practical way.

Anyone signed in?

If you need to verify that a user is logged in and their user account is valid (for example, they have not been blocked), then just do this:

if( Auth::checkCurrentUser() ) {
    
//It's OK
}

Who's in?

Often you need to find out who is logged in and get some information about them:

if( ( $user Auth::getCurrentUser() ) ) {
    echo 
'Current user: '.$user->getUsername();
}

Přihlášení

You have a username and password, you want to verify it, and if the information is correct, then log the user in:

if( Auth::login$username$password ) ) {
    echo 
'Logged in ...';
}

Logout

Based on some action (Jet doesn't address what action - that's up to your application), you need to log the user out:

Auth::logout();

Login handling

The user is not logged in and you want the controller to take care of logging in. In practice, this means displaying the login form instead of the content. How and what the controller does is purely up to the controller.

if( Auth::checkCurrentUser() ) {
    
Auth::handleLogin();
}

Check permissions - can the user do "this and that"?

We have already seen that permission control is handled in the form of roles and their rights. We'll come back to that, but for now let's see how to verify that a user has the necessary right. This is actually very simple.

You have defined permissions to view documents in your application, and you want to know whether a user has permission to see a particular document:

if( Auth::getCurrentUserHasPrivilege'view-document'$document_id ) ) {
    
//It's OK
}

This was a check for a completely arbitrary and defined permission. However, Jet has two basic permissions built in:

Permission to visit a site:

$page MVC::getPage('some-page-id');
if( 
Auth::checkPageAccess$page ) ) {
    
//It's OK
}

Permissions for the ACL action of the application module:

if( Auth::checkModuleActionAccess('Some.Module''module-action') ) {
    
//It's OK
}

Note: Of course, the permissions check must first verify that the user is logged in and their account is active.

User

The user entity must satisfy at least what the Jet\Auth_User_Interface declares. Importantly, only the functional minimum is defined by the interface. Your implementation of the user can of course have any additional features. Be it address, job title, contacts - just anything. That's entirely up to you. To get a better understanding of what Jet Auth requires, I recommend familiarizing yourself with the interface.

Roles and permissions

The same applies to roles as to users. That is, the role must meet at least what the corresponding interface Jet\Auth_Role_Interface requires. If you add, for example, additional internal notes, icons, or anything else you need to the role, then that's perfectly fine and should be the case.

However, in the context of roles, it is a good idea to explain how the permission system works.

Permissions function

As mentioned, the role is what is called privilege which is represented by the Jet\Auth_Role_Privilege_Interface. A privilege is something you can and should define yourself, according to the needs of your application. Although Jet has two built-in permissions already, namely to visit a page and perform an application module action - but the other permissions are purely up to you. The permissions are identified by a text string.

Let's show it right away with an example. Suppose you are creating a company intranet and you need to control who can edit which internal company articles. So you tell yourself that you will create the 'edit-intranet-article' permission. So we have a permission whose existence entitles you to do something - perform some operation in general. However, with an intranet, you can assume that not everyone will be able to edit everything, so there will be a limited set of articles that a given role can edit. The fact that there is an 'edit-intranet-article' permission is not enough by itself. This permission will apply to something. Therefore, the permission consists of two parts. The first part is the name of the permission, which we have already defined in our example ('edit-intranet-article'). The second part of the permission is the value. And it is the value that makes the permission specific. One permission has one name, but it has any number of values. The value is actually an array of allowed values.

So the role has the permission 'edit-intranet-article' and this permission has X values. In our example, these values will be the IDs of the articles that are on the intranet.

And let's further bring the theory to practice. Of course, there can be any number of roles in the system. Each role is again identified by a text string (for clarity and usability). Our hypothetical intranet has, for example, the roles 'editor-HR' and 'editor-accountant' in addition to a number of other roles. And both of these roles can have 'edit-intranet-article' permissions. Just as any other role may (or may not) have this permission. Thus, the permission is not fixed to a role, but is instead shared between roles.

However, the difference is that the 'editor-HR' role has different permission values than the 'editor-accountant' role. Thus, the 'editor-HR' role using the 'edit-intranet-article' permission allows editing of articles 1,8 and 9, while the 'editor-accountant' role allows editing of articles 2,3 and 11, whose IDs are the values of the permission. So both roles allow to edit articles, but each of them is completely different (or even common and identical - this is of course also possible). You have it all sorted out in your app's admin and can tweak How specifically is up to you - Jet doesn't dictate this, it just provides a sample and inspiration in the sample app.

Now let's move from roles and permissions to users. Each user can be in any number of roles. Thus, the hypothetical user MarieM is in the editor-HR role, the user JanH is in the editor-accountant role. This means that MarieM can edit articles 1,8 with 9 and user JanH can edit articles 2,3 and 11. Of course, then there may be a user in the system, which let's call for example BigBoss. And this user has multiple roles. Among other roles, he has the role 'editor-HR' and 'editor-accountant'. This means he can edit articles 1,2,3,8,9 and 11. So the permissions are gradually adding up and increasing.

Classes and modules in the sample application

The sample application with which Jet is distributed includes several sample implementations of the necessary classes and even administration modules. These classes and modules are not intended to be definitive. It is only a sample and maybe even a base, which you can of course use as needed..

Controllers

  • JetApplication\Auth_Controller_Admin
    Controller designed to control access to the administration. All functions are implemented in full.
  • JetApplication\Auth_Controller_Web
    Controller for controlling access to "passworded" parts of the site. It intentionally does not allow to verify module actions (checkModuleActionAccess method always returns false). This is an example of how a controller implementation should fully reflect its intended use.
  • JetApplication\Auth_Controller_REST
    REST API server-specific controller. Again, it has several specifics. It does not use session at all for user login, but always authenticates the user based on HTTP authentication. Thus, it uses a completely different procedure than the previous two controllers.

Users

  • JetApplication\Auth_Administrator_User
    The class represents the administration user. Users are conventionally stored in a database and are therefore an entity ORM DataModel.
  • JetApplication\Auth_Visitor_User
    A class representing a visitor to the site who is registered and can access non-public areas/sections. Again, conventional database storage using ORM.
  • JetApplication\Auth_RESTClient_User
    This class represents the client user account of the REST API server. Again, this is a conventional database storage using ORM.

Roles and permissions

  • JetApplication\Auth_Administrator_Role
    JetApplication\Auth_Administrator_Role_Privilege
    The classes represent the role and privileges of the administration user. Roles are also conventionally stored in the database and are therefore an entity of the ORM DataModel.
  • JetApplication\Auth_Visitor_Role

    JetApplication\Auth_Visitor_Role_Privilege
    Classes representing the role and privileges of a site visitor who is registered and can access non-public parts/sections. Also conventional database storage using ORM.
  • JetApplication\Auth_RESTClient_Role
    JetApplication\Auth_RESTClient_Role_Privilege
    These classes represent the roles and privileges of the REST API server client user account. They also represent conventional database storage using ORM.

Connecting users to roles

Of course, it is necessary to determine what user is in what role. The following classes are used for this purpose. All of these are for a regular database table, and all again use DataModel. None of the classes have anything specific, hence just listing them:

  • JetApplication\Auth_Administrator_User_Roles
  • JetApplication\Auth_Visitor_User_Roles
  • JetApplication\Auth_RESTClient_User_Roles

Modules - login

The controller shall be able to handle the state where the user is not logged in and prompt the user to log in. Thus, for example, display the login page/form. But that is not all. If the user is locked out, then again the relevant information must be displayed to the user in a user-friendly format. And what if the user's password has expired? Or how about doing a two-factor login? What about some solution to a forgotten password?

In short, there are many situations that can occur. And it's up to the controller to deal with it. However, because of the very extensive logic, the controller classes themselves do not solve all of this in the sample application, but instead pass the detected situation to the application modules. So, simply put, the controllers figure out what's going on and what needs to be done, and then let the application module do it in MVC mode. How exactly? Look in the handleLogin() methods of one of the controllers (of class JetApplication\Auth_Controller_Admin, or JetApplication\Auth_Controller_Web).

And what sample application modules handle login and many other related operations in the sample application? They are:

  • Admin.Login
  • Web.Visitor.Login

Modules - role and user management

In practice, it is of course necessary to manage users and their roles (add, delete, set ...). You can also find an example of how to solve such things in the sample application. Thus, in the administration you will find application modules for managing all three types of users and roles that are part of the sample application. These modules are:

  • Admin.ManageAccess.Administrators.Roles
  • Admin.ManageAccess.Administrators.Users
  • Admin.ManageAccess.Visitors.Roles
  • Admin.ManageAccess.Visitors.Users
  • Admin.ManageAccess.RESTClients.Roles
  • Admin.ManageAccess.RESTClients.Users

Not only can you try and test all the modules, but you can use them as a basis for your solution.

Previous chapter
Jet\Translator_Dictionary_Phrase
Next chapter
Jet\Auth_Controller_Interface