Skip to content

Add Bearer token authentication for protected endpoints#8

Draft
Copilot wants to merge 6 commits intomainfrom
copilot/add-bearer-token-authentication
Draft

Add Bearer token authentication for protected endpoints#8
Copilot wants to merge 6 commits intomainfrom
copilot/add-bearer-token-authentication

Conversation

Copy link

Copilot AI commented Jan 4, 2026

Implements Bearer token authentication to protect upload endpoints while keeping health/metrics public.

Changes

Authentication System

  • Added verify_token() dependency with constant-time token comparison (secrets.compare_digest())
  • Validates Authorization: Bearer <token> header format (case-insensitive)
  • Returns 401 for invalid/missing tokens, 500 if API_TOKEN not configured
  • Logs authentication failures without exposing token values

Protected Endpoints (require token)

  • POST /upload - Water meter image upload
  • POST /notify_upload - Upload notification (new endpoint)

Public Endpoints (no authentication)

  • GET / - Root
  • GET /health - Health check
  • GET /metrics - System metrics (new endpoint)

Data Models

  • NotifyUploadRequest with Literal type validation for status field (uploaded, processing, completed, failed)
  • Field constraints for positive integer IDs

Database

  • Added get_metrics() method to centralize metrics retrieval

Configuration

  • Updated .env.example with API_TOKEN and generation instructions
  • Updated README with authentication documentation and usage examples

Usage

# Set API_TOKEN in .env
API_TOKEN=$(openssl rand -hex 32)

# Protected endpoint requires token
curl -X POST http://localhost:8002/upload \
  -H "Authorization: Bearer $API_TOKEN" \
  -F "waterMeterId=1" \
  -F "file=@image.jpg"

# Public endpoints work without token
curl http://localhost:8002/health
curl http://localhost:8002/metrics

Security

  • Constant-time comparison prevents timing attacks
  • Token stored in environment variable
  • Error messages avoid information disclosure
  • Authentication attempts logged for monitoring
Original prompt

Tačka 6: API Token Authentication

Implementirati Bearer token autentikaciju za zaštitu upload endpoint-a.


Zahtevi:

1. Dependency funkcija verify_token() u main.py

Kreirati async dependency funkciju koja:

  • ✅ Proverava Authorization header
  • ✅ Validira format: Bearer <token>
  • ✅ Poredi token sa API_TOKEN env varijablom
  • ✅ Vraća HTTP 401 ako token nije ispravan
  • ✅ Loguje failed authentication pokušaje

Implementacija:

from fastapi import Header, HTTPException
import os

async def verify_token(authorization: str = Header(None)):
    """
    Verify API token from Authorization header.
    
    Expected format: Bearer <token>
    Token is compared against API_TOKEN environment variable.
    
    Raises:
        HTTPException: 401 if token is missing, invalid format, or incorrect
    
    Returns:
        str: The validated token
    """
    if not authorization:
        logger.warning("Authentication failed: Authorization header missing")
        raise HTTPException(
            status_code=401,
            detail="Authorization header missing"
        )
    
    # Extract token from "Bearer <token>"
    parts = authorization.split()
    if len(parts) != 2 or parts[0].lower() != "bearer":
        logger.warning(f"Authentication failed: Invalid authorization header format")
        raise HTTPException(
            status_code=401,
            detail="Invalid authorization header format. Use: Bearer <token>"
        )
    
    token = parts[1]
    expected_token = os.getenv("API_TOKEN", "")
    
    if not expected_token:
        logger.error("API_TOKEN environment variable is not set")
        raise HTTPException(
            status_code=500,
            detail="Server configuration error"
        )
    
    if token != expected_token:
        logger.warning(f"Authentication failed: Invalid token provided")
        raise HTTPException(
            status_code=401,
            detail="Invalid API token"
        )
    
    logger.info("Authentication successful")
    return token

2. Primena autentikacije na endpoint-e

Zaštićeni endpoint-i (ZAHTEVAJU token):

POST /upload

@app.post("/upload")
async def upload_image(
    waterMeterId: int = Form(...),
    file: UploadFile = File(...),
    db: Database = Depends(get_database),
    token: str = Depends(verify_token)  # ← DODAJ
):
    # ... postojeći kod

POST /notify_upload

@app.post("/notify_upload")
async def notify_upload(
    request: NotifyUploadRequest,
    db: Database = Depends(get_database),
    token: str = Depends(verify_token)  # ← DODAJ
):
    # ... postojeći kod

Javni endpoint-i (BEZ autentikacije):

Ovi endpoint-i OSTAJU bez autentikacije:

  • GET / - Root endpoint
  • GET /health - Health check
  • GET /metrics - Metrics

3. .env.example template fajl

Kreirati .env.example fajl u root direktorijumu:

# Database Configuration
DB_HOST=localhost
DB_PORT=3306
DB_NAME=ecitko_db
DB_USER=ecitko_user
DB_PASSWORD=your_secure_password_here

# Upload Configuration
UPLOAD_DIR=/srv/ecitko/uploads

# API Authentication
# Generate a strong random token for production
# Example: openssl rand -hex 32
API_TOKEN=your_secure_api_token_here

4. Provera da je .env u .gitignore

Ako .gitignore ne postoji, kreiraj ga. Ako postoji, dodaj .env ako već nije:

# Environment variables
.env

# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
venv/
ENV/

# Uploads
uploads/
*.jpg
*.jpeg
*.png

# IDE
.vscode/
.idea/
*.swp
*.swo

5. Ažuriranje README.md

Dodaj sekciju o autentikaciji:

## Authentication

This API uses Bearer token authentication for protected endpoints.

### Setup

1. Set the `API_TOKEN` environment variable in `.env`:
   ```bash
   API_TOKEN=your_secure_token_here
  1. Generate a secure token (recommended for production):
    openssl rand -hex 32

Usage

Include the token in the Authorization header:

curl -X POST http://your-server:8002/upload \
  -H "Authorization: Bearer your_secure_token_here" \
  -F "waterMeterId=1" \
  -F "file=@image.jpg"

Protected Endpoints

The following endpoints require authentication:

  • POST /upload - Upload water meter image
  • POST /notify_upload - Notify upload completion

Public Endpoints

The following endpoints are public (no authentication required):

  • GET / - Root endpoint
  • GET /health - Health check
  • GET /metrics - System metrics

Response Codes

  • 401 Unauthorized - Missing, invalid, or incorrect token
  • 200 OK - Request successful with valid token

---

## **Testing**

Nakon implementacije, testiraj:

### **Test 1: Upload BEZ tokena (treba da odbije)**
```bash
curl -X POST http://195.88.87.86:8002/upload \
  -F "waterMeterId=1" \
  -F "file=@test.jpg"

# Expected: 
# HTTP 401
# {"detail": "Authorization header missing"}

**Te...

This pull request was created from Copilot chat.


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Copilot AI and others added 5 commits January 4, 2026 18:29
Co-authored-by: drdule <204492158+drdule@users.noreply.github.com>
…lidation, and refactor metrics

Co-authored-by: drdule <204492158+drdule@users.noreply.github.com>
…stant-time comparison

Co-authored-by: drdule <204492158+drdule@users.noreply.github.com>
…ning useful logging

Co-authored-by: drdule <204492158+drdule@users.noreply.github.com>
Co-authored-by: drdule <204492158+drdule@users.noreply.github.com>
Copilot AI changed the title [WIP] Implement Bearer token authentication for upload endpoint Add Bearer token authentication for protected endpoints Jan 4, 2026
Copilot AI requested a review from drdule January 4, 2026 18:43
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.

2 participants