Osumi Framework
es en eu v9.8.0 GitHub

Authentication (Auth) — Recipes & Best Practices

Authentication in Osumi Framework is typically implemented using:

This document provides practical recipes for implementing a complete authentication workflow.


1. Protecting Routes Using Filters

The most common method of securing endpoints is adding a filter to the route definition.

According to your routing system, filters can be specified like this:

ORoute::post('/profile', ProfileComponent::class, [LoginFilter::class]);

When the route is accessed:

  1. The router identifies the endpoint
  2. Before running the component, the filter chain is executed
  3. If any filter returns "status" !== "ok", the request never reaches the component, returning 403 Forbidden or redirecting if "return" is set

This ensures only authenticated users reach protected logic.


2. Creating the Login Filter

A filter looks like this:

class LoginFilter {
  public static function handle(array $params, array $headers): array {
    global $core;
    $ret = ['status' => 'error', 'id' => null];

    $tk = new OToken($core->config->getExtra('secret'));

    if ($tk->checkToken($headers['Authorization'])) {
        $ret['status'] = 'ok';
        $ret['id'] = intval($tk->getParam('id'));
    }

    return $ret;
  }
}

This filter:

Token‑derived values (like id) can later be consumed by components or DTOs.


3. Creating the Login Endpoint (Issuing Tokens):

  1. Receives credentials via a DTO
  2. Validates them using a service
  3. Generates a token
  4. Returns the token to the client
  5. Client stores token and uses it in the Authorization header

Example Structure

DTO for login:

class LoginDTO extends ODTO {
  #[ODTOField(required: true)]
  public ?string $email = null;

  #[ODTOField(required: true)]
  public ?string $password = null;
}

AuthService handling the logic:

class AuthService extends OService {
  public function login(string $email, string $password): ?array {
    $user = User::findOne(['email' => $email]);
    if (!$user || !password_verify($password, $user->password)) {
      return null;
    }
    $token = new OToken($this->getConfig()->getExtra('secret'));
    $token->addParam('id', $user->id);
    return ['token' => $token->getToken()];
  }
}

LoginComponent:

class LoginComponent extends OComponent {
  private ?AuthService $auth = null;
  public ?string $token = null;
  public string $status = 'error';

  public function __construct() {
    parent::__construct();
    $this->auth = inject(AuthService::class);
  }

  public function run(LoginDTO $dto): void {
    if (!$dto->isValid()) {
      return;
    }

    $data = $this->auth->login($dto->email, $dto->password);

    if ($data) {
      $this->status = 'ok';
      $this->token = $data['token'];
    }
  }
}

The client now includes the token in all subsequent requests:

Authorization: <token>

4. Using Filter Output in Components

Once filters pass, the request object contains filter results:

$filter = $req->getFilter('Login');

Typically you’d do:

$userId = $filter['id']; // authenticated user

You can then pass the ID to services, load models, and perform business logic securely.


5. Using Filter Data Inside DTOs

DTOs can automatically receive values from filters:

#[ODTOField(filter: 'Login', filterProperty: 'id')]
public ?int $idUser = null;

This means:

This greatly simplifies authentication‑dependent endpoints.


6. Recipe: Creating a Protected Endpoint

Example: “Get My Cinemas”

Route

ORoute::get('/my-cinemas', GetCinemasComponent::class, [LoginFilter::class]);

Component

class GetCinemasComponent extends OComponent {
  private ?CinemaService $cs = null;
  public string $status = 'ok';
  public ?CinemaListComponent $list = null;

  public function __construct() {
    parent::__construct();
    $this->cs = inject(CinemaService::class);
    $this->list = new CinemaListComponent();
  }

  public function run(ORequest $req): void {
    $filter = $req->getFilter('Login');

    if (!$filter || !array_key_exists('id', $filter)) {
      $this->status = 'error';
      return;
    }

    $this->list->list = $this->cs->getCinemas($filter['id']);
  }
}

7. Recipe: Enforcing Permissions

You can extend your filter to include role/permission information from the token:

$ret['role'] = $tk->getParam('role');

Then in components:

$filter = $req->getFilter('Login');
if ($filter['role'] !== 'admin') {
  $this->status = 'forbidden';
  return;
}

8. Recipe: Logging Out

Since your auth system is token‑based and stateless:


9. Best Practices


10. Summary

A full authentication workflow in Osumi Framework usually includes:

  1. Login endpoint issuing tokens
  2. LoginFilter validating tokens for protected routes
  3. DTOs capturing and validating input
  4. Services performing authentication logic
  5. Routing applying filters before components
  6. Secure propagation of authenticated user information via filters and DTOs

This architecture ensures: