marko/admin-auth
Admin authentication and role-based authorization --- manages admin users, roles, permissions, and access control for the admin panel. The package provides an AdminUserProvider that integrates with the authentication system, a PermissionRegistry for declaring and matching permissions (including wildcards), role and permission entities with repository interfaces, and AdminAuthMiddleware that enforces #[RequiresPermission] checks on controller methods. Super admin roles bypass all permission checks.
Installation
Section titled “Installation”composer require marko/admin-authProtecting Admin Routes
Section titled “Protecting Admin Routes”Add AdminAuthMiddleware to controller methods or classes to require authentication:
use Marko\AdminAuth\Middleware\AdminAuthMiddleware;use Marko\Routing\Attributes\Get;use Marko\Routing\Attributes\Middleware;
class CatalogController{ #[Get('/admin/catalog/products')] #[Middleware(AdminAuthMiddleware::class)] public function index(): Response { // Only authenticated admin users reach here }}Unauthenticated requests are redirected to the admin login page (or receive a 401 JSON response for API requests).
Requiring Permissions
Section titled “Requiring Permissions”Use #[RequiresPermission] to enforce specific permissions on a route:
use Marko\AdminAuth\Attributes\RequiresPermission;use Marko\AdminAuth\Middleware\AdminAuthMiddleware;use Marko\Routing\Attributes\Get;use Marko\Routing\Attributes\Middleware;
class ProductController{ #[Get('/admin/catalog/products')] #[Middleware(AdminAuthMiddleware::class)] #[RequiresPermission(permission: 'catalog.products.view')] public function index(): Response { // Only admin users with 'catalog.products.view' permission }}Registering Permissions
Section titled “Registering Permissions”Modules register their permissions via PermissionRegistryInterface:
use Marko\AdminAuth\Contracts\PermissionRegistryInterface;
class CatalogPermissions{ public function __construct( private readonly PermissionRegistryInterface $permissionRegistry, ) {}
public function register(): void { $this->permissionRegistry->register( 'catalog.products.view', 'View Products', 'Catalog', ); $this->permissionRegistry->register( 'catalog.products.edit', 'Edit Products', 'Catalog', ); }}Wildcard Permissions
Section titled “Wildcard Permissions”Permissions support wildcard matching. A role with catalog.* can access any catalog. permission:
use Marko\AdminAuth\Contracts\PermissionRegistryInterface;
$permissionRegistry->matches('catalog.*', 'catalog.products.view'); // true$permissionRegistry->matches('catalog.*', 'catalog.products.edit'); // true$permissionRegistry->matches('*', 'anything.here'); // true$permissionRegistry->matches('catalog.products.*', 'catalog.orders'); // falseChecking Permissions in Code
Section titled “Checking Permissions in Code”AdminUserInterface provides methods for checking permissions and roles:
use Marko\AdminAuth\Entity\AdminUserInterface;
class OrderService{ public function cancel( AdminUserInterface $adminUser, int $orderId, ): void { if (!$adminUser->hasPermission('orders.cancel')) { throw new AuthorizationException('Cannot cancel orders'); }
if ($adminUser->hasRole('super-admin')) { // super admin bypass } }}Admin User Entity
Section titled “Admin User Entity”AdminUser implements AdminUserInterface and integrates with the authentication system:
use Marko\AdminAuth\Entity\AdminUserInterface;use Marko\Authentication\Contracts\GuardInterface;
class DashboardController{ public function __construct( private readonly GuardInterface $guard, ) {}
public function index(): Response { $user = $this->guard->user();
if ($user instanceof AdminUserInterface) { $name = $user->getName(); $roles = $user->getRoles(); $permissions = $user->getPermissionKeys(); } }}API Reference
Section titled “API Reference”AdminUserInterface
Section titled “AdminUserInterface”interface AdminUserInterface extends AuthenticatableInterface{ public function getEmail(): string; public function getName(): string; public function setRoles(array $roles, array $permissionKeys = []): void; public function getRoles(): array; public function getPermissionKeys(): array; public function hasPermission(string $key): bool; public function hasRole(string $slug): bool;}PermissionRegistryInterface
Section titled “PermissionRegistryInterface”interface PermissionRegistryInterface{ public function register(string $key, string $label, string $group): void; public function all(): array; public function getByGroup(string $group): array; public function matches(string $pattern, string $permissionKey): bool;}RequiresPermission Attribute
Section titled “RequiresPermission Attribute”#[RequiresPermission(permission: 'section.action')]AdminAuthMiddleware
Section titled “AdminAuthMiddleware”class AdminAuthMiddleware implements MiddlewareInterface{ public function handle(Request $request, callable $next): Response;}Repository Interfaces
Section titled “Repository Interfaces”interface AdminUserRepositoryInterface extends RepositoryInterface{ public function findByEmail(string $email): ?AdminUser; public function getRolesForUser(int $userId): array; public function syncRoles(int $userId, array $roleIds): void;}
interface RoleRepositoryInterface extends RepositoryInterface{ public function findBySlug(string $slug): ?Role; public function getPermissionsForRole(int $roleId): array; public function syncPermissions(int $roleId, array $permissionIds): void; public function isSlugUnique(string $slug, ?int $excludeId = null): bool;}
interface PermissionRepositoryInterface extends RepositoryInterface{ public function findByKey(string $key): ?Permission; public function findByGroup(string $group): array; public function syncFromRegistry(): void;}AdminAuthConfigInterface
Section titled “AdminAuthConfigInterface”interface AdminAuthConfigInterface{ public function getGuardName(): string; public function getSuperAdminRoleSlug(): string;}