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