PHP code example of tobento / app-user

1. Go to this page and download the library: Download tobento/app-user library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

tobento / app-user example snippets


use Tobento\App\AppFactory;

// Create the app
$app = (new AppFactory())->createApp();

// Adding boots
$app->boot(\Tobento\App\User\Boot\User::class);

// Run the app
$app->run();

use Tobento\App\AppFactory;
use Tobento\App\User\UserRepositoryInterface;
use Tobento\App\User\UserFactoryInterface;
use Tobento\App\User\AddressRepositoryInterface;
use Tobento\App\User\AddressFactoryInterface;
use Tobento\App\User\RoleRepositoryInterface;
use Tobento\App\User\RoleFactoryInterface;
use Tobento\Service\Repository\RepositoryInterface;

$app = (new AppFactory())->createApp();

// Add directories:
$app->dirs()
    ->dir(realpath(__DIR__.'/../'), 'root')
    ->dir(realpath(__DIR__.'/../app/'), 'app')
    ->dir($app->dir('app').'config', 'config', group: 'config')
    ->dir($app->dir('root').'public', 'public')
    ->dir($app->dir('root').'vendor', 'vendor');

// Adding boots:
$app->boot(\Tobento\App\User\Boot\User::class);
$app->booting();

// User:
$userRepository = $app->get(UserRepositoryInterface::class);
// var_dump($userRepository instanceof RepositoryInterface);
// bool(true)

$userFactory = $app->get(UserFactoryInterface::class);

// Address:
$addressRepository = $app->get(AddressRepositoryInterface::class);
// var_dump($addressRepository instanceof RepositoryInterface);
// bool(true)

$addressFactory = $app->get(AddressFactoryInterface::class);

// Role:
$roleRepository = $app->get(RoleRepositoryInterface::class);
// var_dump($roleRepository instanceof RepositoryInterface);
// bool(true)

$roleFactory = $app->get(RoleFactoryInterface::class);

// Run the app:
$app->run();

use Tobento\App\AppFactory;
use Tobento\App\User\Authentication\AuthInterface;
use Tobento\App\User\Authentication\Token\TokenStoragesInterface;
use Tobento\App\User\Authentication\Token\TokenStorageInterface;
use Tobento\App\User\Authentication\Token\TokenTransportsInterface;
use Tobento\App\User\Authentication\Token\TokenTransportInterface;
use Tobento\App\User\Authenticator\TokenAuthenticatorInterface;
use Tobento\App\User\Authenticator\UserVerifierInterface;

$app = (new AppFactory())->createApp();

// Add directories:
$app->dirs()
    ->dir(realpath(__DIR__.'/../'), 'root')
    ->dir(realpath(__DIR__.'/../app/'), 'app')
    ->dir($app->dir('app').'config', 'config', group: 'config')
    ->dir($app->dir('root').'public', 'public')
    ->dir($app->dir('root').'vendor', 'vendor');

// Adding boots:
$app->boot(\Tobento\App\User\Boot\User::class);
$app->booting();

// Auth:
$auth = $app->get(AuthInterface::class);

// Token storages:
$tokenStorages = $app->get(TokenStoragesInterface::class);

// Default token storage:
$tokenStorage = $app->get(TokenStorageInterface::class);

// Token transports:
$tokenTransports = $app->get(TokenTransportsInterface::class);

// Default token transport:
$tokenTransport = $app->get(TokenTransportInterface::class);

// Default token authenticator:
$tokenAuthenticator = $app->get(TokenAuthenticatorInterface::class);

// Default user verifier:
$userVerifier = $app->get(UserVerifierInterface::class);

// Run the app:
$app->run();

use Tobento\App\AppFactory;
use Tobento\App\User\UserInterface;
use Tobento\Service\User\UserInterface as ServiceUserInterface;
use Tobento\Service\Acl\Authorizable;
use Psr\Http\Message\ServerRequestInterface;

$app = (new AppFactory())->createApp();

// Add directories:
$app->dirs()
    ->dir(realpath(__DIR__.'/../'), 'root')
    ->dir(realpath(__DIR__.'/../app/'), 'app')
    ->dir($app->dir('app').'config', 'config', group: 'config')
    ->dir($app->dir('root').'public', 'public')
    ->dir($app->dir('root').'vendor', 'vendor');

// Adding boots:
$app->boot(\Tobento\App\Http\Boot\Routing::class);
$app->boot(\Tobento\App\User\Boot\User::class);
$app->booting();

// Routes:
$app->route('GET', 'user', function(ServerRequestInterface $request) {
    
    // Get the current user (may be authenticated or not):
    $user = $request->getAttribute(UserInterface::class);

    // var_dump($user instanceof UserInterface);
    // bool(true)
    
    // var_dump($user instanceof ServiceUserInterface);
    // bool(true)
    
    // var_dump($user instanceof Authorizable);
    // bool(true)
    
    // Check if authenticated:
    $user->isAuthenticated();
    // bool(false)
    
    return $user?->toArray();
});

// Run the app:
$app->run();

use Tobento\App\AppFactory;
use Tobento\App\User\UserInterface;
use Tobento\App\User\Authentication\AuthInterface;
use Tobento\App\User\Authentication\AuthenticatedInterface;
use Tobento\App\User\Authentication\Token\TokenInterface;
use Tobento\Service\User\UserInterface as ServiceUserInterface;
use Tobento\Service\Acl\Authorizable;
use Psr\Http\Message\ServerRequestInterface;

$app = (new AppFactory())->createApp();

// Add directories:
$app->dirs()
    ->dir(realpath(__DIR__.'/../'), 'root')
    ->dir(realpath(__DIR__.'/../app/'), 'app')
    ->dir($app->dir('app').'config', 'config', group: 'config')
    ->dir($app->dir('root').'public', 'public')
    ->dir($app->dir('root').'vendor', 'vendor');

// Adding boots:
$app->boot(\Tobento\App\Http\Boot\Routing::class);
$app->boot(\Tobento\App\User\Boot\User::class);
$app->booting();

// Routes:
$app->route('GET', 'user', function(ServerRequestInterface $request) {
    
    // Get the auth:
    $auth = $request->getAttribute(AuthInterface::class);
    // var_dump($auth instanceof AuthInterface);
    // bool(true)
    
    // You may check if user is authenticated:
    if ($auth->hasAuthenticated()) {
        return null;
    }
    
    // Get authenticated:
    $authenticated = $auth->getAuthenticated();
    // var_dump($auth->getAuthenticated() instanceof AuthenticatedInterface);
    // bool(true)
    
    // Get the authenticated user:
    $user = $authenticated->user();
    // var_dump($user instanceof UserInterface);
    // bool(true)
    
    // var_dump($user instanceof ServiceUserInterface);
    // bool(true)
    
    // var_dump($user instanceof Authorizable);
    // bool(true)
    
    // Get the authenticated token:
    $token = $authenticated->token();
    // var_dump($token instanceof TokenInterface);
    // bool(true)

    // Get the authenticated via:
    // var_dump($authenticated->via());
    // string(9) "loginlink"
    
    // Get the authenticated by (authenticator class name):
    // var_dump($authenticated->by());
    // string(52) "Tobento\App\User\Authenticator\IdentityAuthenticator"
    
    return $user;
});

// Run the app:
$app->run();

use Tobento\App\User\UserRepositoryInterface;
use Tobento\App\User\Authentication\AuthInterface;
use Tobento\App\User\Authentication\Authenticated;
use Tobento\App\User\Authentication\Token\TokenStorageInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;

class AuthController
{
    public function authenticate(
        ServerRequestInterface $request,
        UserRepositoryInterface $userRepository,
        AuthInterface $auth,
        TokenStorageInterface $tokenStorage,
    ): ResponseInterface {
        
        // You may check if user is authenticated:
        // Or use the \Tobento\App\User\Middleware\Unauthenticated::class to do so.
        if ($auth->hasAuthenticated()) {
            // create and return response if is already authenticated.
        }
        
        // authenticate user manually:
        $user = $userRepository->findById(1);
        
        if (is_null($user)) {
            // create and return response if user does not exist.
        }
        
        // create token and start auth:
        $token = $tokenStorage->createToken(
            // Set the payload:
            payload: ['userId' => $user->id(), 'passwordHash' => $user->password()],

            // Set the name of which the user was authenticated via:
            authenticatedVia: 'login.auth', // The name is up to you.
            
            // Set the name of which the user was authenticated by (authenticator name) or null if none:
            authenticatedBy: null,
            
            // Set the point in time the token has been issued or null (now):
            issuedAt: new \DateTimeImmutable('now'),
            
            // Set the point in time after which the token MUST be considered expired or null:
            // The time might depend on the token storage e.g. session expiration!
            expiresAt: new \DateTimeImmutable('now +10 minutes'),
        );
        
        $auth->start(new Authenticated(token: $token, user: $user));
        
        // create and return response:
        return $createdResponse;
    }
}

use Tobento\App\User\Authentication\AuthInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;

class AuthController
{
    public function unauthenticate(
        AuthInterface $auth,
    ): ResponseInterface {
        
        $auth->close();
        
        // create and return response:
        return $createdResponse;
    }
}

use Tobento\App\AppFactory;
use Tobento\Service\Acl\AclInterface;

// Create the app
$app = (new AppFactory())->createApp();

// Adding boots

// The acl boot gets booted automatically by the user boot:
// $app->boot(\Tobento\App\User\Boot\Acl::class);

$app->boot(\Tobento\App\User\Boot\User::class);

$acl = $app->get(AclInterface::class);

// Run the app
$app->run();

use Tobento\Service\Migration\MigrationInterface;
use Tobento\Service\Migration\ActionsInterface;
use Tobento\Service\Migration\Actions;
use Tobento\Service\Repository\Storage\Migration\RepositoryAction;
use Tobento\Service\Repository\Storage\Migration\RepositoryDeleteAction;
use Tobento\App\User\RoleRepositoryInterface;

class RolesStorageMigration implements MigrationInterface
{
    public function __construct(
        protected RoleRepositoryInterface $roleRepository,
    ) {}
    
    /**
     * Return a description of the migration.
     *
     * @return string
     */
    public function description(): string
    {
        return 'User roles.';
    }
        
    /**
     * Return the actions to be processed on install.
     *
     * @return ActionsInterface
     */
    public function install(): ActionsInterface
    {
        return new Actions(
            RepositoryAction::newOrNull(
                repository: $this->roleRepository,
                description: 'Default roles',
                items: [
                    ['key' => 'guest', 'areas' => ['frontend'], 'active' => true],
                    ['key' => 'registered', 'areas' => ['frontend'], 'active' => true],
                    ['key' => 'business', 'areas' => ['frontend'], 'active' => true],
                    ['key' => 'developer', 'areas' => ['backend'], 'active' => true],
                    ['key' => 'administrator', 'areas' => ['backend'], 'active' => true],
                    ['key' => 'editor', 'areas' => ['backend'], 'active' => true],
                ],
            ),
        );
    }

    /**
     * Return the actions to be processed on uninstall.
     *
     * @return ActionsInterface
     */
    public function uninstall(): ActionsInterface
    {
        return new Actions();
    }
}

//...

User\RoleRepositoryInterface::class => static function(ContainerInterface $c) {

    $storage = new InMemoryStorage([
        'roles' => [
            1 => [
                'key' => 'guest',
                'areas' => ['frontend'],
                'active' => true,
            ],
        ],
    ]);
            
    return new User\RoleStorageRepository(
        storage: $storage,
        table: 'roles',
        entityFactory: $c->get(User\RoleFactoryInterface::class),
    );
},

//...

use Tobento\App\AppFactory;
use Tobento\Service\Acl\AclInterface;

// Create the app
$app = (new AppFactory())->createApp();

// Adding boots

// The acl boot gets booted automatically by
// the user boot:
// $app->boot(\Tobento\App\User\Boot\Acl::class);

$app->boot(\Tobento\App\User\Boot\User::class);

$acl = $app->get(AclInterface::class);

$acl->rule('articles.read')
    ->title('Article Read')
    ->description('If a user can read articles');

// Run the app
$app->run();

use Tobento\App\Boot;
use Tobento\App\User\Boot\Acl;

class AnyServiceBoot extends Boot
{
    public const BOOT = [
        // you may ensure the acl boot.
        Acl::class,
    ];
    
    public function boot(Acl $acl)
    {
        $acl->rule('articles.read')
            ->title('Article Read')
            ->description('If a user can read articles');
    }
}
    
use Tobento\Service\Acl\AclInterface;

class ArticleController
{
    public function index(AclInterface $acl): string
    {
        if ($acl->cant('articles.read')) {
            return 'can not read';
        }

        return 'can read';
    }
}

use Tobento\App\User\UserInterface;
use Psr\Http\Message\ServerRequestInterface;

class ArticleController
{
    public function index(ServerRequestInterface $request): string
    {
        $user = $request->getAttribute(UserInterface::class);
        
        if ($user->cant('articles.read')) {
            return 'can not read';
        }

        return 'can read';
    }
}
    
use Tobento\Service\Acl\AclInterface;

class ArticleController
{
    public function index(AclInterface $acl): string
    {
        $user = $this->acl->getCurrentUser();
        
        if (
            $user
            && $user->hasRole()
            && $user->role()->key() !== 'editor'
        ) {
            return 'can not read';
        }
        
        // or
        if ($user?->getRoleKey() !== 'editor') {
            return 'can not read';
        }

        return 'can read';
    }
}
    
use Tobento\App\User\UserInterface;
use Psr\Http\Message\ServerRequestInterface;

class ArticleController
{
    public function index(ServerRequestInterface $request): string
    {
        $user = $request->getAttribute(UserInterface::class);
    
        if ($user->hasRole() && $user->role()->key() !== 'editor') {
            return 'can not read';
        }
        
        // or
        if ($user->getRoleKey() !== 'editor') {
            return 'can not read';
        }

        return 'can read';
    }
}

use Tobento\App\AppFactory;

// Create the app
$app = (new AppFactory())->createApp();

// Adding boots
$app->boot(\Tobento\App\User\Boot\HttpUserErrorHandler::class);
$app->boot(\Tobento\App\User\Boot\User::class);

// Run the app
$app->run();

public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
    // Get the authenticated user if exists:
    $user = $request->getAttribute(AuthInterface::class)?->getAuthenticated()?->user();

    // Otherwise, create guest user:
    if (is_null($user)) {
        $user = $this->userFactory->createGuestUser();
    }

    $request = $request->withAttribute(UserInterface::class, $user);

    // Set user on acl:
    if ($this->acl) {
        $this->acl->setCurrentUser($user);
    }

    return $handler->handle($request);
}
    
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
    // Add auth to the request:
    $request = $request->withAttribute(AuthInterface::class, $this->auth);

    // Handle token:
    $tokenId = $this->tokenTransport->fetchTokenId($request);

    if (!is_null($tokenId)) {
        try {
            $token = $this->tokenStorage->fetchToken($tokenId);
            $user = $this->tokenAuthenticator->authenticate($token);

            $this->auth->start(
                new Authenticated(token: $token, user: $user),
                $this->tokenTransport->name()
            );
        } catch (TokenNotFoundException $e) {
            // ignore TokenNotFoundException as to
            // proceed with handling the response.
            // other exceptions will be handled by the error handler!
        }
    }

    // Handle the response:
    $response = $handler->handle($request);

    if (! $this->auth->hasAuthenticated()) {
        return $response;
    }

    if ($this->auth->isClosed()) {
        $this->tokenStorage->deleteToken($this->auth->getAuthenticated()->token());

        return $this->tokenTransport->removeToken(
            token: $this->auth->getAuthenticated()->token(),
            request: $request,
            response: $response,
        );
    }

    return $this->tokenTransport->commitToken(
        token: $this->auth->getAuthenticated()->token(),
        request: $request,
        response: $response,
    );
}

use Tobento\App\AppFactory;
use Tobento\App\User\Middleware\Authenticated;

$app = (new AppFactory())->createApp();

// Add directories:
$app->dirs()
    ->dir(realpath(__DIR__.'/../'), 'root')
    ->dir(realpath(__DIR__.'/../app/'), 'app')
    ->dir($app->dir('app').'config', 'config', group: 'config')
    ->dir($app->dir('root').'public', 'public')
    ->dir($app->dir('root').'vendor', 'vendor');

// Adding boots:
$app->boot(\Tobento\App\Http\Boot\Routing::class);
$app->boot(\Tobento\App\User\Boot\User::class);
$app->booting();

// Routes:
$app->route('GET', 'account', function() {
    // only for authenticated user!
    return 'response';
})->middleware(Authenticated::class)->name('account');

$app->route('GET', 'account', function() {
    // only for authenticated user!
    return 'response';
})->middleware([
    Authenticated::class,
    
    // you may allow access only to user authenticated via:
    'via' => 'loginform|loginlink',
    
    // or you may allow access only to user authenticated except via:
    'exceptVia' => 'remembered|loginlink',
    
    // you may specify a custom message to show to the user:
    'message' => 'You have insufficient rights to access the resource!',
    
    // you may specify a message level:
    'messageLevel' => 'notice',
    
    // you may specify a route name for redirection:
    'redirectRoute' => 'login',
    
    // or you may specify an uri for redirection
    'redirectUri' => '/login',
]);

// you may use the middleware alias defined in user config:
$app->route('GET', 'account', function() {
    return 'response';
})->middleware(['auth', 'via' => 'loginform|loginlink']);

// Run the app:
$app->run();

use Tobento\App\AppFactory;
use Tobento\App\User\Middleware\Unauthenticated;

$app = (new AppFactory())->createApp();

// Add directories:
$app->dirs()
    ->dir(realpath(__DIR__.'/../'), 'root')
    ->dir(realpath(__DIR__.'/../app/'), 'app')
    ->dir($app->dir('app').'config', 'config', group: 'config')
    ->dir($app->dir('root').'public', 'public')
    ->dir($app->dir('root').'vendor', 'vendor');

// Adding boots:
$app->boot(\Tobento\App\Http\Boot\Routing::class);
$app->boot(\Tobento\App\User\Boot\User::class);
$app->booting();

// Routes:
$app->route('GET', 'login', function() {
    // only for unauthenticated user!
    return 'response';
})->middleware(Unauthenticated::class)->name('login');

$app->route('GET', 'login', function() {
    // only for unauthenticated user!
    return 'response';
})->middleware([
    Unauthenticated::class,
    
    // you may allow access only to user authenticated via:
    'via' => 'remembered|loginlink',
    
    // or you may allow access only to user authenticated except via:
    'exceptVia' => 'remembered|loginlink',
    
    // you may specify a custom message to show to the user:
    'message' => 'Already authenticated!',
    
    // you may specify a message level:
    'messageLevel' => 'notice',
    
    // you may specify a route name for redirection:
    'redirectRoute' => 'home',
    
    // or you may specify an uri for redirection
    'redirectUri' => '/home',
]);

// you may use the middleware alias defined in user config:
$app->route('GET', 'login', function() {
    return 'response';
})->middleware(['guest', 'message' => 'Already authenticated!']);

// Run the app:
$app->run();

use Tobento\App\AppFactory;
use Tobento\App\User\Middleware\Verified;

$app = (new AppFactory())->createApp();

// Add directories:
$app->dirs()
    ->dir(realpath(__DIR__.'/../'), 'root')
    ->dir(realpath(__DIR__.'/../app/'), 'app')
    ->dir($app->dir('app').'config', 'config', group: 'config')
    ->dir($app->dir('root').'public', 'public')
    ->dir($app->dir('root').'vendor', 'vendor');

// Adding boots:
$app->boot(\Tobento\App\Http\Boot\Routing::class);
$app->boot(\Tobento\App\User\Boot\User::class);
$app->booting();

// Routes:
$app->route('GET', 'account', function() {
    // only for users with at least one channel verified!
    return 'response';
})->middleware(Verified::class)->name('account');

$app->route('GET', 'account', function() {
    // only for users with specific channels verified!
    return 'response';
})->middleware([
    Verified::class,
    
    // specify the channels the user must have at least verified one of:
    'oneOf' => 'email|smartphone',
    
    // OR specify the channels the user must have verified all:
    'allOf' => 'email|smartphone',
    
    // you may specify a custom message to show to the user:
    'message' => 'You are not verified to access the resource!',
    
    // you may specify a message level:
    'messageLevel' => 'notice',
    
    // you may specify a route name for redirection:
    'redirectRoute' => 'login',
    
    // or you may specify an uri for redirection
    'redirectUri' => '/login',
]);

// you may use the middleware alias defined in user config:
$app->route('GET', 'account', function() {
    return 'response';
})->middleware(['verified', 'oneOf' => 'email|smartphone']);

// Run the app:
$app->run();

use Tobento\App\AppFactory;
use Tobento\App\User\Middleware\VerifyPermission;

$app = (new AppFactory())->createApp();

// Add directories:
$app->dirs()
    ->dir(realpath(__DIR__.'/../'), 'root')
    ->dir(realpath(__DIR__.'/../app/'), 'app')
    ->dir($app->dir('app').'config', 'config', group: 'config')
    ->dir($app->dir('root').'public', 'public')
    ->dir($app->dir('root').'vendor', 'vendor');

// Adding boots:
$app->boot(\Tobento\App\Http\Boot\Routing::class);
$app->boot(\Tobento\App\User\Boot\User::class);
$app->booting();

// Routes:
$app->route('GET', 'login', function() {
    // only for user with permission login.show!
    return 'response';
})->middleware(VerifyPermission::class)->name('login.show');

// you may specify the following parameters:
$app->route('GET', 'login', function() {
    return 'response';
})->middleware([
    VerifyPermission::class,
    
    // set the permission (optional).
    // if not set it uses the route name:
    'permission' => 'login.show|anotherPermission',
    
    // you may specify a custom message to show to the user:
    'message' => 'You do not have permission to access the resource!',
    
    // you may specify a message level:
    'messageLevel' => 'notice',
    
    // you may specify a route name for redirection:
    'redirectRoute' => 'home',
    
    // or you may specify an uri for redirection
    'redirectUri' => '/home',
    
])->name('login.show');

// you may use the middleware alias defined in user config:
$app->route('GET', 'login', function() {
    return 'response';
})->middleware('can');

// Run the app:
$app->run();

use Tobento\App\AppFactory;
use Tobento\App\User\Middleware\VerifyRoutePermission;

$app = (new AppFactory())->createApp();

// Add directories:
$app->dirs()
    ->dir(realpath(__DIR__.'/../'), 'root')
    ->dir(realpath(__DIR__.'/../app/'), 'app')
    ->dir($app->dir('app').'config', 'config', group: 'config')
    ->dir($app->dir('root').'public', 'public')
    ->dir($app->dir('root').'vendor', 'vendor');

// Adding boots:
$app->boot(\Tobento\App\Http\Boot\Routing::class);
$app->boot(\Tobento\App\User\Boot\User::class);
$app->booting();

// Routes:
$app->routeResource('roles', RolesController::class))
    ->middleware([
        VerifyRoutePermission::class,
        'permissions' => [
            // 'route.name' => 'permission'
            'roles.index' => 'roles',
            'roles.show' => 'roles',
            'roles.create' => 'roles.create',
            'roles.store' => 'roles.create',
            'roles.edit' => 'roles.edit',
            'roles.update' => 'roles.edit',
            'roles.delete' => 'roles.delete',
        ],
        
        // you may specify a custom message to show to the user:
        'message' => 'You do not have permission to access the resource!',

        // you may specify a message level:
        'messageLevel' => 'notice',

        // you may specify a route name for redirection:
        'redirectRoute' => 'home',

        // or you may specify an uri for redirection
        'redirectUri' => '/home',
    ]);

// Run the app:
$app->run();

use Tobento\App\AppFactory;
use Tobento\App\User\Middleware\VerifyRole;

$app = (new AppFactory())->createApp();

// Add directories:
$app->dirs()
    ->dir(realpath(__DIR__.'/../'), 'root')
    ->dir(realpath(__DIR__.'/../app/'), 'app')
    ->dir($app->dir('app').'config', 'config', group: 'config')
    ->dir($app->dir('root').'public', 'public')
    ->dir($app->dir('root').'vendor', 'vendor');

// Adding boots:
$app->boot(\Tobento\App\Http\Boot\Routing::class);
$app->boot(\Tobento\App\User\Boot\User::class);
$app->booting();

// Routes:
$app->route('GET', 'login', function() {
    // only for user with role editor and administrator!
    return 'response';
})->middleware([
    VerifyRole::class,
    
    // set the role.
    'role' => 'editor|administrator',
    
    // you may specify a custom message to show to the user:
    'message' => 'You do not have a 

use Tobento\App\User\Authenticator\IdentityAuthenticator;
use Tobento\App\User\Authenticator\UserVerifiers;
use Tobento\App\User\Authenticator\UserRoleAreaVerifier;
use Tobento\App\User\Authentication\AuthInterface;
use Tobento\App\User\Authentication\Authenticated;
use Tobento\App\User\Authentication\Token\TokenStorageInterface;
use Tobento\App\User\Exception\AuthenticationException;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;

class LoginController
{
    public function login(
        ServerRequestInterface $request,
        IdentityAuthenticator $authenticator,
        AuthInterface $auth,
        TokenStorageInterface $tokenStorage,
    ): ResponseInterface {
        // You may specify the identity attributes to be checked.
        // At least one attribute is      $user = $authenticator->authenticate($request);
        } catch (AuthenticationException $e) {
            // handle exception:
            // create and return response for exception:
            return $createdResponse;
        }
        
        // on success create token and start auth:
        $token = $tokenStorage->createToken(
            payload: ['userId' => $user->id(), 'passwordHash' => $user->password()],
            authenticatedVia: 'loginform',
            authenticatedBy: $authenticator::class,
            // issuedAt: $issuedAt,
            // expiresAt: $expiresAt,
        );
        
        $auth->start(new Authenticated(token: $token, user: $user));
        
        // create and return response:
        return $createdResponse;
    }
}

use Tobento\App\User\Authenticator\AttributesAuthenticator;
use Tobento\App\User\Authenticator\UserVerifiers;
use Tobento\App\User\Authenticator\UserRoleAreaVerifier;
use Tobento\App\User\Authentication\AuthInterface;
use Tobento\App\User\Authentication\Authenticated;
use Tobento\App\User\Authentication\Token\TokenStorageInterface;
use Tobento\App\User\Exception\AuthenticationException;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;

class LoginController
{
    public function login(
        ServerRequestInterface $request,
        AttributesAuthenticator $authenticator,
        AuthInterface $auth,
        TokenStorageInterface $tokenStorage,
    ): ResponseInterface {
        // Specify the user attributes to be identified:
        $authenticator->addAttribute(
            // specify the user attribute name:
            name: 'email',
            
            // specify the reuest input name:
            // optional, if not set name will be used!
            inputName: 'email',
            
            // you may specify the validation rules:
            // default rule is create token and start auth:
        $token = $tokenStorage->createToken(
            payload: ['userId' => $user->id(), 'passwordHash' => $user->password()],
            authenticatedVia: 'loginform',
            authenticatedBy: $authenticator::class,
            // issuedAt: $issuedAt,
            // expiresAt: $expiresAt,
        );
        
        $auth->start(new Authenticated(token: $token, user: $user));
        
        // create and return response:
        return $createdResponse;
        
        // create and return response:
        return $createdResponse;
    }
}

use Tobento\App\User\Authenticator\UserPermissionVerifier;

// User must have one the specified permission.
$verifier = new UserPermissionVerifier('permission');

use Tobento\App\User\Authenticator\UserRoleVerifier;

// User must have one of the specified role.
$verifier = new UserRoleVerifier('editor', 'author');

use Tobento\App\User\Authenticator\UserRoleAreaVerifier;

// User must have one of the specified role area.
$verifier = new UserRoleAreaVerifier('frontend', 'api');

use Tobento\App\User\Authentication;
use Psr\Container\ContainerInterface;

return [    
    // ...

    'interfaces' => [
        // ...

        // Default token authenticator:
        // Authenticator\TokenAuthenticatorInterface::class => Authenticator\TokenAuthenticator::class,
        
        // Example with token verifiers:
        Authenticator\TokenAuthenticatorInterface::class => static function(ContainerInterface $c) {
            return new Authenticator\TokenAuthenticator(
                verifier: new Authenticator\TokenVerifiers(
                    new Authenticator\TokenPasswordHashVerifier(
                        // The token issuers (storage names) to verify password hash. 
                        // If empty it gets verified for all issuers.
                        issuers: ['session'],
                        
                        // The attribute name of the payload:
                        name: 'passwordHash',
                    ),
                ),
            );
        },

        // ...
    ],

];

use Tobento\App\User\Authenticator\TokenPasswordHashVerifier;

$verifier = new TokenPasswordHashVerifier(
    // The token issuers (storage names) to verify password hash. 
    // If empty it gets verified for all issuers.
    issuers: ['session'],
    
    // Will only be verified if authenticated
    // via remembered or loginlink if specified:
    authenticatedVia: 'remembered|loginlink',
    
    // The attribute name of the payload:
    name: 'passwordHash',
);

use Tobento\App\User\Authenticator\TokenPayloadVerifier;

$verifier = new TokenPayloadVerifier(
    // Specify the payload attribute name:
    name: 'remoteAddress',
    
    // Specify the value to match:
    value: $_SERVER['REMOTE_ADDR'] ?? null,

    // The token issuers (storage names) to verify password hash. 
    // If empty it gets verified for all issuers.
    issuers: ['session'],
    
    // Will only be verified if authenticated
    // via remembered or loginlink if specified:
    authenticatedVia: 'remembered|loginlink',
);

use Tobento\App\User\Authentication;
use Psr\Container\ContainerInterface;

return [    
    // ...

    'interfaces' => [
        // ...

        // Define the token storages you wish to support:
        Authentication\Token\TokenStoragesInterface::class => static function(ContainerInterface $c) {
            return new Authentication\Token\TokenStorages(
                // add null storage:
                new Authentication\Token\NullStorage(),
            );
        },
        
        // Define the default token storage used for auth:
        Authentication\Token\TokenStorageInterface::class => static function(ContainerInterface $c) {
            // you might change to null:
            return $c->get(Authentication\Token\TokenStoragesInterface::class)->get('null');
        },

        // ...
    ],

];

use Tobento\App\User\Authentication;
use Psr\Container\ContainerInterface;
use Psr\Clock\ClockInterface;

return [    
    // ...

    'interfaces' => [
        // ...

        // Define the token storages you wish to support:
        Authentication\Token\TokenStoragesInterface::class => static function(ContainerInterface $c) {
            return new Authentication\Token\TokenStorages(
                // add inmemory storage:
                new Authentication\Token\InMemoryStorage(
                    clock: $c->get(ClockInterface::class),
                ),
            );
        },
        
        // Define the default token storage used for auth:
        Authentication\Token\TokenStorageInterface::class => static function(ContainerInterface $c) {
            // you might change to inmemory:
            return $c->get(Authentication\Token\TokenStoragesInterface::class)->get('inmemory');
        },

        // ...
    ],

];

use Tobento\App\User\Authentication;
use Tobento\Service\Storage\StorageInterface;
use Psr\Container\ContainerInterface;
use Psr\Clock\ClockInterface;

return [    
    // ...

    'interfaces' => [
        // ...

        // Define the token storages you wish to support:
        Authentication\Token\TokenStoragesInterface::class => static function(ContainerInterface $c) {
            return new Authentication\Token\TokenStorages(
                new Authentication\Token\RepositoryStorage(
                    clock: $c->get(ClockInterface::class),
                    repository: new Authentication\Token\TokenRepository(
                        storage: $c->get(StorageInterface::class)->new(),
                        table: 'auth_tokens',
                    ),
                    name: 'repository',
                ),
            );
        },
        
        // Define the default token storage used for auth:
        Authentication\Token\TokenStorageInterface::class => static function(ContainerInterface $c) {
            return $c->get(Authentication\Token\TokenStoragesInterface::class)->get('repository');
        },

        // ...
    ],

];

$repositoryStorage->deleteExpiredTokens();

use Tobento\App\User\Authentication;
use Tobento\Service\Session\SessionInterface;
use Psr\Container\ContainerInterface;
use Psr\Clock\ClockInterface;

return [    
    // ...

    'interfaces' => [
        // ...

        // Define the token storages you wish to support:
        Authentication\Token\TokenStoragesInterface::class => static function(ContainerInterface $c) {
            return new Authentication\Token\TokenStorages(
                // add session storage:
                new Authentication\Token\SessionStorage(
                    session: $c->get(SessionInterface::class),
                    clock: $c->get(ClockInterface::class),
                ),
            );
        },
        
        // Define the default token storage used for auth:
        Authentication\Token\TokenStorageInterface::class => static function(ContainerInterface $c) {
            // you might change to session:
            return $c->get(Authentication\Token\TokenStoragesInterface::class)->get('session');
        },

        // ...
    ],

];

use Tobento\App\AppFactory;

// Create the app
$app = (new AppFactory())->createApp();

// Adding boots
$app->boot(\Tobento\App\User\Boot\User::class);
$app->boot(\Tobento\App\Http\Boot\Session::class);

// Run the app
$app->run();

use Tobento\App\User\Authentication;
use Psr\Container\ContainerInterface;
use Psr\Clock\ClockInterface;

return [    
    // ...

    'interfaces' => [
        // ...

        // Define the token transport you wish to support:
        Authentication\Token\TokenTransportsInterface::class => static function(ContainerInterface $c) {
            return new Authentication\Token\TokenTransports(
                new Authentication\Token\CookieTransport(
                    clock: $c->get(ClockInterface::class),
                    cookieName: 'token',
                ),
            );
        },
        
        // Define the default token transport(s) used for auth:
        Authentication\Token\TokenTransportInterface::class => static function(ContainerInterface $c) {
            return $c->get(Authentication\Token\TokenTransportsInterface::class)->get('cookie');
            //return $c->get(Authentication\Token\TokenTransportsInterface::class); // all
            //return $c->get(Authentication\Token\TokenTransportsInterface::class)->only(['cookie']);
        },

        // ...
    ],

];

use Tobento\App\AppFactory;

// Create the app
$app = (new AppFactory())->createApp();

// Adding boots
$app->boot(\Tobento\App\User\Boot\User::class);
$app->boot(\Tobento\App\Http\Boot\Cookies::class);

// Run the app
$app->run();

use Tobento\App\User\Authentication;
use Psr\Container\ContainerInterface;

return [    
    // ...

    'interfaces' => [
        // ...

        // Define the token transport you wish to support:
        Authentication\Token\TokenTransportsInterface::class => static function(ContainerInterface $c) {
            return new Authentication\Token\TokenTransports(
                new Authentication\Token\HeaderTransport(name: 'header', headerName: 'X-Auth-Token'),
            );
        },
        
        // Define the default token transport(s) used for auth:
        Authentication\Token\TokenTransportInterface::class => static function(ContainerInterface $c) {
            return $c->get(Authentication\Token\TokenTransportsInterface::class)->get('header');
            //return $c->get(Authentication\Token\TokenTransportsInterface::class); // all
            //return $c->get(Authentication\Token\TokenTransportsInterface::class)->only(['header']);
        },

        // ...
    ],

];

use Tobento\App\User\Event;

use Tobento\App\User\Migration\RolePermissionsAction;
use Tobento\App\User\RoleRepositoryInterface;
use Tobento\Service\Migration\MigrationInterface;
use Tobento\Service\Migration\ActionsInterface;
use Tobento\Service\Migration\Actions;

class RolesPermissionMigration implements MigrationInterface
{
    public function __construct(
        protected RoleRepositoryInterface $roleRepository,
    ) {}
    
    /**
     * Return a description of the migration.
     *
     * @return string
     */
    public function description(): string
    {
        return 'Role permissions.';
    }
        
    /**
     * Return the actions to be processed on install.
     *
     * @return ActionsInterface
     */
    public function install(): ActionsInterface
    {
        return new Actions(
            new RolePermissionsAction(
                roleRepository: $this->roleRepository,
                add: [
                    'developer' => ['roles', 'roles.create', 'roles.edit', 'roles.delete', 'roles.permissions'],
                    'administrator' => ['roles', 'roles.create', 'roles.edit', 'roles.delete', 'roles.permissions'],
                ],
                description: 'Roles permissions added for developer and administrator',
            ),
        );
    }

    /**
     * Return the actions to be processed on uninstall.
     *
     * @return ActionsInterface
     */
    public function uninstall(): ActionsInterface
    {
        return new Actions(
            new RolePermissionsAction(
                roleRepository: $this->roleRepository,
                remove: [
                    'developer' => ['roles', 'roles.create', 'roles.edit', 'roles.delete', 'roles.permissions'],
                    'administrator' => ['roles', 'roles.create', 'roles.edit', 'roles.delete', 'roles.permissions'],
                ],
                description: 'Roles permissions removed for developer and administrator',
            ),
        );
    }
}

use Tobento\App\User\Authentication;
use Psr\Container\ContainerInterface;
use Psr\Clock\ClockInterface;

return [    
    // ...

    'middlewares' => [
        // Uncomment it and set it on each route individually!
        // User\Middleware\Authentication::class,
    ],
    
    'interfaces' => [
        // ...

        // Define the token storages you wish to support:
        Authentication\Token\TokenStoragesInterface::class => static function(ContainerInterface $c) {
            return new Authentication\Token\TokenStorages(
                new Authentication\Token\SessionStorage(
                    session: $c->get(SessionInterface::class),
                    clock: $c->get(ClockInterface::class),
                ),
            );
        },
        
        // Define the token transport you wish to support:
        Authentication\Token\TokenTransportsInterface::class => static function(ContainerInterface $c) {
            return new Authentication\Token\TokenTransports(
                new Authentication\Token\CookieTransport(
                    clock: $c->get(ClockInterface::class),
                    cookieName: 'token',
                ),
                new Authentication\Token\HeaderTransport(name: 'header', headerName: 'X-Auth-Token'),
            );
        },

        // ...
    ],

];

use Tobento\App\User\Middleware\AuthenticationWith;
use Tobento\Service\Routing\RouteGroupInterface;

// ...

// Routes:
// Web example:
$app->routeGroup('', function(RouteGroupInterface $group) {
    
    // define your web routes:
    
})->middleware([
    AuthenticationWith::class,
    
    // specify the token transport name:
    'transportName' => 'cookie',
    
    // specify the token storage name:
    'storageName' => 'session',
]);

// Api example:
$app->routeGroup('api', function(RouteGroupInterface $group) {
    
    // define your api routes:

})->middleware([
    AuthenticationWith::class,
    
    // specify the token transport name:
    'transportName' => 'header',
    
    // specify the token storage name:
    'storageName' => 'session',
]);

// ...

use Tobento\App\User\PasswordHasherInterface;

class SomeService
{
    public function __construct(
        private PasswordHasherInterface $passwordHasher,
    ) {
        // hash password:
        $hashedPassword = $passwordHasher->hash(plainPassword: 'password');
        
        // verify password:
        $isValid = $passwordHasher->verify(hashedPassword: $hashedPassword, plainPassword: 'password');
    }
}

use Tobento\App\User\UserRepositoryInterface;
use DateTime;

class SomeService
{
    public function __construct(
        private UserRepositoryInterface $userRepository,
    ) {
        $updatedUser = $userRepository->addVerified(
            id: 5, // user id
            channel: 'email',
            verifiedAt: new DateTime('2023-09-24 00:00:00'),
        );
    }
}

use Tobento\App\User\UserRepositoryInterface;
use DateTime;

class SomeService
{
    public function __construct(
        private UserRepositoryInterface $userRepository,
    ) {
        $updatedUser = $userRepository->removeVerified(
            id: 5, // user id
            channel: 'email',
        );
    }
}

use Tobento\App\User\UserInterface;

var_dump($user instanceof UserInterface);
// bool(true)

// Get the verified channels:
$verified = $user->getVerified();
// ['email' => '2023-09-24 00:00:00']

// Get the date verified at for a specific channel:
$emailVerifiedAt = $user->getVerifiedAt(channel: 'email');
// '2023-09-24 00:00:00' or NULL

// Returns true if the specified channels are verified, otherwise false.
$verified = $user->isVerified(channels: ['email', 'smartphone']);

// Returns true if at least one channel is verified, otherwise false.
$verified = $user->isOneVerified();

// or one of the specified channels:
$verified = $user->isOneVerified(channels: ['email', 'smartphone']);
app/config/user.php
app/config/user.php
app/config/database.php
app/config/user.php
app/config/user.php
app/config/user.php
app/config/user.php
app/config/user.php
app/config/user.php
app/config/user.php
app/config/user.php
app/config/user.php
app/config/user.php
app/config/user.php
app/config/user.php

php ap acl:roles

php ap acl:rules

php ap auth:purge-tokens

php ap auth:purge-tokens --storage=name
app/config/user.php
app/config/user.php