diff --git a/README.md b/README.md index 2e00019..f5c1405 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ A FastAPI application that aggregates news from various sources using the Medias ## Requirements - Python 3.8+ -- Mediastack API key +- Mediastack API key (required for fetching news data) ## Setup @@ -34,7 +34,12 @@ A FastAPI application that aggregates news from various sources using the Medias ``` 3. Set up environment variables: ``` + # Required for fetching news from Mediastack API MEDIASTACK_API_KEY=your_api_key_here + + # Optional - For JWT authentication security + # If not set, a secure random key will be generated on startup + # but JWT tokens will be invalidated when the server restarts SECRET_KEY=your_secret_key_here ``` @@ -71,6 +76,14 @@ The API is documented with Swagger UI, available at `/docs` when the server is r - `app/services`: Business logic services - `migrations`: Database migrations +## Security Notes + +The application uses two distinct keys for different purposes: + +1. **Mediastack API Key**: Used to authenticate with the external Mediastack news API service. This key is required to fetch news data. + +2. **Secret Key**: Used internally for JWT token generation and verification in the authentication system. If not provided via the SECRET_KEY environment variable, a secure random key will be generated automatically on each startup. Note that this means all active JWT tokens will be invalidated when the server restarts. + ## Development To run linting checks: diff --git a/app/core/config.py b/app/core/config.py index c83f31b..66eda5e 100644 --- a/app/core/config.py +++ b/app/core/config.py @@ -1,9 +1,13 @@ from typing import List, Union +import logging +import secrets from pathlib import Path from pydantic import AnyHttpUrl, field_validator from pydantic_settings import BaseSettings, SettingsConfigDict +logger = logging.getLogger(__name__) + class Settings(BaseSettings): model_config = SettingsConfigDict(env_file=".env", case_sensitive=True) @@ -30,8 +34,22 @@ class Settings(BaseSettings): MEDIASTACK_BASE_URL: str = "http://api.mediastack.com/v1" # Security - SECRET_KEY: str = "your-secret-key-here" + SECRET_KEY: str = "" ACCESS_TOKEN_EXPIRE_MINUTES: int = 60 * 24 * 8 # 8 days + + @field_validator("SECRET_KEY", mode="before") + @classmethod + def generate_secret_key_if_needed(cls, v: str) -> str: + """Generate a secure secret key if none is provided.""" + if not v: + generated_key = secrets.token_hex(32) + logger.warning( + "No SECRET_KEY provided. Using a randomly generated key. " + "This is OK for development but not recommended for production. " + "The authentication tokens will be invalidated on server restart." + ) + return generated_key + return v # Database DATABASE_PATH: Path = Path("/projects/newsaggregationservice-ks0ts2/app/storage/db/db.sqlite")