A deep dive into my project, CORS, and Authorization
Louiza Mak
Posted on September 20, 2024
Just this week, I finally deployed (for the first time) my most recent project. I call it LifeSense, the humble beginnings of an app that helps you monitor and analyze data from your CGM (Continuous Glucose Monitoring) system. It utilizes OPENAI API to interpret provided data sets and returns a summary of the results and any suggested medical action that should be taken. Of course, this does not substitute a checkup given by a medical professional, but it does offer free and continuous insight as well as trend analysis from people's homes.
Although parts of the project did provide a challenge, such as working with MUI more deeply to achieve a smooth and dynamic line graph, the biggest challenge I faced was setting up a working sessionStorage. It would allow users to stay logged in even when the page is refreshed. This integral part of my post-deployment took longer than any hurdle so far.
CORS
My initial issue was first understanding CORS (Cross-Origin Resource Sharing). I've had console errors in previous projects that were easily fixed, but I never had to communicate between my own backend and frontend before. Because of this project, I feel that I'm a step closer to truly understanding the intricacies of CORS.
I first started with the basics. What is CORS? Why was it made? Why is it the bane of everyone's existence? Where it all began was the 'same-origin policy' introduced in 1996. Understandably, it's a security function to prevent scripts in other domains from accessing sensitive data. However, in cases like mine, where we need to access an API, CORS steps in. Introduced in 2006, CORS is an extension or workaround to the single-origin policy.
CORS is a mechanism that allows resource sharing to other domains while blocking any requests made from rogue JavaScript AJAX requests. I learned that it's triggered whenever you are making HTTP requests to anything other than your exact domain. That includes a different domain, sub-domain, protocol (HTTP/HTTPS), and port. This is the exact scenario I encountered when I decided to host my server on an EC2 container and my client on a S3 bucket. My two solutions were to either allow all origins access or to whitelist a specific domain only.
For security and traffic reasons, it was better for my project to only allow my own client access right now but that may change in the future as I continue to develop LifeSense. But since my console was free from errors and the frontend and backend were talking to each other as they should, I moved on to my next hurdle - authentication.
Authentication
A component I implemented for the first time in this project was a login feature that depended on sessionStorage to persist the user session while browsing the app. The sign-up and login functions were simple to accomplish using Formik and Yup for form validation and error handling. Keeping the user logged through a refresh also worked in the development stage. But when I deployed the app, that aspect broke and had to be completely reworked. Here's my train of thought:
1. What issue am I experiencing?
While browsing the components through the nav bar, my user remains logged in and can access pages that pull data based on the user's ID. Logout and login worked as intended. The issue arose when I was already logged in and then refreshed the browser. The user gets redirected to the home page and tabs that are visible logged in are removed. However, I already had a check_session
Resource in my Flask backend server that checked client-side sessions on refresh. It's supposed to grab the user_id
from the request so there must be a disconnect somewhere.
2. Is the user_id
getting passed in the request?
I double-checked and found that the user_id
was NOT getting passed in the request. Instead, the server received a NoneType, which is why the check_session
failed. This also made sense because now that the front and backend were deployed and no longer hosted on the same machine, I needed to manually store the user_id
where it could be accessed.
I decided to use sessionStorage and assigned it within the promise.then()
after my frontend POST request.
3. How do I pass my sessionStorage data as part of my request?
This is when I learned more about headers
in a request. Previously, I had only used 'Content-Type': 'application/json'
which indicates that the format of the request body is in JSON. This time, I focused on the Authentication header which gives the user permission to access a particular resource. As a security measure, users typically aren't permitted to access data from every URL. In most uses of the Authentication header, you would provide an access_token string for the value. However because my project's scope was limited and I only needed to pass a single integer user_id
, I kept it simple for this deployment.
I checked that the header data and Authentication was now included in the request. The backend Resource could now grab it with request.headers.get('Authorization')
and compare it with the database to ensure the user is registered, logged in, and can continue browsing after browser refresh. Huzzah!
Conclusion
As this was my first time deploying a project, CORS and the communication between the frontend and backend were my biggest hurdles. However, after breaking the problem down I was able to research each moving part of CORS and the request-response cycle to come up with the overall solution to these issues. It was a learning experience and through this, I am now more confident in my understanding of CORS and other aspects of the fetch request. I hope this provided insight and help to any other programmer facing the same issues or having trouble debugging and breaking down problems into smaller digestible parts.
Posted on September 20, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.