Before I started my first serious frontend job, I reached out to a friend who was much more knowledgeable than me stating that I felt like such a phony due to the fact that I didn’t even understand how to make someone log in. He assured me that it would not be a problem, as that would be abstracted away. A few years later I came across this which gave a great deal of information that shows the scope of solutions: https://docs.google.com/spreadsheets/d/1tAX5ZJzluilhoYKjra-uHbMCZraaQkqIHl3RIQ8mVkM/edit#gid=0
Using the list above makes trying to wade through the decisions of what method of authentication to use, which using a JSON Web Token (JWT) https://datatracker.ietf.org/doc/html/rfc7519 and https://jwt.io/introduction . At Visa Checkout we used JWT’s that were only good for 10 minutes and would be refreshed on any interaction with the backend.
After diving down the rabbit hole, it seems that the biggest issue with JWT’s is that they are stateless. For instance the concept of logging out is not built into the standard. JWT’s are able to be used for sessions, but that is where a better solution is to have a session server for managing state. A session server is where all information regarding a user’s state is on the backend and as a cookie on the client side. The JWT is still a good option for using JWTs as API tokens for back-end/microservices services that do not have browser logout requirements.
That being said, is a 5-10 minute delay work for logouts? I mean who even logs out? I understand that for security auditors/certifications/compliance this is not acceptable, but this seems like an acceptable level of technical debt that can be paid for once an idea gets big enough. The solution for handling logouts, which introduces complexity and negates the benefits of JWT’s, is to create a blacklist/blocklist that is referenced against to ensure that the token is still valid. On the client side the risk of an XSS that exposes the refreshToken to keep the token valid forever is a security risk, but I am not sure how to mitigate this beyond keeping XSS vulnerabilities to a minimum.
Moving on I make the decision to use the stack of Vue3 on the frontend and Node/MYSQL on the backend. Researching this combination leads me to these tutorials that seem to be new enough to be relevant and provide enough depth to scale to a production ready web application.
Frontend: https://bezkoder.com/vue-3-authentication-jwt/
Backend: https://github.com/bezkoder/node-js-jwt-auth
Frontend
Getting the Vue application up and running was as simple as following the readme.md, which I take as a sign that the tutorial is of good quality. I clone it and install the dependencies.
npm install
Per the readme, I launch a development server that watches the files for updates and builds automatically.
npm run serve
That command failed due to an issue where the number of watchers was too low.
echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p
Next I open it in the browser and familiarize myself with the files and functionality. With the frontend up and running we are able to test the backend. Typically in my professional experience there would be a mock server for the development of the frontend that is independent of the real backend. In this case my plan is to get the backend deployed and then use hosts.etc file to DNS map the local address to the test server.
Backend
Diving in with JWT, Sequilizer, and MYSQL
https://github.com/bezkoder/node-js-jwt-auth
The options of getting a database going are either using a free service, a paas, or stand up my own. I opt for standing up my own, as it will need to do the same for the server. I have not done this before so I decided to follow the guide below.
sudo apt install mysql-server
sudo service mysql status
sudo mysql
ALTER USER ‘root’@’localhost’ IDENTIFIED WITH mysql_native_password BY ‘y0urReallyRea11y5trong9assw0rdThatDoesNotContainAny$Chars’;
FLUSH PRIVILEGES;
CREATE DATABASE jwt-auth-login;
SHOW DATABASES;
USE jwt_auth_login;
exit
To login again to the mysql server use the following command and the password from above.
mysql -u root -p
Cd into the application folder that we are working on.
To establish a connection to the database we can’t commit the password, so we need to store it as an environmental variable. TODO: .env files to manage the different environmental variables. The only one we are using right now is MYSQL_PASS. I add it the fast and dirty way by adding it to my .bashrc file and the running source ~/.bashrc.
Setup the database configurations in the codes configuration entails changing:
Password should be an environmental variable
The name of the database
Upon testing out the signup functionality an error is thrown from the SQL command that is an unhandled rejection SequelizeForeignKeyConstraintError: Cannot add or update a child row: a foreign key constraint fails (`jwt_auth_login`.`user_roles`, CONSTRAINT `user_roles_ibfk_1` FOREIGN KEY (`roleId`) REFERENCES `roles` (`id`) ON DELETE CASCADE ON UPDATE CASCADE)
This is the command that is causing the issue: INSERT INTO `user_roles` (`createdAt`,`updatedAt`,`roleId`,`userId`) VALUES (‘2021-06-23 23:35:42′,’2021-06-23 23:35:42’,1,6);
The issue is that inserting into the roleId references the roles table, which does not have any value. So we need to make sure that gets populated. On line 27 of server.js, the server has an initialize function commented out. If it is left uncommented then it will throw an error, but not crash. So that is a TODO: fix the initial boot of the backend app to get the schema to automatically be set up on the first launch for the user_roles table.
Next is getting things to deploy and run easier. In this case either merging the two projects, which I am not sure is the best strategy, or using Docker to make images out of them both to run on the web server.
https://nodejs.org/en/docs/guides/nodejs-docker-webapp/
Do what they say to get Docker setup up to the Building step, we will be doing that on the server after we push the new changes.
Deploying the Apps
https://medium.com/@mcsonique/making-ssh-and-scp-work-for-automated-deployments-via-in-gitlab-or-any-other-ci-cd-system-477f73cd63ff
https://filipsjanis.com/articles/scp-upload-files-gitlab-ci.html
For setting up automated deployments I will be using Gitlab to push the changes to the web server. I like to use two reference articles of varying quality to understand the overall process much better. To set up deployments to be automatic I add a private key from the web server to Gitlab as a variable in the settings. Then add a known_hosts file to the repository. For the script of the gitlab-ci.yml we need to tweak the ssh/scp command to use the $PRIVATE_KEY variable that was created above with the -i for identity file option and then reference the known_hosts in the repo with the -o option which allows us to pass more explicit arguments such as ‘UserKnownHostsFile=./known_hosts’.
For the initial setup on the server I will be doing the process manually to get the initial build, naming of the docker image, and the Nginx configurations.
docker build . -t
docker images
docker run -p 49160:8080 -d
docker ps
curl -i localhost:49160
NOTE: I have no idea what the proper way to do all this shit is so I am just making it work. I think that once the image is up and running that it will just need to have docker update run on it or some shit.
On the server, from the step above we need to now build and start the image. Make the Web Server services restart to serve the latest deployment. After the latest code has been pulled we are going to build and run the Docker image on the server. This is more for persistence, as the Docker image will run on the same port in the same way even if the server reboots.
Deploying the Frontend
This is much easier due to the way that Vue creates the static assets at /dist, but this is not server-side rendered code which would increase SEO relevance and performance. For apps that are in the early stage for startups that are in their infancy this approach works and would be considered technical debt.
/var/www/sitename.com
Run the Apps
With the Docker image built and running we need to configure Nginx to allow for traffic on our web server to flow to the Docker container.
touch /etc/nginx/sites-available/api.sitename.com
sudo nano /etc/nginx/sites-available/api.sitename.com
server {
listen 80;
server_name api.sitename.com;
location / {
proxy_pass http://localhost:42420;
proxy_set_header Host $host;
}
}
sudo ln -s /etc/nginx/sites-available/api.sitename.com /etc/nginx/sites-enabled/api.sitename.com
touch /etc/nginx/sites-available/sitename.com
sudo nano /etc/nginx/sites-available/sitename.com
server {
listen 80 default_server;
server_name sitename.com www.sitename.com;
root /var/www/sitename.com;
index index.html;
try_files $uri /index.html;
}
sudo nginx -t
sudo systemctl restart nginx
And this is where the scope of this article is done. There is a login that shows the user being logged in with the JWT. But JWT has its limitations, the biggest being when a user logs out and the JWT needs to be invalidated.
Future enhancements for this sort of work is for FreeHotSprings.com which needs user authentication to support payments and user created data. Adding a mock server for local development and/or DNS mapping locally. Paying off the technical debt of the logout and the cookie-based session server. Creating a mock server for local development and/or DNS mapping locally. To be honest I have never been responsible for a backend app, when changes and tweaks need to be made we will be in new territory. The Vue app would benefit from the SEO and performance boost by rendering the app into a server-side rendered app. The area where this demo is lacking is the user management, which would be out of scope. Basically this code is good enough to understand the concepts, not to run in production. The next demos I will be working on will be based on Nuxt.js and Quasar, which are both JavaScript frameworks that are based on Vue.
I feel relieved to have finally “logged in on the frontend” and use this style/methodology to build larger feature rich full stack applications!