Skip to main content
The security system in Apivalk is designed to be extensible, type-safe, and fully compatible with OpenAPI 3.0 standards. It handles everything from identifying the requester to enforcing granular permissions via scopes.

Core Concepts

The security layer consists of four main pillars:
  1. Identities: Objects representing the current requester (e.g., a logged-in user or a guest).
  2. Scopes and Permissions: Granular strings that define what an identity is allowed to do.
  3. Authenticators: Services that turn credentials (like a JWT) into an Identity.
  4. Middleware: The glue that executes authentication and enforces authorization on routes.

The Security Flow

  1. Request Arrival: An HTTP request enters the application.
  2. Authentication: An authentication middleware (like AuthenticationMiddleware) uses an Authenticator to identify the requester and sets an AuthIdentity on the request.
  3. Authorization: The SecurityMiddleware compares the AuthIdentity’s granted scopes and permissions against the RouteAuthorization defined in the Route.
  4. Controller: If authorized, the controller receives the request and can access the identity via $request->getAuthIdentity().

Route Security Levels

Apivalk supports three levels of route security. The level is determined by whether a RouteAuthorization is present and what scopes/permissions it requires.

1. Public Route (No Authentication)

A route without a RouteAuthorization is fully public. Anyone can access it, including anonymous users. The SecurityMiddleware passes the request through without any checks.
use apivalk\apivalk\Router\Route\Route;

$route = Route::get('/about')->description('About page');

2. Authenticated Route (No Specific Scopes)

A route that requires the user to be authenticated but does not require any specific scopes or permissions. This is useful for endpoints like “get my profile” where you just need to know the user is logged in. Pass a RouteAuthorization with only the security scheme name and omit (or pass empty arrays for) scopes and permissions. The SecurityMiddleware will still enforce that the user is authenticated (not a GuestAuthIdentity), returning a 401 Unauthorized for anonymous requests.
use apivalk\apivalk\Router\Route\Route;
use apivalk\apivalk\Security\RouteAuthorization;

$route = Route::get('/me/profile')
    ->description('Get my profile')
    ->routeAuthorization(new RouteAuthorization('BearerAuth'));

3. Scoped Route (Specific Scopes and/or Permissions)

A route that requires the user to be authenticated and possess specific scopes and/or permissions. The SecurityMiddleware checks each required scope and permission against the user’s identity:
  • Missing scope/permission + authenticated user = 403 Forbidden
  • Missing scope/permission + anonymous user = 401 Unauthorized
use apivalk\apivalk\Router\Route\Route;
use apivalk\apivalk\Security\RouteAuthorization;

$route = Route::get('/admin/settings')
    ->description('Admin Settings')
    ->routeAuthorization(
        new RouteAuthorization('BearerAuth', ['admin:settings'], ['admin:settings:read'])
    );
In this example, the route requires a security scheme named BearerAuth with the admin:settings scope and admin:settings:read permission.

Connection to OpenAPI

The first argument of RouteAuthorization is the security scheme name. This name must match the name parameter of a SecuritySchemeObject in your ComponentsObject. This is how Apivalk connects a route to its security definition in the generated OpenAPI spec, and how Swagger UI knows to show the “Authorize” button for that endpoint.
ComponentsObject → SecuritySchemeObject('http', 'api', ...)


Route → routeAuthorization(new RouteAuthorization('api', [...], [...]))
If the names match, Apivalk uses your full configuration (descriptions, flows, bearer format, etc.) in the generated Swagger file. If they don’t match, Apivalk will generate a basic placeholder scheme automatically.
Keep in mind: Future Auto-Discovery. Apivalk is moving towards full auto-discovery of security schemes. In the future, it will be able to automatically detect and document your security configuration (including names and versions) directly from your middleware and authenticators.
See the OpenAPI Generator documentation for full examples including JWT Bearer, API Key, and OAuth2 setups.

Examples: Getting Started with Middleware

To use the security system, you typically need to register two middlewares in your MiddlewareStack.

1. Authentication Middleware

The Authentication Middleware identifies the user. It uses an authenticator to turn a token into an identity.
use apivalk\apivalk\Security\Authenticator\JwtAuthenticator;
use apivalk\apivalk\Middleware\AuthenticationMiddleware;

$authenticator = new JwtAuthenticator(
    'https://example.com/.well-known/jwks.json',
    $cache, // Instance of CacheInterface or null
    'https://example.com/',
    'my-api-audience'
);

$configuration->addMiddleware(new AuthenticationMiddleware($authenticator));

2. Security Middleware

The Security Middleware enforces the scopes defined on your routes. It MUST run after the authentication middleware.
use apivalk\apivalk\Middleware\SecurityMiddleware;

$configuration->addMiddleware(new SecurityMiddleware());

Full Example: All Three Security Levels

Here is how you would define routes with different security requirements:
use apivalk\apivalk\Router\Route\Route;
use apivalk\apivalk\Security\RouteAuthorization;

// PUBLIC: No security requirements — anyone can access
$publicRoute = Route::get('/about')->description('About us');

// AUTHENTICATED: Requires any valid token, no specific scopes
$authenticatedRoute = Route::get('/me/profile')
    ->description('Get my profile')
    ->routeAuthorization(new RouteAuthorization('BearerAuth'));

// SCOPED: Requires valid token with specific scopes and permissions
$scopedRoute = Route::post('/orders')
    ->description('Create order')
    ->routeAuthorization(
        new RouteAuthorization('BearerAuth', ['order'], ['order:create'])
    );
For more details on custom logic, check the Custom Authentication section.