Full Stack Project: UCLAOutlet

Skills Learned

  • Full-Stack Development Process
  • Backend with Node.js
  • API and Server/Client Design
  • Cloud Data Storage w/ MongoDB Atlas
  • Secure User Auth w/ JWT
  • React Frontend Basics
  • Web Security Principles

Of all the Computer Science classes I took at UCLA, one stood far above the rest when it came to the practical skills I acquired during the quarter. CS35L, or the Software Construction Lab, was a class that taught me a myriad of technologies such as scripting with BASH and Python. The final project for this class was my favorite part, and This required me to build a full stack application with a server and a client. My final product was UCLAOutlet.

Outlet landing page.
This project was a combination of many robust features that are extremely common in many modern e-commerce websites. The core technology stack was Node.js for backend, React with HTTP for the frontend, and MongoDB Atlas for information storage. Sass was used for additonal frontend styling, and Google SMTP service was used for features such as email receipts and OTP authentication.

The core features of the app are:

Product Display

The first function we had to build was the ability for our website to store and display products. The structure to do this involved sending an HTTP GET request to our backend, which then grabs a list of all product information from our cloud MongoDB database (discussed more in the Persistent User Data section.) This would return a list of JSON items which represented a product menu.
Post request for products.
Using Postman to send a POST request to the path /products.
Now that we have this data in JSON form, the front end can parse it to display images and products in a list. The product thumbnails are all stored in a folder called uploads, and each image's name is the same as its ID. This means the frontend can construct a filepath since it knows the product ID's. The uploads folder is publically shared as /uploads, so the path /uploads/$id will grab the image required. You can see this in practice on the home page.
Product list.
List of all products with price, name, and image displayed.
Any one of these products can be clicked on to navigate to its product page, where much more information is visible about it. You are given details, a SKU, size options, and the ability to place an order and add to wishlist. These last two functionalities will be expanded upon much more in the Persistent User Data and Wishlist functionality sections.
Product page.
Product page

Product Search

As seen in the landing page for the website, we also implemented a search functionality for sorting through all products meaningfully. The search bar field takes a string value, then passes a request to the server with that value. The page then displays all SKUs and/or product names that match the requested value.
Product Search example.
An example of product search using the term "t-shirt".
SECURITY NOTE
To prevent end users from being able to pass any request to the database directly, I opted to not change the MongoDB request based on user input. Instead, upon receiving a search request, the server first gets all products from the database. It then uses the info passed in the search request to parse this list. This approach means the end user can never change the way the server interacts with the product database and thus protects it from attacks such as NoSQL injections.


Besides just the search bar, we allowed users to sort their products by categories. There are four categories present in the header of the website, with the clothing category also having subcategories for kids, men's, and women's.
Product Categories.
Clicking on any of these will send a similar request to the product search, but since these are not user-modifiable, we allow direct access to the database. All product json items have a category attribute, and all products in the clothing category also have a subcategory attribute. When the user accesses the category pages, the database searches for all products with a matching attribute and displays them.
Technology category.
Example of the Technology category page.

Advanced User Authentication

My favorite feature of this project was the implementation of advanced user authentication using JWTs (JSON Web Tokens). The user authentication process requires multiple steps, and this begins when you click login from any page on the website.

The login page.
Login page
This page will verify your login by checking your email and password against a database of users. Users are added to this database via the account creation link found below the login button, which will be described more in-depth in the Persistent User Data section. If you entire a valid email-password combination, you will receive a one-time password which you must verify to login.
Example of OTP being used to log on.
E-mail is sent by Google SMTP with your OTP
When an OTP is created, it has three attributes: value, time to live, and userID. The value is an encryped hash of the generated value that the user receives in their email. The time to live is the time at which the code expires, which is always exactly ten minutes after creation. UserID is the identifier used to check if the OTP code belongs to the user who is submitting it. If UserID and value are both correct when the server receives a request, it will return a JSON Web Token that is then embedded into the browser header.
Example of JSON web token in header.
After successful logins, all new HTTP requests are sent with the Authorization header.
This contains info to verify that the JWT is valid and a user is logged in.
SECURITY NOTE
As seen in the Authorization header above, all sensitive info stored in the MongoDB database is encrypted. This includes passwords, OTPs, and JWTs. Info is encrypted using the NodeJS library bcrypt and salted ten times. This means that even if access to the cloud database was gained, user data would not be exposed. JWT's also expire after two hours for extra security, requiring the user to log in again.
Now that we have JWT authentication, the client can make requests to protected routes on the server. These routes will return a 401 error code if the user attempts to access them when not logged in. These routes usually involve user-specific actions, such as ordering items or placing them in/viewing your wishlist.

Persistent User Data

As mentioned in the previous section, there are several way an end user can interact with the client to permanently change data in the server. There are several ways to do this, all of which require a user account. From the user login page seen previously, you can click a link that takes you to the account creation page.
The user creation screen.
The user creation screen.
When you fill out your desired user information here and click register, the client will send a POST request to the server. This first checks whether the email address already has an existing associated account. If it doesn't, then a user is created with the provided username and password.
With a user account created, there are two other ways a user can create persistent data.

Wishlist Functionality

More Info soon!







Order Receipts and 2FA

More Info soon!