{"id":29667343,"url":"https://github.com/thuinanutshell/comprehensive-auth","last_synced_at":"2026-04-04T22:32:01.511Z","repository":{"id":302225001,"uuid":"991916377","full_name":"thuinanutshell/comprehensive-auth","owner":"thuinanutshell","description":"✅ Comprehensive Authentication Feature for Web Apps \u0026 Locust Performance Testing ","archived":false,"fork":false,"pushed_at":"2025-07-21T12:42:14.000Z","size":216,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-01-03T17:16:27.328Z","etag":null,"topics":["auth0","authentication","flask","javascript","locust","pytest","python","react"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/thuinanutshell.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-05-28T10:43:28.000Z","updated_at":"2025-07-21T12:42:17.000Z","dependencies_parsed_at":"2025-07-01T08:35:08.415Z","dependency_job_id":null,"html_url":"https://github.com/thuinanutshell/comprehensive-auth","commit_stats":null,"previous_names":["thuinanutshell/comprehensive-auth"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/thuinanutshell/comprehensive-auth","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thuinanutshell%2Fcomprehensive-auth","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thuinanutshell%2Fcomprehensive-auth/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thuinanutshell%2Fcomprehensive-auth/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thuinanutshell%2Fcomprehensive-auth/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/thuinanutshell","download_url":"https://codeload.github.com/thuinanutshell/comprehensive-auth/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thuinanutshell%2Fcomprehensive-auth/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31416770,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-04T20:09:54.854Z","status":"ssl_error","status_checked_at":"2026-04-04T20:09:44.350Z","response_time":60,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["auth0","authentication","flask","javascript","locust","pytest","python","react"],"created_at":"2025-07-22T17:00:45.334Z","updated_at":"2026-04-04T22:32:01.489Z","avatar_url":"https://github.com/thuinanutshell.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# How to Build a Comprehensive Authentication Feature Using Different Methods (JWT, Session-based \u0026 OAuth)\n\n![Python](https://img.shields.io/badge/Python-3776AB?style=for-the-badge\u0026logo=python\u0026logoColor=white)\n![JavaScript](https://img.shields.io/badge/JavaScript-F7DF1E?style=for-the-badge\u0026logo=javascript\u0026logoColor=black)\n![React](https://img.shields.io/badge/React-61DAFB?style=for-the-badge\u0026logo=react\u0026logoColor=black)\n![Locust](https://img.shields.io/badge/Locust-1A1A1A?style=for-the-badge\u0026logo=locust\u0026logoColor=green)\n![Pytest](https://img.shields.io/badge/PYTEST-0A9EDC?style=for-the-badge\u0026logo=pytest\u0026logoColor=white)\n\n## Table of Contents\n\n- [Features](#features)\n- [Session-based Authentication](#session-based-authentication)\n- [JSON Web Token Authentication](#json-web-token-authentication)\n- [OAuth Authentication](#oauth-authentication)\n- [Performance Testing](#performance-testing)\n\nI realized how much I used to struggle in developing the authentication feature for my applications. Such a simple feature caused a lot of headaches, and I am not ashamed to admit that. Because authentication is so integral in many applications. I am curious about how I could create one similar to that. A comprehensive authentication feature will need to have the following functionalities.\n\n# Features\n\n\u003cimg width=\"888\" height=\"623\" alt=\"Screenshot 2025-07-17 at 8 06 22 PM\" src=\"https://github.com/user-attachments/assets/7a8da77b-5227-4387-a41a-dede3938e15f\" /\u003e\n\n\n\nhttps://github.com/user-attachments/assets/b850d977-bd7f-403a-af1d-bba46228f9ab\n\n\n\n### Sign Up\n\nStandard Registration\n\n- [x] Use first/last names, username, email, and password\n- [x] Confirm account via email\n\n### OAuth Registration\n\n- [x] Use a Google Account to sign up\n\n### Standard Login\n\n- [x] Use an identifier (either username or email) and password\n- [x] If the user forgot their password, there should be an option for them to reset their password by:\n  - [x] Sending a link to reset their password to their registered email\n  - [x] User clicks the link and is redirected to the password reset page\n  - [x] They enter their new password\n  - [x] Log in again\n\n### OAuth Login\n\n- [x] Use a Google Account sign-in\n\n### Profile\n\n- [x] Users should be able to change their username/email, or password\n- [x] Users should be able to delete their accounts\n\n## Session-based Authentication\n\n### Explanation\n\nFirst of all, HTTP is a stateless protocol, which means the client or the server does not keep track of the subsequent requests - each of them is treated as an independent one. Sessions allow the server to associate some information with a client, hence making it easier to retrieve the information for a request. The key difference compared to JWT is that session-based auth has storage both on the server and client side. On the client side, the cookie is stored in session/local storage, and on the server side, it is stored in memory.\n\nBecause sessions are stored on the server, it gives more control to the administrators to invalidate a session ID. Also, note that because we are using cookies, we need to pay attention to CORS to allow communication between two different domains.\n\n![image](https://github.com/user-attachments/assets/0efd95ac-9116-4019-a5b4-7982c959ef47)\n\n### Configuration \u0026 Setup\n\n0. Create a virtual environment inside the `session_auth` using `python3 -m venv .venv` and activate it `. .venv/bin/activate`\n1. Create a `.env` file with the following environment variables\n\n```\n# Flask Environment\nFLASK_ENV=development\n\n# Development variables\nDEV_DATABASE_URI=sqlite:///development.db\nDEV_SECRET_KEY={your-dev-secret-key}\n\n# Production variables\nPROD_DATABASE_URI=sqlite:///production.db\nPROD_SECRET_KEY={your-production-secret-key}\n```\n\n## JSON Web Token Authentication\n\n### Explanation\n\nJSON Web Tokens Components. The format of a JWT is xxxxx.yyyyy.zzzzz, where each part is separated by a dot and represents the header, payload, and signature, respectively.\nJSON Web Token is a standard that defines a way for parties to transmit data. The security part comes from the digital signature - a secret with the HMAC algorithm (Hash-based Message Authentication Code) or a public/private key pair. So basically, you can also encrypt the token such that the claims are hidden. JWT is used for the following purposes:\n\n- **Authorization**: User logged in → request includes JWT → user can access routes, services, and resources associated with that token. I’ve just learned that Single Sign On (SSO) uses JWT! This is when I sign in to my Google account on my laptop, and then I will be automatically logged in to Google’s services like YouTube or Gmail.\n- **Information Exchange**: Secure information transmission because it makes sure that the senders are who they say they are, thanks to the keys.\n\n![image](https://github.com/user-attachments/assets/775a257c-1850-4670-b5d0-94d17ddc3a28)\n\n### Configuration \u0026 Setup\n\n0. Create a virtual environment inside the `jwt_auth` using `python3 -m venv .venv` and activate it `. .venv/bin/activate`\n1. Create a `.env` file with the following environment variables\n\n```\n# Flask Environment\nFLASK_ENV=development\n\n# Development variables\nDEV_DATABASE_URI=sqlite:///development.db\nDEV_JWT_SECRET_KEY={your-dev-secret-key}\nDEV_REDIS_URL=redis://localhost:6379/1\n\n# Production variables\nPROD_DATABASE_URI=sqlite:///production.db\nPROD_JWT_SECRET_KEY={your-production-secret-key}\nPROD_REDIS_URL=redis://localhost:6379/0\n\n# Default Redis (fallback)\nREDIS_URL=redis://localhost:6379/0\n```\n\n2. In order to log out the user, we need to initialize a connection to a Redis server running on - you should set the REDIS_URL in your `.env` file:\n\n```python\ndef get_redis_client():\n    \"\"\"Get Redis client from current app configuration\"\"\"\n    from flask import current_app\n\n    redis_url = current_app.config.get(\"REDIS_URL\")\n    return redis.from_url(redis_url, decode_responses=True)\n\n\n@jwt_manager.token_in_blocklist_loader\ndef check_if_token_is_revoked(jwt_header, jwt_payload: dict):\n    \"\"\"Check if a JWT token is in the blocklist\"\"\"\n    jti = jwt_payload[\"jti\"]\n    redis_client = get_redis_client()\n    token_in_redis = redis_client.get(jti)\n    return token_in_redis is not None\n```\n\n- `host=\"localhost\"`: your local machine\n- `port=6379`: the default Redis port\n- `db=0`: the default Redis database index\n- `decode_responses=True`: ensures Redis returns strings (not bytes)\n  When users log out or a token needs to be invalidated before it expires, you can't remove or \"cancel\" a JWT since it's stateless. So, a common solution is to store a \"blocklist\" of token identifiers (like the jti claim) in Redis, so your app can check against this list when validating tokens.\n\n3. To run the app in development mode, follow the commands:\n\n```\nexport FLASK_ENV=development\nflask --app run init-db\nflask --app run migrate-db -m \"Initial migration\"\nflask --app run upgrade-db\nflask --app run show-db-info\n\npython3 run.py\n```\n\n4. To run the app in testing mode, follow the commands:\n\n```\nexport FLASK_ENV=testing\npython3 run.py\n```\n\n## OAuth Authentication\n\nOAuth2 is an authorization protocol designed to allow a website/app to access resources hosted by another web app on behalf of the user. Therefore, it involves granting access to a set of resources (like user data). OAuth also uses tokens (aka access tokens) to represent authorization\n\n\u003cimg width=\"1456\" height=\"476\" alt=\"image\" src=\"https://github.com/user-attachments/assets/18d3231e-0c7a-49a8-a283-50c41af0637e\" /\u003e\n\n\u003cimg width=\"1456\" height=\"762\" alt=\"image\" src=\"https://github.com/user-attachments/assets/12e95e1e-140a-48aa-a9ed-2f3340510c03\" /\u003e\n\nFirstly, the user sends an authorization request to the authorization server with ID and secret (also scopes and endpoint if any). Then, the authorization server authenticates the user and verifies the requested scopes and grants authorization to the client. Next, the resource owner and authorization server interact to send an access token to the user. Finally, the user uses the access token to request access to protected resources.\n\n\n# Performance Testing\n\nWhen I did some research online, there was often this statement saying that JWT is more scalable compared to session-based auth, while the latter offers more control. Then, one question arose: \"How can I empirically test if this statement is true or not?\" And then I learned about Locust - a load testing tool. To run the locust file in development mode:\n\n```\ncd tests\nlocust -f locustfile.py --host=http://127.0.0.1:5000\n```\n\nThe results from two different setups (number of users 100 vs. 500 with the same ramp-up rate) seemed a bit counterintuitive at first because my expectation was that JWT should be more efficient, both in time and space, compared to session-based. It turned out not to be the case from the experiments (check out the statistics table below). In terms of average size (bytes) and response, session-based authentication turned out to be better! There are a couple of reasons I think why this is the case:\n\n- Only one Flask server setup makes it easier for session-based auth because there is no need for token parsing or JWT decoding/validation overhead or querying from Redis.\n- The session data is stored in local memory, so it makes the lookup faster in a single-server environment.\n\nI did some research online and found some helpful Reddit and StackOverflow threads discussing the benefits of using **JWT in a distributed and microservice system** when you want to scale your software horizontally, because there is no need for a shared session store. The key takeaway I got from running this experiment is that, when someone says something is more scalable, we really need to understand what \"scalable\" exactly means, in which context and applicable to which architecture.\n\n### Session Authentication Load Testing (Number of Users = 100 - Peak Concurrency, Ramp up = 10, 2 Minutes)\n\n\u003cimg width=\"1134\" alt=\"Screenshot 2025-07-03 at 9 12 35 AM\" src=\"https://github.com/user-attachments/assets/c7e17f8f-3b46-44a3-a66f-d713962318fe\" /\u003e\n\n### JWT Authentication Load Testing (Number of Users = 100 - Peak Concurrency, Ramp up = 10, 2 Minutes)\n\n\u003cimg width=\"1134\" alt=\"Screenshot 2025-07-03 at 9 23 16 AM\" src=\"https://github.com/user-attachments/assets/e4ccaa19-b99e-4fa5-963b-9d7d0e283313\" /\u003e\n\n### JWT Authentication Load Testing (Number of Users = 500 - Peak Concurrency, Ramp up = 10, 2 Minutes)\n\n\u003cimg width=\"1141\" alt=\"Screenshot 2025-07-03 at 9 34 52 AM\" src=\"https://github.com/user-attachments/assets/fc898429-4622-4b98-96c2-656d8b72cbb9\" /\u003e\n\n### Session Authentication Load Testing (Number of Users = 500 - Peak Concurrency, Ramp up = 10, 2 Minutes)\n\n\u003cimg width=\"1136\" alt=\"Screenshot 2025-07-03 at 9 38 06 AM\" src=\"https://github.com/user-attachments/assets/8f470720-2e37-42df-bebd-ed069dc502e2\" /\u003e\n\n# References\n\n[1] https://jwt.io/introduction\n\n[2] https://auth0.com/docs/secure/tokens/json-web-tokens\n\n[3] https://roadmap.sh/guides/session-based-authentication\n\n[4] https://roadmap.sh/guides/session-authentication\n\n[5] https://auth0.com/intro-to-iam/what-is-oauth-2\n\n[6] https://www.reddit.com/r/node/comments/1aox0au/whats_the_ultimate_resource_for_jwt_vs_session/\n\n[7] https://stackoverflow.com/questions/43452896/authentication-jwt-usage-vs-session\n\n[8] http://cryto.net/~joepie91/blog/2016/06/13/stop-using-jwt-for-sessions/\n\n[9] https://github.com/authlib/demo-oauth-client/tree/master/flask-google-login\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthuinanutshell%2Fcomprehensive-auth","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthuinanutshell%2Fcomprehensive-auth","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthuinanutshell%2Fcomprehensive-auth/lists"}