How to Build a JSON Authentication Endpoint

3.3 version

How to Build a JSON Authentication Endpoint

In this entry, you'll build a JSON endpoint to log in your users. Of course, when the user logs in, you can load your users from anywhere - like the database. See B) Configuring how Users are Loaded for details.

First, enable the JSON login under your firewall:

  • YAML
    1
    2
    3
    4
    5
    6
    7
    8
    9
    # app/config/security.yml
    security:
        # ...
    
        firewalls:
            main:
                anonymous: ~
                json_login:
                    check_path: /login
    
  • XML
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    <!-- app/config/security.xml -->
    <?xml version="1.0" encoding="UTF-8"?>
    <srv:container xmlns="http://symfony.com/schema/dic/security"
        xmlns:srv="http://symfony.com/schema/dic/services"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://symfony.com/schema/dic/services
            http://symfony.com/schema/dic/services/services-1.0.xsd">
    
        <config>
            <firewall name="main">
                <anonymous />
                <json-login check-path="/login" />
            </firewall>
        </config>
    </srv:container>
    
  • PHP
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    // app/config/security.php
    $container->loadFromExtension('security', array(
        'firewalls' => array(
            'main' => array(
                'anonymous'  => null,
                'json_login' => array(
                    'check_path' => '/login',
                ),
            ),
        ),
    ));
    

Tip

The check_path can also be a route name (but cannot have mandatory wildcards - e.g. /login/{foo} where foo has no default value).

Now, when a request is made to the /login URL, the security system initiates the authentication process. You just need to configure a route matching this path:

  • Annotations
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    // src/AppBundle/Controller/SecurityController.php
    
    // ...
    use Symfony\Component\HttpFoundation\Request;
    use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
    
    class SecurityController extends Controller
    {
        /**
         * @Route("/login", name="login")
         */
        public function loginAction(Request $request)
        {
        }
    }
    
  • YAML
    1
    2
    3
    4
    # app/config/routing.yml
    login:
        path:     /login
        defaults: { _controller: AppBundle:Security:login }
    
  • XML
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    <!-- app/config/routing.xml -->
    <?xml version="1.0" encoding="UTF-8" ?>
    <routes xmlns="http://symfony.com/schema/routing"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://symfony.com/schema/routing
            http://symfony.com/schema/routing/routing-1.0.xsd">
    
        <route id="login" path="/login">
            <default key="_controller">AppBundle:Security:login</default>
        </route>
    </routes>
    
  • PHP
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    // app/config/routing.php
    use Symfony\Component\Routing\RouteCollection;
    use Symfony\Component\Routing\Route;
    
    $collection = new RouteCollection();
    $collection->add('login', new Route('/login', array(
        '_controller' => 'AppBundle:Security:login',
    )));
    
    return $collection;
    

Don't let this empty controller confuse you. When you submit a POST request to the /login URL with the following JSON document as the body, the security system intercepts the requests. It takes care of authenticating the user with the submitted username and password or triggers an error in case the authentication process fails:

1
2
3
4
{
    "username": "dunglas",
    "password": "MyPassword"
}

If the JSON document has a different structure, you can specify the path to access the username and password properties using the username_path and password_path keys (they default respectively to username and password). For example, if the JSON document has the following structure:

1
2
3
4
5
6
7
8
{
    "security": {
        "credentials": {
            "login": "dunglas",
            "password": "MyPassword"
        }
    }
}

The security configuration should be:

  • YAML
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    # app/config/security.yml
    security:
        # ...
    
        firewalls:
            main:
                anonymous: ~
                json_login:
                    check_path:    login
                    username_path: security.credentials.login
                    password_path: security.credentials.password
    
  • XML
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    <!-- app/config/security.xml -->
    <?xml version="1.0" encoding="UTF-8"?>
    <srv:container xmlns="http://symfony.com/schema/dic/security"
        xmlns:srv="http://symfony.com/schema/dic/services"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://symfony.com/schema/dic/services
            http://symfony.com/schema/dic/services/services-1.0.xsd">
    
        <config>
            <firewall name="main">
                <anonymous />
                <json-login check-path="login"
                            username-path="security.credentials.login"
                            password-path="security.credentials.password" />
            </firewall>
        </config>
    </srv:container>
    
  • PHP
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    // app/config/security.php
    $container->loadFromExtension('security', array(
        'firewalls' => array(
            'main' => array(
                'anonymous'  => null,
                'json_login' => array(
                    'check_path' => 'login',
                    'username_path' => 'security.credentials.login',
                    'password_path' => 'security.credentials.password',
                ),
            ),
        ),
    ));
    

This work, including the code samples, is licensed under a Creative Commons BY-SA 3.0 license.