Auth
Multi-guard, attribute-first authentication and authorization for the MonkeysLegion framework. Ground-up rebuild for PHP 8.4 with property hooks, typed constants, and zero hard dependencies.
Features
| Feature | Status |
|---|---|
| Multi-Guard System | JWT, Session, API Key, WebAuthn/Passkey, Composite (try multiple in order) |
| Attribute-First Auth | #[Guard], #[Authenticated], #[Authorize], #[RequiresRole], #[RequiresPermission], #[RateLimit], #[Passkey] |
| JWT Service | HS256/RS256, token families, refresh rotation attack detection |
| Session Guard | Session fixation prevention, token version validation, remember-me |
| Policy Gate | allows(), denies(), authorize(), inspect() with deny reasons |
| RBAC | Hierarchical roles, wildcard permissions, super-admin, decoupled via RoleRepositoryInterface |
| Password Hasher | NIST SP 800-63B policy engine, Argon2ID/bcrypt, auto-rehash |
| Rate Limiting | Per-route, per-user, configurable via attributes |
| Two-Factor Auth | TOTP (RFC 6238), backup/recovery codes |
| PSR-15 Middleware | Authentication, Authorization, Rate Limiting — all attribute-aware |
| PHP 8.4 Native | Property hooks, typed constants, readonly DTOs |
Requirements
- PHP 8.4 or higher
firebase/php-jwt^7.0psr/http-message^2.0
Installation
composer require monkeyscloud/monkeyslegion-auth:dev-2.0.0
Architecture
The package is organized into several clear namespaces:
Attribute/: Route and controller attributes (#[RequiresRole],#[RateLimit], etc.)Guard/: Authentication implementations (JwtGuard,SessionGuard,ApiKeyGuard)Middleware/: PSR-15 middlewares for Auth and Rate LimitingService/: Core logic (AuthService,JwtService,PasswordHasher)Policy/: Authorization gate (Gate)RBAC/: Role-Based Access Control logic (RbacService)TwoFactor/: 2FA implementations (TotpProvider)
Authentication Setup
1. Setting up the AuthManager and Guards
The AuthManager acts as a registry for your guards.
use MonkeysLegion\Auth\Guard\AuthManager;
use MonkeysLegion\Auth\Guard\JwtGuard;
use MonkeysLegion\Auth\Service\JwtService;
// Setup JWT Service
$jwtService = new JwtService('your-secret-key-at-least-32-chars');
// Register Guard
$jwtGuard = new JwtGuard($jwtService, $userProvider);
$manager = new AuthManager(defaultGuard: 'jwt');
$manager->register('jwt', $jwtGuard);
2. Basic Login using AuthService
The AuthService orchestrates credentials checking, hashing, rate limiting, and token issuance.
use MonkeysLegion\Auth\Service\AuthService;
use MonkeysLegion\Auth\Exception\InvalidCredentialsException;
$authService = new AuthService($userProvider, $passwordHasher, $jwtService, $tokenStorage, $rateLimiter);
try {
$result = $authService->login('user@example.com', 'password123');
if ($result->requires2FA) {
// Handle Two-Factor Authentication flow
return ['challenge_token' => $result->challengeToken];
}
return [
'user' => $result->user->toArray(),
'tokens' => $result->tokens->toArray(), // Returns TokenPair with access & refresh tokens
];
} catch (InvalidCredentialsException $e) {
return response('Unauthorized', 401);
}
Guard Implementations
Session Guard (Stateful)
Ideal for traditional web applications. Protects against session fixation and supports "remember me".
use MonkeysLegion\Auth\Guard\SessionGuard;
$guard = new SessionGuard($session, $userProvider);
// Perform login
$guard->login($user, remember: true);
// Authenticate from current request/session
$user = $guard->authenticate($request);
// Logout
$guard->logout();
API Key Guard (Stateless)
Validates API keys via headers (e.g. X-API-Key) or query parameters.
use MonkeysLegion\Auth\Guard\ApiKeyGuard;
$guard = new ApiKeyGuard($userProvider, headerName: 'X-API-Key');
$manager->register('api', $guard);
// Reads X-API-Key from $request
$user = $guard->authenticate($request);
Composite Guard
Tries multiple guards in sequence. First match wins.
use MonkeysLegion\Auth\Guard\CompositeGuard;
$composite = new CompositeGuard([$jwtGuard, $apiKeyGuard]);
$manager->register('mixed', $composite);
// Will try JWT first, then API Key
$user = $composite->authenticate($request);
Attribute-Based Security
MonkeysLegion v2 uses PHP 8 attributes extensively to secure your controllers and routes. These attributes are automatically parsed by the AuthenticationMiddleware and AuthorizationMiddleware.
use MonkeysLegion\Auth\Attribute\Authenticated;
use MonkeysLegion\Auth\Attribute\RequiresRole;
use MonkeysLegion\Auth\Attribute\RequiresPermission;
use MonkeysLegion\Auth\Attribute\RateLimit;
#[Authenticated(guard: 'jwt')]
#[RequiresRole(['admin', 'editor'], mode: 'any')]
#[RateLimit(maxAttempts: 60, decaySeconds: 60)]
class ArticleController
{
#[RequiresPermission('articles.publish')]
public function publish(int $id): Response
{
// Only accessible if:
// 1. Authenticated via JWT
// 2. User has 'admin' OR 'editor' role
// 3. User has 'articles.publish' permission
// 4. Rate limit is not exceeded
}
}
Role-Based Access Control (RBAC)
The RbacService supports exact matches and wildcard permissions (e.g. posts.*), as well as super-admin overrides (*).
use MonkeysLegion\Auth\RBAC\RbacService;
$rbac = new RbacService($roleRepository);
// Check Roles
$rbac->hasRole($user, 'admin');
$rbac->hasAnyRole($user, ['admin', 'editor']);
// Check Permissions
$rbac->hasPermission($user, 'posts.edit');
Policy Gate (Authorization)
The Gate allows you to define fine-grained ability checks or delegate them to Policy classes.
use MonkeysLegion\Auth\Policy\Gate;
$gate = new Gate();
// Define an ability using a closure
$gate->define('update-post', fn($user, $post) => $user->id === $post->authorId);
// Or map a Model to a Policy class
$gate->policy(Post::class, PostPolicy::class);
// Perform Checks
if ($gate->allows($user, 'update-post', $post)) {
// allowed
}
// Check with detailed inspection
$inspection = $gate->inspect($user, 'update-post', $post);
if (!$inspection['allowed']) {
echo $inspection['reason']; // e.g. "Allowed by before callback." or "Denied by ability definition."
}
// Authorize will throw an UnauthorizedException if denied
$gate->authorize($user, 'update-post', $post);
Advanced Features
Token Family Rotation (JWT)
The JwtService and AuthService implement token family rotation to mitigate refresh token theft. If a blacklisted refresh token is reused, the entire token family is compromised. AuthService will automatically increment the user's token version and revoke all tokens.
// Automatically validates refresh token, checks blacklist, rotates family, and returns new TokenPair
$tokens = $authService->refresh($refreshToken, $requestIp);
Password Hasher and Policies
The PasswordHasher enforces NIST SP 800-63B guidelines via the PasswordPolicy DTO.
use MonkeysLegion\Auth\Service\PasswordHasher;
use MonkeysLegion\Auth\DTO\PasswordPolicy;
$hasher = new PasswordHasher(
policy: new PasswordPolicy(
minLength: 12,
requireUppercase: true,
requireNumbers: true,
requireSymbols: true,
rejectCommon: true, // Prevents "password123", etc.
)
);
// Will throw InvalidArgumentException if the policy is not met
$hash = $hasher->hash('MyStr0ng!Pass');
// Verify and check if rehash is needed (e.g. cost factor increased)
if ($hasher->verify('MyStr0ng!Pass', $hash)) {
if ($hasher->needsRehash($hash)) {
// Upgrade hash transparently
$newHash = $hasher->hashWithoutPolicy('MyStr0ng!Pass');
}
}
Rate Limiting
The InMemoryRateLimiter provides fixed-window rate limiting. AuthService uses it out of the box to prevent brute force attacks on the login and register endpoints.
use MonkeysLegion\Auth\RateLimit\InMemoryRateLimiter;
$limiter = new InMemoryRateLimiter();
$limiter->attempt('login:user@example.com', maxAttempts: 5, decaySeconds: 900); // true/false
Two-Factor Authentication (TOTP)
use MonkeysLegion\Auth\TwoFactor\TotpProvider;
$totp = new TotpProvider();
// Generate a new secret for a user
$secret = $totp->generateSecret();
// Get URI for QR Code generation (Google Authenticator, Authy)
$uri = $totp->getProvisioningUri($secret, 'user@example.com', 'My App');
// Verify a code submitted by the user
$isValid = $totp->verify($secret, '123456');
Security Posture
- Token family tracking — detects refresh token reuse attacks
- CSPRNG token IDs —
random_bytes(16)for all token identifiers - Session fixation prevention — session regenerated on every login
- Token versioning — increment version to invalidate all sessions/tokens globally
- Timing-safe comparisons —
hash_equalsfor all credential/token checks - Account lockout — configurable failed attempt limits
- Audit trail — all auth events include correlation IDs
Testing
composer test
composer phpstan
License
MIT © MonkeysCloud