Metadata-Version: 2.4
Name: fastapi-client-cert-auth
Version: 0.1.0
Summary: FastAPI client certificate authentication dependencies for trusted proxy mTLS setups
Project-URL: Homepage, https://github.com/ivolnistov/fastapi-client-cert-auth
Project-URL: Documentation, https://github.com/ivolnistov/fastapi-client-cert-auth/blob/main/docs/README.md
Project-URL: Repository, https://github.com/ivolnistov/fastapi-client-cert-auth
Project-URL: Bug Tracker, https://github.com/ivolnistov/fastapi-client-cert-auth/issues
Project-URL: Changelog, https://github.com/ivolnistov/fastapi-client-cert-auth/blob/main/CHANGELOG.md
Author-email: Ilya Volnistov <i.volnistov@gaijin.team>
Maintainer-email: Ilya Volnistov <i.volnistov@gaijin.team>
License-File: LICENSE
Keywords: authentication,client-certificate,fastapi,mtls,x509
Classifier: Development Status :: 3 - Alpha
Classifier: Framework :: FastAPI
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
Classifier: Topic :: Security
Classifier: Topic :: Security :: Cryptography
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.11
Requires-Dist: cryptography>=42
Requires-Dist: fastapi
Requires-Dist: starlette-context>=0.3
Description-Content-Type: text/markdown

# FastAPI Client Cert Auth

FastAPI dependencies for authenticating service-to-service routes with client
certificate identity forwarded by a trusted TLS terminator.

This package does not perform the TLS handshake inside FastAPI. A reverse proxy,
load balancer, ingress controller, or ASGI server must verify the client
certificate first. The package then parses the forwarded certificate header,
matches it against your own certificate store, checks client IP allowlists, and
exposes the authenticated client in request context.

## Installation

```bash
pip install fastapi-client-cert-auth
```

## Basic Usage

```python
from fastapi import Depends, FastAPI
from fastapi_client_cert_auth import Certificate, get_mtls_dependency

app = FastAPI()


async def get_certificate() -> Certificate:
    return Certificate(
        cn='bundle-service',
        white_listed_ips='10.20.0.0/16,127.0.0.1/32',
    )


@app.get('/protected', dependencies=[Depends(get_mtls_dependency(get_certificate))])
async def protected() -> dict[str, str]:
    return {'status': 'ok'}
```

In a real application, `get_certificate()` should usually depend on
`get_certificate_from_header_dependency()` and look up the parsed certificate in
your database by common name, serial number, fingerprint, or another policy.

## Proxy Contract

The default certificate header is:

```text
X-MTLS-CERT
```

The value must contain a URL-encoded PEM certificate. With Nginx this is usually
forwarded from `$ssl_client_escaped_cert`.

Recommended Nginx shape:

```nginx
location /protected {
    proxy_pass http://127.0.0.1:8080;

    ssl_verify_client on;
    ssl_client_certificate /etc/nginx/client-ca.crt;

    proxy_set_header X-MTLS-CERT $ssl_client_escaped_cert;
    proxy_set_header X-MTLS-VERIFY $ssl_client_verify;
    proxy_set_header X-Real-IP $remote_addr;
}
```

Only trust these headers from infrastructure you control. Do not expose the
FastAPI application directly to the public network while accepting client
certificate identity from headers.

The client IP used for per-certificate allowlists is taken from `X-Real-IP`
first, falling back to the rightmost entry of `X-Forwarded-For`. Configure your
proxy to set `X-Real-IP` from the connection peer (as in the snippet above) so a
client cannot spoof its source address through a forged `X-Forwarded-For`
header. When `X-MTLS-VERIFY` is present it must carry a success value
(`SUCCESS`, `OK`, `1`, or `TRUE`, case-insensitive); a present-but-failed value
is rejected even without `require_verify_header=True`.

## Certificate Header Dependency

```python
import sqlalchemy as sa
from cryptography import x509
from cryptography.hazmat.primitives import hashes
from fastapi import Depends, HTTPException

from fastapi_client_cert_auth import (
    Certificate,
    get_certificate_from_header_dependency,
)

get_certificate_from_header = get_certificate_from_header_dependency(
    require_verify_header=True,
)


async def current_certificate(
    certificate: x509.Certificate = Depends(get_certificate_from_header),
) -> Certificate:
    cn = certificate.subject.get_attributes_for_oid(x509.oid.NameOID.COMMON_NAME)[0].value
    fingerprint = certificate.fingerprint(hashes.SHA256()).hex()

    # Replace this with your database lookup.
    if cn != 'bundle-service':
        raise HTTPException(403, detail='Access denied')

    return Certificate(cn=cn, finger=fingerprint, active=True)
```

## x509 Helpers

The package includes small x509 utilities for local development and internal CA
workflows:

```python
from datetime import UTC, datetime, timedelta

from cryptography import x509
from cryptography.x509.oid import NameOID
from fastapi_client_cert_auth import make_ca, make_client_cert

subject = x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, 'dev-ca')])
ca_key, ca_cert = make_ca(subject, datetime.now(UTC) + timedelta(days=365))
client_key, client_cert = make_client_cert(
    'bundle-service',
    (ca_key, ca_cert),
    datetime.now(UTC) + timedelta(days=90),
)
```

## Migration From `gjn-fastapi-mtls-auth`

The closest imports are:

```python
from fastapi_client_cert_auth import (
    Certificate,
    get_certificate_from_header_dependency,
    get_client_ip_dependency,
    get_mtls_dependency,
)
```

The old misspelled module name `dependancy` is available as a compatibility
shim, but new code should import from `fastapi_client_cert_auth.dependencies`.

