Skip to main content

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

pip install fastapi-client-cert-auth

Basic Usage

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:

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:

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

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:

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:

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.

Metadata

Release files for fastapi-client-cert-auth 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for fastapi-client-cert-auth 0.1.0
File Size Uploaded
fastapi_client_cert_auth-0.1.0.tar.gz 10.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for fastapi-client-cert-auth 0.1.0
File Interpreter ABI Platform
fastapi_client_cert_auth-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 22.5 kB

Release files / fastapi_client_cert_auth-0.1.0.tar.gz

Download URL fastapi_client_cert_auth-0.1.0.tar.gz
Size 10.9 kB
Tags Source
SHA-256 checksum
How to use checksums
b5b8934b0434da354d4c2eef3b9e20226496d8d8ae21e4d17f3b77ecee34de89
BLAKE2b-256 checksum
How to use checksums
ba3044fbe085345f41c3b9fe9654ed13ccbf237b01f9ad590d6cb2658cd0e407
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 3, 2026.

Transparency log

Release files / fastapi_client_cert_auth-0.1.0-py3-none-any.whl

Download URL fastapi_client_cert_auth-0.1.0-py3-none-any.whl
Size 11.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a8b85b1345cca0dd4189b15d27c612df89f9cd7009f0e9060baf5b2d6e1eff33
BLAKE2b-256 checksum
How to use checksums
4e4c09457ef118d9fea8b898b843b991138270ab41d3137a746a816104d44677
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 3, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page