Installation
composer require drenso/symfony-oidc-bundle
Symfony Flex auto-generates .env variables and config/packages/drenso_oidc.yaml.
Configure OIDC Client
Update drenso_oidc.yaml with your IdP details (e.g., well_known_url, client_id, client_secret):
drenso_oidc:
clients:
default:
well_known_url: '%env(OIDC_WELL_KNOWN_URL)%'
client_id: '%env(OIDC_CLIENT_ID)%'
client_secret: '%env(OIDC_CLIENT_SECRET)%'
Implement User Provider
Create a service implementing OidcUserProviderInterface:
use Drenso\OidcBundle\Security\User\OidcUserProviderInterface;
use Drenso\OidcBundle\Security\User\OidcUserData;
use Drenso\OidcBundle\Security\User\OidcTokens;
use Symfony\Component\Security\Core\User\UserInterface;
class CustomOidcUserProvider implements OidcUserProviderInterface {
public function ensureUserExists(string $userIdentifier, OidcUserData $userData, OidcTokens $tokens): UserInterface {
// Create or update user logic here
}
public function loadOidcUser(string $userIdentifier): ?UserInterface {
// Load user logic here
}
}
Register it in services.yaml:
services:
App\Security\CustomOidcUserProvider:
tags: ['security.user_provider']
Firewall Configuration
Enable the oidc listener in security.yaml:
security:
firewalls:
main:
oidc: ~
Trigger Login Add a route to redirect users to the IdP:
#[Route('/login_oidc', name: 'login_oidc')]
public function login(OidcClientInterface $oidcClient): RedirectResponse {
return $oidcClient->generateAuthorizationRedirect();
}
Multi-Client Setup
Configure multiple OIDC clients in drenso_oidc.yaml and inject them by name:
drenso_oidc:
clients:
default: { ... }
auth0: { ... }
Autowire via:
public function __construct(OidcClientInterface $defaultOidcClient, OidcClientInterface $auth0OidcClient) { ... }
Dynamic User Identifier
Use user_identifier_property in firewall config to dynamically fetch identifiers (e.g., email):
security:
firewalls:
main:
oidc:
user_identifier_property: email
Remember Me Enable via:
_remember_me=1 in redirect.enable_remember_me: true in firewall.remember_me authenticator.Logout Handling
Enable end_session_support:
security:
firewalls:
main:
oidc:
enable_end_session_listener: true
logout: true
Custom Claims Mapping
Extend OidcUserData or use a custom well_known_parser service to transform claims before user creation.
Token Validation
Adjust token_leeway_seconds in config to handle clock skew:
drenso_oidc:
clients:
default:
token_leeway_seconds: 60
Caching
Enable caching for .well-known and JWKS endpoints (requires symfony/cache):
drenso_oidc:
clients:
default:
well_known_cache_time: 3600
jwks_cache_time: 3600
IdP-Specific Quirks
allow_discovery_access_token_issuer: true for on-prem v1.Authentication Manager Requirement
enable_authenticator_manager: true in security.yaml.v1.x branch if stuck on older versions.User Identifier Uniqueness
user_identifier_property (default: sub) is unique across users. Non-unique IDs may cause conflicts.Token Validation Failures
token_leeway_seconds if tokens are rejected due to timestamp issues.cache:clear) if JWKS updates fail silently.Logout Limitations
end_session_support may not work with all IdPs (e.g., Facebook). Test thoroughly.Remember Me Conflicts
_remember_me query param matches the remember_me_parameter in client config.Enable Verbose Logging
Add to config/packages/dev/drenso_oidc.yaml:
drenso_oidc:
debug: true
Logs OIDC flows to var/log/dev.log.
Check Redirect URIs
Mismatched check_path in firewall and IdP’s redirect URI breaks auth. Verify:
security:
firewalls:
main:
oidc:
check_path: /login_check # Must match IdP config
Token Inspection
Use OidcTokens in ensureUserExists() to debug token contents:
public function ensureUserExists(string $userIdentifier, OidcUserData $userData, OidcTokens $tokens) {
var_dump($tokens->getIdToken()->getClaims()); // Inspect claims
}
Custom User Data Parsing
Implement a WellKnownParserInterface to modify parsed discovery docs:
use Drenso\OidcBundle\Security\Oidc\WellKnownParserInterface;
class CustomParser implements WellKnownParserInterface {
public function parse(string $url): array { ... }
}
Register as a service and set in config:
drenso_oidc:
clients:
default:
well_known_parser: app.custom_parser
Event Listeners
Subscribe to OidcEvents (e.g., OidcAuthenticationSuccessEvent) for post-auth logic:
use Drenso\OidcBundle\Event\OidcEvents;
use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
#[AsEventListener(OidcEvents::AUTHENTICATION_SUCCESS)]
public function onAuthSuccess(OidcAuthenticationSuccessEvent $event) {
$user = $event->getUser();
// Custom logic (e.g., role assignment)
}
Token Storage
Extend OidcTokens to persist tokens (e.g., for API calls):
use Drenso\OidcBundle\Security\User\OidcTokens;
class PersistentOidcTokens extends OidcTokens {
public function __construct(array $data) {
parent::__construct($data);
// Save to DB/Redis
}
}
Case Sensitivity
user_identifier_property paths are case-sensitive (e.g., email vs Email).
Default Client Fallback
If autowiring fails, the default client is used automatically.
PHP 8 Requirements
Use strict_types=1 in your UserProvider to avoid type issues.
IdP-Specific Headers
Add custom headers via custom_client_headers:
drenso_oidc:
clients:
default:
custom_client_headers:
X-Custom-Header: value
How can I help you explore Laravel packages today?