Configuration

The package is configured with environment variables, that the services of the package read (%env(...)%), and with the services and the routes that the application imports. Everything has a default, and the ones that are not safe to guess (the client of Keycloak, the database) have none.

What the application imports

The services and the routes are the same for every provider: the application imports them and chooses the provider with AUTH_PROVIDER.

# config/services.yaml
imports:
    - { resource: '../vendor/derafu/auth/resources/config/auth-services.yaml' }
# config/routes.yaml
imports:
    - { resource: '../vendor/derafu/auth/resources/config/auth-routes.yaml' }

The services bring everything that the application needs: the manager of the authentication with its two channels (web and API), the authorization with the access rules, the translations of the package, and the three providers, of which only the chosen one is made. See How it works. The session and the flash messages are not part of the provider: the middlewares and the variables SESSION_* come from derafu/session, which a site that uses derafu/foundation already has. The routes are /auth/login, /auth/logout and /auth/profile (see Profile), /auth/callback and the routes of the API tokens: the callback answers 404 with a provider whose login is a form, and the tokens with one that has none. The logout accepts only POST (see Security).

The middlewares

For Derafu HTTP’s shared pipeline, import auth-http-services.yaml instead of auth-services.yaml:

# config/services.yaml
imports:
    - { resource: '../vendor/derafu/auth/resources/config/auth-http-services.yaml' }

This imports the base Auth services and contributes authentication with priority 350 and authorization with priority 300 through derafu_http.middleware. Higher priorities run first. Do not repeat those registrations or rebuild the request handler in the site.

The application supplies the HTTP pipeline and, for the web channel, the session and flash middleware. Foundation already supplies these, together with CSRF: session 500, flash 450, CSRF 400, authentication 350, authorization 300, dispatch 200.

The router goes first because the login and the logout have routes. Session and flash precede authentication, which uses them; authentication precedes authorization, which reads the user it adds to the request.

For another PSR-15 pipeline, keep importing auth-services.yaml and attach its authentication and authorization middlewares in that same order. The base YAML registers them without activating a pipeline. The optional HTTP YAML does not import a kernel or session services, and does not require derafu/http as a package dependency.

The cache pool

The providers use a PSR-6 pool (Psr\Cache\CacheItemPoolInterface): the database and htpasswd providers for the count of the failed logins (they need it: without one the first count fails with a message that says so), and the Keycloak provider for the keys of the realm (it is optional, without it the keys are read in every request that verifies a token). derafu/foundation registers one in the cache directory of the kernel. An application that has its own declares the same id.

The provider

Variable Type Default Description
AUTH_PROVIDER string Required. The provider of the application: keycloak, database or htpasswd, or the name of one that the application adds (see How it works). Set only the variables of that provider (AUTH_KEYCLOAK_*, AUTH_DATABASE_* or AUTH_HTPASSWD_*).

The variable is read when a piece of the provider is needed (the login, the API, the profile), not when the container is built: with it empty or wrong the pages that do not use the provider keep working, and what needs it fails with a message that says what to set and which providers there are.

Variables of the access rules

They are the same in every provider, and they are about who may enter which path, not about how a request is identified.

Variable Type Default Description
AUTH_ENABLED bool true Whether the protected paths are protected. With false no path asks for a user, but the login and the logout still work, and a user that logged in is still the user of the request. For development and for tests.
AUTH_PROTECTED_PATHS json [] The protected paths: a list, or a map from the path to its roles. See Route Protection.

Variables of the web channel

Variable Type Default Description
AUTH_WEB_LOGOUT_PATH string /auth/logout The logout path. It must match the logout route.
AUTH_WEB_LOGIN_REDIRECT_PATH string / Where the user goes after the login when no page was requested before.
AUTH_WEB_LOGOUT_REDIRECT_PATH string Keycloak: /, database and htpasswd: /auth/login Where the user goes after the logout.
AUTH_WEB_UNAUTHORIZED_REDIRECT_PATH string Keycloak: /, database and htpasswd: /auth/login Where a user that is not logged in goes when there is no provider to send it to (the database and htpasswd providers send it to their login page).
AUTH_WEB_REFRESH_INTERVAL_SECONDS int Keycloak: the expiration of the access token, database and htpasswd: 300 Seconds between two times that the session asks the provider again about its user (its roles, whether it still exists). 0 or empty is the default. See The user of the session is a copy.

The login path is not a variable: it is the callback (AUTH_KEYCLOAK_WEB_CALLBACK_PATH) in Keycloak and /auth/login in the database and htpasswd providers.

Variables of the API channel

Variable Type Default Description
AUTH_API_PATHS json ["/api"] The paths where a client is authenticated with the header Authorization (no session, 401 in JSON). A list of paths. See API.
AUTH_API_REALM string API The label of the protection space, in the header WWW-Authenticate of the 401 of the API. Not the realm of Keycloak. See API.

Variables of login attempt limits

Database and Htpasswd use one shared Derafu\Auth\Contract\LoginThrottleInterface instance and the same policy for web login and HTTP Basic. The application must provide a PSR-6 cache pool when this throttle is used.

Variable Type Default Description
AUTH_LOGIN_MAX_ATTEMPTS int 5 Failed attempts for an identity and client network before login is limited. The aggregate network limit is five times this value.
AUTH_LOGIN_LOCK_SECONDS int 900 Duration in seconds of the failed-attempt window and its limit.

Variables of the session

The session is the one of Mezzio, with the session of PHP, and its variables (SESSION_NAME, SESSION_COOKIE_SECURE, SESSION_COOKIE_SAMESITE and the others) are the ones of derafu/session.

Variables of Keycloak

Variable Type Required Default Description
AUTH_KEYCLOAK_URL string Yes - URL of the Keycloak server.
AUTH_KEYCLOAK_CLIENT_ID string Yes - ID of the client.
AUTH_KEYCLOAK_CLIENT_SECRET string Yes - Secret of the client.
AUTH_KEYCLOAK_WEB_REDIRECT_URI string Yes - The callback of the application, as Keycloak must send the user back (a valid redirect URI of the client).
AUTH_KEYCLOAK_REALM string No master The realm.
AUTH_KEYCLOAK_WEB_SCOPES json No ["openid", "profile", "email"] Scopes. openid is needed: it gives the ID token.
AUTH_KEYCLOAK_WEB_CALLBACK_PATH string No /auth/callback Path of the callback: it is the login path of the provider.
AUTH_KEYCLOAK_ISSUER string No the URL of the realm The iss that the tokens must have. Set it if the application reaches Keycloak by another URL than the one Keycloak publishes (inside a network of containers, for example).
AUTH_KEYCLOAK_WEB_END_SESSION bool No true Whether the logout also ends the session of the user in Keycloak.
AUTH_KEYCLOAK_WEB_POST_LOGOUT_REDIRECT_URI string No the page that follows the logout, in the site of the redirect URI Where Keycloak sends the user after the logout. It must be a valid post logout redirect URI of the client.
AUTH_KEYCLOAK_API_AUDIENCE string No the client id The audience (aud) that an access token must have to call the API. See API.
AUTH_KEYCLOAK_API_CLIENT_ID string No the client of the application The client of the API, if it has one of its own: the one that asks Keycloak about the tokens. It goes with its secret. See API.
AUTH_KEYCLOAK_API_CLIENT_SECRET string No the secret of the client of the application The secret of the client of the API.
AUTH_KEYCLOAK_API_INTROSPECTION bool No true Whether Keycloak is asked, in every request of the API, if the token is still active. With true the audience must be the client that asks. See API.
AUTH_KEYCLOAK_HTTP_TIMEOUT_SECONDS int No 30 Seconds of timeout of the requests to Keycloak.
AUTH_KEYCLOAK_HTTP_CONNECT_TIMEOUT_SECONDS int No 30 Seconds of timeout of the connection.
AUTH_KEYCLOAK_HTTP_VERIFY bool No true Verification of the certificate of Keycloak. Set it to false only in development, with a certificate that is not valid.
AUTH_KEYCLOAK_URL=https://auth.example.com
AUTH_KEYCLOAK_REALM=my-realm
AUTH_KEYCLOAK_CLIENT_ID=my-app
AUTH_KEYCLOAK_CLIENT_SECRET=the-secret
AUTH_KEYCLOAK_WEB_REDIRECT_URI=https://app.example.com/auth/callback

How the login works, and how to configure the client in Keycloak, is in Keycloak.

Variables of the database provider

Variable Type Default Description
AUTH_DATABASE_URL string DATABASE_URL The DSN of PDO. If it is empty, DATABASE_URL is used (the connection that the application already has). %kernel.project_dir% is replaced by the directory of the project (useful with SQLite).
AUTH_DATABASE_USER_TABLE string user Table of the users.
AUTH_DATABASE_USER_FIELD_IDENTITY string email Column of the identity.
AUTH_DATABASE_USER_FIELD_PASSWORD string password Column of the hash of the password.
AUTH_DATABASE_USER_FIELD_ACTIVE string active Column that says whether the user is active. It is used by the default query of AUTH_DATABASE_USER_SQL_IS_ACTIVE.
AUTH_DATABASE_USER_SQL_GET_ROLES string the roles of <table>_role and role Query of the roles, with the parameter :identity: the first column of each row is a role.
AUTH_DATABASE_USER_SQL_GET_DETAILS string SELECT * FROM <table> WHERE <identity> = :identity Query of the details of the user, with the parameter :identity: the first row.
AUTH_DATABASE_USER_SQL_IS_ACTIVE string SELECT <active> FROM <table> WHERE <identity> = :identity Query that says whether the user is active, with the parameter :identity: the first value of the first row. false turns the check off. See Database.

More in Database.

Variables of the htpasswd provider

Variable Type Default Description
AUTH_HTPASSWD_PATH string - (required) Path of the .htpasswd file. %kernel.project_dir% is replaced by the directory of the project.
AUTH_HTPASSWD_GROUP_PATH string empty Path of the .htgroup file with the roles of the users (role: user1 user2). Empty: no roles. See Htpasswd.

More in Htpasswd.

Changing the services

Injectable backend services are registered under their interfaces in Derafu\Auth\Contract. An application replaces an implementation by declaring the same service ID after importing the package YAML. Tagged contributions keep their individual IDs; controllers and pipeline middleware have their own entry-point IDs. For example, the anonymous user, that every piece that needs one takes if the application declares it (without it they make AnonymousUser):

Derafu\Auth\Contract\UserInterface:
    class: App\Auth\GuestUser

Or the flow that a provider gives to the web channel:

Derafu\Auth\Contract\WebFlowInterface:
    class: App\Auth\MyKeycloakWebFlow
    lazy: true

The services are lazy (lazy: true), and the ones that an application declares should be too. The translator reaches the channels by autowiring when the application has one (Symfony\Contracts\Translation\TranslatorInterface): see Translations.

Backend service contracts

The built-in configuration implementations use DatabaseConfigurationInterface, HtpasswdConfigurationInterface and KeycloakConfigurationInterface. Replace a configuration service under its interface to obtain the same settings from another source. Environment-variable defaults and validation remain the built-in implementations’ responsibility.

Database and Htpasswd use PasswordUserRepositoryInterface, which extends UserRepositoryInterface with find(): authentication checks a password, while lookup reloads current user details and roles for an existing session. Their service IDs are distinct because they access different storage:

services:
    Derafu\Auth\Contract\PasswordUserRepositoryInterface $database:
        class: App\Auth\DatabaseUsers
        lazy: true
        autowire: true

This replaces the database repository in both its web flow and HTTP Basic scheme. The Htpasswd repository uses Derafu\Auth\Contract\PasswordUserRepositoryInterface $htpasswd.

Keycloak exposes KeycloakUserRepositoryInterface for its OAuth/OIDC operations, KeycloakTokenVerifierInterface, KeycloakAccountClientInterface and KeycloakSessionManagerInterface. The latter extends SessionManagerInterface with tokens and login state. Its account receives token management through the existing ApiTokenManagerInterface. A replacement implements its contract; it does not have to extend the package’s concrete implementation.

Provider-specific flows, API schemes and accounts also use interface IDs:

Contract Backend service IDs (suffix after the interface FQCN)
WebFlowInterface $database, $htpasswd, $keycloak
ApiSchemeInterface $database, $htpasswd, $keycloak
AccountInterface $password (shared by Database and Htpasswd), $keycloak
SessionManagerInterface $password (shared by Database and Htpasswd)

The unqualified interfaces still resolve the services of the provider selected with AUTH_PROVIDER. Backend services reference their explicit backend dependency to avoid resolving themselves through that selection. No concrete-class aliases are registered for these services.

Authentication form definitions

The shared FormManagerInterface service is registered directly with FormManager, independently of provider selection. It accepts a registered definition’s FQCN or an object implementing Derafu\Auth\Contract\FormInterface. Register additional definition services with their class name as the service ID and the derafu_auth.form tag:

services:
    App\Auth\LoginFormDefinition:
        autowire: true
        tags: ['derafu_auth.form']

The container builds each definition with its own dependencies when requested. The manager resolves definitions through a restricted lazy locator and caches each resolved definition by class name. It receives no generic provider configuration and does not instantiate definition classes itself. An unregistered class or an invalid tagged service raises a translatable configuration exception.

An object definition supplied directly needs no registration; this supports runtime context such as Keycloak’s user-specific password help. Form data is applied to each new form instance after obtaining the definition.

Without a DI container, construct FormManager with the form factory, the form data processor and a PSR-11 locator whose keys are the definition class names.

The configuration without a container

The configuration classes take an array, with the same keys that the YAML gives them, so the pieces work without a container too (this is how the tests of the package use them). The manager of the authentication is assembled with the channels, the one of each provider included:

use Derafu\Auth\Authentication\AuthenticationManager;
use Derafu\Auth\Authentication\Channel\Api\ApiChannel;
use Derafu\Auth\Authentication\Channel\Api\ApiConfiguration;
use Derafu\Auth\Authentication\Channel\Web\WebChannel;
use Derafu\Auth\Authentication\Channel\Web\WebConfiguration;
use Derafu\Auth\Authorization\AccessRules;
use Derafu\Auth\Provider\Keycloak\Api\KeycloakBearerScheme;
use Derafu\Auth\Provider\Keycloak\KeycloakConfiguration;
use Derafu\Auth\Provider\Keycloak\KeycloakUserRepository;
use Derafu\Auth\Provider\Keycloak\Web\KeycloakSessionManager;
use Derafu\Auth\Provider\Keycloak\Web\KeycloakWebFlow;

$keycloak = new KeycloakConfiguration([
    'keycloak_url' => 'https://auth.example.com',
    'realm' => 'my-realm',
    'client_id' => 'my-app',
    'client_secret' => 'the-secret',
    'redirect_uri' => 'https://app.example.com/auth/callback',
]);
$users = new KeycloakUserRepository($keycloak);
$sessions = new KeycloakSessionManager();
$web = new WebConfiguration(['logout_redirect_path' => '/bye']);
$rules = new AccessRules(['protected_paths' => ['/dashboard']]);

$authentication = new AuthenticationManager(
    [
        // Asked first.
        new ApiChannel(new ApiConfiguration(['paths' => ['/api']]), new KeycloakBearerScheme($users, $keycloak)),
        // The last one: the channel of every request that is not of another.
        new WebChannel(new KeycloakWebFlow($users, $keycloak, $web, $sessions), $web, $sessions),
    ],
    $rules
);
On this page

Last updated on 30/10/2026 by Anonymous