jacopo/laravel-authentication-acl
Laravel Authentication ACL provides a complete authentication and access control system: users, roles, permissions, login, registration, password reset, social login, and an admin interface for managing users and permissions.
This package is a Laravel 4/5-era drop-in auth + ACL + admin panel. It is archived (no maintenance), so treat it as legacy integration work: pin it to the Laravel version its composer.json supports (roughly Laravel 5.0–5.5 / PHP 7.0–7.1) and do not attempt to modernise it in place — the cost of porting usually exceeds rewriting with spatie/laravel-permission + Laravel Breeze/Jetstream.
Minimum steps:
composer require jacopo/laravel-authentication-acl (check the latest tag compatible with your Laravel version first — release notes on the repo list supported Laravel versions).config/app.php (Jacopo\Authentication\AuthenticationServiceProvider) and the facades/aliases it publishes.laravel-acl config, migrations, views and public assets. Publish all of them so you can override views without touching vendor/.create_users_table migration — this package ships its own users, roles, permissions, role_user, permission_role, user_profiles, and social-login tables, and they collide.php artisan migrate, then seed the default roles/permissions (the package ships seeders and a config array of default roles/permissions — seed them before creating your first user).config/auth.php to Jacopo\Authentication\Models\User (or the package model your version exposes). The admin panel and Auth::user() will not behave correctly otherwise./login, /signup and /admin — the url_prefix, login/signup toggles and admin path all live in the published laravel-acl config.First use case: register a user via /signup, manually attach the admin role in the role_user table, then log into /admin to manage users, roles and permissions from the UI. That single loop validates the whole stack (routes, middleware, guard, ACL tables).
Where to look first: the published config file (it documents the package's extension points better than the README), the package's routes.php, and the Models/ directory — User, Role, Permission, UserProfile define the schema your app will actually query.
Auth guard vs. Laravel auth. The package pre-dates Auth::guard() and Gate/Policy, so day-to-day you work through its own facade/interface rather than Laravel's native can(). In this codebase:
use Illuminate\Support\Facades\Auth;
$user = Auth::user(); // Jacopo\Authentication\Models\User
$user->hasRole('admin'); // role check
$user->hasPermission('users.list'); // permission check
$user->userProfile->first_name; // profile is a relation, not columns on users
Keep these checks in filters/middleware and controllers, and avoid scattering string literals — define permission names in one constants class and reference them from Blade partials too.
Route protection. Apply the package's admin middleware/filter to a route group rather than checking roles inside every controller action:
Route::group(['middleware' => ['web', 'auth.admin']], function () {
Route::get('/reports', 'ReportController@index');
});
For your own feature-level ACL (not admin), wrap a route group with a custom middleware that calls hasPermission() and aborts 403.
Extending the admin panel. The panel is a normal controller + Blade stack you can extend without forking:
auth.admin and the panel's layout.User lifecycle hooks. Registration, email confirmation and password reset are handled by the package's controllers. Rather than overriding controllers, add your own logic in model events or by extending the package's user model:
class User extends \Jacopo\Authentication\Models\User
{
protected static function boot()
{
parent::boot();
static::created(function ($user) {
// provision tenant, send welcome mail, assign default role
});
}
}
Then point config/auth.php (and the package config, if it names a user class) at your subclass.
Authorization in Blade. Because there's no Gate, create a tiny helper/partial:
@if (Auth::user() && Auth::user()->hasPermission('invoices.manage'))
<a href="{{ route('invoices.index') }}">Invoices</a>
@endif
Cache the permission list per request (a $permissions array set by a middleware) if you render menus with many checks — the package hits the DB per call.
Integration with an existing app. Use the package only for auth + ACL and let your app own the rest: keep your domain tables keyed on users.id, and treat user_profiles as the canonical place for names/avatars so the admin panel stays coherent. If you need social login, configure providers in the package config; it delegates to Socialite, so app credentials and callback URLs must match the package's routes.
str_random, array_except semantics, middleware registration differences, and Symfony/Illuminate signature changes. Pin every dependency and commit the lock file. For new work, budget a rewrite instead.users table collision is the #1 install failure. Remove Laravel's stock users migration before migrate, and check the published migrations for a user_profiles foreign key ordering issue (profiles must migrate after users).hasPermission()" on Auth::user(), because you get the stock App\User instead. Also clear config:cache after changing it.permission_role. Seed the default role→permission mappings, or the admin panel look right while every check returns false.app/Http/filters.php (or routes.php for L4); in later ones a middleware in Kernel.php. Match the pattern used by your pinned version — mixing them silently disables protection. Verify by hitting /admin while logged out; it must redirect, not 500 or render.vendor:publish --force will overwrite your customisations — back up first.user_profiles, not users. Queries like User::where('first_name', ...) fail; join user_profiles. Registration creates the profile row — if you create users programmatically, create the profile too.activated/confirmed by default in config, and whether your tests bypass it. This bites when seeding admin users by hand.MAIL_* and the reset route names before demoing; broken links usually come from APP_URL/route name drift, not the package./login/{provider}/callback by default). A mismatch produces a generic Socialite exception that surfaces as a 500 — check storage/logs/laravel.log, not the UI.hasRole/hasPermission inputs and the user id, and inspect role_user / permission_role directly. Most "it's broken" reports are stale seed data or a user with no roles after a manual insert.php artisan route:cache can break the package's closure-based routes on older Laravel versions. If /admin 404s in production but works locally, that's your culprit.Authenticator/Authorization interfaces early so replacing it with spatie/laravel-permission + a modern starter kit is a swap, not a rewrite.How can I help you explore Laravel packages today?