Skip to content

MegaGramProject/Login-Register

Repository files navigation

Login-Register

This GitHub repository contains code that powers both the frontend & backend of logging in and registering users into Megagram. Megagram is a blend of some of the features of Instagram and Amazon, combined into a single website. It was created by me, Rishav Ray, as a personal project to acquire and then showcase my skills.

Because a major portion of the project depends on the cloud, in order to save costs on that, I only activate my frontend, backend and data/cloud-services for about 1-2 days max upon request(my contact-info is presented in the bottom-section), assuming that there isn't too many cloud-services also running at the time. Once they're activated, you should know that the frontend-urls begin with https://megagram.com/loginregister/, and the backend-urls with https://megagram.com/loginregister/api/.

If you so choose, you can use the username 'testuser' and the password 'IamTESTINGMegagram1!!' to login to Megagram as an actual user(although you won't be able to edit/delete this account of-course).

Welcome aboard!

Table of Contents

  1. Project Disclaimers
  2. Key Details of Frontend
  3. Key Details of Backend
  4. Key Details of Data
  5. Key Details of Cloud
  6. Running the Frontend and Backend
  7. Video of Frontend
  8. Video of Backend
  9. Finale(My Contact Info is Here)

Project Disclaimers

Because this project wasn't done as part of a job/business, focusing on every single detail is unneccessary. Here is a list of some of the main things in this project that have been done in this project for the sake of convenience and simplicity rather than for actual effectiveness.

  • The 'users' MySQL table is hosted on my local Macbook instead of the cloud. This doesn't mean that none of of this project's tables/collections/etc are in the cloud, however, because a lot of them are!

  • In one of the steps for registering users, users have to enter a code that was sent to their phone-number/email. For the sake of simplicity, I enabled the backend to not store the confirmation-code anywhere and instead return the confirmation-code after sending the text/email; therefore, the frontend has access to the confirmation-code. Furthermore, users can directly make requests to /createUser and /updateUser and set any contact-info(so long as it is in the format of a valid number/email and isn't taken by someone else) they would like without any confirmation-code-validation. On the bright side, nothing sensitive is accessible to the frontend besides the confirmation-code!

  • Instead of using the conventional JSON Web tokens for verifying that users have logged in, I used something somewhat different that achieves the same goal- I generate authTokens and refreshToken for users that are extremely challenging to crack with brute-force-attacks.

  • The authToken lasts for 45 min and the refreshToken lasts for a week. Both are stored in HTTP-only same-site-strict cookies and the authTokens and refreshTokens aren't stored in plaintext in the database; instead they are salted and hashed and the hashedAuthToken, hashedAuthTokenSalt, hashedRefreshToken, and hashedRefreshTokenSalt are stored in the database. This comes with the disadvantage, however, that a user can only be logged in at-most one browser at a time. However, users can indeed be logged into multiple separate accounts simultaneously.

  • In order to save money on the cloud, the website will only be run on demand. Furthermore, although auto-scaling is enabled in the Google Cloud MySQL spanner and in the GKE-Clusters, the max-nodes/etc is usually set to a very low number like 1 or 2(which obviously defeats the purpose of autoscaling for software in the real world). Furthermore, my AWS-Redis account is on a free tier(hence there are storage and request-limits much lower than what actual software companies have when they use Redis-cloud). The rate-limits on most of the backend endpoints are lower than normal for this exact reason(to reduce money spent on the cloud).

Key Details of Frontend

  • The frontend is powered by Python Django and is located in the login_register_frontend directory in this Github repo.

  • Inside login_register_frontend will be my_app, which contains the static folder and the templates folder neccessary for this project. Templates are written in HTML, and static includes JavaScript-files, CSS-files, an images folder, and a fonts folder.

  • There are four endpoints: /login, /signup, /ageCheck, and /confirmCode. All the endpoints start with https://megagram.com/loginregister/.

  • Each of the frontend pages work for both dark-mode and light-mode of the system, and have been successfully tested across the top 5 most popular browsers and across the different screen-sizes provided by Google-Chrome Dev-Tools.

  • The JavaScript utilizes JQuery to a significant extent due to its concise syntax and grand popularity in the world of web-frontends.

  • The frontend utilizes Google-Cloud for its reCaptcha and OAuth services. Specifically, it utilizes reCaptcha to verify that user-login-attempts aren't done by a robot. The OAuth services give users the ability to create accounts and login to Megagram via their Google account.

  • You can append ?language=Italiano or ?language=日本語 to the URL to trigger backend calls for translations via a language-translation API. For translations that are already cached in Redis, the backend directly returns that instead of spending money and API-usage-tokens on the RapidAPI DeepTranslate API Service.

  • Whenever the user tries to access an endpoint in the frontend that does not exist, they will come across a 'Page-Not-Found' page that is very well styled. It was created by an impeccable Graphics designer and the styling is very exquisite and complex, but I was able to alter the HTML/JS/CSS just a bit so that it fits the 'Megagram' brand.

Key Details of Backend

  • The backend is powered by Python Django and is located in the login_register_backend directory in this Github repo. It supports the methods 'GET', 'POST', 'PATCH', & 'DELETE'.

  • There are 14 endpoints, presented in the screenshot below. All the endpoints start with https://megagram.com/loginregister/api/.

  • The backend utilizes rate-limiting on each one of its endpoints(and the rates can vary per endpoint). For instance, some endpoints(specifically /updateUser, /removeUser, and /loginUser) have rate-limits much lower than the other endpoints for the sake of security- 5 per hour based on ip-address and user-id. This is because the /updateUser and /removeUser endpoints both require user-auth-tokens and keeping the limit low makes brute-force-attacks much less likely to work. Similarly, For /loginUser, users need to send in the correct password, and keeping the limit low significantly hinders brute-force-attacks.

  • For the usersTable, the backend uses a Django model(User) and serializer(UserSerializer). For accessing the userAuthTokens table it uses a google-cloud-mysql-spanner client. For accessing the 'Usernames and their Info' Redis hashset, it uses the Redis-connection that was configured in settings.py as the default(which is also used by Django for rate-limiting).

  • The /updateUser, and /removeUser endpoints both require user-auth-tokens to proceed. The /createUser endpoint provides the newly-created user the user-auth-tokens to proceed(i.e it creates the account and logs in the user on their browser). The /loginUser endpoint enables the user to log in by refreshing their userAuthToken and userRefreshToken and setting their HTTP-only, same-site-strict cookies; logins only work if the provided password by the user to the endpoint is correct.

  • For encrypting/decrypting user-data(specifically the 'contactInfo' for all users, and the 'accountBasedIn' and 'dateOfBirth' for users with isPrivate set to True), the backend uses Google-Cloud Key-Management-Service. Specifically, it uses a global key-ring called 'usersTableMySQL'; each row in the 'users' Table corresponds to an id(which is an integer representing the user-id), and the stringified id is used as the key-id for encrypting and decrypting the specific columns of each user-row.

  • There are 18 helper methods in the backend that are not directly accessible via the Rest API but are used by the methods that are accessible via the Rest API. For the sake of organization of views.py, first all the Rest API methods are listed and then all the helper-methods. Below is a screenshot of the names and parameters of all 18 helper-methods.

Key Details of Data

  • For this repository, there's one locally-hosted MySQL table called 'users'; one MySQL table hosted via Google Cloud MySQL-Spanner called 'userAuthTokens'; there's also an AWS-Redis cache for three purposes in this website. More on that later in this section.

  • The 'users' table has fields that can be described in the screenshot below. Users must be at-least 10 years of age and the accountBasedIn field must be either a US-State, a country, or 'N/A'/'Temporary'. The password must pass a strength test and is only stored in the database after salting and hashing it(i.e user passwords are not stored as plaintext).

  • The 'userAuthTokens' table has the following fields: hashedAuthToken(45-character-string), authTokenSalt(45-character-string), hashedRefreshToken(45-character-string), hashedRefreshTokenSalt, authTokenExpiry(timestamp), and refreshTokenExpiry(timestamp). Auth tokens(last 45 min) are used in cookies and backend-servers to prove authentication required for certain requests(i.e updating/deleting an account), and refresh-tokens(last 7 days and also used in cookies) are used to refresh auth-tokens that have expired. Once both the refresh token and auth token are expired, the user needs to re-login.

  • The three purposes of AWS-Redis for Login-Register are as follows: (1) For rate-limiting all the different endpoints of the backend. (2) For using hash-sets named something like 'Translations from English to Español' which stores key-value pairs where keys are English words and values are their Spanish translations. This is used by the frontend for translating the website; this hash-set aids in efficiency and reducing money/usage-tokens spent when making requests to the Rapid-API Deep-Translate API service. (3) For using the hash-set 'Usernames and their Info' which contains keys that are usernames of Megagram-users and values that are stringified dicts that contain their info(i.e all the other fields of the user in the 'users' table). This hash-set enables efficiency in fetching user-info.

Key Details of Cloud

  • For the Login-Register part of Megagram, the only cloud-service-provider used is GCP(Google Cloud Platform).

  • GCP is used by the frontend for reCaptcha and OAuth, and by the backend for encrypting/decrypting sensitive columns of the 'users' table with the help of Google-Cloud Key-Management-Service(and the keys are all rotated automatically every 70 days).

  • GCP is used for the GKE(Google-Kubernetes-Engine) Cluster, where the auto-scaling and loadbalancing of Kubernetes pods for both the frontend and backend takes place. You can find the actual Docker repositories in Docker Hub under rishavry/login-register-backend and rishavry/login-register-frontend.

  • Last, but not least, GCP is used for the Managed Instance Group(MIG) called 'megagram-server-group'. This MIG has auto-scaling enabled and each instance in this group has a startup-script(located in this repo as megagram-server-startup.sh) that uses nginx to handle traffic at port 443. The nginx routes requests with /loginregister/ and /loginregister-api/ to the load-balancers in the GKE Cluster mentioned in the earlier point. the This MIG has a load-balancer that maps to port 443 of each instance in the MIG, and the ip-address of this load-balancer is the one that is directly associated with https://megagram.com!

Running the Frontend and Backend

Because Code Reproducibility is Very Important!

If you would like to run a development-server/production-server of the frontend on your own machine/VM/container/etc, follow these steps:

0. Ensure that you have the environment-variables set for LOGIN_REGISTER_FRONTEND_DJANGO_SECRET_KEY(to any string) and ALLOWED_HOST(to 'localhost' perhaps).

1. (optional) In terminal, activate a python virtual environment.

2. Run the following terminal-command:

  git clone https://github.com/MegaGramProject/Login-Register.git

3. Run the following terminal-command:

    cd Login-Register/login_register_frontend

4. Run the following terminal-command:

pip install --no-cache-dir -r requirements.txt

5. Run the following terminal-command(feel free to replace 8000 with another available port) for the Development Server:

    python manage.py runserver 8000

For the Production Server, use this command instead(feel free to replace 8000 with another available port):

  gunicorn --workers 4 --bind 0.0.0.0:8000 login_register_frontend.wsgi:application

If you would like to run a development-server/production-server of the backend on your own machine/VM/container/etc, follow these steps:

0. Ensure that you have the environment-variables set for LOGIN_REGISTER_BACKEND_DJANGO_SECRET_KEY(to any string), ALLOWED_HOST(to 'localhost' perhaps), LOCAL_MYSQL_USER, LOCAL_MYSQL_PASSWORD, GOOGLE_APPLICATION_CREDENTIALS(to the path of your Google-Cloud-Services-Credentials-JSON-file), EMAIL_SENDER_AUTH_TOKEN, TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN, TWILIO_PHONE_NUMBER, AWS_REDIS_PASSWORD, DEEP_TRANSLATE_API_KEY, LOCAL_MYSQL_HOST_VIA_NGROK(to 'localhost' perhaps), & GOOGLE_RECAPTCHA_SECRET.

1. (optional) In terminal, activate a python virtual environment.

2. Run the following terminal-command:

  git clone https://github.com/MegaGramProject/Login-Register.git

3. Run the following terminal-command:

    cd Login-Register/login_register_backend

4. Run the following terminal-command:

pip install --no-cache-dir -r requirements.txt

5. Run the following terminal-command(feel free to replace 8001 with another available port) for the Development Server:

    python manage.py runserver 8001

For the Production Server, use this command instead(feel free to replace 8001 with another available port):

  gunicorn --workers 4 --bind 0.0.0.0:8001 login_register_backend.wsgi:application

If you prefer to use Docker/Kubernetes instead, here are the steps for that.

0. Run the following terminal-command for the backend production-server:

  docker pull rishavry/login-register-backend:v4

OR if you would like to use the frontend production-server:

  docker pull rishavry/login-register-frontend:v2

1. Run the following terminal-command to run the backend production-server at port 8000 in your host:

docker run \
  -v /path/to/google/credentials/file/in/host:/path/to/google/credentials/file/in/container \
  -e LOGIN_REGISTER_BACKEND_DJANGO_SECRET_KEY='any string' \
  -e LOCAL_MYSQL_USER='INSERT HERE' \
  -e LOCAL_MYSQL_PASSWORD='INSERT HERE' \
  -e ALLOWED_HOST=localhost \  # Or replace with the IP address of the host if it is not localhost
  -e GOOGLE_APPLICATION_CREDENTIALS='/path/to/google/credentials/file/in/container' \
  -e EMAIL_SENDER_AUTH_TOKEN='INSERT HERE' \
  -e TWILIO_ACCOUNT_SID='INSERT HERE' \
  -e TWILIO_AUTH_TOKEN='INSERT HERE' \
  -e TWILIO_PHONE_NUMBER='INSERT HERE' \
  -e AWS_REDIS_PASSWORD='INSERT HERE' \
  -e DEEP_TRANSLATE_API_KEY='INSERT HERE' \
  -e LOCAL_MYSQL_HOST_VIA_NGROK='localhost' \  # Feel free to change 'localhost' if that is not the host of your Local MySQL 'users' table
  -e GOOGLE_RECAPTCHA_SECRET='INSERT HERE' \
  -p 8000:8000 \  # Feel free to replace the first 8000 with any available host port
  rishavry/login-register-backend:v4

Or if you would like to run the frontend production-server at port 8001 in your host:

docker run \
  -e LOGIN_REGISTER_FRONTEND_DJANGO_SECRET_KEY='any string' \
  -e ALLOWED_HOST=localhost \ # Or replace with the ip-address of the host if it is not localhost
  -p 8001:8001 \ # Feel free to replace the first 8001 with any available host port
  rishavry/login-register-frontend:v2

Video of Frontend

Video of Backend

Finale

Thank you for sticking around till the end! Hope you found what you were looking for. Whether you did or did not, feel free to reach out to me using any of the following methods: