# Search

Search

Loading search index…

No recent searches

No results for "Query here"

# Security - Authentication and Authorization

This guide assumes you have a basic understanding of how Pionia works. If you are new to Pionia, you can start by going through the API Tutorial guide.

Security is a very important aspect of any application. In Pionia, Security is approached in two ways.

- Authentication
- Authorization

## Authentication - Authentication Backends.#

Currently, Pionia does not dictate on any authentication mechanism but provides a way to implement your own authentication mechanism.

Pionia keeps an open mind on how you want to secure your apis. Most commonly, you will find JWT, OAuth, Basic Auth, and many more helpful. You can implement any of these in Pionia. All Pionia is seeking out is context user object that gets returned in your authentication backends.

With this approach, you can make authentication backends that handle mobile, web, and other platforms separately.

Packages like firebase/php-jwt can be used to implement JWT authentication.

Also, you can visit our jwt authentication sample guide on how to implement JWT authentication in Pionia.

### About Authentication Backends#

These are classes that are responsible for authenticating the user. They are responsible for verifying the user’s identity and returning the user object.

All authentication backends must extend the `Pionia\Core\Interceptions\BaseAuthenticationBackend` class and implement the `authenticate` method. This method receives the request object, this also implies that you have access to your headers, request body, and therefore you can implement any authentication mechanism you want.

Authentication backends must return the `Pionia\Core\Helpers\ContextUserObject` object if the user is authenticated, otherwise, they should return `null`.

You can have multiple authentication backends in the same application. Pionia will iterate through all the authentication backends until one of them returns a user object.

Otherwise, it will proceed to process the request without authenticating the user. And as a result, `this->mustAuthenticate` will be failing if the user is not authenticated.

### Creating an Authentication Backend#

To quickly bootstrap your authentication backend, you can use the following command.

Example below creates an authentication backend called `JwtAuthBackend`.

    php pionia  gen:auth  jwt

Notice that we only define `jwt` as the authentication backend name. This is because Pionia will automatically append `AuthBackend` to the name.

Upon running the above command, Pionia will create a new authentication backend in the `app/authentications` directory.

    <?php

    /**
     * This authentication backend is auto-generated from pionia cli.
     * Remember to register your backend in settings.ini
     */

    namespace application\authentications;

    use Pionia\Core\Helpers\ContextUserObject;
    use Pionia\Core\Interceptions\BaseAuthenticationBackend;
    use Pionia\Request\Request;

    class JwtAuthBackend extends BaseAuthenticationBackend
    {
    	/**
    	 * Implement this method and return your 'ContextUserObject'. You can use Porm here too!
    	 */
    	public function authenticate(Request $request):? ContextUserObject
    	{
    		$userObj = new ContextUserObject();

    		# your logic here...

    		return $userObj;
    	}
    }

You can now implement your authentication logic in the `authenticate` method.

To access the headers, you can get them from the `headers` key on the request like `$authHeader = $request->headers->get('Authorization');`.

The ContextUserObject is a simple object that holds the user’s information. You can add any information you want to this object.

It contains the following properties:

- `user` - The object from your db.
- `authenticated` - A boolean value that indicates whether the user is authenticated or not.
- `permissions` - An array of permissions that the user has.
- `authExtra` - Any other extra data you want to set on the user context. This can store staff like role, user id, etc. You will be able to access this data in your services.

### Registering your Authentication Backend#

After creating your authentication backend, you need to register it in the `settings.ini` file under the `authentications` section. Every backend must be given a unique name. Also, you to note that the order of registration matters. Pionia will iterate through the authentications in the order they are registered.

    [authentications]
    jwt_auth = application\authentications\JwtAuthBackend

And that’s it! Your authentication backend is now ready to be used in your application.

### RBAC - Service Level Authorization#

#### Accessing the set Context Object#

To access the context object in your services, you can access it from the `$this->auth();`.

This returns the `ContextUserObject` object that you set in your authentication backend.

    <?php

    class TodoService extends BaseRestService
    {
        public function getTodo()
        {
            $user = $this->auth()->user;
            $permissions = $this->auth()->permissions;
            $authExtra = $this->auth()->authExtra;

            // your logic here...
        }
    }

#### Accessing the AuthExtras#

AuthExtras are an associative array that you can use to store any extra data you want to access in your services. To access a single item from this array, you can use `$this->getAuthExtraByKey($key)`.

You can also check if a certain extra key was set on the extras array by using `$this->authExtraHas($key)`. This will return a boolean value.

    <?php

    class TodoService extends BaseRestService
    {
        public function getTodo()
        {
            $role = $this->getAuthExtraByKey('role');
            $userId = $this->getAuthExtraByKey('userId');

            // your logic here...
        }
    }

### Protecting your Services#

The entire service can be protected by setting the `$serviceRequiresAuth` property to `true`. This will ensure that all actions in the service are secure and only authenticated users can access them.

    <?php

    class TodoService extends BaseRestService
    {
        protected $serviceRequiresAuth = true;

        public function getTodo()
        {
            // your logic here...
        }
    }

### Protecting your Actions#

You can also protect individual actions in two ways.

#### Setting the `$actionsRequiringAuth` property#

You can set the `$actionsRequiringAuth` property to an array of actions that require authentication.

    class TodoService extends BaseRestService
    {
        protected $actionsRequiringAuth = ['getTodo'];

        public function getTodo() // accessing this action will require authentication
        {
            // your logic here...
        }
    }

#### Using the `mustAuthenticate` method#

You can also use the `mustAuthenticate` method in your action to check if the user is authenticated. This method will throw an exception if the user is not authenticated.

    class TodoService extends BaseRestService
    {
        public function getTodo()
        {
            $this->mustAuthenticate(); // user must be authenticated to go beyond this point

            // your logic here...
        }
    }

Setting the `$authMessage` property will set a custom message that will be thrown if the user is not authenticated.

    class TodoService extends BaseRestService
    {
        protected $authMessage = 'You must be authenticated to use any service in the todo context.';

        public function getTodo()
        {
            $this->mustAuthenticate(); // user must be authenticated to go beyond this point

            // your logic here...
        }
    }

## Authorization#

Authorization is the process of determining whether a user has the necessary permissions to access a certain resource.

In Pionia, authorization is done using permissions. Permissions are authorization rules that you can set on your context object in your authentication backend.

### Permissions - Authorities#

There are three ways to check if a user has a certain permission[s]/authority[ies].

### Using the `$actionPermission` service property.#

This can be used both in the normal and in generic services. It defines an associative array that defines permission list per action.

    public array $actionPermissions = [
        'create' => ['create_todo'],
        'update' => ['update_todo'],
        'delete' => ['delete_todo']
    ];

From Version 1.1.4, this can also take up strings like below

    public array $actionPermissions = [
        'create' => 'create_todo',
        'update' => 'update_todo',
        'delete' => 'delete_todo'
    ];

Permissions `create_todo`, `update_todo` are permissions you set on the context user object from your authentication backends. If these are not defined, the service won’t be accessed.

The above implies that to access

#### Using the `can` method#

You can check if a user has a certain permission by using the `can` method that is available on the service.

    <?php

    class TodoService extends BaseRestService
    {
        public function getTodo()
        {
            $this->can('view-todo'); // user must have the permission to go beyond this point
        }
    }

If the user does not have the permission, the `can` method will throw an exception and the action will not be executed.

#### Using the `canAll` method#

`can` checks only one permission, however, you can check multiple permissions by using the `canAll` method.

    <?php

    class TodoService extends BaseRestService
    {
        public function getTodo()
        {
            $this->canAll(['view-todo', 'edit-todo']); // user must have both permissions to go beyond this point
        }
    }

#### Using the `canAny` method#

The two methods mentioned earlier check if the user has the permission. However, you might want to check if the user `has any one` of the permissions. You can do this by using the `canAny` method.

    class TodoService extends BaseRestService
    {
        public function getTodo()
        {
            $this->canAny(['view-todo', 'edit-todo']); // user must have any of the permissions to go beyond this point
        }
    }

### Custom Authorization Messages#

The above methods take a second parameter `$message` which is a custom message that will be thrown if the user does not have the permission.

    class TodoService extends BaseRestService
    {
        public function getTodo()
        {
            $this->can('view-todo', 'You do not have permission to view todo'); // user must have the permission to go beyond this point
        }
    }

All the authentication and authorization should happen early in the an action. This is because, if the user is not authenticated or does not have the necessary permissions, the action should not be executed.

Remember, all authentication backends run after middlewares. This is because middlewares can be used to sanatize the request, and therefore, it is important to run them before the authentication backends.

## Custom Return Codes.#

By default, Pionia will return a `401` return code if the user is not authenticated and a `403` return code if the user does not have the necessary permissions.

You can override these permissions by defining the `UNAUTHENTICATED_CODE` and the `UNAUTHORIZED_CODE` in the `settings.ini` under the `SERVER` section.

    [SERVER]
    ; other settings
    UNAUTHENTICATED_CODE = 10
    UNAUTHORIZED_CODE = 11
    ; other settings

Using the above example, `10` and `11` will override the default `401` and `403` return codes respectively.