Authentication and Authorization with Quantum PHP Framework
If you haven’t noticed yet the Auth
feature was added into Quantum PHP framework from version 1.9
, which includes all the aspects of authentication and authorization.
The philosophy behind the Auth
library is to not rely only to the database but instead consider that user repository can be anything from regular file, database or even some online service working with its API and SDK etc.
This means also that you are not forced to use some kind of predefined table structure for users.
Of course you should somehow map your key fields to perform actions against and all this prepared for you to have complete Auth
system on you website or app.
The version 1.9 comes with simple demo, that shows how you can perform sign up
, sign in
, etc. and see how smooth all that works. It includes also some article posting feature to demonstrate what authenticated users can do wile the guests does not.
By the way the Auth
is specifically implemented for Web
and API
platforms keeping everything as simple as possible.
You need to have configuration file for auth
(comes with the project), where you need to defined the type
and the service
.
The type
can be `web`
or `api`
depending to you project. The service
is just the service class which we will use a bit later.
Auth
library uses strong crypto
and hashing
algorithms to make the feature secure and reliable. Check Security features of Quantum PHP Framework for more information.
The repository for the users, `files`
are used for simplicity and to not make you create database, tables, etc. to see how it works.
The key role here plays the service AuthService
which becomes the bridge between the controller and user repository and allows you to map the keys and field names to correspond to your repository.
In the AuthService
(which should implement AuthServiceInterface
interface) you need to implement several methods but you are free to implement the logic of getting, adding or updating the data the way you want.
In real world the files as a data storage is not quite compatible and that’s why we will implement the Auth
on database today.
Preparing the database
Just create new database with table ` users
` with the following structure:
CREATE TABLE `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(255) NOT NULL,
`password` varchar(255) NOT NULL,
`role` varchar(255) NOT NULL,
`firstname` varchar(255) NOT NULL,
`lastname` varchar(255) NOT NULL,
`activation_token` varchar(255) NOT NULL,
`remember_token` varchar(255) NOT NULL,
`reset_token` varchar(255) NOT NULL,
`refresh_token` varchar(255) NOT NULL,
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Make sure you have correct database details in your .env
file.
Next we need to create our Auth
model to interact with the database.
<?php namespace Base\models; use Quantum\Mvc\Qt_Model; class AuthModel extends Qt_Model
{
public $table = 'users';
protected $fillable = [
'username',
'password',
'activation_token',
'firstname',
'lastname'
];
}
Updating the Auth Service
In project tree under base/ Services/ directory you can find AuthService
class where we should apply our modifications. As mentioned before for demo purposes files were used for storing the data. So we will keep the method names but will overwrite the content to make it work with database.
<?php use Quantum\Libraries\Auth\AuthServiceInterface;
use Quantum\Exceptions\ExceptionMessages;
use Quantum\Factory\ModelFactory;
use Quantum\Mvc\Qt_Service;
use Base\Models\AuthModel; class AuthService extends Qt_Service implements AuthServiceInterface {
public function __init(ModelFactory $modelFactory)
{
$this->authModel = $modelFactory->get(AuthModel::class);
} public function getVisibleFields()
{
return [
'username',
'firstname',
'lastname',
'role'
];
} public function getDefinedKeys()
{
return [
'usernameKey' => 'username',
'passwordKey' => 'password',
'activationTokenKey' => 'activation_token',
'rememberTokenKey' => 'remember_token',
'resetTokenKey' => 'reset_token',
'accessTokenKey' => 'access_token',
'refreshTokenKey' => 'refresh_token'
];
} public function get($field, $value) : array
{
return $this->authModel
->findOneBy($field, $value)
->asArray();
} public function add($data)
{
$user = $this->authModel
->create()
->fillObjectProps($data)
->save(); return $user->asArray();
} public function update($field, $value, $data)
{
$user = $this->authModel->findOneBy($field, $value); foreach ($data as $key => $value) {
$user->$key = $value;
} $user->save();
} }
In the code above, first we initialized our AuthModel
model. in next 2 methods we have specified the visible fields and the key fields. The visible fields are the once which are kept with `user`
object after sign in. The key fields are only for Auth
library.
Next 3 methods as you guess is for getting, adding and updating user related properties.
Pay attention that you can write completely different code according to your needs. You may like to break the user table with multiple tables, etc. But that would require more advanced code so we will stick with one table for now…
You may not believe me, but you already have complete Auth feature for your project! (as the demo projects already comes with AuthController
and middlewares
).
So let’s just go through in what we already have…
Those are routes
, middlewares
and the controller AuthController
.
Routes
We will consider the Web
platform routes.
<?php return function ($route) { $route->group('guest', function ($route) { $route->add('[:alpha:2]?/signin', 'GET|POST', 'AuthController', 'Signin'); $route->add('[:alpha:2]?/signup', 'GET|POST', 'AuthController', 'signup') ->middlewares(['Signup']); $route->add('[:alpha:2]?/activate/[:any]', 'GET', 'AuthController', 'activate') ->middlewares(['Activate']); $route->add('[:alpha:2]?/forget', 'GET|POST', 'AuthController', 'forget') ->middlewares(['Forget']); $route->add('[:alpha:2]?/reset/[:any]', 'GET|POST', 'AuthController', 'reset')->middlewares(['Reset']); }) ->middlewares(['Guest']); $route->group('auth', function ($route) {
$route->add('[:alpha:2]?/signout', 'GET', 'AuthController', 'signout'); })->middlewares(['Auth']); };
As you can see there are 5 routes in guest
group and one in auth
group.
Notice that almost each route has middlware. You can go ahead and check each of them and made modifications if you need. Those are mainly validations that are perform against the data in request and then move forward to controller.
Controller
In the AuthController
each methods are already defined according to the routes. You can check and modify each of them by yourself.. we will look only the signin
here.
<?php public function signin(Request $request)
{
if ($request->getMethod() == 'POST') {
try {
if (auth()->signin($request->get('email'), $request->get('password'), !!$request->get('remember'))) {
redirect(base_url() . '/' . current_lang());
}
} catch (AuthException $e) {
session()->setFlash('error', $e->getMessage());
redirect(base_url() . '/' . current_lang() . '/signin');
}
} else {
$this->view->render($this->signinView);
}
}
As you can see in order to access Auth
library methods we using auth()
helper facade. Same way we can access to other Auth
methods via auth()
helper method. For example if we want to print current logged in user first and last names in view, we will do it like this:
<div>
<?php
echo auth()->user()->firstname . ' ' . auth()->user()->lastname
?>
</div>
To get familiar with other Auth
methods check the documentation.
Originally published at http://blog.softberg.org on January 26, 2020.