Skip to content
Snippets Groups Projects
Commit c34b83ab authored by Reiter, Christoph's avatar Reiter, Christoph :snake:
Browse files

Rework and clean up the bundle config

* Document all options and add examples
* Make the config key names less ambiguous
* Make the local validation leeway configurable (default to 120 seconds still)
parent dd901135
No related branches found
No related tags found
No related merge requests found
Pipeline #54011 passed
# ????-??-?? # ????-??-??
* Rework bundle config
* Make the config option naming less ambiguous
* Add an option to specify the local validation time leeway
\ No newline at end of file
# DBP API-Keycloak-Bundle # DBP API-Keycloak-Bundle
[GitLab](https://packagist.org/packages/dbp/api-keycloak-bundle) | [Packagist](https://packagist.org/packages/dbp/api-keycloak-bundle) [GitLab](https://packagist.org/packages/dbp/api-keycloak-bundle) | [Packagist](https://packagist.org/packages/dbp/api-keycloak-bundle)
## Bundle Configuration
```yaml
# Default configuration for "DbpRelayKeycloakBundle"
dbp_relay_keycloak:
# The Keycloak server URL
server_url: ~ # Example: 'https://keycloak.example.com/auth'
# The Keycloak Realm
realm: ~ # Example: myrealm
# The ID for the keycloak client (authorization code flow) used for API docs or similar
frontend_client_id: ~ # Example: client-docs
# If remote validation should be used. If set to false the token signature will
# be only checked locally and not send to the keycloak server
remote_validation: false
# The ID of the client (client credentials flow) used for remote token validation
# (optional)
remote_validation_client_id: ~ # Example: client-token-check
# The client secret for the client referenced by client_id (optional)
remote_validation_client_secret: ~ # Example: mysecret
# If set only tokens which contain this audience are accepted (optional)
required_audience: ~ # Example: my-api
# How much the system time of the API server and the Keycloak server
# can be out of sync (in seconds). Used for local token validation.
local_validation_leeway: 120
```
\ No newline at end of file
...@@ -14,13 +14,43 @@ class Configuration implements ConfigurationInterface ...@@ -14,13 +14,43 @@ class Configuration implements ConfigurationInterface
$treeBuilder = new TreeBuilder('dbp_relay_keycloak'); $treeBuilder = new TreeBuilder('dbp_relay_keycloak');
$treeBuilder->getRootNode() $treeBuilder->getRootNode()
->children() ->children()
->scalarNode('server_url')->end() ->scalarNode('server_url')
->scalarNode('realm')->end() ->info('The Keycloak server URL')
->scalarNode('client_id')->end() ->example('https://keycloak.example.com/auth')
->scalarNode('client_secret')->end() ->end()
->scalarNode('audience')->end() ->scalarNode('realm')
->booleanNode('local_validation')->defaultTrue()->end() ->info('The Keycloak Realm')
->scalarNode('frontend_client_id')->end() ->example('myrealm')
->end()
// API docs
->scalarNode('frontend_client_id')
->info('The ID for the keycloak client (authorization code flow) used for API docs or similar')
->example('client-docs')
->end()
// Remote validation
->booleanNode('remote_validation')
->info("If remote validation should be used. If set to false the token signature will\nbe only checked locally and not send to the keycloak server")
->example(false)
->defaultFalse()
->end()
->scalarNode('remote_validation_client_id')
->info("The ID of the client (client credentials flow) used for remote token validation\n(optional)")
->example('client-token-check')
->end()
->scalarNode('remote_validation_client_secret')
->info('The client secret for the client referenced by client_id (optional)')
->example('mysecret')
->end()
// Settings for token validation
->scalarNode('required_audience')
->info('If set only tokens which contain this audience are accepted (optional)')
->example('my-api')
->end()
->integerNode('local_validation_leeway')
->defaultValue(120)
->min(0)
->info("How much the system time of the API server and the Keycloak server\ncan be out of sync (in seconds). Used for local token validation.")
->end()
->end(); ->end();
return $treeBuilder; return $treeBuilder;
......
...@@ -40,10 +40,11 @@ class KeycloakBearerUserProvider implements KeycloakBearerUserProviderInterface, ...@@ -40,10 +40,11 @@ class KeycloakBearerUserProvider implements KeycloakBearerUserProviderInterface,
$config = $this->config; $config = $this->config;
$keycloak = new Keycloak( $keycloak = new Keycloak(
$config['server_url'], $config['realm'], $config['server_url'], $config['realm'],
$config['client_id'], $config['client_secret']); $config['remote_validation_client_id'], $config['remote_validation_client_secret']);
if ($config['local_validation']) { if (!$config['remote_validation']) {
$validator = new KeycloakLocalTokenValidator($keycloak, $this->certCachePool); $leeway = $config['local_validation_leeway'];
$validator = new KeycloakLocalTokenValidator($keycloak, $this->certCachePool, $leeway);
} else { } else {
$validator = new KeycloakRemoteTokenValidator($keycloak); $validator = new KeycloakRemoteTokenValidator($keycloak);
} }
...@@ -51,8 +52,8 @@ class KeycloakBearerUserProvider implements KeycloakBearerUserProviderInterface, ...@@ -51,8 +52,8 @@ class KeycloakBearerUserProvider implements KeycloakBearerUserProviderInterface,
$jwt = $validator->validate($accessToken); $jwt = $validator->validate($accessToken);
if (($config['audience'] ?? '') !== '') { if (($config['required_audience'] ?? '') !== '') {
$validator::checkAudience($jwt, $config['audience']); $validator::checkAudience($jwt, $config['required_audience']);
} }
return $this->loadUserByValidatedToken($jwt); return $this->loadUserByValidatedToken($jwt);
......
...@@ -20,17 +20,16 @@ class KeycloakLocalTokenValidator extends KeycloakTokenValidatorBase ...@@ -20,17 +20,16 @@ class KeycloakLocalTokenValidator extends KeycloakTokenValidatorBase
private $keycloak; private $keycloak;
private $cachePool; private $cachePool;
private $clientHandler; private $clientHandler;
private $leewaySeconds;
/* The duration the public keycloak cert is cached */ /* The duration the public keycloak cert is cached */
private const CERT_CACHE_TTL_SECONDS = 3600; private const CERT_CACHE_TTL_SECONDS = 3600;
/* The leeway given for time based checks for token validation, in case the clocks of the server are out of sync */ public function __construct(Keycloak $keycloak, ?CacheItemPoolInterface $cachePool, int $leewaySeconds)
private const LOCAL_LEEWAY_SECONDS = 120;
public function __construct(Keycloak $keycloak, ?CacheItemPoolInterface $cachePool)
{ {
$this->keycloak = $keycloak; $this->keycloak = $keycloak;
$this->cachePool = $cachePool; $this->cachePool = $cachePool;
$this->leewaySeconds = $leewaySeconds;
$this->clientHandler = null; $this->clientHandler = null;
} }
...@@ -116,9 +115,9 @@ class KeycloakLocalTokenValidator extends KeycloakTokenValidatorBase ...@@ -116,9 +115,9 @@ class KeycloakLocalTokenValidator extends KeycloakTokenValidatorBase
$validate = $validate $validate = $validate
->algs(['RS256', 'RS512']) ->algs(['RS256', 'RS512'])
->keyset($keySet) ->keyset($keySet)
->exp(self::LOCAL_LEEWAY_SECONDS) ->exp($this->leewaySeconds)
->iat(self::LOCAL_LEEWAY_SECONDS) ->iat($this->leewaySeconds)
->nbf(self::LOCAL_LEEWAY_SECONDS) ->nbf($this->leewaySeconds)
->iss($issuer); ->iss($issuer);
assert($validate instanceof Validate); assert($validate instanceof Validate);
$jwtResult = $validate->run(); $jwtResult = $validate->run();
......
...@@ -30,7 +30,7 @@ class KeycloakLocalTokenValidatorTest extends TestCase ...@@ -30,7 +30,7 @@ class KeycloakLocalTokenValidatorTest extends TestCase
$this->keycloak = $keycloak; $this->keycloak = $keycloak;
$cache = new ArrayAdapter(); $cache = new ArrayAdapter();
$this->tokenValidator = new KeycloakLocalTokenValidator($keycloak, $cache); $this->tokenValidator = new KeycloakLocalTokenValidator($keycloak, $cache, 0);
$this->mockResponses([]); $this->mockResponses([]);
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment