How to alter the user login response data
OK, I'd like to post one more answer approach, using the response event listener (with thanks to @Berdir). This is the one I'm using in the end as it avoids duplicating the login function logic. The previous answer may still be useful if you want to alter the actual login logic flow as well.

The event subscriber class

&lt;?php

namespace Drupal\example\Listener;

use Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Drupal\Component\Serialization\Xml;
use Drupal\Component\Serialization\Json;

/**
 * Class ExampleLoginListener.
 *
 * @package Drupal\example
 */
class ExampleLoginListener implements EventSubscriberInterface
{

  /**
   * @var path.current service
   */
  private $currentPath;
  /**
   * @var jwt.authentication.jwt service
   */
  private $jwtAuth;

  /**
   * Constructor with dependency injection
   */
  public function __construct($currentPath, $JwtAuth) {
    $this-&gt;currentPath = $currentPath;
    $this-&gt;jwtAuth = $JwtAuth;
  }

  /**
   * Add JWT access token to user login API response
   */
  public function onHttpLoginResponse(FilterResponseEvent $event) {
    // Halt if not user login request
    if ($this-&gt;currentPath-&gt;getPath() !== '/user/login') {
      return;
    }
    // Get response
    $response = $event-&gt;getResponse();
    // Ensure not error response
    if ($response-&gt;getStatusCode() !== 200) {
      return;
    }
    // Get request
    $request = $event-&gt;getRequest();
    // Just handle JSON format for now
    if ($request-&gt;query-&gt;get('_format') !== 'json') {
      return;
    }
    // Decode and add JWT token
    if ($content = $response-&gt;getContent()) {
      if ($decoded = Json::decode($content)) {
        // Add JWT access_token
        $access_token = $this-&gt;jwtAuth-&gt;generateToken();
        $decoded['access_token'] = $access_token;
        // Set new response JSON
        $response-&gt;setContent(Json::encode($decoded));
        $event-&gt;setResponse($response);
      }
    }
  }

  /**
   * The subscribed events.
   */
  public static function getSubscribedEvents() {
    $events = [];
    $events[KernelEvents::RESPONSE][] = ['onHttpLoginResponse'];
    return $events;
  }

}


and the example.services.yml entry

services:
  example.login_response_listener:
    class: '\Drupal\example\Listener\ExampleLoginListener'
    arguments: ['@path.current', '@jwt.authentication.jwt']
    tags:
      - { name: event_subscriber }


In this way, we can add the JWT access_token to the /user/login?_format=json API response.