-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement JWT authentication with access and refresh tokens (#8)
* Implement JWT authentication with access and refresh tokens * Refactor battle, building, and city tests to assert 401 status code for unauthorized access * Remove unused mock patches from battle, building, and city tests to simplify code * Update super-linter workflow to trigger on pull requests to the main branch
Showing
12 changed files
with
124 additions
and
56 deletions.
There are no files selected for viewing
This file contains 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 |
---|---|---|
|
@@ -3,7 +3,9 @@ name: Lint | |
|
||
on: | ||
push: null | ||
pull_request: null | ||
pull_request: | ||
branches: | ||
- main | ||
|
||
permissions: {} | ||
|
||
|
This file contains 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 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,57 @@ | ||
import os | ||
from datetime import datetime, timedelta, timezone | ||
from functools import wraps | ||
|
||
import jwt | ||
from flask import jsonify, request | ||
|
||
SECRET_KEY = os.getenv("SECRET_JWT_KEY", "SuperSecretKey") | ||
ACCESS_TOKEN_EXPIRY = timedelta(hours=1) | ||
REFRESH_TOKEN_EXPIRY = timedelta(days=30) | ||
|
||
|
||
def generate_access_token(player_id: int) -> str: | ||
"""Generate a JWT token for a user.""" | ||
payload = { | ||
"player_id": player_id, | ||
"exp": datetime.now(timezone.utc) + ACCESS_TOKEN_EXPIRY, # Expiration | ||
"iat": datetime.now(timezone.utc), # Issued at | ||
} | ||
return jwt.encode(payload, SECRET_KEY, algorithm="HS256") | ||
|
||
|
||
def generate_refresh_token(player_id: int) -> str: | ||
"""Generate a long-lived refresh token.""" | ||
payload = { | ||
"player_id": player_id, | ||
"exp": datetime.now(timezone.utc) + REFRESH_TOKEN_EXPIRY, | ||
"iat": datetime.now(timezone.utc), | ||
} | ||
return jwt.encode(payload, SECRET_KEY, algorithm="HS256") | ||
|
||
|
||
def verify_token(token: str) -> dict | None: | ||
"""Verify a JWT token and return the payload.""" | ||
try: | ||
return jwt.decode(token, SECRET_KEY, algorithms=["HS256"]) | ||
except jwt.ExpiredSignatureError: | ||
return None # Token expired | ||
except jwt.InvalidTokenError: | ||
return None # Invalid token | ||
|
||
|
||
def token_required(f): | ||
@wraps(f) | ||
def decorated(*args, **kwargs): | ||
token = request.headers.get("Authorization") | ||
if not token: | ||
return jsonify(message="Token is missing"), 401 | ||
|
||
decoded = verify_token(token) | ||
if not decoded: | ||
return jsonify(message="Token is invalid or expired"), 401 | ||
|
||
request.player_id = decoded["player_id"] # Attach user ID to the request | ||
return f(*args, **kwargs) | ||
|
||
return decorated |
This file contains 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,6 +1,7 @@ | ||
argon2-cffi>=23.1.0 | ||
flask>=3.0.3 | ||
flask-cors>=5.0.0 | ||
pyjwt>=2.10.0 | ||
pymysql>=1.1.1 | ||
pytest>=8.3.3 | ||
python-dotenv>=1.0.1 |
This file contains 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 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 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 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 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 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 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 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