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
The application puts four middlewares between the router and the dispatcher, in this order (the others of the list are the ones that every site has):
Psr\Http\Server\RequestHandlerInterface:
class: Derafu\Http\Service\RequestHandler
public: true
arguments:
$middlewares:
- '@Derafu\Http\Middleware\StaticFilesMiddleware'
- '@Derafu\Http\Middleware\RequestFactoryMiddleware'
- '@Derafu\Http\Middleware\RouterMiddleware'
- '@Mezzio\Session\SessionMiddleware'
- '@Mezzio\Flash\FlashMessageMiddleware'
- '@Derafu\Auth\Authentication\AuthenticationMiddleware'
- '@Derafu\Auth\Authorization\AuthorizationMiddleware'
- '@Derafu\Http\Middleware\DispatcherMiddleware'
- '@Derafu\Http\Middleware\ResponseNormalizerMiddleware'
With the database provider the middleware of the CSRF tokens of derafu/csrf goes after the flash messages (see Database).
The router goes first because the login and the logout have routes: a path that has no route never reaches the authentication. The session and the flash messages go before the authentication, which uses them, and the authentication before the authorization, which reads the user that it puts in the request.
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 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. |
AUTH_DATABASE_LOGIN_MAX_ATTEMPTS |
int | 5 |
Failed attempts of an identity from a network before the login is limited. |
AUTH_DATABASE_LOGIN_LOCK_SECONDS |
int | 900 |
Seconds of the window of the failed attempts, and so of the limit. |
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. |
AUTH_HTPASSWD_LOGIN_MAX_ATTEMPTS |
int | 5 |
Failed attempts of an identity from a network before the login is limited. |
AUTH_HTPASSWD_LOGIN_LOCK_SECONDS |
int | 900 |
Seconds of the window of the failed attempts, and so of the limit. |
More in Htpasswd.
Changing the services
The services of the package are declared by their interface or class, so an application replaces one by declaring the same id after the import. 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.
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
);