ColinLefter / Accord

A real-time privacy-first social media platform leveraging feature-rich direct messaging text channels. Built as part of the course project for COSC 310 at UBC.
5 stars 1 forks source link

Authentication #143

Open ColinLefter opened 8 months ago

ColinLefter commented 8 months ago

Changes to the Application Development Workflow

Authentication has been integrated with an authentication provider called Clerk. They support a variety of useful features like multifactor authentication, password resets, updating account data and single sign-on with partners like Microsoft, GitHub, Google and Apple. This provider handles all session data management and allows us to have individual accounts. However, this also introduces additional complexity because the provider maintains their own database to manage our accounts. This means that we need to sync our MongoDB database with their database because we are introducing additional functionality tied to users (friend lists, server channels etc.). The way this is being handled is with a webhook, which is a backend API endpoint that listens to real-time updates from the provider.

The cases that are being handled are the following: creating a new account, editing an existing account and deleting an existing account. The problem with this approach is that our backend API endpoint that syncs our MongoDB database with the provider's data needs to be publicly exposed so that the provider can send us data. This means our application needs to be hosted on the internet to make this possible. As our application is not ready to be hosted, the ideal solution is to only host the required backend API endpoint. However, this is not directly possible because you can only host your local development server (localhost:3000), which means it is mandatory to host your entire web application. The solution to this is to utilize a proxy server that acts as an intermediary between the authentication provider and our MongoDB database. The proxy server intercepts requests to a singular authorized API endpoint (clerk-mongodb-sync) and pushes them towards our application that exists on localhost:3000. This means that when we run our application, we are also hosting a proxy server on the internet that only hosts a singular API endpoint and nothing else. Take a look at how this intermediary setup is coded in proxyServer.ts:

const app = express();
const PORT = 3001; // Port for the proxy server
const TARGET_PORT = 3000; // Port that our Next.js app is running on

We have a port for the proxy server that is at port 3001 and a port for the main application that is at 3000. We are sending data from a backend server hosted on port 3001 to our complete application hosted locally on port 3000. The way we are regulating what API endpoints are accessible by the proxy is through the following

// Configure the proxy middleware to intercept requests to "/api/webhooks/clerk-mongodb-sync"
// Clerk sends data to "http://<ngrok-url>/api/webhooks/clerk-mongodb-sync"
// It gets forwarded to my local "http://localhost:3000/api/webhooks/clerk-mongodb-sync" endpoint
// I.e. the only thing exposed on the internet is this singular proxy server that only hosts the Clerk webhook endpoint that we forward locally
app.use('/api/webhooks/clerk-mongodb-sync', createProxyMiddleware(options as any) as any);

app.listen(PORT, () => {
    console.log(`Proxy server listening on port ${PORT}.`);
});

Now take a look at what happens if I try to access an API endpoint manually by going to the hosted domain and entering the following: https://OUR SERVER.app/api/webhooks/clerk-mongodb-sync

Screenshot 2024-03-25 at 1 32 14 PM

The first request is a GET request as I have entered a URL in my browser. It is unauthorized because the backend API endpoint only accepts POST requests. This means even if someone tries to access a backend API endpoint through their browser, they won't be able to as they will receive ERROR 401. In future security implementations, we would also need to prevent POST requests to our API endpoint from anyone other than the provider, as currently, it is likely possible that someone else can attempt to send data to our backend API endpoint through a POST request as we are not checking who it is coming from by way of a token or some unique identifier from the provider. This is a serious security measure that needs to be accounted for, but is not something to worry about in our case right now as nobody knows our API endpoint exists, let alone the domain.

The Proxy Server

Our application runs on port 3000 on the local development server. The proxy server is an intermediary that runs on port 3001. I have configured this setup such that if the proxy server is running but the main application (accord) is not running on port 3000, you cannot access the public URL. Here is what that looks like

Attempting to access the proxy server on Google without having our web application running

Screenshot 2024-03-25 at 11 24 16 AM

What is happening here is a demonstration of the dependency of the proxy server on the resources of our web application. This is because the proxy depends on accessing certain resources from our local application at port 3000 that do not exist on the internet. This proxy server exists to prevent hosting our entire web application and only host one singular backend API endpoint (the webhook) to facilitate the syncronisation between our Accounts collection in our MongoDB database and that which the provider maintains.

This server at https://ant-inspired-bass.ngrok-free.app only exists as long as someone tunnels to the server. Multiple people can tunnel to the server at the same time, in which case everyone maintains their own instance of the server and connects to the one they just launched. If someone has already tunnelled to the server and you proceed to tunnel to it as well, you will be at a different port (+1 to the port of the latest server).

To tunnel to the server, you need to be authenticated through a token that is associated with your account on ngrok. Although I have not checked if anyone can tunnel to the server with their own token, I believe this is not possible at it should be protected. If that is the case, let me know and I will give you my token to use in your .env file or I will add you as a user of our server (if that is an option).

Running the Application

  1. npm run accord

When you first do npm run accord, you will likely need to install some dependencies such as concurrently and http-proxy-middleware. The reason you will need concurrently is because I have grouped 3 commands under npm run accord that run concurrently. These include ngrok http --domain=ant-inspired-bass.ngrok-free.app 3001, ts-node -P tsconfig.node.json proxyServer. ts and npm run dev.

Installing ngrok

  1. Create an account with ngrok
  2. Download the agent to your local machine
  3. Follow the instructions on that same page (like adding your token to your local machine)
notbaopham commented 8 months ago

My man is working absolutely overtime

ColinLefter commented 8 months ago

💀