4 July, 2019
In the first part of a Developers Guide to API Platform, I focused on building an API. I presented some of the basic functionalities of API and also, I showed you how to build the simple application called “Flashcards” which helps to learn new words. In the second part of the article, I’ll present the further steps of this application development.
In the previous part of the article, I built the app which allows managing subjects, lessons and flashcards. However, it still needs to be improved to gain its final shape. That’s why, it’s necessary to create the following elements:
- users’ authentication,
- adding your own logic to requests,
- assigning the logged-in user to a subject,
- limiting users access to their own resources only,
- extending the documentation.
It’s been a while since the premiere of the first part of the article. Quite a lot of the elements of the application have been improved and added to the API Platform. That’s why the first step will be the update of all the libraries to their newest versions.
Composer will inform you that the bundle “symfony/lts” was abandoned. You can remove it safely using the command below:
Now, you can move to the creation of the aforementioned elements, starting with users’ authentication.
API Platform allows the implementation of a simple authentication with the use of JWT. If you want to use it, you need to install lexik/jwt-authentication-bundle library:
Once you install the library, you should generate the keys which will be used by the library. You can do it using the command below:
Now, you need to change the password for the private key in the .env file. Please note that it should be the same as the one provided whilst generating the key.
The next step is to configure the Symfony security component the way it uses the library presented above. Users who use the application should be saved and stored in the database. To make it happen, you need to use the entity user provider. You can find the guide about the configuration of entity user provider on the official Symfony website.
It can look like in the example below:
Then, you need to configure Symfony security to use the functionalities provided by JWT library. You have to configure the sign-in form and you should add the main firewall.
It’s also necessary to create a new routing for login. It should be compliant with the security configuration settings.
Theoretically, everything should work properly now. However, it appears that it’s necessary to encrypt the password during the creation of a new user to make sure that all of the elements work fine. At this point, you can use API Platform events.
You can use the events triggered by Kernel (such as kernel.request, kernel.view or kernel.response) to extend the basic functionalities of endpoints. API Platform uses these events in its own processes as well. If you need to execute your own operation/code before or after any other event, you need to set a proper priority for your Event Listener. API Platform provides a few const elements which define what priority should be set to the listener for the action to be performed at the time you expect it.
You can choose from the following list of the const elements: PRE_READ, POST_READ, PRE_DESERIALIZE, POST_DESERIALIZE, PRE_VALIDATE, POST_VALIDATE, PRE_WRITE, POST_WRITE, PRE_SERIALIZE, POST_SERIALIZE, PRE_RESPOND, POST_RESPOND.
The first action you should execute is to set encoder you’ll use while encrypting a password. If you are interested in the newest recommendations, I’ll advise you to visit the Symfony documentation website. However, I have used argon2i encoder. If doing so, you should remember to add a proper encoder configuration to security.yaml (as per the example below).
You should also correct a configuration of the user model itself. The field for encrypted password should allow you to type up to 255 characters. Additionally, a user should have been assigned with a default group.
In the presented case, it’s necessary to encrypt a new user’s password before adding them to the database. Password encryption should be also performed anytime a user decides to change their password. To do it, it’s necessary to create your own event subscriber which listens for the kernel.view event with “PRE_WRITE” priority.
getControllerResult() method gives back the processed object. You are only looking for a case when a request is connected to the User object with the plainPassword property set. If you meet both of these conditions – you need to encrypt a password, using a previously created encoder.
Once you configured security for your application, you need to resolve an issue connected to the fact that all the endpoints are still available for the users who are not logged in. There are at least two methods you can use to solve this problem.
The first method is to add the proper registry to section access_control in the security.yaml file to secure endpoints from unauthorized access. You can read more about this on the documentation website.
The second mechanism you can use is the addition of access_control attribute to the resource configuration.
Access to the Resource Subject should be available only for the users with the role ROLE_USER.
You can also set some more complicated rules for granting the user with access to the resources. If you want to allow the user to have access to the resources connected to their profile, you can use the following command:
Now, the method PUT on Resource Subject can be only performed by the user who created this resource.
The issue of access to the data is almost solved. There is however a remaining problem of the data displayed on the listing which appears regardless of the logged-in user. API Platform provides the mechanism called Extension. It allows you modifying the queries which are sent to the database. Thanks to this functionality, you can limit the collection’s results the way it contains the resources which are connected with the logged-in user.
It’s as simple as implementing the interface QueryCollectionExtensionInterface
Every time you decide to add the aforementioned code whilst getting the subjects for the database query – in the code there will be a condition added. It will limit the search results. This class should be extended with protection for the lessons and flashcards classes. Besides that, it’s possible to limit the database query for the single elements. To do it, you should implement the interface QueryItemExtensionInterface.
Extending the documentation
Unfortunately, API Platform won’t create the API documentation for the endpoint which allows signing-in. However, there is a possibility to add your own data to the documentation. You need to decorate the service api_platform.swagger.normalizer.documentation. Inside the decorator, you have access to the array which contains all the elements of the documentation. You can modify the array freely. Below, there’s a presentation of how to add the information about the signing-in endpoint to the array.
Thanks to that, In the documentation you have the information about how to log in to the app.
The application is now fully functional. You can create new lessons, subjects or flashcards and users have access to their own data only. Backend work is done and you can create an application which will use the API you built. Again, the API Platform comes with help. The creators of this library prepared the generators of the clients. You can read more about it in the documentation.
Besides the functionalities I’ve presented, API Platform offers a variety of different solutions, such as CQRS, DTO, integration with ElasticSearch, MongoDB, GraphQL as well as the administration panel generated on the base of JSON-LD. Using this library, you can significantly shorten the time you spend on the repeated tasks.
I hope that this extended article gave you a glimpse of what API Platform is and how to use it. If you want to deepen your knowledge, once again – I recommend visiting the API Platform documentation website. And if you want to take a look into the application I’ve created for this article purposes, you can visit Gitlab.