Skip to content

feat: add authentication system and rate limiting for enhanced security#584

Closed
shbhmexe wants to merge 1 commit intoAOSSIE-Org:mainfrom
shbhmexe:main
Closed

feat: add authentication system and rate limiting for enhanced security#584
shbhmexe wants to merge 1 commit intoAOSSIE-Org:mainfrom
shbhmexe:main

Conversation

@shbhmexe
Copy link

@shbhmexe shbhmexe commented Oct 16, 2025

🔒 Security Enhancement: Authentication & Rate Limiting Implementation

🎯 Issue Addressed

Fixes critical security vulnerabilities identified in code review:

  • No authentication system - Application was completely open to anyone
  • Wide-open CORS policy - Accepted requests from ANY origin (allow_origins=["*"])
  • No rate limiting - Vulnerable to DDoS and abuse attacks
  • Missing security headers - No protection against common web attacks
  • Single-threaded processing - Performance bottleneck with max_workers=1

🚀 What Was Implemented

1. Authentication System

  • API Key Authentication: Secure communication between Tauri app and backend

    • Header-based authentication (X-API-Key)
    • Configurable via environment variables
    • Supports both development and production keys
  • JWT Token Authentication: Future-ready for web interfaces

    • Token generation endpoint (POST /auth/token)
    • 30-minute token expiration (configurable)
    • Secure token verification with python-jose
  • Flexible Auth Middleware:

    • get_current_user() - Required authentication
    • get_current_user_optional() - Optional authentication
    • Supports both API key and JWT token methods

2. Rate Limiting 🛡️

  • Implemented slowapi for request throttling
  • Default limits: 60 requests/minute per IP
  • Configurable via environment variables
  • Rate limit headers in responses:
    • X-RateLimit-Limit
    • X-RateLimit-Remaining
    • X-RateLimit-Reset
  • Health endpoint exempt from rate limiting

3. CORS Security 🔒

  • Before: allow_origins=["*"] (CRITICAL vulnerability)
  • After: Restricted to configured origins from .env
  • Default: http://localhost:1420, tauri://localhost
  • Proper CORS headers:
    • Specific allowed methods
    • Explicit allowed headers
    • Credential support maintained

4. Security Headers 🛡️

New middleware adds essential security headers to all responses:

  • X-Content-Type-Options: nosniff - Prevents MIME sniffing
  • X-Frame-Options: DENY - Prevents clickjacking
  • X-XSS-Protection: 1; mode=block - XSS protection
  • Strict-Transport-Security - Enforces HTTPS (production)

5. Trusted Host Middleware

  • Validates Host header
  • Prevents host header injection attacks
  • Allowed hosts: localhost, 127.0.0.1, 0.0.0.0

6. Performance Optimization

  • ProcessPoolExecutor now uses optimal worker count
  • Before: max_workers=1 (single-threaded)
  • After: max(1, cpu_count - 1) (multi-threaded)
  • Better parallel processing for image operations

7. Environment Configuration 🔧

Created comprehensive .env.example template:

  • Security keys (SECRET_KEY, API_KEY)
  • CORS origins configuration
  • Rate limit customization
  • Database path configuration
  • All sensitive data externalized

8. New API Endpoints 🔌

  • POST /auth/token - Generate JWT access token
  • GET /auth/status - Check authentication status
  • Enhanced GET /health - Now returns version info

📁 Files Modified

Backend

  • ✏️ backend/main.py - Added auth, rate limiting, security middleware
  • ✏️ backend/requirements.txt - Added python-jose, slowapi
  • ✏️ backend/app/config/settings.py - Added security configuration
  • ✏️ sync-microservice/main.py - Fixed CORS configuration

New Files Created

  • backend/app/middleware/auth.py - Authentication logic
  • backend/app/middleware/__init__.py - Middleware package
  • backend/app/routes/auth.py - Authentication endpoints
  • backend/.env.example - Environment template
  • backend/SECURITY.md - Comprehensive security guide
  • backend/MIGRATION_GUIDE.md - Step-by-step migration instructions
  • backend/tests/test_auth.py - Authentication test suite

🔧 Technical Details

Dependencies Added

python-jose[cryptography]==3.3.0  # JWT token handling
slowapi==0.1.9                     # Rate limiting

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

* **New Features**
  * Added JWT token-based and API key authentication system with secure token generation endpoints.
  * Added rate limiting protection for API endpoints.
  * Added authentication status checking capabilities.

* **Security**
  * Enhanced CORS restrictions with specific allowed methods and headers.
  * Implemented security headers middleware.
  * Introduced environment-based configuration for services and authentication.

* **Tests**
  * Added comprehensive authentication tests including token generation, validation, and security header verification.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

- Implement JWT token and API key authentication
- Add rate limiting middleware (60 req/min default)
- Fix CORS vulnerability with restricted origins
- Add security headers (X-Frame-Options, CSP, HSTS)
- Optimize ProcessPoolExecutor worker count
- Create comprehensive security documentation
- Add authentication tests

BREAKING CHANGE: CORS now requires configured origins in .env
@github-actions
Copy link
Contributor

⚠️ No issue was linked in the PR description.
Please make sure to link an issue (e.g., 'Fixes #issue_number')

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 16, 2025

Walkthrough

The pull request introduces comprehensive authentication infrastructure with JWT and API key support, implements rate limiting and security headers, transitions configuration to environment-based loading, and refines CORS policies across both the main backend and sync-microservice components.

Changes

Cohort / File(s) Summary
Configuration & Dependencies
backend/app/config/settings.py, backend/requirements.txt
Environment-based configuration for SYNC_MICROSERVICE_URL and DATABASE_PATH with defaults; added python-jose[cryptography] and slowapi dependencies for JWT and rate limiting
Authentication Middleware
backend/app/middleware/__init__.py, backend/app/middleware/auth.py
New authentication module with JWT token creation/verification and API key validation; HTTPBearer security scheme; dual-mode auth supporting API keys and JWT tokens with optional and required resolver functions
Authentication Routes
backend/app/routes/auth.py
New auth router with token generation (POST /token) and status checking (GET /status); models for TokenRequest, TokenResponse, and AuthStatusResponse; API key validation with 403 error handling
Main Application Integration
backend/main.py
Rate limiting via Limiter, security headers middleware, refined CORS (restricted to ALLOWED_ORIGINS), trusted host middleware, health endpoint with rate-limit exemption, auth router inclusion, increased worker pool
Authentication Tests
backend/tests/test_auth.py
Test coverage for health endpoint, auth status checks, token generation/verification, invalid token handling, security headers presence, and rate-limiting smoke test
Sync-Microservice CORS
sync-microservice/main.py
Environment-driven ALLOWED_ORIGINS configuration; tightened CORS methods to GET/POST/PUT/DELETE/OPTIONS and headers to Content-Type/Authorization/X-API-Key/Accept

Sequence Diagram(s)

sequenceDiagram
    actor Client
    participant Backend
    participant Auth Middleware
    
    rect rgba(100, 150, 200, 0.2)
    Note over Client,Auth Middleware: API Key Authentication Flow
    Client->>Backend: POST /token<br/>{client_id, api_key}
    Backend->>Auth Middleware: verify_api_key(x_api_key)
    Auth Middleware-->>Backend: ✓ Valid
    Backend->>Auth Middleware: create_access_token(data)
    Auth Middleware-->>Backend: JWT Token
    Backend-->>Client: {access_token, token_type, expires_in}
    end
    
    rect rgba(150, 150, 100, 0.2)
    Note over Client,Auth Middleware: JWT Authentication Flow
    Client->>Backend: GET /protected<br/>Authorization: Bearer {token}
    Backend->>Auth Middleware: get_current_user(credentials)
    Auth Middleware->>Auth Middleware: verify_token(token)
    Auth Middleware-->>Backend: User Payload
    Backend-->>Client: Protected Resource
    end
    
    rect rgba(100, 150, 100, 0.2)
    Note over Client,Auth Middleware: Status Check Flow
    Client->>Backend: GET /status<br/>X-API-Key: {key}
    Backend->>Auth Middleware: verify_api_key(x_api_key)
    Auth Middleware-->>Backend: ✓ Valid
    Backend-->>Client: {authenticated: true, auth_method: api_key}
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

The changes introduce a complete authentication system spanning middleware, routes, configuration, and integration. While mostly new code with clear separation of concerns, the JWT/API key logic requires verification of token creation/verification correctness, CORS integration validation, and proper error handling. Test coverage mitigates risk. Scope spreads across 8 files with moderate heterogeneity.

Possibly related PRs

Suggested labels

GSoC 2025

Poem

🐰 A token takes flight, API keys alight,
Security headers guard through the night,
Rate limits tamed, CORS now refined,
Auth flows aligned, with environment in mind! 🔐✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The pull request title "feat: add authentication system and rate limiting for enhanced security" accurately reflects the primary changes in the changeset. The raw summary confirms that the PR implements two major features: a comprehensive authentication system (API key and JWT token authentication with new middleware and routes) and rate limiting via slowapi. The title also appropriately emphasizes the security purpose, which aligns with the additional security enhancements included such as security headers middleware, CORS configuration, and host validation. The title is concise, specific, and uses the conventional "feat:" prefix to communicate the type of change.
Docstring Coverage ✅ Passed Docstring coverage is 90.91% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Contributor

⚠️ No issue was linked in the PR description.
Please make sure to link an issue (e.g., 'Fixes #issue_number')

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (8)
backend/requirements.txt (1)

11-12: Configure a shared backend for rate limiting in production

slowapi’s default in-memory storage won’t enforce limits across multiple workers/instances. Use a shared backend (e.g., Redis via limits[redis]) and configure Limiter(storage_uri="redis://...") for consistent enforcement.

Based on learnings

sync-microservice/main.py (1)

9-11: Trim and sanitize ALLOWED_ORIGINS

Strip whitespace and ignore empty entries to prevent silent CORS mismatches when env has spaces.

Apply:

-ALLOWED_ORIGINS = os.getenv(
-    "ALLOWED_ORIGINS", "http://localhost:8000,http://localhost:1420,tauri://localhost"
-).split(",")
+ALLOWED_ORIGINS = [
+    o.strip()
+    for o in os.getenv(
+        "ALLOWED_ORIGINS",
+        "http://localhost:8000,http://localhost:1420,tauri://localhost",
+    ).split(",")
+    if o.strip()
+]
backend/app/config/settings.py (1)

14-17: Sanitize ALLOWED_ORIGINS

Trim and ignore empties to avoid CORS mismatches.

-ALLOWED_ORIGINS = os.getenv(
-    "ALLOWED_ORIGINS", "http://localhost:1420,tauri://localhost"
-).split(",")
+ALLOWED_ORIGINS = [
+    o.strip()
+    for o in os.getenv("ALLOWED_ORIGINS", "http://localhost:1420,tauri://localhost").split(",")
+    if o.strip()
+]
backend/tests/test_auth.py (1)

93-105: Make HSTS assertion environment-aware

If HSTS is later restricted to HTTPS/production (recommended), this test will fail locally. Consider asserting presence conditionally (e.g., only when APP_ENV=production or when scheme is https).

backend/main.py (1)

38-41: Plan for shared rate-limit storage in multi-worker deployments

Default in-memory limits won’t be shared across uvicorn/gunicorn workers or instances. Configure Limiter(storage_uri="redis://...") in production.

Based on learnings

backend/app/routes/auth.py (2)

11-13: Remove unused import or reuse centralized verifier

verify_api_key is imported but unused. Either remove the import or reuse centralized validation for consistency.

-from app.middleware.auth import create_access_token, verify_api_key
+from app.middleware.auth import create_access_token

59-66: Unify API key validation semantics

Consider reusing the centralized verify_api_key logic (adjusted for body vs header) to keep status codes/messages consistent across the app.

backend/app/middleware/auth.py (1)

19-38: Add iat claim for token hygiene

Include issued-at (iat) to aid debugging and validation windows; optionally add nbf if needed.

-    to_encode.update({"exp": expire})
+    to_encode.update({"exp": expire, "iat": datetime.utcnow()})
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 589d71f and 3a2f6dd.

📒 Files selected for processing (8)
  • backend/app/config/settings.py (2 hunks)
  • backend/app/middleware/__init__.py (1 hunks)
  • backend/app/middleware/auth.py (1 hunks)
  • backend/app/routes/auth.py (1 hunks)
  • backend/main.py (5 hunks)
  • backend/requirements.txt (1 hunks)
  • backend/tests/test_auth.py (1 hunks)
  • sync-microservice/main.py (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
backend/app/middleware/__init__.py (1)
backend/app/middleware/auth.py (5)
  • create_access_token (19-38)
  • verify_token (41-62)
  • verify_api_key (65-91)
  • get_current_user (124-155)
  • get_current_user_optional (94-121)
backend/tests/test_auth.py (2)
backend/app/middleware/auth.py (2)
  • create_access_token (19-38)
  • verify_token (41-62)
backend/tests/test_folders.py (1)
  • client (76-78)
backend/app/routes/auth.py (1)
backend/app/middleware/auth.py (2)
  • create_access_token (19-38)
  • verify_api_key (65-91)
backend/app/config/settings.py (1)
frontend/src/config/Backend.ts (1)
  • SYNC_MICROSERVICE_URL (2-2)
🔇 Additional comments (2)
backend/app/middleware/__init__.py (1)

5-19: Re-exports look good

Clear, minimal public surface; names align with auth.py.

sync-microservice/main.py (1)

24-28: No changes needed for CORS handling of Tauri origins
Starlette’s CORSMiddleware accepts custom-scheme origins via literal or regex matching, so including "tauri://localhost" in ALLOWED_ORIGINS suffices.

Comment on lines +8 to +12
SECRET_KEY = os.getenv("SECRET_KEY", "dev-secret-key-change-in-production")
API_KEY = os.getenv("API_KEY", "dev-api-key-change-in-production")
ALGORITHM = os.getenv("ALGORITHM", "HS256")
ACCESS_TOKEN_EXPIRE_MINUTES = int(os.getenv("ACCESS_TOKEN_EXPIRE_MINUTES", "30"))

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Avoid shipping dev secrets in production

SECRET_KEY/API_KEY have permissive dev defaults. Enforce env presence in prod (fail-fast or warn) to prevent weak secrets in deployments.

Example:

+APP_ENV = os.getenv("APP_ENV", "development").lower()
 SECRET_KEY = os.getenv("SECRET_KEY", "dev-secret-key-change-in-production")
 API_KEY = os.getenv("API_KEY", "dev-api-key-change-in-production")
@@
-ACCESS_TOKEN_EXPIRE_MINUTES = int(os.getenv("ACCESS_TOKEN_EXPIRE_MINUTES", "30"))
+ACCESS_TOKEN_EXPIRE_MINUTES = int(os.getenv("ACCESS_TOKEN_EXPIRE_MINUTES", "30"))
+if APP_ENV == "production" and (
+    SECRET_KEY == "dev-secret-key-change-in-production"
+    or API_KEY == "dev-api-key-change-in-production"
+):
+    raise RuntimeError("SECRET_KEY/API_KEY must be set in production")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
SECRET_KEY = os.getenv("SECRET_KEY", "dev-secret-key-change-in-production")
API_KEY = os.getenv("API_KEY", "dev-api-key-change-in-production")
ALGORITHM = os.getenv("ALGORITHM", "HS256")
ACCESS_TOKEN_EXPIRE_MINUTES = int(os.getenv("ACCESS_TOKEN_EXPIRE_MINUTES", "30"))
APP_ENV = os.getenv("APP_ENV", "development").lower()
SECRET_KEY = os.getenv("SECRET_KEY", "dev-secret-key-change-in-production")
API_KEY = os.getenv("API_KEY", "dev-api-key-change-in-production")
ALGORITHM = os.getenv("ALGORITHM", "HS256")
ACCESS_TOKEN_EXPIRE_MINUTES = int(os.getenv("ACCESS_TOKEN_EXPIRE_MINUTES", "30"))
if APP_ENV == "production" and (
SECRET_KEY == "dev-secret-key-change-in-production"
or API_KEY == "dev-api-key-change-in-production"
):
raise RuntimeError("SECRET_KEY/API_KEY must be set in production")
🤖 Prompt for AI Agents
In backend/app/config/settings.py around lines 8 to 12, SECRET_KEY and API_KEY
currently fall back to permissive dev defaults; remove or avoid silent defaults
and enforce environment presence in production by either reading them without a
default (os.environ["SECRET_KEY"], os.environ["API_KEY"]) or validating after
load and raising an error when running in production (e.g., when ENV/DEBUG
indicates prod). Implement a fail-fast behavior: if running in production and
either SECRET_KEY or API_KEY is missing or empty, raise a clear exception on
startup (or exit) with a message instructing to set the env var; for local/dev
runs keep acknowleged dev-only defaults or explicit debug branch that logs a
warning rather than silently using weak keys.

Comment on lines +132 to +141
# Add security headers middleware
@app.middleware("http")
async def add_security_headers(request: Request, call_next):
"""Add security headers to all responses."""
response = await call_next(request)
response.headers["X-Content-Type-Options"] = "nosniff"
response.headers["X-Frame-Options"] = "DENY"
response.headers["X-XSS-Protection"] = "1; mode=block"
response.headers["Strict-Transport-Security"] = "max-age=31536000; includeSubDomains"
return response
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Gate HSTS to HTTPS/production; drop obsolete X-XSS-Protection

  • Only set Strict-Transport-Security when request.url.scheme == "https" (and preferably behind a proxy) or APP_ENV=production.
  • X-XSS-Protection is deprecated; consider removing it.
 @app.middleware("http")
 async def add_security_headers(request: Request, call_next):
     """Add security headers to all responses."""
     response = await call_next(request)
     response.headers["X-Content-Type-Options"] = "nosniff"
     response.headers["X-Frame-Options"] = "DENY"
-    response.headers["X-XSS-Protection"] = "1; mode=block"
-    response.headers["Strict-Transport-Security"] = "max-age=31536000; includeSubDomains"
+    # Only set HSTS over HTTPS / in production
+    if request.url.scheme == "https" or os.getenv("APP_ENV", "development").lower() == "production":
+        response.headers["Strict-Transport-Security"] = "max-age=31536000; includeSubDomains"
     return response
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Add security headers middleware
@app.middleware("http")
async def add_security_headers(request: Request, call_next):
"""Add security headers to all responses."""
response = await call_next(request)
response.headers["X-Content-Type-Options"] = "nosniff"
response.headers["X-Frame-Options"] = "DENY"
response.headers["X-XSS-Protection"] = "1; mode=block"
response.headers["Strict-Transport-Security"] = "max-age=31536000; includeSubDomains"
return response
# Add security headers middleware
@app.middleware("http")
async def add_security_headers(request: Request, call_next):
"""Add security headers to all responses."""
response = await call_next(request)
response.headers["X-Content-Type-Options"] = "nosniff"
response.headers["X-Frame-Options"] = "DENY"
# Only set HSTS over HTTPS / in production
if request.url.scheme == "https" or os.getenv("APP_ENV", "development").lower() == "production":
response.headers["Strict-Transport-Security"] = "max-age=31536000; includeSubDomains"
return response
🤖 Prompt for AI Agents
In backend/main.py around lines 132 to 141, the middleware unconditionally sets
HSTS and the obsolete X-XSS-Protection header; update it to (1) remove
X-XSS-Protection entirely and (2) only add Strict-Transport-Security when the
request is actually over HTTPS or when running in production (e.g.,
os.getenv("APP_ENV") == "production"); implement the check using
request.url.scheme == "https" or an APP_ENV guard (and note that when behind a
proxy you must rely on your proxy config / trusted forwarded proto header), then
only set the HSTS header in that branch while leaving the other security headers
as-is.

@Hemil36
Copy link
Contributor

Hemil36 commented Oct 21, 2025

@shbhmexe Looks great — really nice work tightening up the security side

Quick question though — since the app mainly runs offline through Tauri (local backend + frontend), are things like API key/JWT auth and rate limiting still needed here?

Just trying to understand the reasoning — CORS and the security headers make total sense, but the auth and throttling bits feel more relevant if we ever go online or add multi-user access.

@rahulharpal1603
Copy link
Contributor

@shbhmexe
This app doesn't need authentication because for each user, the backend runs locally on their device, nobody else is going to use and access the data from this backend.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants