monkeyscloud/monkeyslegion-auth

Authentication and authorization system for the MonkeysLegion framework.

dev-main / 1.0.x-dev 2025-05-25 16:33 UTC

This package is auto-updated.

Last update: 2025-05-25 16:33:18 UTC


README

A drop-in authentication and authorization scaffolding for MonkeysLegion.
Provides:

  • Password hashing (bcrypt via PasswordHasher)
  • JWT issuance & verification (JwtService)
  • User registration & login (AuthService)
  • PSR-15 middleware to authenticate routes (JwtAuthMiddleware)
  • Attribute-based authorization (#[Can]) and policies
  • Helper functions (auth_check(), auth_user_id()) for templates

📦 Installation

composer require monkeyscloud/monkeyslegion-auth:^1.0@dev

Ensure your composer.json autoloads:

"autoload": {
  "psr-4": {
    "MonkeysLegion\\Auth\\": "src/"
  }
}

Then:

composer dump-autoload

🛠️ Configuration

Register services in your DI container (config/app.php):

use MonkeysLegion\Auth\PasswordHasher;
use MonkeysLegion\Auth\JwtService;
use MonkeysLegion\Auth\AuthService;
use MonkeysLegion\Auth\Middleware\JwtAuthMiddleware;
use MonkeysLegion\Auth\Middleware\AuthorizationMiddleware;
use MonkeysLegion\AuthService\AuthorizationService;

// Password hashing & JWT
PasswordHasher::class => fn() => new PasswordHasher(),

JwtService::class => fn($c) => new JwtService(
    $c->get(MonkeysLegion\Mlc\Config::class)->get('auth.jwt_secret'),
    (int)$c->get(MonkeysLegion\Mlc\Config::class)->get('auth.jwt_ttl', 3600)
),

AuthService::class => fn($c) => new AuthService(
    $c->get(App\Repository\UserRepository::class),
    $c->get(PasswordHasher::class),
    $c->get(JwtService::class)
),

// Authentication middleware
JwtAuthMiddleware::class => fn($c) => new JwtAuthMiddleware(
    $c->get(JwtService::class),
    $c->get(Psr\Http\Message\ResponseFactoryInterface::class)
),

// Authorization service & middleware
AuthorizationService::class => fn() => tap(new AuthorizationService(), function($svc) {
    $svc->registerPolicy(App\Entity\Post::class, App\Policy\PostPolicy::class);
    // register more policies here...
}),

AuthorizationMiddleware::class => fn($c) => new AuthorizationMiddleware(
    $c->get(AuthorizationService::class)
),

Add both middleware to your HTTP pipeline:

[
  /* ... other middleware ... */
  $c->get(JwtAuthMiddleware::class),
  $c->get(AuthorizationMiddleware::class),
],

🧩 Usage

Registration

// in a controller action
$email    = $request->getParsedBody()['email'];
$password = $request->getParsedBody()['password'];

$user = $container
    ->get(AuthService::class)
    ->register($email, $password);

// $user is your App\Entity\User instance

Login & Token

$token = $container
    ->get(AuthService::class)
    ->login($email, $password);

// return JSON with token
return new JsonResponse(['token' => $token]);

Protecting Endpoints

Send the JWT in an Authorization: Bearer {token} header. The JwtAuthMiddleware will decode & verify and inject userId into request attributes.

In your controllers you can then retrieve:

$userId = $request->getAttribute('userId');

🛡️ Attribute-Based Authorization

Use the #[Can] attribute on your controller methods (or classes) to enforce abilities via your registered policies:

use MonkeysLegion\Auth\Attributes\Can;

final class PostController
{
    #[Can('edit', App\Entity\Post::class)]
    public function edit(ServerRequestInterface $req): ResponseInterface
    {
        $post = /* load post by id from repository */;
        // if unauthorized, middleware will have thrown
        // now handle the edit…
    }
}
  • Policies live in App\Policy*Policy.php and implement PolicyInterface.
  • The before hook can short-circuit (e.g. admin users).
  • Then your ability method (e.g. edit($user, $post)) decides.

🖋 Template Helpers

Add to your MLView helpers (e.g. src/Template/helpers.php):

if (! function_exists('auth_user_id')) {
    function auth_user_id(): ?int {
        $req = ML_CONTAINER->get(Psr\Http\Message\ServerRequestInterface::class);
        return $req->getAttribute('userId');
    }
}
if (! function_exists('auth_check')) {
    function auth_check(): bool {
        return auth_user_id() !== null;
    }
}

Then in your views:

@if(auth_check())
  <p>Welcome back, user #{{ auth_user_id() }}!</p>
@else
  <p>Please <a href="/login">log in</a>.</p>
@endif

⚙️ Extending

  • Remember-me: add long-lived cookies & DB tokens.
  • Password reset: generate time-bound tokens, email links, reset form.
  • Additional abilities: register more policies and use #[Can] on routes or methods.

📝 License

MIT © 2025 MonkeysCloud