apikey-gateway 1.1.3
A Python library for API key validation
Requires Python: >=3.8

# API Key Gateway

A Python library that provides an `@apikey_login` decorator to validate API keys with service-aware authentication.

## Features

- **CLI Decorator**: Automatically adds `--apikey/-a` CLI parameter to decorated functions
- **FastAPI Middleware**: Built-in support for FastAPI applications
- **Strong Security**: Validates API keys using argon2id hashing algorithm
- **Service-Aware**: Keys are scoped to specific services
- **Remote Key Management**: Fetches valid public keys from a remote JSON endpoint
- **Caching**: Caches valid API key-service pairs for improved performance
- **Retry Mechanism**: Automatically retries fetching keys on network failures
- **Flexible Configuration**: Supports custom API key URLs and authentication headers

## Installation

```bash
uv install apikey-gateway
```

## Usage

The library supports two usage modes: **CLI Decorator** and **FastAPI Middleware**.

### CLI Decorator
The `apikey_login` decorator automatically adds `--apikey/-a` CLI parameter to decorated functions.

```python
from apikey_gateway import apikey_login

@apikey_login(service="media-match")
def media_app():
    print("API key validated for media-match service!")
    # Your media application logic here

@apikey_login(service="analytics")
def analytics_app():
    print("API key validated for analytics service!")
    # Your analytics application logic here

if __name__ == "__main__":
    media_app()  # or analytics_app()
```

Run with:
```bash
python app.py --apikey your-secret-key
```

### FastAPI Middleware
The library provides built-in FastAPI middleware for validating API keys on all requests.

```python
from fastapi import FastAPI
from apikey_gateway import add_apikey_gateway_middleware

# Create your FastAPI app
app = FastAPI(title="My API")

# Add API key validation middleware
add_apikey_gateway_middleware(
    app=app,
    service="my-service",
    verbose=True
)

# All endpoints below will require API key validation
@app.get("/protected/resource")
def protected_resource():
    return {"message": "Access granted to protected resource"}

# To run: uvicorn app:app --reload
```

Test the FastAPI endpoint:
```bash
# With X-AKGATEWAY-API-KEY header (default)
curl -H "X-AKGATEWAY-API-KEY: your-secret-key" http://localhost:8000/protected/resource

# With Authorization Bearer header
curl -H "Authorization: Bearer your-secret-key" http://localhost:8000/protected/resource
```

Middleware Features:
- Automatically checks `X-AKGATEWAY-API-KEY` and `Authorization Bearer` headers
- Supports custom API key headers
- Can be configured with a custom public keys URL
- Supports `X-JSONBIN-API-KEY` header for JSONBin API key authentication
- Allows customization of both API key header and JSONBin API key header names

## How It Works

### For CLI Applications
1. The application specifies the `service` name when using the `@apikey_login` decorator
2. The user provides a secret API key via the `--apikey/-a` CLI parameter
3. Followed by the same validation steps as FastAPI applications...

### For FastAPI Applications
1. The application adds the middleware with a specific `service` name
2. The client provides a secret API key via `X-AKGATEWAY-API-KEY` or `Authorization Bearer` header
3. Followed by the same validation steps as CLI applications...

### Common Validation Steps
3. The library computes an argon2id hash (public key) from the secret key
4. It fetches the list of valid public keys from `https://api.jsonbin.io/v3/b/691ec6a543b1c97be9b8ea6d`
5. Valid keys are filtered to only those belonging to the specified service
6. If the computed public key matches any valid service-specific public key, access is granted

## JSON Format

The remote JSON follows a service-aware structure where keys are organized by service name:

### Service-Aware Structure
```json
{
  "service1": {
    "key_id_1": "argon2id_hash_here",
    "key_id_2": "another_hash_here"
  },
  "service2": "single_key_hash_here"
}
```

- Top-level keys are service names
- Each service can have either multiple keys (as a dictionary) or a single key (as a string)
- The library automatically handles both formats when fetching keys

## License

MIT