forked from methods/NandS_BookAPIV.2
-
Notifications
You must be signed in to change notification settings - Fork 1
Add /auth/login endpoint for jwt securitzation #13
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
codesungrape
merged 18 commits into
main
from
Add-/auth/register-endpoint-for-JWT-securitsation
Aug 18, 2025
Merged
Changes from all commits
Commits
Show all changes
18 commits
Select commit
Hold shift + click to select a range
846ea7f
Setup fixture for mock database with user inserted
codesungrape 99751e9
Refactor fixture using exisitng test_app mock database
codesungrape 4fe49bd
Install and initialize Flask-Bcrypt globally in app
codesungrape a5ae537
Use Flask-Bcrypt in mock_user_data fixture; standardize password key
codesungrape e863158
Add /auth/login test suite covering success, failures, and edge cases
codesungrape 6fea4f4
Implement /login route with JWT authentication using PyJWT
codesungrape f4f7116
Run formatting
codesungrape 692e051
Standardize password key in seed_users.py and related tests
codesungrape 1453502
Add minimal Flask app & route to isolate decorator
codesungrape 0e9ee60
Add test for auth header missing & malformed
codesungrape d983fe4
Add tests 4 jwt.decode errors with monkeypatch + unittest.mock patch
codesungrape e334ba9
Add tests for invalid user_id in token and missing user in DB
codesungrape 72284ce
Add test for require_jwt decorator happy path
codesungrape 64e88c3
Add require_jwt decorator: validate Bearer token and attach user
codesungrape e80878a
Silence Pylint errors- WIP
codesungrape 4a53143
Add fallback key for testing; fix Copilot typo.
codesungrape 3d7e3b3
Add fallback key for testing in login_user()
codesungrape 709a4bc
Drop default key; set SECRET_KEY via test_app fixture
codesungrape File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,9 @@ | ||
| """Module for Flask extensions.""" | ||
|
|
||
| from flask_bcrypt import Bcrypt | ||
| from flask_pymongo import PyMongo | ||
|
|
||
| # Createempty PyMongo extension object globally | ||
| # This way, we can import it in other files and avoid a code smell: tighly-coupled, cyclic error | ||
| mongo = PyMongo() | ||
| bcrypt = Bcrypt() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,68 @@ | ||
| # pylint: disable=too-many-return-statements | ||
| """ | ||
| This module provides decorators for Flask routes, including JWT authentication. | ||
| """ | ||
| import functools | ||
| import jwt | ||
| from flask import current_app, g, jsonify, request | ||
| from bson.objectid import ObjectId | ||
| from bson.errors import InvalidId | ||
| from app.extensions import mongo | ||
|
|
||
|
|
||
| def require_jwt(f): | ||
| """Protects routes by verifying JWT tokens in the | ||
| 'Authorization: Bearer <token>' header, decoding and validating the token, | ||
| and attaching the authenticated user to the request context. | ||
| """ | ||
|
|
||
| @functools.wraps(f) | ||
| def decorated_function(*args, **kwargs): | ||
| # 1. Get Authorization header | ||
| auth_header = request.headers.get("Authorization", "") | ||
| if not auth_header: | ||
| return jsonify({"error": "Authorization header missing"}), 401 | ||
|
|
||
| # 2. Expect exactly: "Bearer <token>" (case-insensitive) | ||
| parts = auth_header.split() | ||
| if len(parts) != 2 or parts[0].lower() != "bearer": | ||
| return jsonify({"error": "Malformed Authorization header"}), 401 | ||
|
|
||
| token = parts[1] | ||
|
|
||
| # 3. Decode & verify JWT | ||
| try: | ||
| payload = jwt.decode( | ||
| token, | ||
| current_app.config["SECRET_KEY"], | ||
| algorithms=["HS256"], | ||
| # options={"require": ["exp", "sub"]} # optional: force required claims | ||
| ) | ||
| except jwt.ExpiredSignatureError: | ||
| return jsonify({"error": "Token has expired"}), 401 | ||
| except jwt.InvalidTokenError: | ||
| return jsonify({"error": "Invalid token. Please log in again."}), 401 | ||
|
|
||
| # 4. Extract user id from payload | ||
| user_id = payload.get("sub") | ||
| if not user_id: | ||
| return jsonify({"error": "Token missing subject (sub) claim"}), 401 | ||
|
|
||
| # 5. Convert to ObjectId and fetch user | ||
| try: | ||
| oid = ObjectId(user_id) | ||
| except (InvalidId, TypeError): | ||
| return jsonify({"error": "Invalid user id in token"}), 401 | ||
|
|
||
| # Exclude sensitive fields such as password | ||
| user = mongo.db.users.find_one({"_id": oid}, {"password": 0}) | ||
| if not user: | ||
| return jsonify({"error": "User not found"}), 401 | ||
|
|
||
| # 6. Attach safe user object to request context | ||
| g.current_user = user | ||
|
|
||
| # 7. Call original route | ||
| return f(*args, **kwargs) | ||
|
|
||
| return decorated_function |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,4 +10,5 @@ black | |
| isort | ||
| flask_pymongo | ||
| flask-bcrypt | ||
| email-validator | ||
| email-validator | ||
| PyJWT | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] Import organization: datetime and jwt imports should be grouped together as they are both third-party libraries