bengor-user/symfony-security-bridge
Adapter bridge to integrate BenGorUser’s User model with the Symfony Security component. Install via Composer, fully tested with PHPSpec, and documented in the main BenGorUser/User library docs. PHP 5.5+.
Install the Package
composer require bengor-user/symfony-security-bridge
Ensure your Laravel project uses PHP ≥5.5 (compatible with Symfony Security).
Bridge Your User Model
Extend your Laravel User model with SymfonyUserInterface and implement required methods:
use BenGorUser\SymfonySecurityBridge\User\SymfonyUserInterface;
class User extends Authenticatable implements SymfonyUserInterface
{
public function getRoles()
{
return ['ROLE_USER']; // Return array of roles
}
public function getPassword()
{
return $this->password; // Return hashed password
}
public function getSalt()
{
return null; // Symfony 3+ ignores this; return null for Laravel Hash::check()
}
public function eraseCredentials()
{
// Clear sensitive data after login
$this->password = null;
}
}
Configure Symfony Security
In config/auth.php, set the provider to use the bridged user:
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\User::class,
],
],
First Use Case: Authentication
Use Symfony’s UserChecker or AuthenticationUtils in a Laravel controller:
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
public function login(Request $request, TokenStorage $tokenStorage)
{
$user = User::where('email', $request->email)->first();
$token = new UsernamePasswordToken($user, $this->generatePassword(), 'main', $user->getRoles());
$tokenStorage->setToken($token);
}
Define Roles in User Model
public function getRoles()
{
return $this->roles->pluck('name')->toArray(); // Assume `roles` is a relationship
}
Use Symfony’s Voter System
Create a custom voter:
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
class AdminVoter extends Voter
{
protected function supports($attribute, $subject)
{
return $attribute === 'IS_ADMIN';
}
protected function voteOnAttribute($attribute, $subject, TokenInterface $token)
{
return in_array('ROLE_ADMIN', $token->getUser()->getRoles());
}
}
Integrate with Laravel Middleware
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
class AdminMiddleware
{
public function __construct(AuthorizationCheckerInterface $authChecker)
{
$this->authChecker = $authChecker;
}
public function handle($request, Closure $next)
{
if (!$this->authChecker->isGranted('IS_ADMIN')) {
abort(403);
}
return $next($request);
}
}
getPassword() to return the hashed password (use Laravel’s Hash::make()).
Symfony’s UserPasswordHasher will work seamlessly with Laravel’s Hash facade.SessionAuthenticationStrategy
use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface;
class LaravelSuccessHandler implements AuthenticationSuccessHandlerInterface
{
public function onAuthenticationSuccess(Request $request, TokenInterface $token)
{
// Redirect or set flash messages using Laravel's session
session()->flash('status', 'Logged in!');
return redirect('/dashboard');
}
}
Role Format Mismatch
ROLE_ (e.g., ['ROLE_ADMIN']).can() method uses plain strings (e.g., ['admin']).getRoles():
public function getRoles()
{
return array_map(fn($role) => "ROLE_{$role}", $this->roles);
}
Password Hashing Conflicts
UserPasswordHasher, ensure your Laravel User model’s password field is not auto-hashed twice.shouldHash in Authenticatable or manually hash passwords before saving.TokenStorage in Laravel
TokenStorage is not natively available in Laravel.AppServiceProvider:
$this->app->bind(TokenStorage::class, function () {
return new TokenStorage(new AnonymousToken('main', null, []));
});
Deprecated Methods
getSalt() is ignored in Symfony 4+).getSalt() to return null for compatibility.Check User Roles Dump the user’s roles during authentication:
dd($token->getUser()->getRoles());
Verify Token Creation Ensure tokens are created correctly:
$token = new UsernamePasswordToken(
$user,
$this->generatePassword(), // Use a dummy password; Symfony validates against getPassword()
'main',
$user->getRoles()
);
Symfony Event Listeners
Use Symfony’s events (e.g., security.interactive_login) via Laravel’s event system:
Event::listen('security.interactive_login', function ($event) {
// Custom logic after login
});
Custom User Providers
Extend SymfonyUserProvider to fetch users from non-standard sources:
use BenGorUser\SymfonySecurityBridge\User\SymfonyUserProvider;
class CustomUserProvider extends SymfonyUserProvider
{
public function loadUserByUsername($username)
{
return User::where('email', $username)->firstOrFail();
}
}
Laravel-Powered Firewalls
Combine with spatie/laravel-permission for granular RBAC:
public function getRoles()
{
return $this->getAllPermissions()->pluck('name')->map(fn($p) => "ROLE_{$p}");
}
Testing
Mock Symfony’s TokenStorage in PHPUnit:
$token = $this->createMock(TokenInterface::class);
$token->method('getUser')->willReturn($user);
$storage = $this->createMock(TokenStorage::class);
$storage->method('getToken')->willReturn($token);
$this->app->instance(TokenStorage::class, $storage);
How can I help you explore Laravel packages today?