Skip to content

Commit 5079f28

Browse files
VianpyroCopilot
andauthored
Change JWT encryption from HMAC to RSA with rotating keys (#37)
* Enhance Docker and JWT handling: update Dockerfile to install OpenSSL, modify jwt_helper to use RSA keys, and add entrypoint script for key generation * Implement JWT key rotation: add endpoint to rotate keys, update key management in jwt_helper, and modify .gitignore for key files * Refactor JWT key handling: remove hardcoded key loading, utilize dynamic key retrieval in token generation * Add timestamp logging for key creation in JWT rotation * Change route to be more RESTful * Remove JWT_SECRET_KEY from config and add script for cleaning up expired JWT keys * Refactor JWT tests to use public key for encoding/decoding and enhance sample token generation * Remove redundant sample_person_id fixture from test_refresh.py * Refactor key cleanup script to use dynamic expiry days from JWT configuration * Refactor JWT handling by moving related functions and classes to a new jwtoken module, removing deprecated admin routes and cleanup scripts. * Refactor JWT key handling by consolidating functions into common module and removing deprecated keys_id module * Refactor JWT test suite by removing obsolete test files and consolidating test fixtures into a new structure * Clarify entrypoint script comment to specify starting the API * Optimize payload generation Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Add key management functionality and refactor key paths - Introduced key rotation logic to ensure keys directory and active_kid.txt file exist. - Updated key paths in the rotate_keys function to use constants from the new config file. - Created a new config file for JWT key management constants. - Refactored keys_cleanup.py to use the new CREATED_AT_FILE constant. * Refactor Dockerfile to remove entrypoint script and directly run key rotation before starting the app; update keys_rotation.py for improved key management and error handling. * Add OpenSSL installation steps for Linux, macOS, and Windows in Pytest CI workflow * Fix variable name inconsistency for key directory in jwtoken and keys_rotation modules * Refactor Dockerfile to use entrypoint script for key rotation and app startup; add entrypoint.sh for improved process management. * Reorganize Dockerfile to copy entrypoint script before exposing Flask port; ensure proper permissions are set for execution. * Remove entrypoint script copy command from Dockerfile; streamline application setup. * Update ENTRYPOINT in Dockerfile to use entrypoint.sh for improved process management * Update ENTRYPOINT in Dockerfile to use entrypoint.sh for improved process management * Refactor app.py to use ACTIVE_KID_FILE constant for key existence check; improve code readability. * Remove OpenSSL installation step for macOS in Pytest CI workflow; clarify that it's pre-installed. * Refactor datetime usage in token generation and logging setup; improve consistency and readability. * Remove entrypoint.sh and update ENTRYPOINT in Dockerfile to directly run app.py; streamline container startup process. * Add curl to fix healthcheck and remove cached libraries to save up space --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 78d9142 commit 5079f28

30 files changed

+499
-181
lines changed

.devcontainer/Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ FROM alpine:latest
22

33
# Install common tools
44
RUN apk add --no-cache bash git \
5-
python3 py3-pip
5+
python3 py3-pip openssl
66

77
# Setup default user
88
ARG USERNAME=vscode

.github/workflows/pytest.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,16 @@ jobs:
3333
python-version: ${{ matrix.python-version }}
3434
cache: "pip"
3535

36+
- name: Install OpenSSL (Linux)
37+
if: runner.os == 'Linux'
38+
run: sudo apt-get update && sudo apt-get install -y libssl-dev
39+
40+
# MacOS does not require OpenSSL installation as it is pre-installed
41+
42+
- name: Install OpenSSL (Windows)
43+
if: runner.os == 'Windows'
44+
run: choco install openssl.light --no-progress
45+
3646
- name: Install dependencies
3747
run: |
3848
pip install -r requirements.txt

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
logs/
33
*.log
44

5-
# Ignore all pem files
5+
# Ignore JWT keys
6+
keys/
67
*.pem
78

89
# Upload folder

Dockerfile

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,26 @@ FROM python:3.13-slim
44
# Set a specific working directory in the container
55
WORKDIR /app
66

7-
# Install dependencies separately for better caching
7+
# Install dependencies
8+
RUN apt-get update && apt-get install -y --no-install-recommends curl openssl \
9+
&& apt-get clean && rm -rf /var/lib/apt/lists/*
10+
11+
# Install required Python packages
812
COPY requirements.txt .
913
RUN pip install --no-cache-dir -r requirements.txt
1014

1115
# Copy the rest of the application files
1216
COPY . .
1317

14-
# Expose the Flask port
15-
EXPOSE 5000
16-
1718
# Create a non-root user and set permissions for the /app directory
1819
RUN adduser --disabled-password --gecos '' apiuser && chown -R apiuser /app
1920
USER apiuser
2021

22+
# Expose the Flask port
23+
EXPOSE 5000
24+
2125
# Add health check for the container
2226
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
2327
CMD curl --fail http://localhost:5000/ || exit 1
2428

25-
# Command to run the app
26-
CMD ["python", "app.py"]
29+
ENTRYPOINT ["python3", "app.py"]

app.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
import argparse
2+
import os
23
import traceback
34

45
from flask import Flask, jsonify, request
56
from flask_cors import CORS
67
from werkzeug.exceptions import HTTPException
78

9+
from config.jwtoken import ACTIVE_KID_FILE
810
from config.logging import setup_logging
911
from config.ratelimit import limiter
1012
from config.settings import Config
1113
from routes import register_routes
1214
from utility.database import extract_error_message
15+
from utility.jwtoken.keys_rotation import rotate_keys
1316

1417
app = Flask(__name__)
1518
app.config.from_object(Config)
@@ -26,6 +29,10 @@
2629
if app.config["TESTING"]:
2730
limiter.enabled = False
2831

32+
# Ensure the keys directory and active_kid.txt file exist
33+
if not os.path.exists(ACTIVE_KID_FILE):
34+
rotate_keys()
35+
2936

3037
@app.route("/")
3138
def home():

config/jwtoken.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
from pathlib import Path
2+
3+
KEYS_DIR = Path("keys")
4+
5+
ACTIVE_KID_FILE = KEYS_DIR / "active_kid.txt"
6+
CREATED_AT_FILE = "created_at.txt"
7+
PRIVATE_KEY_FILE = "private.pem"
8+
PUBLIC_KEY_FILE = "public.pem"

config/logging.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,12 @@ def setup_logging():
1212
logger.setLevel(logging.DEBUG)
1313

1414
# Create a file handler that rotates logs daily
15-
timestamp = datetime.datetime.now().strftime("%Y-%m-%d")
15+
current_time = datetime.datetime.now().strftime("%Y-%m-%d")
1616

1717
# Create a directory for logs if it doesn't exist
1818
if not os.path.exists(LOGS_DIRECTORY):
1919
os.makedirs(LOGS_DIRECTORY)
20-
log_filename = f"{LOGS_DIRECTORY}/{timestamp}.log"
20+
log_filename = f"{LOGS_DIRECTORY}/{current_time}.log"
2121

2222
# Set up timed rotating file handler
2323
file_handler = TimedRotatingFileHandler(

config/settings.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ class Config:
2020
MYSQL_CURSORCLASS = "DictCursor"
2121

2222
# JWT configuration
23-
JWT_SECRET_KEY = os.getenv("JWT_SECRET_KEY")
2423
JWT_ACCESS_TOKEN_EXPIRY = timedelta(hours=1)
2524
JWT_REFRESH_TOKEN_EXPIRY = timedelta(days=30)
2625

jwt_helper.py

Lines changed: 0 additions & 78 deletions
This file was deleted.

0 commit comments

Comments
 (0)