Auth, Session & Validation
Table of Contents
- AuthService
- JwtService
- Auth Attributes
- Auth DTOs
- Auth Exceptions
- Auth Events
- SessionManager
- Validation Attributes
- Gate (Authorization)
- RBAC
AuthService
MonkeysLegion\Auth\Service\AuthService
Core authentication service — guard-agnostic credential-based auth with rate limiting, token versioning, 2FA, and refresh token family tracking.
Constructor
new AuthService(
users: UserProviderInterface,
hasher: PasswordHasher,
jwt: JwtService,
tokenStorage: ?TokenStorageInterface = null,
rateLimiter: ?RateLimiterInterface = null,
twoFactor: ?TwoFactorProviderInterface = null,
events: ?EventDispatcherInterface = null,
);
Methods
register(string $email, string $password, array $attributes = [], ?string $ip = null): AuthenticatableInterface
Register a new user. Hashes password, dispatches UserRegistered event.
Throws: UserAlreadyExistsException, RateLimitExceededException
login(string $email, string $password, ?string $ip = null, ?string $userAgent = null): AuthResult
Authenticate with email/password. Returns tokens or 2FA challenge.
Throws: InvalidCredentialsException, AccountLockedException, RateLimitExceededException
$result = $auth->login('user@example.com', 'password');
if ($result->requires2FA) {
// Send $result->challengeToken to client for 2FA verification
} else {
$tokens = $result->tokens; // TokenPair
}
verify2FA(string $challengeToken, string $code, ?string $ip, ?string $ua): AuthResult
Complete login after 2FA verification (TOTP or recovery code).
Throws: TwoFactorInvalidException, TokenInvalidException
refresh(string $refreshToken, ?string $ip = null): TokenPair
Refresh an access token. Implements token family rotation for reuse attack detection.
Throws: TokenExpiredException, TokenRevokedException, TokenInvalidException
validateAccessToken(string $token): array<string, mixed>
Validate an access token, check blacklist and version.
Throws: TokenExpiredException, TokenRevokedException, TokenInvalidException
getUserFromToken(string $token): ?AuthenticatableInterface
Extract user from access token. Returns null on any failure.
logout(string $accessToken, bool $allDevices = false, ?string $ip = null): void
Revoke tokens. If $allDevices = true, increments token version to invalidate all sessions.
changePassword(AuthenticatableInterface $user, string $current, string $new, ?string $ip = null): void
Change password. Automatically invalidates all tokens.
Throws: InvalidCredentialsException
issueTokenPair(AuthenticatableInterface $user, ?string $familyId = null): TokenPair
Issue a new access + refresh token pair for a user.
JwtService
MonkeysLegion\Auth\Service\JwtService
JWT token issuance and verification using HS256/RS256.
Properties (PHP 8.4 Hooks)
| Property | Type | Description |
|---|---|---|
$accessTtl | int | Access token TTL in seconds |
$refreshTtl | int | Refresh token TTL in seconds |
Constructor
| Param | Type | Default | Description |
|---|---|---|---|
$secret | string | — | Signing key |
$accessTtl | int | 1800 | Access token TTL (30 min) |
$refreshTtl | int | 604800 | Refresh token TTL (7 days) |
$leeway | int | 0 | Clock skew tolerance |
$nbfSkew | int | 0 | NBF backward offset |
$issuer | ?string | null | JWT iss claim |
$audience | ?string | null | JWT aud claim |
$algorithm | string | 'HS256' | Signing algorithm |
Methods
| Method | Signature | Returns | Description |
|---|---|---|---|
issueAccessToken() | (array $claims) | string | Issue access token |
issueRefreshToken() | (array $claims, ?string $familyId) | string | Issue refresh token with family tracking |
issue() | (array $claims, int $ttl) | string | Issue custom-TTL token |
decode() | (string $token) | array | Decode & verify token |
decodeWithLeeway() | (string $token, int $leeway) | array | Decode with extra leeway |
getExpiration() | (string $token) | ?int | Get exp claim (no sig verify) |
isExpired() | (string $token) | bool | Check if expired (no sig verify) |
getTokenId() | (string $token) | ?string | Extract jti claim |
generateTokenId() | () | string | CSPRNG token ID |
Auth Attributes
| Attribute | Target | Description |
|---|---|---|
#[Authenticated] | Method/Class | Require authenticated user |
#[Authorize(policy, method)] | Method | Check authorization policy |
#[RequiresRole('admin')] | Method/Class | Require specific role |
#[RequiresPermission('posts.edit')] | Method/Class | Require specific permission |
#[RateLimit(max: 60, per: 60)] | Method | Per-route rate limiting |
#[Guard('jwt')] | Method/Class | Specify auth guard |
#[Passkey] | Method | WebAuthn passkey auth |
Auth DTOs
TokenPair
final readonly class TokenPair {
public string $accessToken;
public string $refreshToken;
public int $accessExpiresAt;
public int $refreshExpiresAt;
public ?string $familyId;
}
AuthResult
final class AuthResult {
public readonly bool $success;
public readonly bool $requires2FA;
public readonly ?AuthenticatableInterface $user;
public readonly ?TokenPair $tokens;
public readonly ?string $challengeToken;
public readonly ?string $guardUsed;
public static function success(user, tokens, guard): self;
public static function requires2FA(challengeToken): self;
}
Auth Exceptions
| Exception | HTTP | Description |
|---|---|---|
InvalidCredentialsException | 401 | Wrong email/password |
TokenExpiredException | 401 | JWT has expired |
TokenInvalidException | 401 | JWT signature/format invalid |
TokenRevokedException | 401 | Token blacklisted or version mismatch |
UnauthorizedException | 401 | Generic unauthorized |
ForbiddenException | 403 | Insufficient permissions |
AccountLockedException | 423 | Too many failed attempts |
RateLimitExceededException | 429 | Rate limit hit |
UserAlreadyExistsException | 409 | Duplicate registration |
TwoFactorRequiredException | 403 | 2FA challenge needed |
TwoFactorInvalidException | 401 | Invalid 2FA code |
InvalidApiKeyException | 401 | Bad API key |
Auth Events
All events are dispatched via PSR-14.
| Event | Properties | Description |
|---|---|---|
LoginSucceeded | $user, $ipAddress, $userAgent | Successful login |
LoginFailed | $email, $reason, $ipAddress, $userAgent | Failed login attempt |
Logout | $userId, $allDevices, $ipAddress | User logged out |
UserRegistered | $user, $ipAddress | New registration |
PasswordChanged | $userId, $ipAddress | Password updated |
TokenRefreshed | $userId, $ipAddress | Token pair refreshed |
PasskeyAuthenticated | $user, $credentialId | WebAuthn login |
SessionManager
MonkeysLegion\Session\SessionManager implements SessionInterface
Session management with file, database, and Redis drivers.
Properties (PHP 8.4 Hooks)
| Property | Type | Description |
|---|---|---|
$id | string | Session ID (get/set) |
$isStarted | bool | Whether session is active |
$attributes | AttributeBag | Key-value attribute bag |
$flashes | FlashBag | Flash message bag |
$metadata | MetadataBag | Session metadata (IP, UA, timestamps) |
Lifecycle Methods
| Method | Signature | Returns | Description |
|---|---|---|---|
start() | () | bool | Start session (locks, reads, initializes) |
save() | () | bool | Serialize and persist session data |
regenerate() | (bool $destroy = false) | bool | Regenerate session ID |
invalidate() | () | bool | Clear all data and regenerate |
destroy() | () | bool | Alias for invalidate() |
Data Methods
| Method | Signature | Returns | Description |
|---|---|---|---|
get() | (string $key, mixed $default = null) | mixed | Get attribute |
set() | (string $key, mixed $value) | void | Set attribute |
has() | (string $key) | bool | Check attribute exists |
remove() | (string $key) | void | Remove attribute |
pull() | (string $key, mixed $default = null) | mixed | Get and remove |
all() | () | array | All attributes |
Flash Data
| Method | Signature | Description |
|---|---|---|
flash() | (string $key, mixed $value): void | Flash data (available next request) |
reflash() | (): void | Keep all flash data for another request |
keep() | (string ...$keys): void | Keep specific flash keys |
now() | (string $key, mixed $value): void | Flash for current request only |
CSRF
| Method | Signature | Returns | Description |
|---|---|---|---|
token() | () | string | Get CSRF token |
regenerateToken() | () | void | Generate new CSRF token |
Session Drivers
| Driver | Config Key | Description |
|---|---|---|
FileDriver | SESSION_DRIVER=file | File-based (default) |
DatabaseDriver | SESSION_DRIVER=database | Database table |
RedisDriver | SESSION_DRIVER=redis | Redis store |
Validation Attributes
All in MonkeysLegion\Validation\Attributes\. Used on DTO constructor parameters.
| Attribute | Parameters | Description |
|---|---|---|
#[NotBlank] | — | Must not be empty |
#[Length(min, max)] | int $min, int $max | String length range |
#[Email] | — | Valid email format |
#[Range(min, max)] | float $min, float $max | Numeric range |
#[Regex(pattern)] | string $pattern | PCRE pattern match |
#[Url] | — | Valid URL format |
#[Uuid] | — | Valid UUID format |
#[Choice(choices)] | array $choices | Value must be in list |
#[Unique(table, column)] | string $table, string $column | Database uniqueness |
#[Confirmed] | — | Must match {field}_confirmation |
#[Date] | — | Valid date string |
#[Numeric] | — | Numeric value |
#[Integer] | — | Integer value |
#[Boolean] | — | Boolean value |
#[ArrayOf(type)] | string $type | Array of specific type |
#[MinCount(n)] | int $n | Minimum array elements |
#[MaxCount(n)] | int $n | Maximum array elements |
Usage in DTOs
final readonly class CreateUserRequest
{
public function __construct(
#[NotBlank]
#[Email]
public string $email,
#[NotBlank]
#[Length(min: 8, max: 128)]
public string $password,
#[Length(max: 255)]
public string $name = '',
) {}
}
DTOs are auto-validated when type-hinted in controller methods. Returns 422 Unprocessable Content on failure.
Gate (Authorization)
MonkeysLegion\Auth\Policy\Gate
Policy-based authorization.
Usage
// In controller
#[Authorize(PostPolicy::class, 'update')]
public function update(ServerRequestInterface $request, string $id): Response { ... }
// Policy class
final class PostPolicy
{
public function update(AuthenticatableInterface $user, Post $post): bool
{
return $user->getAuthIdentifier() === $post->author_id;
}
}
RBAC
MonkeysLegion\Auth\RBAC\RbacService
Role-Based Access Control.
Methods
| Method | Signature | Returns | Description |
|---|---|---|---|
hasRole() | (user, string $role) | bool | Check user has role |
hasPermission() | (user, string $permission) | bool | Check user has permission |
assignRole() | (user, string $role) | void | Assign role to user |
revokeRole() | (user, string $role) | void | Remove role from user |
Configuration (config/auth.mlc)
rbac {
roles {
admin { permissions = ["*"] }
editor { permissions = ["posts.*", "media.*"], inherits = ["viewer"] }
viewer { permissions = ["posts.view", "media.view"] }
}
}