bekirozturk/symfony-oauth2-bundle
Installation
composer require bekirozturk/symfony-oauth2-bundle
Ensure the bundle is registered in config/bundles.php:
return [
// ...
BekirozTurk\SymfonyOAuth2Bundle\SymfonyOAuth2Bundle::class => ['all' => true],
];
Configure .env
Add OAuth2 provider details (replace placeholders with your provider's values):
OAUTH2_CLIENT_ID=your_client_id
OAUTH2_CLIENT_SECRET=your_client_secret
OAUTH2_REDIRECT_URI=http://your-app.test/login/oauth2/callback
OAUTH2_PROVIDER=google # or 'github', 'facebook', etc.
OAUTH2_SCOPES=openid,email,profile
First Use Case Trigger OAuth2 login via a route or controller:
use Symfony\Component\Routing\Annotation\Route;
use BekirozTurk\SymfonyOAuth2Bundle\Controller\OAuth2Controller;
class AuthController extends AbstractController
{
#[Route('/login', name: 'app_login')]
public function login(OAuth2Controller $oauth2): Response
{
return $oauth2->redirectToProvider();
}
}
Authentication Flow
$oauth2->redirectToProvider(); // Triggers PKCE flow
#[Route('/login/oauth2/callback', name: 'oauth2_callback')]
public function callback(OAuth2Controller $oauth2): Response
{
$user = $oauth2->handleCallback(); // Returns User entity or null
if ($user) {
$this->loginManager->authenticateUser($user);
return $this->redirectToRoute('home');
}
throw $this->createNotFoundException();
}
User Entity Integration
User entity to include OAuth2 fields:
#[ORM\Entity]
class AppUser extends AbstractUser
{
#[ORM\Column(type: 'string', length: 255, nullable: true)]
private ?string $oauthProvider = null;
#[ORM\Column(type: 'string', length: 255, nullable: true)]
private ?string $oauthId = null;
}
UserProvider in config/packages/security.yaml:
providers:
app_user_provider:
entity:
class: App\Entity\AppUser
property: email
Multi-Provider Support
# config/packages/oauth2.yaml
oauth2:
providers:
google:
client_id: '%env(OAUTH2_GOOGLE_CLIENT_ID)%'
client_secret: '%env(OAUTH2_GOOGLE_CLIENT_SECRET)%'
scopes: ['openid', 'email', 'profile']
github:
client_id: '%env(OAUTH2_GITHUB_CLIENT_ID)%'
# ...
$oauth2->setProvider('github')->redirectToProvider();
firewalls:
main:
oauth2: true # Enables OAuth2 guard
form_login: # Fallback to form login if needed
login_path: app_login
UserProvider to map OAuth2 responses to your user entity:
use BekirozTurk\SymfonyOAuth2Bundle\Security\User\OAuth2UserProvider;
class CustomOAuth2UserProvider extends OAuth2UserProvider
{
public function loadUserByOAuth2Response($response): UserInterface
{
$user = $this->findOrCreateUser($response);
$user->setOAuthProvider($response->getProvider());
$user->setOAuthId($response->getId());
return $user;
}
}
Register as a service:
services:
BekirozTurk\SymfonyOAuth2Bundle\Security\User\OAuth2UserProvider:
class: App\Security\User\CustomOAuth2UserProvider
PKCE Misconfiguration
OAUTH2_REDIRECT_URI matches the callback route exactly (including http/https).http://localhost instead of http://your-app.test to avoid PKCE verification issues.State Parameter Errors
state mismatch errors, regenerate the state parameter or ensure it’s stored securely (e.g., in the session).$oauth2->getState(); // Check current state
Provider-Specific Quirks
openid scope and may need additional scopes like https://www.googleapis.com/auth/userinfo.profile.user:email scope for email access (not included by default).email and public_profile scopes explicitly.User Entity Not Persisting
User entity extends AbstractUser and includes the required OAuth2 fields (oauthProvider, oauthId).UserProvider is correctly configured in security.yaml.Enable Debug Mode
Add to config/packages/dev/oauth2.yaml:
oauth2:
debug: true
This logs OAuth2 responses and errors to var/log/dev.log.
Inspect Responses Dump the OAuth2 response in the callback:
$response = $oauth2->getResponse();
dump($response->getEmail(), $response->getProvider());
Clear Cache After config changes, run:
php bin/console cache:clear
Custom Providers
Extend the ProviderInterface to support non-standard OAuth2 providers:
use BekirozTurk\SymfonyOAuth2Bundle\Provider\ProviderInterface;
class CustomProvider implements ProviderInterface
{
public function getAuthorizationUrl(): string { /* ... */ }
public function getAccessTokenUrl(): string { /* ... */ }
public function getUserDetailsUrl(): string { /* ... */ }
}
Register as a service tagged as oauth2.provider.
Event Listeners
Listen to OAuth2 events (e.g., oauth2.login.success):
use Symfony\Component\EventDispatcher\GenericEvent;
class OAuth2Listener
{
public function onOAuth2Login(GenericEvent $event): void
{
$user = $event->getSubject();
// Custom logic (e.g., log activity, update user metadata)
}
}
Configure in config/services.yaml:
services:
App\EventListener\OAuth2Listener:
tags:
- { name: kernel.event_listener, event: oauth2.login.success, method: onOAuth2Login }
CSRF Protection Disable CSRF for OAuth2 routes if using PKCE (recommended):
# config/packages/security.yaml
firewalls:
main:
pattern: ^/login/oauth2
security: false # Bypass CSRF for OAuth2
OAUTH2_GOOGLE_CLIENT_ID=...
OAUTH2_GITHUB_CLIENT_SECRET=...
oauth2:
providers:
microsoft:
scopes: ['openid', 'profile', 'email', 'offline_access']
$this->get('session')->start(); // Add to controller if needed
How can I help you explore Laravel packages today?