Skip to content

Implement API token authentication for upload endpoints#7

Draft
Copilot wants to merge 4 commits intomainfrom
copilot/add-api-token-authentication
Draft

Implement API token authentication for upload endpoints#7
Copilot wants to merge 4 commits intomainfrom
copilot/add-api-token-authentication

Conversation

Copy link

Copilot AI commented Jan 4, 2026

Adds Bearer token authentication to protect image upload endpoints while keeping monitoring endpoints public.

Changes

Authentication

  • Added verify_token() dependency function validating Bearer tokens against API_TOKEN environment variable
  • Uses hmac.compare_digest() for constant-time comparison (timing attack protection)
  • Returns 401 for missing/invalid tokens, 500 if token not configured
  • Logs invalid authentication attempts

Protected Endpoints (require Bearer token)

  • POST /upload - existing upload endpoint
  • POST /notify_upload - new endpoint verifying image exists in database and on disk

Public Endpoints (no authentication)

  • GET / - root
  • GET /health - health check
  • GET /metrics - new endpoint returning system statistics (total images, active meters, images per meter)

Configuration

  • Added API_TOKEN to .env.example
  • Updated dependencies to patched versions (fastapi 0.109.1, python-multipart 0.0.18, mysql-connector-python 9.1.0)

Documentation

  • Added authentication setup and usage examples to README.md

Example

# In main.py
async def verify_token(authorization: Optional[str] = Header(None)):
    if not authorization:
        raise HTTPException(status_code=401, detail="Authorization header missing")
    
    parts = authorization.split()
    if len(parts) != 2 or parts[0].lower() != "bearer":
        raise HTTPException(status_code=401, detail="Invalid authorization header format")
    
    token = parts[1]
    expected_token = os.getenv("API_TOKEN", "")
    
    if not hmac.compare_digest(token, expected_token):
        raise HTTPException(status_code=401, detail="Invalid API token")
    
    return token

@app.post("/upload")
async def upload_image(
    waterMeterId: int = Form(...),
    file: UploadFile = File(...),
    db: Database = Depends(get_database),
    token: str = Depends(verify_token)  # Authentication dependency
):
    # ... existing upload logic

Usage:

curl -X POST http://server:8002/upload \
  -H "Authorization: Bearer ecitko_global_2026" \
  -F "waterMeterId=1" \
  -F "file=@meter.jpg"
Original prompt

Tačka 6: API Token Autentikacija

Implementirati API token autentikaciju za zaštitu endpoint-a.


Zahtevi:

1. Environment varijabla za API token

Dodaj u .env fajl (ili kreiraj ako ne postoji):

API_TOKEN=ecitko_global_2026

Napomena: .env fajl mora biti u .gitignore (ne sme ići na GitHub)


2. Dependency funkcija verify_token() u main.py

from fastapi import Header, HTTPException
from typing import Optional

async def verify_token(authorization: Optional[str] = Header(None)):
    """
    Verify API token from Authorization header.
    
    Expected format: Authorization: Bearer <token>
    
    Args:
        authorization: Authorization header value
        
    Returns:
        str: The validated token
        
    Raises:
        HTTPException: 401 if token is missing or invalid
    """
    if not authorization:
        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":
        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 not configured in environment variables")
        raise HTTPException(
            status_code=500,
            detail="Server authentication not configured"
        )
    
    if token != expected_token:
        logger.warning(f"Invalid token attempt: {token[:10]}...")
        raise HTTPException(
            status_code=401,
            detail="Invalid API token"
        )
    
    return token

3. Primena autentikacije na endpoint-e

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

  • POST /upload
  • POST /notify_upload

Javni endpoint-i (NE zahtevaju token):

  • GET / (root)
  • GET /health (health check)
  • GET /metrics (metrics)

Primer za /upload endpoint:

@app.post("/upload")
async def upload_image(
    waterMeterId: int = Form(...),
    file: UploadFile = File(...),
    db: Database = Depends(get_database),
    token: str = Depends(verify_token)  # ← DODAJ OVO
):
    """
    Upload an image for a water meter.
    
    Requires authentication via Bearer token in Authorization header.
    
    Args:
        waterMeterId: The ID of the water meter
        file: The image file to upload (JPEG, JPG, or PNG)
        db: Database dependency
        token: Validated API token (from Authorization header)
    
    Returns:
        JSON response with message, image_id, and image_url
    """
    # Ostatak koda ostaje ISTI
    # ...

Primer za /notify_upload endpoint:

@app.post("/notify_upload")
async def notify_upload(
    request: NotifyUploadRequest,
    db: Database = Depends(get_database),
    token: str = Depends(verify_token)  # ← DODAJ OVO
):
    """
    Notify upload endpoint - potvrda da je slika upload-ovana.
    
    Requires authentication via Bearer token in Authorization header.
    
    Args:
        request: Notification request data
        db: Database dependency
        token: Validated API token (from Authorization header)
    
    Returns:
        JSON response with verification status
    """
    # Ostatak koda ostaje ISTI
    # ...

4. Ažuriraj imports u main.py

Proveri da imaš:

from fastapi import FastAPI, File, UploadFile, Form, Depends, HTTPException, Header
from typing import Optional

5. Dodaj .env.example fajl

Kreiraj fajl .env.example u root direktorijumu:

# Database configuration
DB_HOST=localhost
DB_PORT=3306
DB_USER=ecitko_user
DB_PASSWORD=your_password_here
DB_NAME=ecitko_db

# Upload configuration
UPLOAD_DIR=/srv/ecitko/uploads

# API Authentication
API_TOKEN=your_secret_token_here

Napomena: Ovo je template fajl (IDE na GitHub), stvarni .env ostaje lokalno.


6. Proveri .gitignore

Proveri da .gitignore sadrži:

.env
*.pyc
__pycache__/
venv/
uploads/
*.log

Testiranje:

Test 1: Upload BEZ tokena (treba da ODBIJE):

curl -X POST http://195.88.87.86:8002/upload \
  -F "waterMeterId=1" \
  -F "file=@test.jpg"

# Očekivani response (401):
{
  "detail": "Authorization header missing"
}

Test 2: Upload SA pogrešnim tokenom (treba da ODBIJE):

curl -X POST http://195.88.87.86:8002/upload \
  -H "Authorization: Bearer pogresni_token" \
  -F "waterMeterId=1" \
  -F "file=@test.jpg"

# Očekivani response (401):
{
  "detail": "Invalid API token"
}

Test 3: Upload SA ispravnim tokenom (treba da PROĐE):

curl -X POST http://195.88.87.86:8002/upload \
  -H "Authorization: Bearer ecitko_global_2026" \
  -F "waterMeterId=1" \
  -F "file=@test....

</details>



<!-- START COPILOT CODING AGENT SUFFIX -->

*This pull request was created from Copilot chat.*
>

<!-- START COPILOT CODING AGENT TIPS -->
---

✨ Let Copilot coding agent [set things up for you](https://github.com/drdule/ecitko/issues/new?title=✨+Set+up+Copilot+instructions&body=Configure%20instructions%20for%20this%20repository%20as%20documented%20in%20%5BBest%20practices%20for%20Copilot%20coding%20agent%20in%20your%20repository%5D%28https://gh.io/copilot-coding-agent-tips%29%2E%0A%0A%3COnboard%20this%20repo%3E&assignees=copilot) — coding agent works faster and does higher quality work when set up for your repo.

Copilot AI and others added 3 commits January 4, 2026 18:21
Co-authored-by: drdule <204492158+drdule@users.noreply.github.com>
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 API token authentication for endpoint protection Implement API token authentication for upload endpoints Jan 4, 2026
Copilot AI requested a review from drdule January 4, 2026 18:30
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