TL;DR: Symfony is a PHP framework, made up of a lot of decoupled and reusable components. It's a framework that promotes standardization and professionalism, supports best practices and interoperability of applications. In this tutorial, I'll show you how easy it is to build a web application with Symfony and add authentication to it without banging your head on a wall! Check out the repo to get the code.


Symfony Framework has a vibrant community just like its counterpart - Laravel. The ecosystem around Symfony Framework thrives from the utmost significance of the plethora of reusable PHP components that Symfony provides. These components are used throughout PHP land. Tools like WordPress, Drupal, phpBB and Laravel depend on Symfony Framework components a lot. Well-known PHP projects such as Silex, Twig and Swiftmailer originated from Symfony projects. One notable Symfony community is Sensiolabs Connect, which is an extensive professional network for Symfony developers.

Symfony Components

Symfony Framework consists of a set of reusable PHP components. Symfony Components are decoupled and reusable PHP libraries. Many PHP applications around the world make use of Symfony Framework components. You can use any of these components in your own applications independently from the Symfony Framework. These amazing components include:

  • Asset - Manages URL generation and versioning of web assets such as CSS stylesheets, JavaScript files and image files
  • Browserkit - Simulates the behavior of a web browser
  • Console - Eases the creation of beautiful and testable command line interfaces
  • Debug - Provides tools to ease debugging PHP code
  • DomCrawler - Eases DOM navigation for HTML and XML documents. Laravel utilizes this component for integrated testing
  • Filesystem - Provides basic utilities for the filesystem
  • HttpKernel - Provides the building blocks to create flexible and fast HTTP-based frameworks
  • Routing - Maps an HTTP request to a set of configuration variables
  • Yaml - Loads and dumps YAML files.

Symfony Framework allows a developer to create plugins called Bundles. Everything is a bundle in Symfony, including both the core framework functionality and the code written for your application. This gives a developer the flexibility to use pre-built features packaged in third-party bundles or to distribute your own bundles. Read more about Symfony Bundles.

We'll be building a simple character listing app with Symfony 3.1 like we did with Laravel here. Our app will simply list 10 Game of Thrones characters and their real names. Once we add authentication to the app, all logged-in users will have the privilege of knowing these celebrity characters personally.

Let's Get Started

Symfony utilizes Composer to manage its dependencies. So, before using Symfony, make sure you have Composer installed on your machine. We can install Symfony Framework by issuing the composer create-project command in your terminal like so: composer create-project symfony/framework-standard-edition GOT or using the symfony installer. There are several Symfony distributions available, such as Symfony Rest Edition, Hello World Edition and CMF Standard Edition. A developer is at liberty to choose any to get started with.

It's faster to spin up a new app using the symfony command like so: symfony new GOT . Check out the Symfony docs to learn how to set up the Symfony installer.

Explore Directory Structure

Symfony Framework automatically ships with a default directory structure like the one below:


your-project/
├─ app/
│  ├─ config/
│  └─ Resources/
├─ bin/
│  └─ console
├─ src/
│  └─ AppBundle/
├─ tests/
│  └─ AppBundle/
├─ var/
│  ├─ cache/
│  ├─ logs/
│  └─ sessions/
├─ vendor/
│  └─ ...
└─ web/
   ├─ app.php
   └─ ...

The recommended purpose for each of these directories can be found below:

  • app/config - Contains all the configuration defined for any environment
  • app/Resources - Contains all the templates and translation files for the application
  • bin - Contains the executable files
  • src/AppBundle - Contains all the Symfony-specific code (controllers and routes), your domain code (e.g., Doctrine classes) and all your business logic
  • tests/AppBundle - Contains all the tests of the application
  • var/cache - Contains all the cache files generated by the application
  • var/logs - Contains all the log files generated by the application
  • var/sessions - Contains all the session files generated by the application
  • vendor - Contains all application dependencies installed by Composer
  • web - Contains all the front controller files and all the web assets, such as stylesheets, JavaScript files and images.

Setting Up the Controllers

Open up the src/AppBundle/Controller directory and delete the DefaultController.php file. We are going to create new controller files, so we don't actually need the DefaultController. Now, go ahead and create ListController.php, RegistrationController.php and SecurityController.php files inside the directory.

Open up src/AppBundle/Controller/ListController.php and configure it like so:


<?php

namespace AppBundle\Controller;

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;

class ListController extends Controller
{
    public function showAction(Request $request)
    {
        $characters = [
          'Daenerys Targaryen' => 'Emilia Clarke',
          'Jon Snow'           => 'Kit Harington',
          'Arya Stark'         => 'Maisie Williams',
          'Melisandre'         => 'Carice van Houten',
          'Khal Drogo'         => 'Jason Momoa',
          'Tyrion Lannister'   => 'Peter Dinklage',
          'Ramsay Bolton'      => 'Iwan Rheon',
          'Petyr Baelish'      => 'Aidan Gillen',
          'Brienne of Tarth'   => 'Gwendoline Christie',
          'Lord Varys'         => 'Conleth Hill'
        ];

        return $this->render('default/index.html.twig', array('character' => $characters));
    }
}

$this->render('default/index.html.twig', array('character' => $characters)) indicates that we are passing the $characters array to a view called index.html.twig in a default directory. We'll create that view in the later part of this post.

Open up src/AppBundle/Controller/RegistrationController.php and configure it like so:


<?php
namespace AppBundle\Controller;

use AppBundle\Entity\User;
use AppBundle\Form\UserType;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;

class RegistrationController extends Controller
{
    /**
     * @Route("/register", name="register")
     */
    public function registerAction(Request $request)
    {
        // Create a new blank user and process the form
        $user = new User();
        $form = $this->createForm(UserType::class, $user);
        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {
            // Encode the new users password
            $encoder = $this->get('security.password_encoder');
            $password = $encoder->encodePassword($user, $user->getPlainPassword());
            $user->setPassword($password);

            // Set their role
            $user->setRole('ROLE_USER');

            // Save
            $em = $this->getDoctrine()->getManager();
            $em->persist($user);
            $em->flush();

            return $this->redirectToRoute('login');
        }

        return $this->render('auth/register.html.twig', [
            'form' => $form->createView(),
        ]);
    }
}

Here, we mapped this controller to the register route. The registerAction processes the user registration form and persists to database; if the form has not been submitted yet, it renders the form created from Symfony FormBuilder. Next, let's create the FormBuilder.

Creating User Type

Create a Form folder inside src/AppBundle directory. Now, go ahead and create UserType.php inside src/AppBundle/Form directory and configure it like so:


<?php
namespace AppBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\Extension\Core\Type\RepeatedType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class UserType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('name', TextType::class)
            ->add('email', EmailType::class)
            ->add('plainPassword', RepeatedType::class, [
                'type' => PasswordType::class,
                'first_options' => ['label' => 'Password'],
                'second_options' => ['label' => 'Confirm Password'],
            ]);
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => 'AppBundle\Entity\User',
        ]);
    }
}

Symfony ships with a Form builder that helps us with generation of forms in our applications. Read more about Symfony Forms.

Open up src/AppBundle/Controller/SecurityController.php and configure it like so:


<?php

namespace AppBundle\Controller;

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;

class SecurityController extends Controller
{
    /**
     * @Route("/login", name="login")
     */
    public function loginAction(Request $request)
    {
       $helper = $this->get('security.authentication_utils');

       return $this->render(
           'auth/login.html.twig',
           array(
               'last_username' => $helper->getLastUsername(),
               'error'         => $helper->getLastAuthenticationError(),
           )
       );
    }

    /**
     * @Route("/login_check", name="security_login_check")
     */
    public function loginCheckAction()
    {

    }

    /**
     * @Route("/logout", name="logout")
     */
    public function logoutAction()
    {

    }
}

This controller above actually takes care of our login process. Once the /login route is hit, the loginAction renders the login template, then the login_check route will be hit once a user clicks the login button. A user will be logged out once the logout route has been hit.

Setting Up the Model

Symfony Framework is configured in a way that you can use any ORM (Object Relational Mapping) of your choice. The Symfony full-stack Framework doesn't integrate any ORM by default; the Symfony Standard Edition, which is the most widely used distribution, comes integrated with Doctrine. The Doctrine ORM lets you map objects to a relational database.

Configure the Database

Before we set up our model entity, let's configure our database connection. The default driver Symfony ships with is MySQL.

Open up app/config/parameters.yml and set up your config like so:


# This file is auto-generated during the composer install
parameters:
    database_host: 127.0.0.1
    database_port: 3306
    database_name: symfony-got
    database_user: homestead
    database_password: secret
    mailer_transport: smtp
    mailer_host: 127.0.0.1
    mailer_user: null
    mailer_password: null
    secret: 81d300585b3dfdf6a3161e48d970e2baea252e42

Now that Doctrine can connect to your database, the following command can automatically generate an empty symfony-got database for you like so:

php bin/console doctrine:database:create

You can also just use Laravel Homestead to set up your app. With that, you don't have to run the doctrine:database:create command since vagrant would have created the database during provisioning.

Create the User Entity

Create an Entity folder inside src/AppBundle directory. Now, go ahead and create User.php inside the src/AppBundle/Entity directory and configure it like so:


<?php
namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;

/**
 * @ORM\Entity
 * @UniqueEntity(fields="email", message="This email address is already in use")
 */
class User implements UserInterface
{
    /**
     * @ORM\Id;
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * @ORM\Column(type="string", length=255, unique=true)
     */
    protected $email;

    /**
     * @ORM\Column(type="string", length=40)
     */
    protected $name;

    /**
     * @ORM\Column(type="string", length=50)
     */
    protected $role;

    /**
     * @Assert\Length(max=4096)
     */
    protected $plainPassword;

    /**
     * @ORM\Column(type="string", length=64)
     */
    protected $password;

    public function eraseCredentials()
    {
        return null;
    }

    public function getRole()
    {
        return $this->role;
    }

    public function setRole($role = null)
    {
        $this->role = $role;
    }

    public function getRoles()
    {
        return [$this->getRole()];
    }

    public function getId()
    {
        return $this->id;
    }

    public function setName($name)
    {
        $this->name = $name;
    }

    public function getName()
    {
        return $this->name;
    }

    public function getUsername()
    {
        return $this->email;
    }

    public function getEmail()
    {
        return $this->email;
    }

    public function setEmail($email)
    {
        $this->email = $email;
    }

    public function getPassword()
    {
        return $this->password;
    }

    public function setPassword($password)
    {
        $this->password = $password;
    }

    public function getPlainPassword()
    {
        return $this->plainPassword;
    }

    public function setPlainPassword($plainPassword)
    {
        $this->plainPassword = $plainPassword;
    }

    public function getSalt()
    {
        return null;
    }
}

You now have a basic User entity with mapping information so that Doctrine knows exactly how to persist it. There is no table in our database yet, all we have is the User Schema. Fortunately, Doctrine can automatically create all the database tables needed for every known entity in your application. To do this, run:


php bin/console doctrine:schema:update --force

Setting Up the Routes

Symfony Framework allows several options for setting up routes. It supports annotations and the option of explicitly stating it in app/config/routing.yml. The goal of the Symfony routing system is to parse any URL and determine which controller should be executed. In this tutorial, we'll make use of annotations within our Controllers.

Symfony Routing Diagram

(Source: Symfony)

Open up src/AppBundle/Controller/ListController.php and configure it like so:

  /**
   * @Route("/", name="welcome")
   */
  public function showAction(Request $request)
  {
      $characters = [
        'Daenerys Targaryen' => 'Emilia Clarke',
        'Jon Snow'           => 'Kit Harington',
        'Arya Stark'         => 'Maisie Williams',
        'Melisandre'         => 'Carice van Houten',
        'Khal Drogo'         => 'Jason Momoa',
        'Tyrion Lannister'   => 'Peter Dinklage',
        'Ramsay Bolton'      => 'Iwan Rheon',
        'Petyr Baelish'      => 'Aidan Gillen',
        'Brienne of Tarth'   => 'Gwendoline Christie',
        'Lord Varys'         => 'Conleth Hill'
      ];

      return $this->render('default/index.html.twig', array('character' => $characters));
  }

I just added the @Route annotation. Once a request hits the / route, it invokes the showAction method of the ListController and renders the returned value in the index twig view. We'll configure the twig view later in this post.

Setting Up Authentication

Symfony 3 ships with an awesome security component called Guard that simplifies authentication process. Let's take advantage of it in our app. The first step is to configure the Symfony security settings.

Open up app/config/security.yml and configure it like so:


security:
    encoders:
        AppBundle\Entity\User: bcrypt
    providers:
        database_users:
            entity: { class: AppBundle:User, property: email }
    firewalls:
        # disables authentication for assets and the profiler, adapt it according to your needs
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false

        main:
            anonymous: ~
            guard:
                authenticators:
                    - app.form_login_authenticator
            logout:
                path: /logout
                target: /

These settings configure Symfony to use bcrypt to hash passwords, email field when looking up users and the Symfony Guard authenticator that we will create in a bit.

Create a new folder Security inside the src/AppBundle directory. Now, go ahead and create our custom Guard Authenticator FormLoginAuthenticator.php inside the Security directory.

FormLoginAuthenticator.php


<?php
namespace AppBundle\Security;

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Guard\Authenticator\AbstractFormLoginAuthenticator;
use Symfony\Component\Security\Core\Security;

class FormLoginAuthenticator extends AbstractFormLoginAuthenticator
{
    private $router;
    private $encoder;

    public function __construct(RouterInterface $router, UserPasswordEncoderInterface $encoder)
    {
        $this->router = $router;
        $this->encoder = $encoder;
    }

    public function getCredentials(Request $request)
    {
        if ($request->getPathInfo() != '/login_check') {
          return;
        }

        $email = $request->request->get('_email');
        $request->getSession()->set(Security::LAST_USERNAME, $email);
        $password = $request->request->get('_password');

        return [
            'email' => $email,
            'password' => $password,
        ];
    }

    public function getUser($credentials, UserProviderInterface $userProvider)
    {
        $email = $credentials['email'];

        return $userProvider->loadUserByUsername($email);
    }

    public function checkCredentials($credentials, UserInterface $user)
    {
        $plainPassword = $credentials['password'];
        if ($this->encoder->isPasswordValid($user, $plainPassword)) {
            return true;
        }

        throw new BadCredentialsException();
    }

    public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
    {
        $url = $this->router->generate('welcome');

        return new RedirectResponse($url);
    }

    public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
    {
       $request->getSession()->set(Security::AUTHENTICATION_ERROR, $exception);

       $url = $this->router->generate('login');

       return new RedirectResponse($url);
    }

    protected function getLoginUrl()
    {
        return $this->router->generate('login');
    }

    protected function getDefaultSuccessRedirectUrl()
    {
        return $this->router->generate('welcome');
    }

    public function supportsRememberMe()
    {
        return false;
    }
}

The getCredentials() fetches the email address and password from the request, getUser() uses that to load the user and then checkCredentials() checks if the password matches the user account that getUser() fetched. If it matches and the user exists, then onAuthenticationSuccess() is called, otherwise onAuthenticationFailure() is called. The getLoginUrl() and getDefaultSuccessRedirectUrl() must be implemented since we are inheriting the GuardAuthenticator. These methods return the login URL and the URL that a successful login should redirect to respectively.The remember_me functionality is not needed in this app, so we return false in supportsRememberMe().

Symfony Framework needs to be aware about our custom Guard Authenticator. Now, open up app/config/services.yml and register in the Symfony Services list like so:


# Learn more about services, parameters and containers at
# http://symfony.com/doc/current/book/service_container.html
parameters:
#    parameter_name: value

services:
    app.form_login_authenticator:
        class: AppBundle\Security\FormLoginAuthenticator
        arguments: ["@router", "@security.password_encoder"]

Setting Up Views

Symfony Framework ships with a powerful templating engine called Twig. Twig allows you to write concise, readable templates that are more friendly to web designers and, in several ways, more powerful than PHP templates.

The views needed for authentication in this app are in the app/Resources/views/auth directory. The base layout for our application has also been configured in the app/Resources/views/base.html.twig. All of these views use the Bootstrap CSS framework, but you are free to customize them however you wish.

Open up your app/Resources/views/default/index.html.twig and configure it like so:

{% extends 'base.html.twig' %}



{% block body %}
<div class="container">
    <div class="row">
        <div class="col-md-10 col-md-offset-1">
            <div class="panel panel-success">
                <div class="panel-heading">List of Game of Thrones Characters</div>

                {% if app.user != null %}
                  <table class="table">
                      <tr>
                          <th>Character</th>
                          <th>Real Name</th>
                      </tr>

                      {% for key, item in character %}
                        <tr>
                          <td>{{ key }}</td><td>{{ item }}</td>
                        </tr>
                      {% endfor %}
                  </table>
                {% endif %}


            </div>

            {% if app.user == null %}
              <a href="/login" class="btn btn-info"> You need to login to see the list 😜😜 >></a>
            {% endif %}
        </div>
    </div>
</div>
{% endblock %}

Here, we are looping through the $characters array data passed from the ListController for appropriate rendering in the index.html.twig view.

app.user == null - You can check if a user is authenticated or not. It returns true if a user is logged-in and null if a user is not.

Create login.html.twig and register.html.twig templates inside app/Resources/views/auth directory. Configure them respectively like so:

login.html.twig

{% extends 'base.html.twig' %}



{% block body %}
<div class="container">
    <div class="row">
        <div class="col-md-8 col-md-offset-2">
            <div class="panel panel-default">
                <div class="panel-heading">Login</div>
                <div class="panel-body">
                    <form class="form-horizontal" role="form" method="POST" action="{{ path('security_login_check') }}">
                        {% if error %}
                            <div class="alert alert-danger">
                                {{ error.messageKey|trans(error.messageData) }}
                            </div>
                        {% endif %}

                        <div class="form-group">
                            <label for="email" class="col-md-4 control-label">E-Mail Address</label>

                            <div class="col-md-6">
                                <input id="email" type="email" class="form-control" name="_email" value="{{ last_username }}">
                            </div>
                        </div>

                        <div class="form-group">
                            <label for="password" class="col-md-4 control-label">Password</label>

                            <div class="col-md-6">
                                <input id="password" type="password" class="form-control" name="_password">
                            </div>
                        </div>

                        <div class="form-group">
                            <div class="col-md-6 col-md-offset-4">
                                <button type="submit" class="btn btn-primary">
                                    <i class="fa fa-btn fa-sign-in"></i> Login
                                </button>
                            </div>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
{% endblock %}

register.html.twig

{% extends 'base.html.twig' %}



{% block body %}
<div class="container">
  <div class="row">
    <div class="col-md-8 col-md-offset-2">
        <div class="panel panel-default">
          <div class="panel-heading">Register</div>
          <div class="panel-body">
            {{ form_start(form) }}
            <div class="form_group">
                <div class="col-md-8 col-md-offset-2">
                  {{ form_row(form.name, {'attr': {'class': 'form-control'}}) }}
                </div>
            </div>

            <div class="form_group">
                <div class="col-md-8 col-md-offset-2">
                {{ form_row(form.email, {'attr': {'class': 'form-control'}}) }}
                </div>
            </div>

            <div class="form_group">
                <div class="col-md-8 col-md-offset-2">
                {{ form_row(form.plainPassword.first, {'attr': {'class': 'form-control'}}) }}
                </div>
            </div>

            <div class="form_group">
                <div class="col-md-8 col-md-offset-2">
                {{ form_row(form.plainPassword.second, {'attr': {'class': 'form-control'}}) }}
              </div>
            </div>

            <div class="form-group">
                <div class="col-md-8 col-md-offset-4" style="margin-top:5px;">
                    <button type="submit" class="btn btn-primary">
                        <i class="fa fa-btn fa-user"></i> Register
                    </button>
                </div>
            </div>
            {{ form_end(form) }}
          </div>
        </div>
    </div>
  </div>
</div>
{% endblock %}

We are making use of the Symfony built-in form methods in this template.

With all the routes and views fully set up, your application should look like this:

Landing Page

Landing Page

Login Page

Login Page

Register Page

Register Page

Try registering a user on the application. It should register you successfully and log you in like so:

Landing Page while authenticated

The Profile Debug Toolbar

Symfony ships with an awesome web debug toolbar. It is one of the most recognizable elements of Symfony applications. It provides all sorts of information about sessions, cookies, variables, load time, service providers, request-response time cycle, app size and also an extensive error log. This toolbar actually increases the productivity of Symfony developers because it makes debugging super easy!

Symfony debug Toolbar

Symfony Profiling

Symfony Error Notice

Symfony Error Profiling

You can disable the toolbar by setting the value of toolbar to false in app/config/config_dev.yml like so:


web_profiler:
    toolbar: false
    intercept_redirects: false

Symfony vs. Other PHP Frameworks

Right now, Laravel is the most trending PHP framework in the world. Laravel thrives on excellent and straightforward documentation. It's also easier to learn. Symfony, on the other hand, is a very stable and mature framework. It's great for very large web projects. Symfony has been around for so long that several PHP projects and frameworks like Laravel depend on many of its components. Symfony forces you as a developer to learn Solid OOP. Many open-source projects build on Symfony components, thus allowing a developer who is well-versed in Symfony to easily work on other PHP projects. CakePHP has an awesome inbuilt ORM that makes building queries and relationships a piece of cake. If you want an excellent comparison of Symfony and other PHP frameworks, check here.

Aside: Using Auth0 with Symfony Framework

Auth0 issues JSON Web Tokens on every login for your users. This means that you can have a solid identity infrastructure, including single sign-on, user management, support for social identity providers (Facebook, Github, Twitter, etc.), enterprise identity providers (Active Directory, LDAP, SAML, etc.) and your own database of users with just a few lines of code.

We can easily set up authentication in our Symfony apps by using the Lock Widget. If you don't already have an Auth0 account, sign up for one now. Navigate to the Auth0 management dashboard, select Applications from the navigational menu, then select the app you want to connect with Symfony.

Step 1: Install and Configure Auth0 plugin

Follow the instructions here to configure the Auth0 plugin.

Note: Symfony 3 only works with 0.5.*@dev version of the hwi/oauth-bundle

Step 2: Register the Callback

Head over to your Auth0 dashboard and register a callback like so: http://symfony-got.app/auth0/callback and logout URL http://symfony-got.app/logout.

Step 3: Include Auth0's Lock Widget

Open up default/index.html.twig and configure it like so:

{% extends 'base.html.twig' %}



{% block body %}
<script src="//cdn.auth0.com/js/lock/10.0/lock.min.js"></script>
<script type="text/javascript">

  var lock = new Auth0Lock('YOUR_AUTH0_CLIENT_ID', 'YOUR_AUTH0_DOMAIN');


  function signin() {
    lock.show({
        callbackURL: 'YOUR_AUTH0_CALLBACK_URL'
      , responseType: 'code'
      , authParams: {
        scope: 'openid email'  // Learn about scopes: https://auth0.com/docs/scopes
      }
    });
  }
</script>
<button onclick="window.signin();">Login</button>
{% endblock %}

When the login button is clicked, the Auth0 lock widget comes up:

Auth0 Lock Widget

Step 4: Use Lock Widget

Register with the Lock Widget. Once a user registers, it stores the user information in your Auth0 dashboard. We can retrieve this info using the HWI\Bundle\OAuthBundle\Security\Core\User\OAuthUserProvider class methods.

logged_in_user

Logged In User

With Auth0, you can have all your users' information stored without having to run your own database. You can configure the Lock UI to handle authentication. Auth0 provides powerful analytics about users signing up on your platform such as the browser the user logged in with, the location, device, number of logins and more, out of the box!

Wrapping Up

You have just built your first app with Symfony 3. We just barely scratched the surface in this tutorial. This is designed to help you get started in building your own apps with Symfony framework. You can leverage the knowledge gained here to build bigger and better apps. Symfony teaches you a lot about how to develop good, transparent, well-tested and well-coded applications with PHP. When compared with other PHP frameworks, Symfony is very stable. In fact, several PHP frameworks depend on Symfony components. It’s one of the few frameworks out there bundled with good documentation and certifications for developers. Would you prefer Laravel over Symfony or vice versa? Let me know your thoughts in the comment section 😊

"Symfony is one of the few frameworks out there with certifications for developers."