Diving deep into Strapi to build the EOS feature request system in GSOC’20
Hola a todos! Give me company as I explain to you the internals of Strapi and how I am using various mechanisms to implement custom and secure solutions for our feature request system. Grab your drink, sit back and relax as I walk you through the details.
What did I do this week?
Strapi is the magic behind our awesome applications at EOS. It is the leading open-source headless CMS. It’s 100% Javascript, fully customizable and developer-first.
This week I completed user authentication and authorization to our system. This includes register, login, logout, forgot password and reset password functionality. Lets have a look at how we can do this with Strapi and React.
Strapi exposes a plugin system where each plugin is like a small independent sub-application. It has its own business logic with dedicated models, controllers, services, middlewares or hooks. It can also have its own UI integrated in the admin panel.
I used the Roles and Permissions plugin to complete my tasks of the week. This plugin provides a way to protect our API with a full authentication process based on JWT
. Each time an API request is sent, the server checks if an Authorization
header is present and verifies if the user making the request has access to the resource. To do so, our JWT
contains our user ID and we are able to match the group our user is in and at the end to know if the group allows access to the route.
The API response to the authentication route contains the user’s JWT
in the jwt
key. We will have to store this JWT
in our application, it's important because you will have to use it the next requests.
We decided to store the JWT
in a httpOnly
cookie instead of the easy solution of storing it in localStorage
which makes our application prone to XSS attacks. This means that we need to customize Strapi to set the jwt
in the cookie instead of returning it in the API response. This will ensure that our front end application does not have to store sensitive data for future authenticated requests.
Strapi internally uses Koa.js
, a new web framework designed by the team behind Express, which aims to be a smaller, more expressive, and more robust foundation for web applications and APIs. I quickly learnt basic syntax as I am already familiar with Node.js
and Express
.
Now every plugin in Strapi follows a fixed architecture and all installed plugins can be found in the node_modules
folder with the naming convention of strapi-plugin-*
.
To customize those installed plugins we can work in the /extensions
directory. It contains all the plugins' customizable files. I customized the user-permissions
plugin by modifying the Auth.js
controller file to issue a JWT
to the user and set it in a httpOnly
cookie after all server side validations are done. I also changed the response by the controller to return a “status” key with “Authenticated” value instead of the token. This confirms that the user has been authenticated.
Wait! Strapi reads the token from the Authorization
header in the API request so this won’t work for now. I further added a permissions.js
file to the policies
folder where I attach the token to the header after reading it from the cookie which was set earlier.
Policies in Strapi are functions which have the ability to execute specific logic on each request before it reaches the controller’s action. They are mostly used for securing business logic easily. Each route of the project can be associated to an array of policies.
Now to implement the logout functionality I set up a custom /logout
route with a controller to set the token to null in the cookie. This destroys the user session safely in seconds.
Patience folks! Our user can forget his/her password too. I used sendgrid
service to send email to users with a link which contains a URL param code
which is required to reset user password.
I implemented the client side functionality including the requests to the API in a React custom hook named useAuth
to finally complete the agile task.
What is coming up next?
This week I will implement the functionality to create a new request and read and display the existing feature requests along with all attributes like votes and comments in an engaging UI.
I will also try to setup providers which are currently broken due to an issue in Strapi v3.0.1 here.
Did I get stuck anywhere?
We all are bound to fall. Any great thing does not start perfect. At first I could not figure out how to manage the user state properly and tried using a reducer
but the state reset itself to the initial one on page refresh. Finally I implemented the idea to read the state from localStorage
without saving any sensitive information.
Strapi uses the sendmail
service by default to send mails to the users but it was not working and was always returning a 400
status code. I added sendgrid
service to overcome this problem.
All this hustle and bustle marked the end of my week with a mesmerizing annular solar eclipse on June 21, 2020. Stay tuned as we come up with some amazing features in the weeks ahead.