Since Symfony 2.8 we have a nice and shiny Guard authentication component. Guard can replace SonataUserBundle and in the rest of this post, we will show you how. As there is great documentation about Guard setup, we will skip that part and go straight to making it work with Sonata Admin. We presume you already have User Entity, Authenticator and User Provider.
This post was created after my talk about Symfony Guard, be sure to check that out here.
First of all, we need a simple login form:
<?php
namespace YourBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\OptionsResolver\OptionsResolver;
class LoginType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('username')
->add('password', PasswordType::class);
}
public function configureOptions(OptionsResolver $resolver)
{
parent::configureOptions($resolver);
$resolver->setDefaults(array(
'csrf_protection' => true
));
}
}
And a nice template to go with it (Thanks to SonataUserBundle this was an easy one):
{% extends 'SonataAdminBundle::standard_layout.html.twig' %}
{% block sonata_nav %}
{% endblock sonata_nav %}
{% block logo %}
{% endblock logo %}
{% block sonata_left_side %}
{% endblock sonata_left_side %}
{% block body_attributes %}class="sonata-bc login-page"{% endblock %}
{% block sonata_wrapper %}
<div class="login-box">
<div class="login-logo">
<a href="{{ path('sonata_admin_dashboard') }}">
<div>
<img style="width:64px;" src="{{ asset('path/to/your/logo') }}" alt="Login">
</div>
<span>Login</span>
</a>
</div>
<div class="login-box-body">
{% block sonata_user_login_form %}
{% block sonata_user_login_error %}
{% if error %}
<div class="alert alert-danger alert-error">
{{ error }}
</div>
{% endif %}
{% endblock %}
{% for label, flashes in app.session.flashbag.all %}
{% for flash in flashes %}
<div class="alert alert-{{ label }}">
{{ flash }}
</div>
{% endfor %}
{% endfor %}
<p class="login-box-msg">{{ 'Authentication'|trans }}</p>
<form action="{{ path("admin_login") }}" method="post" role="form">
{{ form_row(form._token) }}
<div class="form-group has-feedback">
<input type="text" class="form-control" id="username" name="{{ form.username.vars.full_name }}" value="{{ last_username }}" required="required" placeholder="Username"/>
<span class="glyphicon glyphicon-user form-control-feedback"></span>
</div>
<div class="form-group has-feedback">
<input type="password" class="form-control" id="password" name="{{ form.password.vars.full_name }}" required="required" placeholder="Password"/>
<span class="glyphicon glyphicon-lock form-control-feedback"></span>
</div>
<div class="row">
<div class="col-xs-4">
<button type="submit" class="btn btn-primary btn-block btn-flat">Login</button>
</div>
</div>
</form>
{% endblock %}
</div>
</div>
{% endblock sonata_wrapper %}
We also need a Controller that will handle login and logout, logout function is empty because Symfony will handle that part but we still need to have it:
<?php
namespace YourBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use YourBundle\Form\Type\LoginType;
use Symfony\Component\HttpFoundation\Request;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
class SecurityController extends Controller
{
/**
* @Route("/admin/login", name="admin_login")
*/
public function loginAction()
{
$helper = $this->get('security.authentication_utils');
$form = $this->createForm(LoginType::class, array(
'username' => $helper->getLastUsername()
));
return $this->render('YourBundle:Security:login.html.twig', array(
'last_username' => $helper->getLastUsername(),
'form' => $form->createView(),
'error' => $helper->getLastAuthenticationError(),
));
}
/**
* @Route("/admin/logout", name="admin_logout")
*/
public function logoutAction()
{
}
}
At the end, the only and the most important thing left is the firewall:
firewalls:
#....
admin:
pattern: /admin(.*)
form_login:
provider: users
login_path: admin_login #our route for login
use_forward: false
check_path: admin_login
failure_path: null
logout:
path: admin_logout #our route for logout
target: admin_login
anonymous: true
guard:
authenticators:
- app.authenticator.admin_login #if on Symfony 3.3 use FQCN
And we are done! Go to your login route and try your great new Authentication.