Metadata-Version: 2.4
Name: fastapi-rfc3230-digest-header-middleware
Version: 1.0.3
Summary: Implements a middleware that protects FastAPI endpoints with RFC 3230 Digest headers.
Project-URL: Repository, https://github.com/Mari6814/fastapi-rfc3230-digest-header-middleware
Project-URL: Documentation, https://github.com/Mari6814/fastapi-rfc3230-digest-header-middleware
Author-email: Marian Plivelic <marianplivelic@gmail.com>
License: MIT License
        
        Copyright (c) 2025 Mari6814
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
License-File: LICENSE
Keywords: digest-header,fastapi,middleware,rfc3230
Classifier: Development Status :: 5 - Production/Stable
Classifier: Framework :: FastAPI
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
Requires-Python: >=3.10
Requires-Dist: fastapi>=0.121.3
Requires-Dist: rfc3230-digest-headers>=1.1.2
Description-Content-Type: text/markdown

[![Test](https://github.com/Mari6814/fastapi-rfc3230-digest-header-middleware/actions/workflows/ci.yml/badge.svg)](https://github.com/Mari6814/fastapi-rfc3230-digest-header-middleware/actions/workflows/ci.yml)
[![Coverage](https://github.com/Mari6814/fastapi-rfc3230-digest-header-middleware/raw/main/badges/coverage.svg)](https://github.com/Mari6814/fastapi-rfc3230-digest-header-middleware/raw/main/badges/coverage.svg)
[![Versions](https://github.com/Mari6814/fastapi-rfc3230-digest-header-middleware/raw/main/badges/python-versions.svg)](https://github.com/Mari6814/fastapi-rfc3230-digest-header-middleware/raw/main/badges/python-versions.svg)

# FastAPI RFC 3230 Digest Header Middleware

## Introduction

This package provides a FastAPI middleware that enforces [RFC 3230 Digest headers](https://datatracker.ietf.org/doc/html/rfc3230) for HTTP requests. It validates the `Digest` header against the request body, ensuring message integrity and allowing you to specify which digest algorithms are accepted.

## Installation

Install via pip:

```bash
pip install fastapi-rfc3230-digest-header-middleware
```

## Usage Example

Add the middleware to your FastAPI app:

```python
from fastapi import FastAPI, Request
from fastapi_rfc3230_digest_header_middleware import Middleware

app = FastAPI()
app.add_middleware(Middleware)

@app.post("/echo")
async def echo(request: Request):
    body = await request.body()
    return body
```

This will require all POST requests to `/echo` to include a valid `Digest`
header matching the request body.  If the client sends an invalid request, the
server will respond with a `422 Unprocessable Entity` error and include details
about the validation failure. The response will also include a `Want-Digest` header
indicating the accepted digest algorithms.

## Client side

### Sending requests with Digest Header
The client must compute the digest of the request body using one of the accepted
algorithms and include it in the `Digest` header. For example, to compute a
SHA-256 digest in Python you can use the `rfc3230-digest-headers` package:

```python
from rfc3230_digest_headers import create_digest

body = b"Hello, World!"
digest_header = create_digest(body)
headers = {"Digest": digest_header.header_value}
```

if you want to manually create the header, you can do it like this:

```python
import hashlib
import base64
body = b"Hello, World!"
sha256_digest = hashlib.sha256(body).digest()
digest_value = base64.b64encode(sha256_digest).decode('utf-8')
digest_header = f"SHA-256={digest_value}"
headers = {"Digest": digest_header}
```

### Client side handling of Want-Digest Header
The client should also be able to handle the `Want-Digest` header in case of a `422` response. The `rfc3230-digest-headers` package can help with parsing this header as well.

```python
from rfc3230_digest_headers import create_digest
want_digest_header = response.headers.get("Want-Digest", "")

# The `digest_value` will include the appropriate digests according to the server's Want-Digest header
digest_header = create_digest(body, want_digest_header)
```

## Configuration

You can customize which digest algorithms are allowed or provide a custom callback to extract the bytes to validate:

### Allow Only Specific Algorithms

```python
from fastapi import FastAPI, Request
from fastapi_rfc3230_digest_header_middleware import Middleware
from rfc3230_digest_headers import DigestHeaderAlgorithm

qvalues = {
    DigestHeaderAlgorithm.SHA256: None,  # allow SHA-256
    DigestHeaderAlgorithm.MD5: 0.0,      # explicitly reject MD5
}

app = FastAPI()
app.add_middleware(Middleware, qvalues=qvalues)

@app.post("/echo")
async def echo(request: Request):
    body = await request.body()
    return body
```

### Custom Instance Bytes Callback
The `instance` are the bytes the server and client agreed on to include in the
digest. By default, this is the entire request body, but `instance` of a request
may not always be the request body. You can provide a custom callback to extract
the bytes to validate:

```python
async def get_instance_bytes(request: Request) -> bytes:
    # Default instance bytes logic
    return await request.body()

app = FastAPI()
app.add_middleware(Middleware, instance_bytes_callback=get_instance_bytes)
```

# License
MIT License