Introduction
Drupal 8 provides REST API functionality out of the box for allowing access to the site endpoints through REST. This includes the REST version of the user login endpoint. If a user logins through this endpoint, Drupal will provide a default response. Sometimes we might need to alter the response to inject our values and this article aims to show you how to do it.
Drupal 8 provides the option to access the user login system via the REST API after enabling the REST module. This is an ideal solution if you intend to build a full or partial headless system accessible by a mobile app or a react based FE application.
The API endpoint is accessible by passing a request to the /user endpoint of the site with parameter ?_format=json. The user name and password need to be passed as JSON is in the body of the request. By default, it provides a response like the one below.
Now, there is nothing wrong with this and it's acceptable and compliments its normal HTML counterpart. However, we might need to add or remove the response content according to our needs in certain cases.
In our case, we were required to add the additional fields in the user profile to the default JSON response.
Example Scenario
Let us see the sample scenario we are going to play with:
- We add a new field to the User profile, say First Name or Age.
- We need this field to appear in the default response on successful login via REST along with the other response parameters.
- This should not come up in the regular HTML based login.
Default Technical Implementation
Before we start modifying the response, we need to understand how this has been implemented first. Like most other pages in Drupal, this login page starts as a route and we have to go to the Controller mentioned in the routing file. The route is described in the following routing file.
web/core/modules/user/user.routing.yml
Unusually two routes are having the same URL but this is understandable once we become familiar with Drupal’s logic. You see, the same /user route is used for the normal HTML based login and the REST-based login. The only differentiating factor is the presence or absence of ?_format=json parameter in the URL, which determines the controller we want.
user.login.http:
path: '/user/login'
defaults:
_controller: \Drupal\user\Controller\UserAuthenticationController::login
methods: [POST]
requirements:
_user_is_logged_in: 'FALSE'
_format: 'json'
user.login:
path: '/user/login'
defaults:
_form: '\Drupal\user\Form\UserLoginForm'
_title: 'Log in'
requirements:
_user_is_logged_in: 'FALSE'
options:
_maintenance_access: TRUE
Now the route we want to modify is the one using ?_format=json, which leads us to the following Controller:
\Drupal\user\Controller\UserAuthenticationController::login
This is the Controller, which supplies the default response when the user logins via REST. So to modify the response, we need to modify this Controller.
Extending the Controller
Since Drupal 8 uses OOP concepts, we need to extend this Controller to modify the methods. To implement the scenario above, follow the steps below:
- Create a custom module. (You can use the drupal console to generate the boilerplate code). I am naming it rest_login_addons.
- Within the module, create an src folder and another folder within it called Controller to look like this.
rest_login_addons/src/Controller.
- Create a Controller file called RestLoginAddonsController.php and place it within the folder.
- Now extend the UserAuthenticationController we had seen above in our Controller file, as shown below:
In the above code, most of the work is already done by the parent Controller. We are only modifying the login method. In the login method, we are calling the parent method and then modifying the response data.
The response data is serialized. So, to modify it we need to unserialize the data by calling the serializer service.
The serializer service is already included in the core UserAuthenticationController via dependency injection. (Go through the constructor of this method to get an idea of how dependency injection has been achieved.)
Note: For more information on Servies and Dependency Injection, have a look at this video.
We then proceed to load the current user from the uid obtained in the response data. Both the user and the original data is passed to a custom method fetchCustomFields to fetch the age field.
Finally, the data is returned and encoded again using the serializer service.
Altering the route using Event Subscribers
So now, we have the Controller ready. However, this is not sufficient. Accessing the route still results in Drupal loading the old Controller. So we need to alter the route and replace the existing controller by following the steps below:
- In the module we created earlier, create a services.yml file so that we have a rest_login_addons.services.yml file.
- We need to create a service and tag it as an event subscriber service, as shown below:
Note: If you would like to understand services and event subscribers in detail, please have a look at our blog on Drupal 8 services and Drupal 8 Event subscribers.
We have defined the service in the above file. A service is basically a class that does useful work. You mark a class as a service by adding it in the services.yml file and extending another service or a pre-existing interface.
With that done, we proceed to create the actual service class.
The service class basically extends the RouteSubscriberBase class, which we need to do if we need to alter a route. In this service, we specify the name of the Controller class we mentioned earlier and the method.
That’s it! We have all the ingredients required for injecting the custom data into the user login rest response. We need to clear the cache to ensure that Drupal recognizes all the changes to the routing system.
Now lets access /user/login?_format=json. As you can see from the image below, the age field value in the default response is visible.
Conclusion
So we have used services, routes, and controllers to alter the user login endpoint response. We can do much more with these three ingredients, including altering the response messages and status codes. Due to OOP paradigms used in Drupal 8 we can use lesser code and extend the existing code in a cleaner way.
Binny Thomas, PHP/Drupal Engineer - L2
Addicted to Quora, he is a geek but also gentle and nostalgic. His idea of an enjoyable holiday is a quiet day relaxing at home. And if you find him pulling his hair, he is probably deep-diving in his thoughts!
Leave us a comment