jengo/auth
Status: In Active Development • Pre-Release
jengo/auth is a unified authentication and authorization engine for CodeIgniter 4 and the Jengo Framework, powered by Vima.
Key Capabilities
- Universal Guard: Intelligently auto-detects Bearer tokens for API requests and seamlessly falls back to session cookies and remember-me tokens for web applications.
- Pluggable Response Modifiers: Switch between traditional CodeIgniter 4 HTML views, REST API JSON envelopes, and Inertia.js SPA responses via configuration.
- Vima Authorization Engine: Advanced Role-Based Access Control (RBAC) with role hierarchies, Attribute-Based Access Control (ABAC) policies, direct grants, explicit denies, and TypeScript map generation.
- Declarative PHP 8 Security Attributes: Guard controllers and specific methods cleanly using
#[Authenticate],#[Can],#[Role], and#[Guest]. - Smart Throttling & Brute-Force Protection: Multi-signal rate limiter that evaluates IP and identity to prevent brute-force attacks without penalizing shared corporate networks.
- Post-Auth Actions Pipeline: Modular multi-step verification pipeline supporting Email 2FA, Email Activation, and custom flows.
- Personal Access Tokens (PAT): Issue, inspect, and revoke scoped API tokens for mobile clients, third-party integrations, and background services.
- Passwordless Magic Links & Self-Service Resets: Built-in time-limited secure magic link logins and password recovery workflows.
- CodeIgniter Shield Migration: Seamlessly import users, credentials, and password hashes from existing CodeIgniter Shield installations with a single command.
Installation
Install the package via Composer:
composer require jengo/authRun the interactive setup command:
php spark jengo:auth setupExecute database migrations:
php spark migrateThe setup command publishes app/Config/Auth.php, app/Config/Vima.php, app/Libraries/Vima/Setup.php, and registers standard authentication routes in app/Config/Routes.php.
Route Publishing
Configure and publish authentication routes in app/Config/Routes.php:
Standard Publishing
// app/Config/Routes.php
service('auth')->routes($routes);Customized Paths & Prefixes
service('auth')->routes($routes, [
'prefix' => 'auth',
'paths' => [
'login' => 'sign-in',
'logout' => 'sign-out',
'register' => 'join',
],
'logoutMethod' => 'post', // 'post' (default) or 'get'
]);Selective Flow Registration
// Register only specific flows
service('auth')->routes($routes, [
'only' => ['login', 'register', 'password-reset'],
]);
// Or exclude specific flows
service('auth')->routes($routes, [
'except' => ['magic-link', 'tokens'],
]);Controller Overrides & Named Prefixes
service('auth')->routes($routes, [
'as' => 'admin.', // Generates admin.login, admin.register, etc.
'controllers' => [
'login' => \App\Controllers\AdminLoginController::class,
],
]);Quick Start
1. Authentication State
// Check if current visitor is authenticated
if (auth()->check()) {
$user = auth()->user(); // Jengo\Auth\Entities\User
$userId = auth()->id();
}
// Check if visitor is a guest
if (auth()->guest()) {
return redirect()->to('/login');
}2. Attempting Logins
$credentials = [
'email' => $this->request->getPost('email'),
'password' => $this->request->getPost('password'),
];
$result = auth()->attempt($credentials, remember: true);
if ($result->isSuccessful()) {
return redirect()->to('/dashboard');
}
return redirect()->back()->with('error', $result->getMessage());3. Personal Access Tokens
Issue scoped tokens for API or mobile clients:
$user = auth()->user();
// Create token with specific abilities/scopes
$token = auth()->createTokenFor($user, 'mobile-app', ['posts.read', 'posts.create']);
// Plaintext token is only displayed once
echo $token->plainTextToken;Authorization with Vima
jengo/auth integrates the Vima authorization framework for enterprise permission handling.
Defining Roles and Permissions
Edit app/Libraries/Vima/Setup.php:
namespace App\Libraries\Vima;
use Vima\Core\Config\Contracts\SetupProviderInterface;
use Vima\Core\Role\Entities\Role;
use Vima\Core\Permission\Entities\Permission;
class Setup implements SetupProviderInterface
{
public function get(): array
{
return [
'permissions' => [
Permission::define('posts.create', 'Create new blog posts'),
Permission::define('posts.edit', 'Edit existing blog posts'),
Permission::define('posts.publish', 'Publish drafts live'),
Permission::define('users.manage', 'Manage team members'),
],
'roles' => [
Role::define('editor', 'Content Editor')
->withPermissions(['posts.create', 'posts.edit']),
Role::define('admin', 'Administrator')
->inherits(['editor'])
->withPermissions(['posts.publish', 'users.manage']),
],
];
}
}Synchronizing to Database
Sync definitions into your database:
php spark vima:syncGenerating TypeScript Mappings
Generate type-safe PHP mappers and TypeScript mapping interfaces for your frontend (React, Vue, Inertia):
php spark vima:maps:generate --tsThis scaffolds PHP classes in app/Mappers/Vima/ and TypeScript definitions in resources/js/vima/.
Checking Permissions & Policies
// Check permission via global helper
if (can('posts.publish')) {
// Current user can publish posts
}
// Check with entity context (ABAC Policy)
if (can('posts.edit', $post)) {
// Current user can edit this specific post instance
}Fluent Grants and Explicit Denies
Explicit denies strictly override any role-level grant:
$user = auth()->user();
// Grant role or direct permission
auth()->user($user)->grant()->role('editor');
auth()->user($user)->grant()->permission('reports.view');
// Explicitly deny permission (overrides role grant)
auth()->user($user)->deny()->permission('posts.publish', 'Account suspended by admin');Declarative PHP 8 Attributes
Protect controllers and actions declaratively without writing manual filters:
namespace App\Controllers;
use Jengo\Auth\Attributes\Authenticate;
use Jengo\Auth\Attributes\Can;
use Jengo\Auth\Attributes\Role;
use Jengo\Auth\Attributes\Guest;
#[Authenticate]
class ArticleController extends BaseController
{
#[Can('posts.create')]
public function new()
{
return view('articles/create');
}
#[Role('admin')]
public function destroy(int $id)
{
// Only users with the 'admin' role can delete
}
}To restrict an endpoint to guests only (e.g., login or registration page):
#[Guest]
class LoginController extends BaseController
{
public function show()
{
return view('auth/login');
}
}Response Modifiers
jengo/auth can format its controller responses for different frontends by adjusting $responseModifier in app/Config/Auth.php:
StandardViewModifier: Renders traditional CodeIgniter 4 HTML view templates and standard redirects.JsonModifier: Outputs clean REST API JSON responses (status,message,data,errors).InertiaModifier: Renders Inertia.js component responses (Inertia::render(...)) for React, Vue, and Svelte SPAs.
Custom Notification Senders & Queues
Swap the default mailer with an asynchronous background queue or third-party service by implementing Jengo\Auth\Contracts\NotificationSenderInterface:
namespace App\Notifications;
use Jengo\Auth\Contracts\NotificationSenderInterface;
use Jengo\Auth\Entities\User;
class QueuedAuthNotifier implements NotificationSenderInterface
{
public function sendMagicLink(User $user, string $token, string $url): bool
{
queue('emails')->push(new SendMagicLinkJob($user->getEmail(), $url));
return true;
}
public function sendPasswordReset(User $user, string $token, string $url): bool
{
queue('emails')->push(new SendPasswordResetJob($user->getEmail(), $url));
return true;
}
public function sendMfaCode(User $user, string $code): bool
{
return true;
}
public function sendActivation(User $user, string $token, string $url): bool
{
return true;
}
public function sendNotification(string $type, User $user, array $data = []): bool
{
return true;
}
}Register your custom sender in app/Config/Auth.php:
public string $notifier = \App\Notifications\QueuedAuthNotifier::class;Custom Guards
Register custom authentication drivers (e.g., JWT, HMAC, LDAP) via auth()->extend():
// Register a custom guard
auth()->extend('jwt', fn() => new \App\Authentication\Guards\JwtGuard());
// Use it explicitly
if (auth()->guard('jwt')->check()) {
$user = auth()->guard('jwt')->user();
}You can set your custom guard as default in app/Config/Auth.php:
public string $defaultGuard = 'jwt';CLI Commands
Jengo Auth Commands
# Publish configuration, migrations, and routes
php spark jengo:auth setup
# Migrate users and credentials from CodeIgniter Shield
php spark jengo:auth import:shield [--dry-run]Vima Authorization Commands
# Synchronize roles and permissions from Setup.php to database
php spark vima:sync
# Generate PHP mappers and TypeScript interfaces
php spark vima:maps:generate [--ts]
# Create role or permission directly via CLI
php spark vima:role:create <name>
php spark vima:permission:create <name>
# Grant role or permission to user
php spark vima:grant <user-id> <role|permission>
# Explicitly deny permission to user
php spark vima:deny <user-id> <permission> [reason]
# Scaffold an ABAC Policy class
php spark vima:make:policy <PolicyName>