How to Build a JSON Authentication Endpoint

How to Build a JSON Authentication Endpoint

In this entry, you’ll build a JSON endpoint to log in your users. When the user logs in, you can load your users from anywhere - like the database. See 2b) The “User Provider” for details.

First, enable the JSON login under your firewall:

  • YAML

    1. # config/packages/security.yaml
    2. security:
    3. # ...
    4. firewalls:
    5. main:
    6. anonymous: lazy
    7. json_login:
    8. check_path: /login
  • XML

    1. <!-- config/packages/security.xml -->
    2. <?xml version="1.0" encoding="UTF-8" ?>
    3. <srv:container xmlns="http://symfony.com/schema/dic/security"
    4. xmlns:srv="http://symfony.com/schema/dic/services"
    5. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    6. xsi:schemaLocation="http://symfony.com/schema/dic/services
    7. https://symfony.com/schema/dic/services/services-1.0.xsd">
    8. <config>
    9. <firewall name="main">
    10. <anonymous lazy="true"/>
    11. <json-login check-path="/login"/>
    12. </firewall>
    13. </config>
    14. </srv:container>
  • PHP

    1. // config/packages/security.php
    2. $container->loadFromExtension('security', [
    3. 'firewalls' => [
    4. 'main' => [
    5. 'anonymous' => 'lazy',
    6. 'json_login' => [
    7. 'check_path' => '/login',
    8. ],
    9. ],
    10. ],
    11. ]);

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).

The next step is to configure a route in your app matching this path:

  • Annotations

    1. // src/Controller/SecurityController.php
    2. namespace App\Controller;
    3. // ...
    4. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
    5. use Symfony\Component\HttpFoundation\Request;
    6. use Symfony\Component\Routing\Annotation\Route;
    7. class SecurityController extends AbstractController
    8. {
    9. /**
    10. * @Route("/login", name="login", methods={"POST"})
    11. */
    12. public function login(Request $request): Response
    13. {
    14. $user = $this->getUser();
    15. return $this->json([
    16. 'username' => $user->getUsername(),
    17. 'roles' => $user->getRoles(),
    18. ]);
    19. }
    20. }
  • YAML

    1. # config/routes.yaml
    2. login:
    3. path: /login
    4. controller: App\Controller\SecurityController::login
    5. methods: POST
  • XML

    1. <!-- config/routes.xml -->
    2. <?xml version="1.0" encoding="UTF-8" ?>
    3. <routes xmlns="http://symfony.com/schema/routing"
    4. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    5. xsi:schemaLocation="http://symfony.com/schema/routing
    6. https://symfony.com/schema/routing/routing-1.0.xsd">
    7. <route id="login" path="/login" controller="App\Controller\SecurityController::login" methods="POST"/>
    8. </routes>
  • PHP

    1. // config/routes.php
    2. use App\Controller\SecurityController;
    3. use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;
    4. return function (RoutingConfigurator $routes) {
    5. $routes->add('login', '/login')
    6. ->controller([SecurityController::class, 'login'])
    7. ->methods(['POST'])
    8. ;
    9. };

Now, when you make a POST request, with the header Content-Type: application/json, to the /login URL with the following JSON document as the body, the security system intercepts the request and initiates the authentication process:

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

Symfony takes care of authenticating the user with the submitted username and password or triggers an error in case the authentication process fails. If the authentication is successful, the controller defined earlier will be called.

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. "security": {
  3. "credentials": {
  4. "login": "dunglas",
  5. "password": "MyPassword"
  6. }
  7. }
  8. }

The security configuration should be:

  • YAML

    1. # config/packages/security.yaml
    2. security:
    3. # ...
    4. firewalls:
    5. main:
    6. anonymous: lazy
    7. json_login:
    8. check_path: login
    9. username_path: security.credentials.login
    10. password_path: security.credentials.password
  • XML

    1. <!-- config/packages/security.xml -->
    2. <?xml version="1.0" encoding="UTF-8" ?>
    3. <srv:container xmlns="http://symfony.com/schema/dic/security"
    4. xmlns:srv="http://symfony.com/schema/dic/services"
    5. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    6. xsi:schemaLocation="http://symfony.com/schema/dic/services
    7. https://symfony.com/schema/dic/services/services-1.0.xsd">
    8. <config>
    9. <firewall name="main">
    10. <anonymous lazy="true"/>
    11. <json-login check-path="login"
    12. username-path="security.credentials.login"
    13. password-path="security.credentials.password"/>
    14. </firewall>
    15. </config>
    16. </srv:container>
  • PHP

    1. // config/packages/security.php
    2. $container->loadFromExtension('security', [
    3. 'firewalls' => [
    4. 'main' => [
    5. 'anonymous' => 'lazy',
    6. 'json_login' => [
    7. 'check_path' => 'login',
    8. 'username_path' => 'security.credentials.login',
    9. 'password_path' => 'security.credentials.password',
    10. ],
    11. ],
    12. ],
    13. ]);

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